Вот прочитал статью MS-rem'a. Хочу сделать фильтр ZwQueryDirectoryFile. Замена нужна для однопоточного приложения. Мне не понятны пара моментов. Я раньше никогда ничего не писал на АСме(поэтому и пишу здесь), только на Делфе(rulezz). Опыт у меня достаточный, уже думаю пора взяться за асм'шку, тем более что подвернулась такая задача. Первое мое недопонимание относится к реализации метода. В статье он перед установкой хука останавливает все потоки, но когда вызывается реальная функция этого не делается, видимо решил сэкономить время. Но в тоже время, он восстанавливает начало функции при помощи Апи WriteMemory, на чем теряет дохера процессорного времени. Я в своей реализации хочу писать напрямую, для этого меняю защиту памяти VirtualProtect'ом. Второе... на Делфе я это реализовал, вполне сносно, работает. Но смысла писать в Делфи на асме я не вижу. В общем компилятор у меня MASM32. Я уже порядком замаялся, код не особо хочет переноситься, с BASM'а на MASM. Да и дебажить код при помощи Olly уже совсем не могу. Короче вот мой код. Моя проблема в том, что мало практики работы в асме и мне нужна подсказка, в правильном направлении я иду или нет. Да, и еще я решил сэкономить время на том, что использую предыдущее состояние стека и здесь мне пришлось изрядно помучаться, я потратил на это около 5 часов. Ms-Rem использовал структуру для инжекта (far_jmp в статье). Я изрядно намучался заполнять память при помощи асм'а ( по большей части из-за того, что где-то услышал, будто бы процессоры не любят не выровненную память, а прогеры обязаны угождать процессору), поэтому я прошу Масм, чтобы он сам создал для меня нужную структуру, вариант мне очень нравиться. Код (Text): .const sLibName db 'ntdll.dll',0 sProcName db 'ZwQueryDirectoryFile',0 .data? QDFile dd ? ;Здесь адрес заменяемой функции LastProtectMode dd ? ;Защита памяти OldCode dw ?,?,?,? ;Старое начало функции RetPos dd ? ;Временная переменная .code MyFunc proc push EDX ; Запоминаем с чем пришли ;========================================= mov EAX, DWORD PTR OldCode ; Восстанавливаем mov DX, OldCode+4 ; начальные байты mov [QDFile],EAX ; исходной mov WORD PTR [QDFile+4],DX ; функции ;========================================= pop EDX ; Я должен остаться незамеченным pop RetPos ;========================================= call QDFile ;Вызываем оригинальную функцию ;========================================= ;Здесь я напишу фильтр, но это уже потом push RetPos push EAX push EDX ;========================================= mov EAX, DWORD PTR [@ICode] ; Ставим mov DX, WORD PTR [@ICode+4] ; обработчик mov [[QDFile]],EAX mov WORD PTR [QDFile]+4,DX ;========================================= pop EDX ; Меня и вовсе тут и не было.. pop EAX ; mov EAX,0C000000Fh ;Код не работает, иначе бы файлов на компе не было бы ret MyFunc endp ;Здесь новое начало функции с дальним джампом на мою функцию ;В асме я ничего не смыслю, поэтому использовал вариант Ms-Rem'а @ICode: push LROFFSET MyFunc ret SetHook proc invoke GetModuleHandle, OFFSET sLibName invoke GetProcAddress,EAX, OFFSET sProcName mov QDFile, EAX invoke VirtualProtect,EAX, 6, PAGE_EXECUTE_READWRITE, OFFSET LastProtectMode ;Как показал OllyDbg данные нормально создаются, ;но не передаются куда надо, следовательно ошибка тута ;сохраняем старое начало функции mov EAX, [QDFile] mov DX, WORD PTR [QDFile+4] mov DWORD PTR OldCode, EAX mov OldCode+4, DX ;записываем новое начало mov EAX, DWORD PTR [@ICode] mov DX, WORD PTR [@ICode+4] mov [QDFile], EAX mov WORD PTR [QDFile+4], DX ret SetHook endp UnSetHook proc mov EAX, DWORD PTR OldCode mov DX, WORD PTR OldCode+4 mov [QDFile], EAX mov WORD PTR [QDFile+4],DX; invoke VirtualProtect,QDFile,6,LastProtectMode,OFFSET RetPos ret UnSetHook endp Помогите кто может, направьте мой ум на путь истинный, читать я люблю, но общение более эффективно, в данном случае только чей-то огромный жизненный опыт сможет мне помочь, подсказать, где спрятана ошибка, и почему данные не передаются куда нужно. Любые подсказки насчет оптимизации жду.
пишется splicing от слова splice. кошмар! в буффер копировать старые байты никак? ща найду код.. я писал давненько.. пгди
Код (Text): #include <windows.h> #include <stdio.h> #include "ldasm.h" #define DPRINT(X) printf X #pragma warning(disable:4311 4312) char szF[]=" > NormalFunction( 0x%08x, 0x%08x )\n"; char blabla[] = "\xC3"; // Normal function VOID NormalFunction( IN ULONG Argument1, IN ULONG Argument2 ) { printf(szF, Argument1, Argument2); } // Hook buffer ULONG HookBuffer[32]; ULONG BufferWritten = 0; // Hook VOID HookFunction( IN ULONG Argument1, IN ULONG Argument2 ) { DPRINT((" > HookFunction( 0x%08x, 0x%08x )\n", Argument1, Argument2)); __asm { leave lea eax, HookBuffer jmp eax } } // // Probe address for reading DWORD // void ProbeForReadUlong( PVOID Address ) { if( IsBadReadPtr( Address, 4 ) ) RaiseException( STATUS_ACCESS_VIOLATION, 0, 0, 0 ); } // // Probe region for writing // void ProbeForWriteRegion( PVOID Address, ULONG Length ) { if( IsBadWritePtr( Address, Length) ) RaiseException( STATUS_ACCESS_VIOLATION, 0, 0, 0 ); } #define EmitJumpCommand( From, To ) \ { \ *(BYTE*)(From) = 0xE9; \ *(ULONG*)((ULONG)(From)+1) = (ULONG)(To) - (ULONG)(From) - 5; \ } BOOLEAN SpliceFunctionStart( IN PVOID OriginalAddress, IN PVOID HookFunction, OUT PVOID SplicingBuffer, IN ULONG MaxLength, OUT PULONG BytesWritten ) { ULONG Len; ULONG Ptr, NextAddress; BOOLEAN Status = FALSE; DPRINT(("Entering SpliceFunctionStart( 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x )\n", (ULONG)OriginalAddress, (ULONG)HookFunction, (ULONG)SplicingBuffer, sizeof(MaxLength), (ULONG)BytesWritten)); // // Probe arguments // DPRINT(("Probing arguments\n")); __try { ProbeForReadUlong( OriginalAddress ); ProbeForWriteRegion( SplicingBuffer, MaxLength ); ProbeForWriteRegion( BytesWritten, sizeof(ULONG) ); } __except( EXCEPTION_EXECUTE_HANDLER ) { DPRINT(("Arguments probing failed.\n")); return 0; } // // Copy integer number of instructions to the buffer // DPRINT(("Copying instructions\n")); *BytesWritten = 0; for( Ptr = (ULONG)OriginalAddress; Ptr < ((ULONG)OriginalAddress+5); Ptr+=Len ) { Len = size_of_code( (BYTE*)Ptr ); DPRINT(("Command decoded at address 0x%08x, length 0x%08x\n", Ptr, Len)); if( Ptr < ((ULONG)OriginalAddress+5) ) { if( (Ptr-(ULONG)OriginalAddress+5) >= MaxLength ) { DPRINT(("Error: buffer is too small\n")); Status = 0; goto _exit; } memcpy( (PVOID)((ULONG)SplicingBuffer+(Ptr-(ULONG)OriginalAddress)), (PVOID)Ptr, Len ); *BytesWritten += Len; } } NextAddress = Ptr; Ptr += (ULONG)SplicingBuffer -(ULONG)OriginalAddress; DPRINT(("*BytesWritten = 0x%08x, Ptr = 0x%08x, NextAddress = 0x%08x\n", *BytesWritten, Ptr, NextAddress)); DPRINT(("Generating splicing buffer\n")); // // Emit splicing jump to the buffer // DWORD Old; VirtualProtect( OriginalAddress, 5, PAGE_READWRITE, &Old ); EmitJumpCommand( OriginalAddress, HookFunction ); VirtualProtect( OriginalAddress, 5, Old, &Old ); DPRINT(("Original address bytes: %02x %02x %02x %02x %02x\n", ((PBYTE)OriginalAddress)[0], ((PBYTE)OriginalAddress)[1], ((PBYTE)OriginalAddress)[2], ((PBYTE)OriginalAddress)[3], ((PBYTE)OriginalAddress)[4] )); EmitJumpCommand( Ptr, NextAddress ); Status = 1; _exit: return Status; } int main(int argc, CHAR* argv[]) { BOOLEAN Status; DPRINT(("Normal call:\n")); NormalFunction( 0x12345678, 0xBAADFEED ); Status = SpliceFunctionStart( &NormalFunction, &HookFunction, HookBuffer, sizeof(HookBuffer), &BufferWritten ); if( !Status ) { DPRINT(("SpliceFunctionStart failed with status 0x%08x\n", Status)); return Status; } // __try {__asm int 3; // } __except( EXCEPTION_EXECUTE_HANDLER) { } DPRINT(("Spliced call:\n")); NormalFunction( 0x12345678, 0xBAADFEED ); return 0; } Копируются инструкции в буффер без изменения. По-хорошему, нужно их релокать на новый адрес.
1. ОШИБКА РАЗ Код (Text): pop RetPos ;<------ПОМЕНЯТЬ МЕСТАМИ ЭТО и НИЖНЮЮ СТРОЧКУ (push) ;========================================= call QDFile ;Вызываем оригинальную функцию ;========================================= ;Здесь я напишу фильтр, но это уже потом push RetPos 2. ОШИБКА ДВА Код (Text): mov [[QDFile]],EAX Двойного вложения в асме нет вы вероятно имели ввиду это: Код (Text): mov ecx, dword ptr [QDFile] mov dword ptr [ecx], eax 3. ОШИБКА ТРИ Код (Text): invoke GetModuleHandle, OFFSET sLibName invoke GetProcAddress,EAX, OFFSET sProcName mov QDFile, EAX invoke VirtualProtect,EAX, 6, PAGE_EXECUTE_READWRITE, OFFSET LastProtectMode ;Как показал OllyDbg данные нормально создаются, ;но не передаются куда надо, следовательно ошибка тута ;сохраняем старое начало функции mov EAX, [QDFile] mov DX, WORD PTR [QDFile+4] Та же ошибка. Двойного вложения в асме нет. Смотри Код (Text): mov QDFile, EAX сохраняешь АДРЕС функции в переменную а чтобы сохранить первые байты этой функции нужно так Код (Text): mov ecx, QDFile ;в есх загоняем адрес функции из переменной QDFile mov eax, dword ptr [ecx] ;считываем в еах - первые 4 байта mov DX, WORD PTR [ecx+4] ;считываем в дх - еще 2 байта mov DWORD PTR OldCode, EAX ;сохраняем mov OldCode+4, DX ;записываем новое начало mov EAX, DWORD PTR [@ICode] mov DX, WORD PTR [@ICode+4] mov [ecx], EAX mov WORD PTR [ecx+4], DX а ты записываешь не по адресу, куда указывает переменная, а в саму переменную Отсюда трабла Кстати, юзай вначале и вконце фунций pushad / popad - это сохраняет все регистры
Спасибо, Great, только такое же я мог написан и на Делфи, мне нужен именно асм. Magnum, а вот это действительно то, что я искал. Делфовый ассемблятор умеет двигать mov [QDFile], EAX а этот почему то не умеет. А скажите, почему он делает длинный джамп как push Code ret а не как-нибудь там jmp Code ?
Yashin И дельфийский и масмовский асм может "двигать" Двигать не умеешь ты В QDFile находится АДРЕС функции, а не тело функции. Чтобы достать первые байты из тела функции нужно: 1. Достать адрес функции (mov ecx, [QDFile] ) 2. Достать первые байты, которые лежат по заданному адресу (mov eax, dword ptr [ecx]) QDfile - ПЕРЕМЕННАЯ! где лежит АДРЕС функции. mov [QDFile], eax - ты перезаписываешь переменную! эммм.... Это вы где вычитали??? jmp Code тоже можно использовать В общем, рано вы в сплайс полезли разберитесь сперва с основами ассемблера
Мне кажется, чтоб инъекция была не столь заметной. Спасибо учту, тут пишут, что стоит Агнера почитать. Ладно не буду создавать очередное "Какие книжки почитать?" и скроюсь на недельку. Всем спасибо. Тему можете не закрывать, если что получится, сюда напишу о результатах.