Как то раз столкнулся с интересной проблемой при открытии исполняемого файла в WinDbg на отладку. Для опытного реверсера ситуация, скорее всего, достаточно тривиальная, тем не менее с ней из раза в раз сталкиваются те, кто впервые пытается отладить типовой исполняемый модуль (.exe) при помощи отладчика WinDbg. Заключается она в том, что совершенно непонятно как поставить точку останова на непосредственно в точку входа, то есть на первую инструкцию (основной функции) отлаживаемого исполняемого файла. Обычно для отладки приложение открывается через меню File - Open Executable... (или просто нажатием комбинации клавиш Ctrl+E). По умолчанию, после загрузки исполняемого файла, отладчик останавливается задолго до начала непосредственно кода самой программы, где-то глубоко в коде загрузчика образов Windows. Вот типичный пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
ntdll!LdrpDoDebuggerBreak: 779d05ad 6a0c push 0Ch 779d05af 68a0079877 push offset ntdll! ?? ::FNODOBFM::`string'+0x3ba (779807a0) 779d05b4 e86b22fbff call ntdll!_SEH_prolog4 (77982824) 779d05b9 33db xor ebx,ebx 779d05bb 53 push ebx 779d05bc 6a01 push 1 779d05be 8d45e7 lea eax,[ebp-19h] 779d05c1 50 push eax 779d05c2 6a11 push 11h 779d05c4 6afe push 0FFFFFFFEh 779d05c6 5e pop esi 779d05c7 56 push esi 779d05c8 e86356faff call ntdll!ZwQueryInformationThread (77975c30) 779d05cd 3bc3 cmp eax,ebx 779d05cf 7c1c jl ntdll!LdrpDoDebuggerBreak+0x40 (779d05ed) 779d05d1 385de7 cmp byte ptr [ebp-19h],bl 779d05d4 7517 jne ntdll!LdrpDoDebuggerBreak+0x40 (779d05ed) 779d05d6 895dfc mov dword ptr [ebp-4],ebx 779d05d9 cc int 3 779d05da 8975fc mov dword ptr [ebp-4],esi 779d05dd eb0e jmp ntdll!LdrpDoDebuggerBreak+0x40 (779d05ed) 779d05df 33c0 xor eax,eax 779d05e1 40 inc eax 779d05e2 c3 ret 779d05e3 8b65e8 mov esp,dword ptr [ebp-18h] 779d05e6 c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh 779d05ed e87722fbff call ntdll!_SEH_epilog4 (77982869) 779d05f2 c3 ret 779d05f3 90 nop 779d05f4 90 nop 779d05f5 90 nop 779d05f6 90 nop 779d05f7 90 nop |
Отладчик "встает" на инструкции int 3 (программная точка останова) в глубине функции LdrpDoDebuggerBreak
. Начиная выполнять пошаговую отладку (при помощи клавиш F10 или F11), в конечном итоге, проходим по всей логике загрузчика образов и управление передается на код программы. При таком подходе тратится драгоценное время, на ненужные, в большинстве случаев, действия. Зачем нам это всё нужно и как сразу сделать останов на точке входа приложения?
Метод 1: совсем простой
Если ваш отладчик WinDbg ведет себя подобным образом, нам потребуется выполнить серию дополнительных действий. Для того, что бы попасть непосредственно в точку входа программы, необходимо при срабатывании точки останова (в загрузчике образов) выполнить серию команд:
bp @$exentry
g
либо такую вот серию:
bu @$exentry
g
В итоге, после того, как мы продолжим выполнение при помощи команды g
, отладчик встанет на первой инструкции, размещающейся в точке входа исполняемого модуля.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
. . . 00404f15 ebc8 jmp image00400000+0x4edf (00404edf) 00404f17 8b7514 mov esi,dword ptr [ebp+14h] 00404f1a 8b7de4 mov edi,dword ptr [ebp-1Ch] 00404f1d 56 push esi 00404f1e e883e0ffff call image00400000+0x2fa6 (00402fa6) 00404f23 59 pop ecx 00404f24 c3 ret 00404f25 e8e45d0000 call image00400000+0xad0e (0040ad0e) 00404f2a e900000000 jmp image00400000+0x4f2f (00404f2f) 00404f2f 6a14 push 14h 00404f31 6860df4100 push offset image00400000+0x1df60 (0041df60) 00404f36 e8a50e0000 call image00400000+0x5de0 (00405de0) 00404f3b e8b2220000 call image00400000+0x71f2 (004071f2) 00404f40 0fb7f0 movzx esi,ax 00404f43 6a02 push 2 . . . |
Метод 2: чуть сложнее
Получаем содержимое структуры PEB (Блок окружения процесса, Process Environment Block) командой:
1 2 3 4 5 6 7 8 9 10 11 12 |
0:000> !peb PEB at 7ffd8000 InheritedAddressSpace: No ReadImageFileExecOptions: No BeingDebugged: Yes ImageBaseAddress: 00400000 Ldr 77a08880 Ldr.Initialized: Yes Ldr.InInitializationOrderModuleList: 00612068 . 00614030 Ldr.InLoadOrderModuleList: 00611fc8 . 00614220 Ldr.InMemoryOrderModuleList: 00611fd0 . 00614228 . . . |
В данном выводе нас интересует содержимое члена ImageBaseAddress, который определяет базовый адрес загрузки кодового сегмента образа в виртуальное пространство процесса.
Затем выводим дамп заголовка:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
0:000> !dh 00400000 File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (i386) 4 number of sections 57DDB6E1 time date stamp Sun Sep 18 00:34:25 2016 0 file pointer to symbol table 0 number of symbols E0 size of optional header 103 characteristics Relocations stripped Executable 32 bit word machine OPTIONAL HEADER VALUES 10B magic # 12.00 linker version 11800 size of code 26800 size of initialized data 0 size of uninitialized data 4F25 address of entry point 1000 base of code ----- new ----- 00400000 image base 1000 section alignment 200 file alignment 2 subsystem (Windows GUI) 5.01 operating system version 0.00 image version 5.01 subsystem version 3B000 size of image 400 size of headers 48853 checksum 00100000 size of stack reserve 00001000 size of stack commit 00100000 size of heap reserve 00001000 size of heap commit 8100 DLL characteristics NX compatible Terminal server aware 0 [ 0] address [size] of Export Directory 1E22C [ B4] address [size] of Import Directory 23000 [ 17220] address [size] of Resource Directory 0 [ 0] address [size] of Exception Directory 36400 [ 3EA8] address [size] of Security Directory 0 [ 0] address [size] of Base Relocation Directory 0 [ 0] address [size] of Debug Directory 0 [ 0] address [size] of Description Directory 0 [ 0] address [size] of Special Directory 0 [ 0] address [size] of Thread Storage Directory 1DD80 [ 40] address [size] of Load Configuration Directory 0 [ 0] address [size] of Bound Import Directory 13000 [ 270] address [size] of Import Address Table Directory 0 [ 0] address [size] of Delay Import Directory 0 [ 0] address [size] of COR20 Header Directory 0 [ 0] address [size] of Reserved Directory SECTION HEADER #1 .text name 11714 virtual size 1000 virtual address 11800 size of raw data 400 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 60000020 flags Code (no align specified) Execute Read SECTION HEADER #2 .rdata name BF96 virtual size 13000 virtual address C000 size of raw data 11C00 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 40000040 flags Initialized Data (no align specified) Read Only SECTION HEADER #3 .data name 3288 virtual size 1F000 virtual address 1400 size of raw data 1DC00 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers C0000040 flags Initialized Data (no align specified) Read Write SECTION HEADER #4 .rsrc name 17220 virtual size 23000 virtual address 17400 size of raw data 1F000 file pointer to raw data 0 file pointer to relocation table 0 file pointer to line numbers 0 number of relocations 0 number of line numbers 40000040 flags Initialized Data (no align specified) Read Only |
В данном выводе получаем адрес точки входа (address of entry point), который в нашем случае равен значению 4F25. Соответственно, актуальный стартовый адрес равен сумме ImageBaseAddress и полученного только что address of entry point. В нашем примере получается: 00400000 + 4F25. Проверим правильность вычислений, дизассемблируя блок по данному адресу:
1 2 3 4 5 6 7 8 9 10 11 |
0:000> u 00400000+4F25 *** ERROR: Module load completed but symbols could not be loaded for image00400000 image00400000+0x4f25: 00404f25 e8e45d0000 call image00400000+0xad0e (0040ad0e) 00404f2a e900000000 jmp image00400000+0x4f2f (00404f2f) 00404f2f 6a14 push 14h 00404f31 6860df4100 push offset image00400000+0x1df60 (0041df60) 00404f36 e8a50e0000 call image00400000+0x5de0 (00405de0) 00404f3b e8b2220000 call image00400000+0x71f2 (004071f2) 00404f40 0fb7f0 movzx esi,ax 00404f43 6a02 push 2 |
как мы видим, первая команда изучаемого исполняемого файла у нас соответствует той команде, которую мы видели при использовании метода №1, что подтверждает правильность вычислений. Теперь мы можем выставить точку останова на данный адрес командой:
bp 00400000+4F25
и продолжить выполнение программы:
g
Спасибо, но не всегда срабатывает. Делаю все по инструкции, а приложение не запускается. Черный экран командной строки и тишина