В прятки с отладчиком Крис Касперски ака мыщъх Хакер, номер #086, стр. 086-120-5 А вот с "лишним" байтом все значительно сложнее. Некоторые (впрочем, очень немногочисленные функции) состоят из одного jmp xxx, следом за которым расположен другой jmp. Естественно, это не сами функции, это просто линкер сформировал таблицу переходов, но нам-то от этого не легче! К тому же иногда встречаются функции короче пяти байт (GetCurrentProcess) и внедрить в них jmp (даже без mov) уже невозможно! ; API-функция GetCurrentProcess занимает всего 4 байта и внедрить ; в нее jump, не испортив начала следующей функции, уже невозможно ; (на самом деле сделать это можно, но сложно! в GetCurrrentProcess мы пишем ; push esp/push esp/push esp/push esp/push esp, а в GetModeleHandleA ; внедряем jump на sub_thunk, который анализирует,что находится на вершине ; стека: если там четыре esp, то был вызван GetCurrentProcress, в ; противном случае это GetModuleHandleA) .text:77E956D7 ; HANDLE GetCurrentProcess(void) .text:77E956D7 public GetCurrentProcess .text:77E956D7 GetCurrentProcess proc near ; CODE XREF: UnhandledExceptionF .text:77E956D7 83 C8 FF or eax, 0FFFFFFFFh .text:77E956DA C3 retn .text:77E956DA GetCurrentProcess endp .text:77E956DA .text:77E956DB ; Exported entry 315. GetModuleHandleA .text:77E956DB .text:77E956DB ; HMODULE __stdcall GetModuleHandleA(LPCSTR lpModuleName) .text:77E956DB public GetModuleHandleA .text:77E956DB GetModuleHandleA proc near ; CODE XREF: .text:77E815D6?p .text:77E956DB .text:77E956DB lpModuleName = dword ptr 8 .text:77E956DB .text:77E956DB 55 push ebp .text:77E956DC 8B EC mov ebp, esp Ладно, не будем высаживаться на измену. Все это заморочки. Ведь перехват работает вполне корректно, и простейший перехватчик выглядит так: [код, внедряющийся в начало API-функции и передающий управление на thunk] #define JUMP_SZ 0x6 // размер jump __declspec( naked ) jump() // "голая" функция без пролога и эпилога { __asm { mov eax, offset thunk ; заносим в eax смещение нашего thunk'а jmp eax ; передаем на него управление } } Единственная проблема — как определить его длину? Должны же мы знать, сколько байт копировать? Оператор sizeof возвращает размер указателя на функцию, но отнюдь не размер самой функции. Какие еще есть пути? Можно, например, определить размер jump'а вручную (в данном случае он равен 6 байтам) или расположить за его концом фиктивную процедуру fictonic(). Разница смещений fictonic() и jump() в общем случае и будет размером jump. Почему в общем случае? Да потому, что компилятор не подписывался всегда размещать функции в порядке их объявления (хотя чаще всего все происходит именно так). Но это еще что! Если откомпилировать программу с ключом /Zi (отладочная информация), то компилятор будет возвращать совсем не адрес функции jump(), а указатель на "переходник", расположенный совсем в другом месте! |