Распаковка Ntkrnlpacker. [Intro] Итак, в связи с болезнью появилось свободное время, которое я трачу на реверсинг и математику. Что мне удалось написать под температурой 38 читаем ниже. Пинаемся, кусаемся и критикуем как обычно в комментариях=). [Начнем] Вот, что пишут про данный прот И все в таком духе. В общем как обычно. Раньше об этом агрегате не слышала, стало интересно посмотреть, что за зверь. А зверь оказался интересный. Начнем с entry point запакованной программы Code: 00401061 C> 68 5D544100 PUSH anyprog.0041545D 00401066 E8 01000000 CALL anyprog.0040106C 0040106B C3 RETN 0040106C C3 RETN И, черт возьми, в этом было нечто знакомое ;-) Проблема возникла с отладкой. Оля до определенного момента дебажила, а потом прога вылетала. Причем что только я не перепробовала. Конечно, возникла идея приаттачится к процессу, но об этом чуть позже. Сначала расскажу о тех анти-дебаг приемах, которые используются (встретившиеся мне до того, как Оля уперлась рогами и отказалась работать). Ниже привожу два куска кода, которые с завидным постоянством и незначительной модификацией кочуют из сорца в сорец) Спаливание Оли через RDTSC Code: 00415F3D 0F31 RDTSC 00415F3F 33C9 XOR ECX,ECX 00415F41 03C8 ADD ECX,EAX 00415F43 0F31 RDTSC 00415F45 2BC1 SUB EAX,ECX 00415F47 3D FF0F0000 CMP EAX,0FFF 00415F4C 72 04 JB SHORT anyprog.00415F52 00415F4E 0F31 RDTSC 00415F50 50 PUSH EAX И обнуление др-регистров (через seh) Code: 00415FE6 55 PUSH EBP 00415FE7 8BEC MOV EBP,ESP 00415FE9 BB 00010000 MOV EBX,100 00415FEE F7D3 NOT EBX 00415FF0 8B45 10 MOV EAX,DWORD PTR SS:[EBP+10] 00415FF3 2198 C0000000 AND DWORD PTR DS:[EAX+C0],EBX 00415FF9 8BB8 C4000000 MOV EDI,DWORD PTR DS:[EAX+C4] 00415FFF C740 04 00000000 MOV DWORD PTR DS:[EAX+4],0 ; обнуляем др0 регистр 00416006 3E:FF37 PUSH DWORD PTR DS:[EDI] 00416009 64:8F05 00000000 POP DWORD PTR FS:[0] 00416010 C740 08 00000000 MOV DWORD PTR DS:[EAX+8],0 ; обнуляем др1 регистр 00416017 8380 C4000000 08 ADD DWORD PTR DS:[EAX+C4],8 0041601E 8BB8 A4000000 MOV EDI,DWORD PTR DS:[EAX+A4] 00416024 C740 0C 00000000 MOV DWORD PTR DS:[EAX+C],0 ; обнуляем др2 регистр 0041602B 89B8 B8000000 MOV DWORD PTR DS:[EAX+B8],EDI 00416031 8B5D 10 MOV EBX,DWORD PTR SS:[EBP+10] 00416034 33C0 XOR EAX,EAX 00416036 8943 10 MOV DWORD PTR DS:[EBX+10],EAX ; обнуляем др3 регистр 00416039 8943 14 MOV DWORD PTR DS:[EBX+14],EAX ; обнуляем др6 0041603C C743 18 55010000 MOV DWORD PTR DS:[EBX+18],155 ; пишем в др7 00416043 8B83 B0000000 MOV EAX,DWORD PTR DS:[EBX+B0] Потом проверка на бряки. Причем проверяет только первый байт функции. В общем не страшно. Code: 00415FA7 55 PUSH EBP 00415FA8 8BEC MOV EBP,ESP 00415FAA 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] 00415FAD 0340 3C ADD EAX,DWORD PTR DS:[EAX+3C] 00415FB0 05 80000000 ADD EAX,80 00415FB5 8B08 MOV ECX,DWORD PTR DS:[EAX] 00415FB7 034D 08 ADD ECX,DWORD PTR SS:[EBP+8] 00415FBA 83C1 10 ADD ECX,10 00415FBD 8B01 MOV EAX,DWORD PTR DS:[ECX] 00415FBF 0345 08 ADD EAX,DWORD PTR SS:[EBP+8] 00415FC2 8B18 MOV EBX,DWORD PTR DS:[EAX] 00415FC4 803B CC CMP BYTE PTR DS:[EBX],0CC ; проверочка 00415FC7 74 18 JE SHORT anyprog.00415FE1 00415FC9 8B55 0C MOV EDX,DWORD PTR SS:[EBP+C] 00415FCC 891A MOV DWORD PTR DS:[EDX],EBX 00415FCE 83C0 04 ADD EAX,4 00415FD1 8B18 MOV EBX,DWORD PTR DS:[EAX] 00415FD3 803B CC CMP BYTE PTR DS:[EBX],0CC ; проверочка 00415FD6 74 09 JE SHORT anyprog.00415FE1 00415FD8 8B55 10 MOV EDX,DWORD PTR SS:[EBP+10] 00415FDB 891A MOV DWORD PTR DS:[EDX],EBX 00415FDD 5D POP EBP Себя защита распаковывает долго и извратно. Там очень большой цикл (повторяющийся много раз), который здесь не приводится. А теперь вернемся к нашему процессу. Аттачимся. Оля ругнется на невалидный файл, так как присутствует антидамп, но о нем чуть позже. Теперь вопрос, как в такой ситуации найти oep? Ответ – посмотреть в стек. Кстати, как в самом протекторе, так и в нескольких тестовых запакованных программах с oep байт не крадется, что для нас большой плюс. Значит скролим окно стека вниз к самому началу и ищем первый адрес возврата, принадлежащий коду программы. Этот адрес будет находиться в непосредственной близости от oep. Будете ворчать, что способ нехороший. Знаю я, знаю) Но тем не менее в сработал успешно (несколько раз, а это уже статистика). Опять же. Дамп снимать еще пока нельзя. Так как у нас уже искажены переменные и с немалой долей вероятности программа будет работать неправильно, а некоторые (и много таких) программы вообще проверяют значение переменных. Так как в таком случае нам получить правильный дамп? Заинжектить длл, которая будет перехватывать какую-либо функцию, вызывающуюся непосредственно в oep-процедуре. Если,например, это делфи-программа, то идельно подходит GetModuleHandleA (в зависмости от компилятора это может быть __set_app_type или GetVersion). Причем нужно проверять адрес возврата, который мы знаем точно. И после этого (если все совпало), зацикливаем программу и дампим. В мое тестовом примере была прога на MingWin32 GCC 3.x. Оставим пока эту идею с dll и поговорим об импорте. Часть функций есть, а часть адресов нет. Смотрим (привожу кусок). Code: 00407120 00F00000 00407124 00F00084 00407128 00F00108 0040712C 00F0018C 00407130 00F00210 00407134 00F00294 00407138 00F00318 0040713C 00F0039C 00407140 00F00420 00407144 00000000 00407148 00000000 0040714C 77C0EEEB msvcrt.__getmainargs 00407150 77C0F1C5 msvcrt.__p__environ 00407154 77C0F1DB msvcrt.__p__fmode 00407158 77C2537C msvcrt.__set_app_type Что у нас по адресу 00F00000? Code: 00F00000 60 PUSHAD 00F00001 E8 00000000 CALL 00F00006 00F00006 5D POP EBP 00F00007 81ED 0F469400 SUB EBP,94460F 00F0000D FFB5 81469400 PUSH DWORD PTR SS:[EBP+944681] 00F00013 FFB5 85469400 PUSH DWORD PTR SS:[EBP+944685] 00F00019 FFB5 89469400 PUSH DWORD PTR SS:[EBP+944689] 00F0001F FFB5 79469400 PUSH DWORD PTR SS:[EBP+944679] 00F00025 FFB5 7D469400 PUSH DWORD PTR SS:[EBP+94467D] 00F0002B E8 08000000 CALL 00F00038 00F00030 894424 1C MOV DWORD PTR SS:[ESP+1C],EAX 00F00034 61 POPAD 00F00035 FFE0 JMP EAX 00F00037 C3 RETN Джамп еах передает управление оригинальной функции. Все переходники одинаковые. То же самое по адресу 00F0018C и всем остальным. Из этого всего делаем вывод. Запросто теперь напишем скрипт, восстанавливающий импорт. Он попросит ввести начало таблицы и ее конец. Принцип такой. Проходимся по всей таблице, берем невалидный адрес, устанавливаем eip на него ищем опкод jmp eax, ставим на него точку останова, выполняем… Кладем значение eax на место невалидного адреса. Code: var iat_begin var iat_end var element var iat_end_ var jaddr ask "Iat begin" mov iat_begin,$RESULT cmp iat_begin,0 je fin ask "Iat end" mov iat_end,$RESULT add iat_end,4 mov iat_end_,$RESULT add iat_end_,4 cmp iat_end,0 je fin mov element,iat_begin mov eip,[element] start: findop eip,#FFE0# ; jmp eax mov jaddr,$RESULT bphws jaddr,"x" ; хардвар на джамп еах run mov [element],eax add element,4 cmp element,iat_end_ je fin cmp [element],0 ; ноль? пропускаем je add_ cmp [element],50000000 ; адрес уже есть? пропускаем ja add_ mov eip,[element] bphwc jaddr jmp start fin: msg "Done" ret add_: add element,4 cmp element,iat_end_ je fin je fin cmp [element],0 je add_ cmp [element],50000000 ; адрес уже есть? пропускаем ja add_ mov eip,[element] bphwc jaddr jmp start Получаем нормальную таблицу. А теперь снова о dll. В моем случае oep Code: 00401240 55 PUSH EBP 00401241 89E5 MOV EBP,ESP 00401243 83EC 08 SUB ESP,8 00401246 C70424 02000000 MOV DWORD PTR SS:[ESP],2 0040124D FF15 58714000 CALL DWORD PTR DS:[407158] ; msvcrt.__set_app_type 00401253 E8 A8FEFFFF CALL anyprog.00401100 Значит интересует нас функция __set_app_type из msvcrt.dll. Ее и будем перехватывать. Далее код искомой dll. Ассемблер …. Я здесь ничего не оптимизировала и писала на скорую руку. Важно было быстро получить результат Code: .386 .model flat,stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib kernel32.lib includelib user32.lib .data lib db "msvcrt.dll",0 proc_ db "__set_app_type",0 buffersbytes db 6 dup (?) func_ dword ? .code unhook proc mov esi,offset buffersbytes mov edi,func_ mov ecx,6 rep movsb ret unhook endp hook_ proc mov edi,func_ mov byte ptr[edi],068h mov dword ptr [edi+1],offset hooked_ mov byte ptr[edi+5],0c3h ret hook_ endp loop_ proc local dwOldProtect:dword invoke VirtualProtectEx,-1,00401253h, 3, PAGE_EXECUTE_READWRITE,addr dwOldProtect mov edi,00401253h mov word ptr[edi],0feebh ; бесконечный цикл ret loop_ endp hooked_ proc par1:dword cmp dword ptr[esp+4],00401253h ; адрес возврата jnz ret_ call loop_ ret_: call unhook push par1 call func_ pop par1 call hook_ ret hooked_ endp DllEntry proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD local dwOldProtect:dword cmp reason,DLL_PROCESS_ATTACH jnz ret__ pushad invoke GetModuleHandleA,offset lib test eax,eax jnz gaddress_ invoke LoadLibrary,offset lib test eax,eax je ext gaddress_: invoke GetProcAddress,eax,offset proc_ test eax,eax je ext mov func_,eax invoke VirtualProtectEx,-1,func_, 6, PAGE_EXECUTE_READWRITE,addr dwOldProtect test eax,eax jz ext mov edi,func_ mov esi,offset buffersbytes mov dl,byte ptr[edi] mov byte ptr[esi],dl mov edx,dword ptr[edi+1] mov dword ptr[esi+1],edx mov dl,byte ptr[edi+5] mov byte ptr[esi+5],dl mov byte ptr[edi],068h mov dword ptr [edi+1],offset hooked_ mov byte ptr[edi+5],0c3h ext: popad jmp ret_ ret__: call unhook ret_: ret DllEntry Endp End DllEntry Вот. Ее и заинжектим. Дамп. Выбираем опцию LordPE: Active dump engine->IntelliDump->Select! И дампим. Импорт восстанавливаем с помощью Imprec и нашего скрипта… На этом этапе последнее- это исправление двух байт, использующихся для зацикливания по адресу 00401253h (адрес возврата из функции). EBFE на E8A8. Все. -----------------------Добавлено------------------------------- Мешало отладке то, что прот устанавливал на память аттрибут PAGE_EXECUTE_READ|PAGE_GUARD. В Phantom plugin надо включить опцию Custom handler exceptions, тогда все будет норм отлаживаться. Антиотладка Code: 00416957 6A 04 PUSH 4 00416959 68 00100000 PUSH 1000 0041695E 68 00100000 PUSH 1000 00416963 6A 00 PUSH 0 00416965 E8 78FFFFFF CALL CyberPro.004168E2 ; JMP to kernel32.VirtualAlloc 0041696A C600 C3 MOV BYTE PTR DS:[EAX],0C3 0041696D 8985 9F029D01 MOV DWORD PTR SS:[EBP+19D029F],EAX 00416973 8D85 9B029D01 LEA EAX,DWORD PTR SS:[EBP+19D029B] 00416979 50 PUSH EAX 0041697A 68 20010000 PUSH 120 0041697F 6A 10 PUSH 10 00416981 FFB5 9F029D01 PUSH DWORD PTR SS:[EBP+19D029F] 00416987 E8 50FFFFFF CALL CyberPro.004168DC ; JMP to kernel32.VirtualProtect 0041698C 8B85 9F029D01 MOV EAX,DWORD PTR SS:[EBP+19D029F] 00416992 FFD0 CALL EAX 00416994 0F31 RDTSC 00416996 50 PUSH EAX 00416997 C3 RETN (с) 0x0c0de 2007
хм не очень секу в асме -- но вродебы сам писал старался и не боян - в общем наконецто норм статья -- молодцом