Глава 2. Обзор отладчиков и дизассемблеров
В этой главе мы рассмотрим отладочные и дизассемблирующие программы, кроме трех наиболее известных, о которых пойдет речь в последующих двух главах.
I
Утилиты фирмы Microsoft
EDITBIN.EXE
Название программы многообещающе, но в действительности программу нельзя назвать редактором.
Основное ее предназначение - конвертировать OMF-формат объектных файлов в COFF-формат. Кроме того,
данная утилита позволяет менять некоторые другие атрибуты исполняемых и объектных модулей. Если в
командной строке данной программы указать имя объектного модуля, то, в случае если модуль будет в
OMF-формате, он будет преобразован в COFF-формат. Рассмотрим ключи данной программы, которые можно применять как к исполняемым, так и к объектным модулям.
/BIND - позволяет указать пути к динамическим библиотекам, которые используют данный
исполняемый модуль. Например,
EDITBIN /BIND:PATH=c:\edit;d:\dll EDIT.EXE.
/HEAP - изменяет размер кучи в байтах. Например,
EDITBIN /HEAP:100000,100000 (см. Опции программы LINK.EXE).
/LARGEADDRESSAWARE - указывает, что приложение оперирует адресами, большими 2 гигабайт.
/NOLOGO - подавляет вывод информации о программе.
/REBASE - устанавливает базовый адрес модуля. По умолчанию для исполняемого модуля базовый адрес равен 400000Н, для динамической библиотеки - 10000000H.
/RELEASE - устанавливает контрольную сумму в заголовке исполняемого модуля.
/SECTION - изменяет атрибуты секций исполняемого модуля. Полный формат опции
/SECTION:name[=newname][,attributes][,alignment]
Значение атрибутов
Атрибут | Значение |
---|---|
c | code |
d | discardable |
е | executable |
i | initialized data |
k | cached virtual memory |
m | link remove |
o | link info |
p | paged virtual memory |
r | read |
s | shared |
u | uninitialized data |
w | write |
Значение опции выравнивания
1 | 1 |
2 | 2 |
4 | 4 |
8 | 8 |
p | 16 |
t | 32 |
s | 64 |
x | no alignment |
/STACK - изменяет значение требуемого для загружаемого модуля стека.
Например: EDITBIN /STACK:10000,10000 EDIT.EXE
/SUBSYSTEM - переопределяет подсистему, в которой работает данная программа.
Например, если программа оттранслирована с опцией /SUBSYSTEM:WINDOWS, можно изменить
установку, не перекомпилируя ее. EDITBIN /SUBSYSTEM:CONSOLE EDIT.EXE.
/SWAPRUN - устанавливает для исполняемого модуля атрибут "помещать модуль в SWAP-файл".
/VERSION - устанавливает версию для исполняемого модуля.
/WS (/WS:AGGRESSIVE) - ycтaнaвливает атрибут AGGRESSIVE, который используется операционной системой Windows NT и Windows 2000.
Утилита весьма полезна для быстрого изменения атрибутов исполняемых и объектных модулей.
DUMPBIN.EXE
Программа используется для исследования загружаемых и объектных модулей COFF-формата и выводит информацию в текущую консоль. Ключи данной программы:
/ALL - выводит всю доступную информацию о модуле, кроме ассемблерного кода.
/ARCH - выводит содержимое секции .arch заголовка модуля.
/ARCHIVEMEMBERS - выводит минимальную информацию о элементах объектной библиотеки.
/DEPENDENTS - выводит имена динамических библиотек, откуда данным модулем импортируются функции.
/DIRECTIVES - выводит содержимое секции .drective, создаваемой компилятором (только для объектных модулей).
/DISASM - дизассемблирует содержимое секций модуля с использованием и символьной информации, если она присутствует там.
/EXPORTS - выдает все экспортируемые модулем имена.
/HEADER - выдает заголовки модуля и всех его секций. В случае объектной библиотеки выдает заголовки всех составляющих ее объектных модулей.
/IMPORTS - выдает все имена, импортируемые данным модулем.
/LINENUMBERS - выдает номера строк объектного модуля, если таковые имеются.
/LINKERMEMBER[:{1|2}] - выдает все имена в объектной библиотеке, определяемые как public.
/LINKERMEMBER:1 - в порядке следования объектных модулей в библиотеке.
/LINKERMEMBER:2 - вначале выдает смещение и индекс объектных модулей, а затем список имен в алфавитном порядке для каждого модуля.
/LINKERMEMBER - сочетание ключей 1 и 2.
/OUT - определяет, что вывод осуществляется не в консоль, а в файл (/OUT:ED.TXT).
/PDATA - выдает содержимое таблиц исключения.
/RAWDATA - выдает дамп каждой секции файла. Разновидности данного ключа: /RAWDATA:BYTE, /RAWDATA:SHORTS, /RAWDATA:LONGS, /RAWDATA:NONE, /RAWDATA:,number. Здесь number определяет ширину строк.
/SUMMARY - выдает минимальную информацию о секциях.
/SYMBOLS - выдает таблицу символов COFF-файла.
Рассматриваемая программа является весьма мощным средством дизассемблирования. Пусть программа
называется prog.asm. Выполним трансляцию программы следующим образом.
ml /с /coff /Zi /Zd prog.asm link /debug /subsystem:windows prog.obj53
При этом, кроме исполняемого модуля prog.exe, появляется еще и файл prog.pdb, содержащий отладочную информацию.
Выполним теперь команду DUMPBIN /DISASM /OUT:PROG.TXT PROG.EXE. В результате получим практически исходный ассемблерный код. Часть этого кода представлена на Рис. 4.2.1.
_START: 0040101С: 6A00 push 0 0040101E: E843020000 call _GetModuleHandleA@4 00401023: A344404000 mov [00404044],eax 00401028: C7051C404000 mov dword ptr ds:[40401Ch],4003h 03400000 00401032: C70520404000 mov dword ptr ds:[404020h],offset @ILT+0(_WNDPROC@0) 05104000 0040103C: C70524404000 mov dword ptr ds:[404024h],0 00000000 00401046: C70528404000 mov dword ptr ds:[404028h],0 00000000 00401050: A144404000 mov eax,[00404044] 00401055: A32C404000 mov [0040402C],eax 0040105A: 68007F0000 push 7F00h 0040105F: 6A00 push 0 00401061: E8D6010000 call _LoadIconA@8 00401066: A330404000 mov [00404030],eax 0040106B: 68037F0000 push 7F03h 00401070: 6A00 push 0 00401072: E8BF010000 call _LoadCursorA@8
Рис. 4.2.1. Часть дизассемблированиого кода.
Как видите, это весьма прозрачный текст, ничем не отличающийся от обычного ассемблерного текста. В конце файла можно обнаружить довольно интересную информацию. Вот фрагмент.
_LoadIconA@8: 0040123С: FF2510514000 jmp dword ptr [_imp_LoadIconA@8]
В действительности, когда мы вызываем, например, функцию _LoadIconA@8, то на самом деле происходит переход на адрес, где стоит команда
jmp dword ptr [_imp_LoadIconA@8].
По этой причине в своих программах вместо объявления LoadIcon@8, можно объявлять _imp_LoadIconA@8, что несколько увеличит производительность Вашей программы. Ранее мы не говорили о такой возможности по двум причинам:
- Повышение производительности здесь минимально.
- Транслятор TASM32 делает все несколько иначе.
Более подробно данный вопрос рассматривается в Главе 4.1.
II
Утилиты других производителей
DUMPPE.EXE
Данная программа рассматривалась нами в гл. 1.1. Она во многом похожа на предыдущую программу DUMPBIN.EXE, но более удобна, хотя и обладает несколько меньшими возможностями.
Данная программа широко известна в среде программистов, скажем так, хакерского направления. Название программы происходит от фразы "Hacker's View". Основная задача, которую выполняет данная программа - просматривать и редактировать загружаемые модули. Причем просмотр и редактирование допускается в трех вариантах: двоичный, текстовый и ассемблерный. Хороших дизассемблирующих программ создано довольно много, а вот программ, подобных данной, можно по пальцам перечесть.
Интерфейс программы весьма напоминает интерфейс редакторов таких программ, как FAR или Norton Commander (см. Рис. 4.2.2.). Все команды осуществляются при помощи функциональных клавиш с использованием клавиш "Alt" и "Ctrl". Например, нажимая клавишу F4, вы получаете возможность выбрать способ представления двоичного файла: текстовый, ассемблерный или двоичный. Нажимая клавишу F3 (при условии, если Вы находитесь в двоичном или ассемблерном просмотре), Вы получаете возможность редактировать файл. Если же, находясь в ассемблерном просмотре, Вы после F3 нажмете еще и F2, то сможете редактировать машинную команду в символьном виде. Мы не будем далее останавливаться на командах данной программы, поскольку они просты, очевидны и могут быть получены просто по F1, а перейдем сразу к простому примеру использования данной программы, хотя пример тематически и относится к материалу Гл. 4.6. Чтобы слишком не загромождать рассмотрение возьмем простую консольную программу.
Рис. 4.2.2. Внешний вид программы HIEW.EXE
Ниже (Рис. 4.2.3) представлена простая консольная программа, выводящая на экран текстовую строку.
.386P ; плоская модель .MODEL FLAT, stdcall ; константы STD_OUTPUT_HANDLE equ -11 INVALID_HANDLE_VALUE equ -1 ; прототипы внешних процедур EXTERN GetStdHandle@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN ExitProcess@4:NEAR ; директивы компоновщику для подключения библиотек includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------------------- ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' BUF DB "Строка для вывода",0 LENS DWORD ? ; количество выведенных символов HANDL DWORD ? _DATA ENDS ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить HANDLE вывода PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 CMP EAX,INVALID_HANDLE_VALUE JNE _EX MOV HANDL,EAX ; вывод строки PUSH 0 PUSH OFFSET LENS PUSH 17 PUSH OFFSET BUF PUSH HANDL CALL WriteConsoleA@20 _EX: PUSH 0 CALL ExitProcess@4 _TEXT ENDS END START
Рис. 4.2.3. Консольная программа.
Программа на Рис. 4.2.3 проста и корректна. Представьте теперь, что при отладке Вы случайно изменили одну команду: вместо JE поставили JNE. В результате поспе трансляции программа перестала работать. Можно исправить ее, не прибегая к ассемблерному тексту? Конечно. Для этого в начале ее следует дизассемблировать, найти ошибку, а потом воспользоваться программой HIEW.EXE. Вообще говоря, можно ограничиться только программой HIEW, так как она вполне корректно дизассемблирует. Однако мы нарочно проведем исправление в два этапа.
Дизассемблируем модуль при помощи программы DUMPBIN.EXE. Вот дизассемблированный текст программы (Рис. 4.2.4).
Dump of file cons1.exe File Type: EXECUTABLE IMAGE 00401000: 6A F5 push 0F5h 00401002: E8 2B 00 00 00 call 00401032 00401007: 83 F8 FF cmp eax,0FFFFFFFFh 0040100A: 75 1E jne 0040102A 0040100C: A3 16 30 40 00 mov [00403016],eax 00401011: 6A 00 push 0 00401013: 68 12 30 40 00 push 403012h 00401018: 6A 11 push 11h 0040101A: 68 00 30 40 00 push 403000h 0040101F: FF 35 16 30 40 00 push dword ptr ds:[00403016h] 00401025: E8 0E 00 00 00 call 00401038 0040102A: 6A 00 push 0 0040102C: E8 0D 00 00 00 call 0040103E 00401031: CC int 3 00401032: FF 25 08 20 40 00 jmp dword ptr ds:[00402008h] 00401038: FF 25 00 20 40 00 jmp dword ptr ds:[00402000h] 0040103E: FF 25 04 20 40 00 jmp dword ptr ds:[00402004h]
Рис. 4.2.4. Дизассемблированный код программы на Рис. 4.2.3.
По дизассемблированному коду легко обнаружить ошибку. Кстати, команду cmp eax,0FFh надо, естественно, понимать как cmp eax,0FFFFFFFFh. Запомним нужный код 83F8FF. Запускаем программу HIEW.EXE, нажимаем клавишу F7 и ищем нужное сочетание. Далее клавиша F3, затем F2 и далее - заменяем команду JNE на JE. Клавиша F9 закрепляет изменение. В результате мы исправили программу, не прибегая к ее перетрансляции.
DEWIN.EXE
Программа работает в командном режиме, но по сравнению, например, с DUMPBIN.EXE обладает рядом достоинств. Главное из этих достоинств - это распознавание языков высокого уровня. Кроме того, Вы сами можете писать скрипт-процедуры на предлагаемом макроязыке.
IDA PRO
Один из самых мощных дизассемблеров. Возможности настолько велики, что многие программисты считают его всемогущим. Работая над текстом дизассемблируемой программы. Вы можете называть своими именами метки и процедуры, давать свои комментарии так, что дизассемблированный текст становится в конце концов ясным и понятным. Все сохраняется в специальную базу и при последующем запуске, естественно, восстанавливаются. Внешний вид дизассемблера IDA PRO показан на Рис. 4.2.4. Мы дизассемблировали одну из наших старых программ. Обратите, кстати, внимание на ссылку offset WNDPROC. Название WNDPROC дано уже нами в процессе анализа кода программы. Но рассмотрим все по порядку.
Рис. 4.2.4. Пример дизассемблирования программы с помощью самого мощного дизассемблера IDA PRO (под Windows).
Рассмотрим некоторые возможности этого дизассемблера.
1. Переименование процедур и меток в программе. При дизассемблировании IDA PRO дает, разумеется, свои названия процедурам и меткам. Вы можете ввести свои названия, тем самым сделав программу более понятной. Все изменения, сделанные в тексте, сохраняются в специальной базе и могут быть восстановлены при повторном запуске.
2. Распознавание библиотечных и API-функций (см. Рис. 4.2.4). Дизассемблер не просто распознает эти функции, но и комментирует параметры этих функций.
3. При помощи контекстного меню или двойного щелчка Вы можете перейти по команде JMP или команде CALL в указанное место программы и так продолжать осуществлять переходы любое количество раз. Возвратиться на любое количество шагов можно, используя кнопку "стрелка" на панели инструментов.
4. При помощи Shift+Ins, Ins, а так же пунктов меню "EDIT" в любом месте программы можно записать комментарий. Комментарий, как и введенные названия меток, запоминается в базе данных программы. Но это еще не самое приятное. В комментарии может присутствовать адрес строки программы или имя метки. Если сделать двойной щелчок по адресу или метке, то мы как раз и очутимся на этом месте.
5. Сворачивание и разворачивание процедур. При помощи клавиши "-" на дополнительной клавиатуре можно свернуть процедуру, а при помощи "+" развернуть процедуру. Представление процедур в свернутом виде позволяет представить программу в более компактном и более понятном виде.
6. IDA PRO весьма аккуратно распознает не только код, но и данные. На Рис. 4.2.5 показана дизассемблированная часть нашей программы, содержащей данные.
7. Создание и выполнение командных файлов. Язык командных файлов очень близок к языку Си. У меня нет намерения рассказывать о языке, который использует IDA PRO, приведу только один такой командный файл, содержащийся в пакете IDA PRO.
Рис. 4.2.5. Часть программы, содержащей данные, дизассемблированная при помощи IDA PRO.
// // This example shows how to get list of functions. // #include <idc.idc> static main() { auto ea,x; for (ea=NextFunction(0); ea != BADADDR; ea=NextFunction(ea)) { Message("Function at %081X:%s",ea,GetFunctionName(ea)); x = GetFunctionFlags(ea); if ( x & FUNC_NORET ) Message(" Noret"); if ( x & FUNC_FAR ) Message(" Far"); Message("\n") ; } ea = ChooseFunction("Please choose a function"); Message("The user chose function at %081X\n",ea); }
Рис. 4.2.6. Пример командного файла IDA PRO.
Прокомментируем программу на Рис. 4.2.6. Как легко догадаться, организация цикла и условные конструкции имеют в точности тот же синтаксис, что и в языке Си. Главное здесь - разобрать смысл используемых библиотечных функций. Легко видеть, что функция Message просто выводит строку в окно сообщений, которое находится под основным окном. Функция ChooseFunction вызывает окно, которое вызывается также из меню: "Jump to Function". Функция GetFunctionFlags возвращает информацию об указанной функции. Наконец функция NextFunction осуществляет переход на следующую функцию. Она возвращает адрес функции. Аргументом же ее является адрес функции, от которой осуществляется переход на следующую функцию. Оставляю Вам изучение командного языка, поддерживаемого IDA PRO, который представлен в помощи программы.
Теоретически можно написать любую сколь угодно сложную программу по анализу дизассемблированного кода.
8. Программа IDA PRO осуществляет дизассемблирование модулей самых различных форматов: .OBJ, .EXE, .DLL, VXD, .ZIP, .NLM и др.
9. Функциональность IDA PRO может быть значительно усилена посредством подключаемых модулей - plugin'ов. Подключаемые модули пишутся на языке C++ и имеют структуру РЕ-модулей. Подключение модулей осуществляется через горячие клавиши или через пункты меню Edit\Plugins. Подключаемые модули находятся в специальном каталоге Plugins, где находится и файл конфигурации, где указаны эти модули.
10. Еще одна приятная особенность дизассемблера - он создает ассемблерный файл, с которым затем можно работать уже в текстовом режиме.