Allscreen программа
I
Сейчас мы рассмотрим простой57 пример, демонстрирующий некоторые приемы такого типа работы. Задача, которую ставим перед собой, не так сложна, и решить ее можно, воспользовавшись только дизассемблером W32Dasm.
Данная программа (Allscreen - программа, с помощью которой можно "снимать" окна и отдельные части экрана) попала ко мне как Shareware Release. Программа написана на Delphi, но мы увидим, что решить поставленную задачу можно, и не зная, на чем написана программа. При запуске ее на экране появляется окно, изображенное на Рисунок 4.6.1. Ближе познакомившись с предметом, Вы убедитесь, что чаще всего приходится искать место в программе, соответствующее какому-либо визуальному эффекту: открытие окна, закрытие окна, вывод текста и т.п.
Будем искать функцию последнего
Рисунок 4.6.7. Фрагмент с вызовом функции DialogBoxParam.
Вспомним параметры функции DialogBoxParam. Четвертый параметр как раз представляет собой адрес процедуры окна. Итак, получаем адрес 00409C01. Обратимся к этому адресу (см. Рисунок 4.6.8).
:00409C01 55 push ebp :00409C02 8BEC mov ebp, esp :00409C04 8B450C mov eax, dword ptr [ebp+0C] :00409C07 2D10010000 sub eax, 00000110 :00409COC 7413 je 00409C21 :00409COE 48 dec eax :00409COF OF84EB000000 je 00409D00 :00409C15 2D01020000 sub eax, 00000201 :00409C1A 7444 je 00409C60 :00409C1C E969010000 jmp 00409D8A
Часть дизассемблированиого
Здесь, разумеется, мы рассматриваем только средства MASM.
Часть IV Отладка анализ кода программ драйверы
Часть IV. Отладка, анализ кода программ, драйверы
Вопросы, затронутые в данной части, относятся к категории сложных. Отладка и анализ кода программ невозможен без хорошего знания ассемблера. Однако если Вы дошли до этого места книги, значит Вам по плечу и такой материал.
Часть программы содержащей данные дизассемблированная при помощи 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); }
Циклы
3. Циклы.
С организацией цикла в языке Си мы уже познакомились в этой главе. Отмечу две наиболее часто практикуемые структуры.
1-я структура.
L1: ... ... ... CMP EAX,10 JL L1
2-я структура.
L2: CMP EAX,10 JA L1 ... ... ... JMP L2 L1:
Первая структура может быть несколько видоизменена и принимать следующий вид.
JMP L2 L1: ... ... ... CMP EAX,10 JL L1 L2:
Как по мановению волшебной палочки, в последней структуре цикл "ДО" превращается в цикл "ПОКА".
В своем рассмотрении я не упомянул цикл "FOR" - этого "монстра" языка Си, но, в принципе, это все тот же цикл "ПОКА".
Данные
Данные.
Есть несколько вариантов работы с данными.
Во-первых, имеется пункт меню "HexData\Hex Display of Data...", где можно просмотреть содержимое сегментов данных в шестнадцатеричном и строковом варианте. Кроме того, сам код программы также можно просматривать в шестнадцатеричном виде. Для этого используется пункт "HexData\Hex Display of Code...".
Во-вторых, имеется пункт меню "Refs\String Data References". Это весьма мощное и полезное средство. При выборе этого пункта появляется список строк, на которые имеются ссылки в тексте программы. Во всяком случае, это то, что сумел определить дизассемблер при анализе программы. Выбрав нужную строку, можно двойным щелчком перенестись в соответствующее место программы. Если ссылок на данную строку несколько, то, продолжая делать двойные щелчки, мы будем переходить во все нужные места программы. На Рисунок 4.4.4 изображено окно ссылок на строковые типы данных.
DEWIN EXE
DEWIN.EXE
Программа работает в командном режиме, но по сравнению, например, с DUMPBIN.EXE обладает рядом достоинств. Главное из этих достоинств - это распознавание языков высокого уровня. Кроме того, Вы сами можете писать скрипт-процедуры на предлагаемом макроязыке.
Динамические виртуальные драйверы
IV
В последнем разделе главы мы рассмотрим динамические виртуальные драйверы. Существует три способа загрузки виртуальных динамических драйверов.
Поместить драйвер в каталог \SYSTEM\IOSUBSYS. Драйверы из этого директория загружаются супервизором ввода- вывода. Использовать сервис VxDLDR. Эту сервисную функцию можно вызывать только из виртуальных драйверов. Использовать функцию CreateFile.
Именно последним способом загрузки динамических драйверов мы сейчас и займемся. Схема использования динамических виртуальных драйверов следующая:
Открываем драйвер с помощью функции CreateFile. При удачном открытии функция возвращает идентификатор, который затем используется при вызове функций экспортируемых данным драйвером. Используем функции динамического драйвера посредством вызова функции API DeviceIoControl. Закрываем драйвер с помощью CloseHandle, при этом он автоматически выгружается из памяти.
Перейдем теперь к программе загрузки динамического драйвера. Программа показана на Рисунок 4.7.4. Она загружает драйвер msg.vxd и вызывает сервис 3 драйвера.
; файл FILES1.ASM .386P ; плоская модель .MODEL FLAT, stdcall ; константы STD_INPUT_HANDLE equ -10 FILE_FLAG_DELETE_ON_CLOSE equ 4000000h
; прототипы внешних процедур EXTERN GetStdHandle@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetCommandLineA@0:NEAR EXTERN CreateFileA@28:NEAR EXTERN CloseHandle@4:NEAR EXTERN MessageBoxA@16:NEAR EXTERN ReadConsoleA@20:NEAR EXTERN DeviceIoControl@32:NEAR ; ;------------------------------------------------ ;директивы компоновщику для подключения библиотек includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------------------ ; ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' HANDL DWORD ? HFILE DWORD ? BUF DB "\\.\msg.vxd",0 CAP DB "Окно сообщения",0 MES DB "Ошибка загрузки драйвера",0 BUFER DB 20 DUP (0) LENS DWORD ? ; количество выведенных символов MES1 DB "Вызов сервиса OK!",0 _DATA ENDS
; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить HANDLE ввода PUSH STD_INPUT_HANDLE CALL GetStdHandle@4 MOV HANDL,EAX ; открыть файл PUSH 0 PUSH FILE_FLAG_DELETE_ON_CLOSE PUSH 0 PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET BUF CALL CreateFileA@28 CMP EAX,-1 JE _ERR MOV HFILE,EAX ; вызов сервиса VXD PUSH 0 PUSH 0 PUSH 0 PUSH 0 PUSH 18 PUSH OFFSET MES1 PUSH 3 ; номер сервиса PUSH HFILE CALL DeviceIoControl@32 ; ждать клавиши ENTER PUSH 0 PUSH OFFSET LENS PUSH 200 PUSH OFFSET BUFER PUSH HANDL CALL ReadConsoleA@20 ; закрыть и выгрузить драйвер PUSH HFILE CALL CloseHandle@4 _EXIT: ; конец работы программы PUSH 0 CALL ExitProcess@4 _ERR: PUSH 0 ; MB_OK PUSH OFFSET CAP PUSH OFFSET MES PUSH 0 ; дескриптор окна CALL MessageBoxA@16 JMP _EXIT _TEXT ENDS END START
Дизассемблированный код функции
Рисунок 4.5.8. Дизассемблированный код функции main с Рисунок 4.5.7, при трансляции использована опция -x (исключить исключения, Borland C++). Дизассемблер IDA PRO.
Дизассемблированный код функции main с Рисунок Дизассемблер IDA PRO
Рисунок 4.5.8. Дизассемблированный код функции main с Рисунок 4.5.7. Дизассемблер IDA PRO.
Как видите, получившийся код достаточно сложен. Я не намерен проводить его детальный анализ, т.к. в этом случае нам пришлось бы включать в него и анализ библиотечных программ. Остановимся только на некоторых ключевых моментах.
Оператор new сводится к выполнению библиотечной процедуры: unknown_libname_8. Последняя процедура выделяет память для свойств экземпляра объекта (300 байт). Конструктор хранится и выполняется непосредственно в теле программы. Это связано с тем, что и сам конструктор определен в тексте класса. Для эксперимента попробуйте вынести текст конструктора в отдельную функцию, и Вы увидите, что конструктор будет вызываться из main, как обычная функция. Процедуру @_InitExceptBlockLDTC вставляет транслятор для поддержки так называемых исключений (Exception). Вы можете удалить информацию для использования исключений, несколько сократив получившийся код, но тогда Вы не сможете использовать операторы исключений, такие как try или catch. При вызове какого-либо метода в стек всегда помещается, по крайней мере, один параметр - указатель на экземпляр объекта.
Приведу теперь фрагмент той же дизассемблированной программы, но с использованием опции -x, что для транслятора Borland C++ означает - не использовать исключения. Как видите, текст программы оказался значительно проще.
.text 00401122 _main proc near ; DATA XREF: .data:0040B0C8 .text 00401122 .text 00401122 argc = dword ptr 8 .text 00401122 argv = dword ptr 0Ch .text 00401122 envp = dword ptr 10h .text 00401122 .text 00401122 push ebp .text 00401123 mov ebp, esp .text 00401125 push ebx .text 00401126 push 12Ch .text 0040112B call unknown_libname_8 .text 00401130 pop ecx .text 00401131 mov ebx, eax .text 00401133 test eax, eax .text 00401135 jz short loc_40115D .text 00401137 push offset aPrivet ; src .text 0040113C push ebx ; dest .text 0040113D call _strcpy .text 00401142 add esp, 8 .text 00401145 push offset aPrivet_0 ; src .text 0040114A lea edx, [ebx+0C8h] .text 00401150 push edx ; dest .text 00401151 call _strcpy .text 00401156 add esp, 8 .text 00401159 mov ecx, ebx .text 0040115B jmp short loc_40115F .text 0040115D ;-----------------———-—----—-—---——— .text 0040115D .text 0040115D loc_40115D: ; CODE XREF: _main+13 .text 0040115D mov ecx, ebx .text 0040115F .text 0040115F loc_40115F: ; CODE XREF: _main+39 .text 0040115F mov ebx, ecx .text 00401161 push ebx ; dest .text 00401162 call sub_401108 .text 00401167 pop ecx .text 00401168 push ebx ; char .text 00401169 push offset aS ; _va_args .text 0040116E call _printf .text 00401173 add esp, 8 .text 00401176 push ebx ; handle .text 00401177 call _rtl_close .text 0040117C pop ecx .text 0040117D xor eax, eax .text 0040117F pop ebx .text 00401180 pop ebp .text 00401181 retn .text 00401181 _main endp
Дизассемблированный код программы на Рисунок
Рисунок 4.2.4. Дизассемблированный код программы на Рисунок 4.2.3.
По дизассемблированному коду легко обнаружить ошибку. Кстати, команду cmp eax,0FFh надо, естественно, понимать как cmp eax,0FFFFFFFFh. Запомним нужный код 83F8FF. Запускаем программу HIEW.EXE, нажимаем клавишу F7 и ищем нужное сочетание. Далее клавиша F3, затем F2 и далее - заменяем команду JNE на JE. Клавиша F9 закрепляет изменение. В результате мы исправили программу, не прибегая к ее перетрансляции.
Дизассемблированный текст программы (Рисунок ) Транслятор Borland C++
Рисунок 4.5.6. Дизассемблированный текст программы (Рисунок 4.5.5). Транслятор Borland C++ 5.0.
Кстати, функция ExitProcess(0) введена в текст программы для быстрого поиска нужного фрагмента в отладчике или дизассемблированном коде. Сразу видно, что оптимизацией здесь и не пахло. По такому тексту достаточно просто восстановить исходную Си-программу. А вот код, оптимизированный транслятором Visual C++ 6.0 (Рисунок 4.5.7).
.text 00401000 sub_401000 proc near ; CODE XREF: start+AF .text 00401000 push esi .text 00401001 mov esi, 0Ah .text 00401006 .text 00401006 loc_401006: ; CODE XREF: sub_401000+18 .text 00401006 push 61h .text 00401008 push 61h .text 0040100A push offset aCC ; "%c %c\n" .text 0040100F call sub_401030 ;printf .text 00401014 add esp, 0Ch .text 00401017 dec esi .text 00401018 jnz short loc_401006 .text 0040101A push 0 ; uExitCode .text 0040101C call ds:ExitProcess .text 00401022 pop esi .text 00401023 retn .text 00401023 sub_401000 endp
Рисунок 4.5.7. Дизассемблированный текст программы (Рисунок 4.5.5). Транслятор Visual C++ 6.0.
Я думаю, что текст на Рисунок 4.5.7 удивит Вас. Однако, что же здесь удивительного? Взгляните на текст Си-программы. Заданные нами два символьных массива - абсолютно ни к чему. Транслятор Visual C++ очень точно это подметил и изменил код так, что, как ни старайся, текст исходной программы восстановить не удасться. Конечно, такая оптимизация оказалась возможной только потому, что наши массивы используются в ограниченной области.
Рассмотрим далее некоторые способы оптимизации, которые могут пригодиться нам и при написании программ на ассемблере.
DUMPBIN EXE
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
DUMPPE EXE
DUMPPE.EXE
Данная программа рассматривалась нами в гл. 1.1. Она во многом похожа на предыдущую программу DUMPBIN.EXE, но более удобна, хотя и обладает несколько меньшими возможностями.
EDITBIN EXE
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.
Утилита весьма полезна для быстрого изменения атрибутов исполняемых и объектных модулей.
Если отладочную информацию в исполняемом модуле
Если отладочную информацию в исполняемом модуле
Turbo Debugger не распознает, Вам при отладке придется использовать окно CPU. Окно разделено на пять частей (см. Рисунок 4.3.2 против часовой стрелки):
1. Инструкции микропроцессора.
2. Сегмент данных.
3. Сегмент стека.
4. Состояние регистров.
5. Основные флаги.
Файл VXD DEF используемый для
Оттранслируем драйвер на Рисунок 4.7.2 согласно ранее указанному алгоритму. Только при компоновке добавьте ключ /MAP, в результате, кроме драйвера VXD1.VXD, в каталоге появится файл VXD1.MAP. Содержимое этого файла мы поместили на Рисунок 4.7.3.
VXD1
Timestamp is 3bb5ad7a (Sat Sep 29 17:16:10 2001)
Preferred load address is 00400000
Start Length Name Class 0001:00000000 00000050H _LDATA CODE 0001:00000050 00000007H _LTEXT CODE
Address Publics by Value Rva+Base Lib:Object
0001:00000000 VXD1_DDB 00401000 vxd1.obj 0001:00000050 VXD1_Control 00401050 f vxd1.obj 0001:00000057 INIT 00401057 f vxd1.obj
entry point at 0000:00000000
Static symbols
Формат входа в каталог импорта
Формат входа в каталог импорта
Смещение
Длина поля
Название поля
Описание поля
00h |
DWORD |
Import LookUp |
Содержит ссылку на таблицу относительных адресов (относительно базового адреса), указывающих на соответствующие имена импортируемой функции, или непосредственно номер импортируемого входа. |
04h |
DWORD |
Time/Date Stamp |
Отметка о времени создания, часто содержит ноль. |
08h |
DWORD |
Forward Chain |
Связано с возможностью передачи экспорта в другие библиотеки. Обычно равно 0FFFFFFFFh. |
0Ch |
DWORD |
Name RVA |
Ссылка на библиотеку импорта в виде ASCII строки с нулем на конце. Например, KERNEL32.DLL или USER32.DLL. |
10h |
DWORD |
Addres Table RVA |
Ссылка на таблицу адресов импорта, заполняется системой при связывании. |
Таблица просмотра импорта или таблица имен сервисов. Ссылка из поля Import LookUp на массив, содержащий ссылки на таблицу просмотра импорта. При импортировании по номеру старший бит элемента массива равен 1.
ТипСодержимое
Word | Номер функции |
Hint | ASCII имя функции |
Таблица адресов импорта. Данная таблица принимает в себя информацию после связывания загрузчиком импорта из внешних библиотек, она завершается нулевым элементом.
Фрагмент дизассемблированного текста Передвижение по дизассемблированному тексту
Передвижение по дизассемблированному тексту.
При передвижении по тексту текущая строка подсвечивается другим цветом, при этом особо выделяются переходы и вызовы процедур. Передвижение облегчается также с помощью пункта меню "Goto":
Goto Code Start - переход на начало листинга;
Goto Program Entry Point - переход на точку входа программы, наиболее важный пункт меню;
Goto Page - переход на страницу с заданным номером, по умолчанию число строк на странице составляет пятьдесят;
Goto Code Location - переход по заданному адресу, в случае отсутствия адреса учитывается диапазон и близость к другим адресам.
Другим способом передвижения по дизассемблированному тексту является пункт "Search" - поиск. Здесь нет никаких отличий от подобных команд других программ.
В случае, если текущая строка находится в команде перехода или вызова процедуры, с помощью кнопок, расположенных на панели инструментов, можно перейти по соответствующему адресу. Такое передвижение можно продолжать, пока Вы не обнаружите нужный фрагмент программы. Но самое приятное здесь то, что можно передвигаться и в обратном направлении. При этом нужные кнопки на панели инструментов автоматически подсвечиваются.
Кроме того, те адреса, куда производится переход, содержат список адресов, откуда производятся переходы. Подсветив строку, где расположен адрес, и дважды щелкнув правой кнопкой мыши по этому адресу, мы перейдем к соответствующей строке.
Фрагмент кода осуществляющего в частности задержку
Рисунок 4.6.4. Фрагмент кода, осуществляющего, в частности, задержку.
Я сразу взял несколько больше кода, захватив и несколько верхних строк. По сути дела, перед нами вся процедура задержки. Нет смысла пытаться понять, что означает та или иная команда call, хотя легко сообразить, что, например, call 00413E84 убирает строку с экрана.
Для того чтобы решить проблему задержки, достаточно "выключить" этот фрагмент из программы. Проще всего это можно сделать, поставив в начало команды pop ebx / ret, используя такой редактор, как HIEW. В результате задержка исчезает.
Перейдем теперь ко второй проблеме — ограничение на количество запусков. Уже из самого вида окна ясно, что оно формируется в самой программе. Следовательно, опять можно попытаться найти текст, который изображается на экране, в самой программе.
:00443326 8ВС0 mov eax, eax :00443328 53 push ebx :00443329 8BD8 mov ebx, eax :0044332B 803DEC56440001 cmp byte ptr [004456EC], 01 :00443332 7546 jne 0044337A :00443334 A124564400 mov eax, dword ptr [00445624] :00443339 E84E2CFEFF call 00425F8C :0044333E A1D8564400 mov eax, dword ptr [004456D8] :00443343 E87816FEFF call 004249CO :00443348 FF05F0564400 inc dword ptr [004456F0] :0044334E C605EC56440000 mov byte ptr [004456EC], 00 :00443355 833DF05644000F cmp dword ptr [004456FO], 0000000F :0044335C 7E1C jle 0044337A :0044335E 6AOO push 00000000 :00443360 668BODB0334400 mov cx, word ptr [004433B0] :00443367 B202 mov dl, 02
* Possible StringData Ref from Code Obj ->"This Software Has Been Used Over" | :00443369 B8BC334400 mov eax, 004433BC :0044336E E8BDAEFEFF call 0042E230 :00443373 8BC3 mov eax, ebx :00443375 E84214FEFF call 004247BC
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: |:00443332(C), :0044335C(C) :0044337A 33D2 xor edx, edx :0044337C 8B83F4010000 mov eax, dword ptr [ebx+000001F4] :00443382 E8A52DFFFF call 0043612C :00443387 33D2 xor edx, edx :00443389 8B83F8010000 mov eax, dword ptr [ebx+000001F8] :0044338F E8982DFFFF call 0043612C :00443394 33D2 xor edx, edx :00443396 8B83FC010000 mov eax, dword ptr [ebx+000001FC] :0044339C E88B2DFFFF call 0043612C :004433A1 33D2 xor edx, edx :004433A3 8B8314020000 mov eax, dword ptr [ebx+00000214] :004433A9 E87E2DFFFF call 0043612C :004433AE 5B pop ebx :004433AF C3 ret
Фрагмент кода проверки количества запусков
Рисунок 4.6.5. Фрагмент кода проверки количества запусков.
Опять мы представляем весь необходимый фрагмент. Найденной выше ссылкой на искомую строку легко обнаруживаем "подозрительные" команды:
cmp dword ptr [004456F0], 0000000F jle 0044337A
Вспомним, что программа перестает работать как раз после пятнадцати запусков. Проще всего исправить ситуацию, "забив" фрагмент программы с 0044335E по 00443375 командами NOP (90), используя редактор HIEW.
Фрагмент РЕзаголовка
Рисунок 4.1.3. Фрагмент РЕ-заголовка.
На Рисунок 4.1.3 показан фрагмент PE-заголовка. Обратите внимание, что по смещению 3CH действительно находится адрес начала основного заголовка (символы PE).
Фрагмент списка импортированных модулей и функций
Рисунок 4.4.5. Фрагмент списка импортированных модулей и функций.
Информационное окно содержит несколько окон-списков: содержимое регистров микропроцессора, значение флагов микропроцессора, точки останова, содержимое сегментных регистров, история трассировок, история событий, базовые адреса, два дисплея данных. Ниже мы объясним также значения кнопок этого окна.
Обратимся теперь к управляющему окну. Кнопка "Run" запускает загруженную в отладчик программу, кнопка "Pause" приостанавливает работу программы, кнопка "Terminate" останавливает выполнение программы и выгружает ее из отладчика. Кнопки "Step Over" и "Step Into" используются для пошагового исполнения программы. Первая кнопка, выполняя инструкции, "перескакивает" код процедур и цепочечные команды с повторением, вторая кнопка выполняет все инструкции последовательно. Кроме того, имеются кнопки "AutoStep Over" и "AutoStep Into" для автоматического пошагового выполнения программы. В случае API-функций даже использование "Step Into" не приведет к пошаговому выполнению кода функции в силу того, что код функции не доступен для пользовательских программ. Очень удобно, что при пошаговом выполнении происходит передвижение не только в окне отладчика, но и в окне дизассемблера.
Функции и процедуры
5. Функции и процедуры.
Функции и процедуры идентифицируются достаточно просто. Структуру вызова и внутреннюю часть процедур Вы уже хорошо знаете. Остается только напомнить некоторые положения. Вызов процедуры:
PUSH par1 PUSH par2 PUSH par3 CALL 232343
Здесь все достаточно просто. Главное - распознать параметры и понять порядок помещения их в стек. Надо также иметь в виду, что существует протокол передачи параметров через регистры (см. главу 3.7). После вызова процедуры может стоять команда очистки стека ADD ESP,N.
Внутренняя часть процедуры также нами неоднократно разбиралась (см. Гл. 1.2, 3.7). Думаю, что она достаточно Вами узнаваема, и мы не будем здесь на этом останавливаться.
Не забывайте, что функции возвращают результат через регистр EAX. Это может помочь Вам быстро разобраться в назначении функции.
Функция main консольного приложения
Итак, окончательный результат наших изысканий может быть представлен на Рисунок 4.5.2.
char s1[32]; char * s2="абвгдежзийклмнопрстуфхцчшщыьъэюя"; void main() { int i; i=0; do { s1[i]=s2[31-i]; i++; } while(i>32); s1[32]=0; printf("%s\n",s1); }
Исправление исполняемых модулей
Глава 6. Исправление исполняемых модулей
Сразу скажу, что исправление исполняемых модулей - дело тонкое и требует не столько знаний, сколько навыков. Впрочем, без знания ассемблера Вам не обойтись. Но если Вы дошли до данной главы, то с этим, я думаю, все в порядке. В предисловии я уже обосновывал необходимость изучения методов изучения и исправления кода программ. Считаю, что общество и государство нуждается в специалистах и по защите, и по взлому компьютерных систем, как оно нуждается в профессиональной армии. В своей книге я лишь слегка касаюсь исправления модулей.
Обзор отладчиков и дизассемблеров
Глава 2. Обзор отладчиков и дизассемблеров
В этой главе мы рассмотрим отладочные и дизассемблирующие программы, кроме трех наиболее известных, о которых пойдет речь в последующих двух главах.
Описание работы с дизассемблером WDasm и отладчиком ICE
Глава 4. Описание работы с дизассемблером W32Dasm и отладчиком ICE
В данной главе я попытаюсь рассказать о двух моих любимых средствах анализа программ.
Основы анализа кода программ
Глава 5. Основы анализа кода программ
Большинство исполняемых модулей были написаны на языках высокого уровня и не содержат отладочной информации. Однако анализировать код программы и в этом случае приходится. Для того чтобы ускорить процесс анализа необходимо знать или, по крайней мере, иметь перед глазами некоторые стандартные ассемблерные структуры, соответствующие определенным структурам языков высокого уровня. Замечу, что речь, разумеется, будет идти о 32-битных приложениях.
Структура и написание драйверов VXD
Глава 7. Структура и написание драйверов .VXD
Последнюю главу нашей книги я посящаю написанию виртуальных драйверов. Сокращение VxD следует понимать как Virtual Device Driver, "x" относится к слову Device (имеется в виду любое устройство). Вопрос несколько устаревает, так как в Windows 98 принята на вооружение несколько иная концепция драйверов устройств, а Windows NT никогда не поддерживала VxD-драйверы, а использует модель, которая называется kernel mode (режим ядра). Все же программирование VxD остается актуальным по сей день, и знать основные положения должен каждый программирующий на языке ассемблера. В данной главе, отходя от нашей обычной практики, мы будем интенсивно использовать макроопределения, содержащиеся во включаемых (inc) файлах пакета DDK. Тем самым нам удастся поместить весь материал в одну главу. Кроме того, данная глава ориентирована на работу с MASM32.
Структура исполняемых модулей
Глава 1. Структура исполняемых модулей
Исполняемым форматом в Windows является формат PE. Сокращение PE означает Portable Executable, т.е. переносимый исполняемый формат. Этот формат имеют как ЕХЕ-файлы, так и динамические библиотеки. Важно, что сейчас фирма Microsoft ввела "новый" формат и для объектных модулей - это COFF-формат (COFF - Common Object File Format), который, однако, на поверку оказался, в сущности, все тем же PE-форматом. Заметим в этой связи, что фирма Borland по-прежнему работает с объектными файлами, имеющими структуру OMF (Object Module Format). Старый NE-формат (NE - New Executable), используемый старой операционной системой Windows и рассчитанный на сегментную структуру памяти, ушел в небытие. Кроме того, есть еще формат VXD-драйверов - LE-формат (Linear Executable, т.е. линейный исполняемый). Этого формата мы коснемся, когда будем говорить о драйверах в операционной системе Windows. Таким образом, данная глава будет посвящена разбору структуры исполняемых РЕ-модулей. Поскольку в состав исполняемого РЕ-модуля входит и DOS-программа (STUB), мы начнем наше рассмотрение со структуры DOS-программ. Наше рассмотрение будет кратким, и мы воспользуемся таблицей из книги [1].
Структура ЕХЕ-программ для MS DOS
Смещение
Длина
Название
Комментарий
+0 |
2 |
MZ |
подпись, признак ЕХЕ-программы |
+2 |
2 |
PartPag |
длина неполной последней страницы |
+4 |
2 |
PageCnt |
длина в страницах (512 б), включая заголовок и последнюю страницу |
+6 |
2 |
ReloCnt |
число элементов в таблице перемещения |
+8 |
2 |
HdrSize |
длина заголовка в параграфах |
+0AН |
2 |
MinMem |
минимум требуемой памяти за концом программы |
+0CH |
2 |
MaxMem |
максимум требуемой памяти за концом программы |
+0EН |
2 |
ReloSS |
сегментный адрес стека |
+10H |
2 |
EXESp |
значение регистра SP |
+12Н |
2 |
ChkSum |
контрольная сумма |
+14Н |
2 |
ExeIP |
значение регистра IP |
+16H |
2 |
ReloCS |
сегментный адрес кодового сегмента |
+18H |
2 |
TablOff |
смещение в файле первого элемента таблицы перемещения |
+1АН |
2 |
Overlay |
номер оверлея, 0 для главного модуля |
|
* Конец форматированной порции заголовка **
+1СН
|
** Начало таблицы перемещения (возможно с 1СН) **
+? 4*? смещ. сегмент ... смещ. сегмент
HIEW EXE
HIEW.EXE
Данная программа широко известна в среде программистов, скажем так, хакерского направления. Название программы происходит от фразы "Hacker's View". Основная задача, которую выполняет данная программа - просматривать и редактировать загружаемые модули. Причем просмотр и редактирование допускается в трех вариантах: двоичный, текстовый и ассемблерный. Хороших дизассемблирующих программ создано довольно много, а вот программ, подобных данной, можно по пальцам перечесть.
Интерфейс программы весьма напоминает интерфейс редакторов таких программ, как FAR или Norton Commander (см. Рисунок 4.2.2.). Все команды осуществляются при помощи функциональных клавиш с использованием клавиш "Alt" и "Ctrl". Например, нажимая клавишу F4, вы получаете возможность выбрать способ представления двоичного файла: текстовый, ассемблерный или двоичный. Нажимая клавишу F3 (при условии, если Вы находитесь в двоичном или ассемблерном просмотре), Вы получаете возможность редактировать файл. Если же, находясь в ассемблерном просмотре, Вы после F3 нажмете еще и F2, то сможете редактировать машинную команду в символьном виде. Мы не будем далее останавливаться на командах данной программы, поскольку они просты, очевидны и могут быть получены просто по F1, а перейдем сразу к простому примеру использования данной программы, хотя пример тематически и относится к материалу Гл. 4.6. Чтобы слишком не загромождать рассмотрение возьмем простую консольную программу.
IDA PRO
IDA PRO
Один из самых мощных дизассемблеров. Возможности настолько велики, что многие программисты считают его всемогущим. Работая над текстом дизассемблируемой программы. Вы можете называть своими именами метки и процедуры, давать свои комментарии так, что дизассемблированный текст становится в конце концов ясным и понятным. Все сохраняется в специальную базу и при последующем запуске, естественно, восстанавливаются. Внешний вид дизассемблера IDA PRO показан на Рисунок 4.2.4. Мы дизассемблировали одну из наших старых программ. Обратите, кстати, внимание на ссылку offset WNDPROC. Название WNDPROC дано уже нами в процессе анализа кода программы. Но рассмотрим все по порядку.
Импортированные и экспортированные функции
Импортированные и экспортированные функции.
Список импортированных функций и модулей находится в начале дизассемблированного текста (см. Рисунок 4.4.5). Кроме того, список импортированных функций можно получить из меню "Functions\Imports". Выбрав нужную функцию в списке, двойным щелчком можно получить все места программы, где вызывается эта функция. Экспортированные функции также можно получить в соответствующем окне, выбрав пункт "Functions\Exports".
Как выглядит фрагмент программы представленный на Рисунок в окне дизассемблера WDasm
Рисунок 4.5.4. Как выглядит фрагмент программы, представленный на Рисунок 4.5.3 в окне дизассемблера W32Dasm.
Как видите, дизассемблер W32Dasm менее информативен, и из данного фрагмента можно заключить только, что для локальных переменных отведено 84 байта. Саму же структуру локальных переменных можно понять только путем анализа кода, который следует ниже.
Каталог ресурсов (Resource Directory Table)
Каталог ресурсов (Resource Directory Table)
Смещение
Длина поля
Название поля
Описание поля
00h |
DWORD |
Flags |
Пока не используются, должны быть сброшены в ноль. |
04h |
DWORD |
Time/Date Stamp |
Дата и время создания ресурсов от ресурсного компилятора. |
08h |
WORD |
Major Version |
Старшая часть версии ресурсов. Обычно равно нулю. |
0Ah |
WORD |
Minor Version |
Младшая часть версии ресурсов. Обычно равно нулю. |
0Ch |
WORD |
Name Entry |
Количество входов в таблицу имен (элементов массива) ресурсов. Таблица располагается в самом начале массива входов и содержит строковые имена, ассоциируемые с ресурсами. |
0Eh |
WORD |
ID_Num Entry |
Количество элементов массива, использующих целые ID. |
За каталогом ресурсов сразу следует массив переменной длины, содержащий ресурсные входы. Name Entry содержит число ресурсных входов, имеющих имена (связанные с каждым входом). Имена нечувствительны к регистру и расположены в порядке возрастания. ID_Num Entry определяет число входов имеющих в качестве имени 32-битовый идентификатор. Эти входы так же отсортированы по возрастанию. Данная структура позволяет получать быстрый доступ к ресурсам по имени или по идентификатору, но для отдельно взятого ресурса поддерживается только одна из форм поиска. Что согласуется с синтаксисом .RC и .RES файлов. Каждый вход в таблицу ресурсов имеет следующий формат.
Классические структуры языка Си
II
В данном разделе мы рассмотрим некоторые классические структуры языка Си и их отображение в язык ассемблера.
Локальная область данных цепочек (потоков)
Локальная область данных цепочек (потоков)
Локальная область данных цепочек, это специальный, протяженный блок данных. Каждая цепочка получит собственный блок при создании.
Таблица разделов цепочек | TLS Directory Table |
Данные цепочек | TLS Data |
Индексные переменные | Index Variables |
Адреса обратных вызовов | CallBack |
Локальные переменные
4. Локальные переменные.
Чаще всего нам приходится работать с локальными переменными. С глобальными переменными мы уже встречались, они хранятся во вполне определенных секциях. Хороший дизассемблер их легко локализует. С локальными переменными часто разбираться гораздо сложнее. Особенно это касается записей или массивов. Хороший дизассемблер значительно упростит задачу. Рассмотрим, например, следующий фрагмент, взятый из IDA PRO.
CODE 00401108 _main proc near ; DATA XREF: DATA:0040B044 CODE 00401108 CODE 00401108 var_54 = dword ptr -54h CODE 00401108 var_28 = byte ptr -28h CODE 00401108 argc = dword ptr 8 CODE 00401108 argv = dword ptr 0Ch CODE 00401108 envp = dword ptr 10h CODE 00401108 CODE 00401108 push ebp CODE 00401109 mov ebp, esp CODE 0040110B add esp, 0FFFFFFACh CODE 0040110E push ebx CODE 0040110F xor ebx, ebx
Модификация кода данных и регистров
Модификация кода, данных и регистров.
Отладчик позволяет модифицировать загруженный в него код. Сделать это можно, обратившись к кнопке "Patch Code" в управляющем окне (см. Рисунок 4.4.8). Важно отметить, что модификации подвергается только код, загруженный в отладчик, а не дизассемблированный текст. Найдя нужное место в отлаживаемом коде, и модифицировав его, вы можете тут же проверить результат модификации, запустив программу. Если модификация оказалась правильной, можно приступать уже к модификации самого модуля.
Для модификации регистров и ячеек памяти исполняемого процесса, существует специальная кнопка "Modify Data" на панели информационного окна. Окно расположено на Рисунок 4.4.9. Окно несколько загромождено элементами, но, присмотревшись, Вы поймете, что все элементы на своем месте. В верхней части окна расположены текущие значения основных флагов микропроцессора, которые Вы можете изменить. Для того чтобы модифицировать содержимое регистра или ячейку памяти следует вначале установить модифицирующую величину - "Enter Value". Далее следует выбрать нужный регистр и нажать кнопку слева от нее. Чтобы установить старое значение, следует нажать кнопку "R" справа от регистра. Чтобы изменить содержимое ячейки памяти, следует вначале записать адрес ячейки в поле "Mem Lock", а затем воспользоваться кнопкой "Mem". Другие операции, предоставляемые данным окном, также достаточно очевидны.
Начальная установка
Начальная установка.
Установка пакета достаточно проста. Основной вопрос, который возникает при установке - выбор видеоадаптера и мыши. Что касается видеоадаптера, то обычно рекомендуют выбрать стандартный VGA-адаптер, и с видеосистемой не будет никаких проблем. Отладчик SoftIce предполагает использование мыши для управления. Однако мышь является второстепенным средством управления, и можно обойтись без нее.
После установки и перезапуска Вам, скорее всего, понадобится еще некоторая настройка файла WINICE.DAT. Вот основные рекомендации.
Строка PHYSMB=32 определяет реальный объем физической памяти в мегабайтах. Укажите реальный объем памяти на Вашем компьютере.
Строка INIT= - определяет оконные установки. Лично мне нравятся установки, определяемые строкой:INIT="SET FONT 1; SET ORIGIN 30 30; LINES 65; WIDTH 90; WR; WF; WD 4; WL; WC 30; X;". Впрочем, Вы сами сможете подобрать параметры по Вашему вкусу. Но главное заключается в том, что, открыв окно отладчика, можно оперативно изменить существующие установки.
В конце файла установок стоят строки типа ;EXP=c:\windows\system\kernel32.dll - снимите с них комментарий. Это необходимо сделать, чтобы отладчик распознавал импортируемые приложением функции стандартных динамических библиотек.
В Windows 9x SoftIce при инсталляции помещает в конце файла AUTOEXEC.BAT строку типа C:\ICE\WINICE.EXE. При желании Вы всегда можете поставить комментарий на эту строку.
Начало функции окна
Рисунок 4.6.7. Начало функции окна.
Взглянув на Рисунок 4.6.8, можно убедиться, что это действительно начало некоторой функции. Для того чтобы быть уверенным, что это нужная функция, необходимо сделать некие изменения и проверить результат.
Поставим теперь себе цель, найти ту же самую функцию, но отладчиком SoftIce.
Загружаем загрузчиком LOADER32.EXE программу FILES.EXE в отладчик SoftIce. Далее Ctrl-D, и мы имеем запущенную программу. Запускаем диалоговые окна, кроме последнего диалогового окна. Перед тем как вызвать последнее диалоговое окно, снова войдем в отладчик и установим точку останова на функцию DialogBoxParamA:
ВРХ DialogBoxParamA
Снова входим в программу, запускаем нужное диалоговое окно, при этом происходит автоматический выход в отладчик: сработала точка останова. Далее нажмем клавишу F11, при этом окажемся в точке вызова либо сразу, либо после закрытия диалогового окна (опять происходит выход в отладчик). Посмотрев на вызов функции DialogBoxParam, мы легко определим адрес процедуры окна. Разумеется, он совпадает с тем, который мы нашли ранее, с помощью отладчика программы W32Dasm (см. Рисунок 4.6.7).
Продолжая разговор об отладчике SoftIce, замечу, что выход на функцию окна можно проделать и другим способом. При помощи команды HWND можно определить дескрипторы запущенных окон. Далее можно поставить точку останова на содержимое первого параметра, который как раз должен быть равен дескриптору окна. Поставить точку останова можно, например, такой командой
ВРХ EIP IF(EBP+8==N)
здесь N - найденный ранее дескриптор окна. После этого остается только проделать что-то с окном, чтобы на функцию его пришло какое-нибудь сообщение.
57 Простой с точки зрения возможных проблем, возникающих при исправлении исполняемых модулей.
Начало работы
Начало работы.
Внешний вид программы показан на Рисунок 4.4.1. Меню дополняется панелью инструментов, элементы которой активизируются в зависимости от ситуации.
Начнем с содержимого DEFфайла
II
Начнем с содержимого DEF-файла. Содержимое показано на Рисунок 4.7. Здесь перечислены сегменты на все случаи жизни. Вам нет необходимости использовать все определенные здесь сегменты. Т.о. образом данный файл можно использовать при создании любого виртуального драйвера. Сегменты, принадлежащие к одному классу, после компоновки объединяются в один сегмент. Менять следует только первую строку, где задается имя драйвера. Отметим, что имя драйвера следует задавать заглавными буквами. Кроме того, в первой строке можно задать тип драйвера. По умолчанию этот тип статический. Если бы мы записали строку VXD VXD1 DYNAMIC, то компоновщик создал бы динамический виртуальный драйвер.
VXD VXD1 SEGMENTS _LPTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE _LTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE _LDATA CLASS 'LCODE' PRELOAD NONDISCARDABLE _TEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE _DATA CLASS 'LCODE' PRELOAD NONDISCARDABLE CONST CLASS 'LCODE' PRELOAD NONDISCARDABLE _TLS CLASS 'LCODE' PRELOAD NONDISCARDABLE _BSS CLASS 'LCODE' PRELOAD NONDISCARDABLE _LMGTABLE CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL _LMSGDATA CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL _IMSGTABLE CLASS 'MCODE' PRELOAD DISCARDABLE IOPL _IMSGDATA CLASS 'MCODE' PRELOAD DISCARDABLE IOPL _ITEXT CLASS 'ICODE' DISCARDABLE _IDATA CLASS 'ICODE' DISCARDABLE _PTEXT CLASS 'PCODE' NONDISCARDABLE _PMSGTABLE CLASS 'MCODE' NONDISCARDABLE IOPL _PMSGDATA CLASS 'MCODE' NONDISCARDABLE IOPL _PDATA CLASS 'PDATA' NONDISCARDABLE SHARED _STEXT CLASS 'SCODE' RESIDENT _SDATA CLASS 'SCODE' RESIDENT _DBOSTART CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING _DBOCODE CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING _DBODATA CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING _16ICODE CLASS '16ICODE' PRELOAD DISCARDABLE _RCODE CLASS 'RCODE' EXPORTS VXD1_DDB @1
Объектное программирование
6. Объектное программирование.
Объектное программирование в значительной степени усложняет получаемый исполняемый код. Мы рассмотрим один простой пример. На Рисунок 4.5.7 представлена программа на C++.
#include <windows.h> #include <stdio.h> class stroka { public: char с[200]; char g[100]; stroka() { strcpy(c,"Privet"); strcpy(g,"Privet");} int strcp(); };
stroka::stri() { strcpy(c,g); return 0; }
main() { stroka * s = new stroka; s->strcp(); printf("%s\n",s->c); delete s; }
Общая структура РЕмодуля
Общая структура РЕ-модуля.
Начало заголовка ЕХЕ-файлов в WIN32 представляет собой небольшую DOS-программу, основное предназначение которой заключается в том, чтобы при запуске в операционной системе MS DOS сделать сообщение о том, что данный модуль не предназначен для работы в MS DOS. Программа LINK.EXE (TLINK32.EXE) устанавливает свой вариант DOS-программы. Однако при желании Вы всегда можете поставить свою программу-заглушку (stub в переводе заглушка).
Рассмотрим общую структуру РЕ-заголовка.
СмещениеКомментарий
00H | Обычный DOS-заголовок. |
1CH | Четыре байта до выравнивания до 20Н байт, т.е. выравнивание до двух параграфов. |
20H | Информация о программе, обычно отсутствующая. |
3CH | Смещение 32-битного РЕ-заголовка. |
40H | Таблица перемещения для программы-заглушки. У стандартных заглушек таблица, разумеется, пуста. Не смотря на это TablOff должен показывать именно сюда. |
40H+?? | Здесь начинается тело самой заглушки, которая начинается за таблицей перемещения. Естественно, в стандартных заглушках нет ничего, кроме сообщения о невозможности запуска программы. Однако заглушка может иметь и весьма разрушительные свойства. |
?? | Здесь начинается собственно PE-заголовок. Сюда показывает содержимое четырех байт по адресу 3CH. Начало должно быть выровнено по 8-байтной границе. |
?? | Таблица описаний секций файлов (Object Table). |
?? | Остальная информация: coff-символы, отладочная информация, таблица импорта и таблица экспорта, ресурсы и т.д. Данный раздел называется Image Pages, т.е. страницы образов. |
Общая структура РЕзаголовка
Рисунок 4.1.2. Общая структура РЕ-заголовка.
00000: 4D 5A 0A 00 02 00 00 00 | 04 00 0F 00 FF FF 00 00 MZ 00010: C0 00 00 00 00 00 00 00 | 40 00 00 00 00 00 00 00 00020: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 00030: 00 00 00 00 00 00 00 00 | 00 00 00 00 80 00 00 00 00040: B4 09 BA 10 00 OE IF CD | 21 B8 01 4C CD 21 90 90 00050: 54 68 69 73 20 69 73 20 | 61 20 57 69 6E 33 32 20 00060: 70 72 6F 67 72 61 6D 2E | 0D OA 24 00 00 00 00 00 00070: 00 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 00080: 50 45 00 00 4C 01 03 00 | 39 30 00 00 00 00 00 00 PE 00090: 00 00 00 00 E0 00 OE 03 | 0B 01 02 34 00 30 00 00 000A0: 00 10 00 00 00 60 00 00 | 30 96 00 00 00 70 00 00 000B0: 00 A0 00 00 00 00 40 00 | 00 10 00 00 00 02 00 00
Обзор команд отладчика
Обзор команд отладчика
Вызов отладчика. Вызов отладчика осуществляется клавишами Ctrl+D, повторное нажатие этих клавиш приводит к закрытию отладчика. Закрытие окна отладчика можно осуществить и клавишей F5.
Помощь. Для получения списка всех команд наберите в командном окне команду "h". Для получения информации по конкретной команде наберите "h <команда>". Нижняя часть командного окна - панель помощи. В ней появляются подсказки. Например, если Вы введете букву "w" (на эту букву начинаются оконные команды) на панели появится список команд, начинающихся на "w".
Работа с окнами. При вызове отладчика на экране появляется его окно (см. Рисунок 4.4.12.), разделенное на несколько окон, каждое из которых несет на себе определенную информационную нагрузку. Количество и размер этих окон определяется начальной установкой (см. начало), но может меняться по нашему желанию. Для ввода команд существует командное окно. Обычно курсор находится именно в командном окне. Каждое окно имеет свое мнемоническое обозначение, например "c" - окно кода, "d" - окно данных, "r" - окно регистров, "f" - окно сопроцессора, "w" - окно наблюдения за переменными, "s" - окно стека, "l" - окно локальных переменных, "x" - окно регистров Pentium III. В часть окон можно перейти командой Alt+"мнемоническое обозначение окна". Перейти можно в каждое такое окно, выяснить это, как Вы понимаете, достаточно просто. Обратный переход осуществляется той же командой. Для создания соответствующего окна, если оно отсутствует, используются команды, в которых первым идет символ "w", вторым символом является символ, обозначающий окно. Например, если у Вас нет на экране окна данных, то создать его можно командой wd, та же команда удалит окно данных из перечня показываемых окон. Высота окна определяется параметром данной команды, например wc 20. Запомните еще несколько полезных команд. Ctrl+"стрелка вверх-вниз" - скроллинг окна кода, Alt+"стрелка вверх-вниз" - скроллинг окна данных, Alt+Ctrl "стрелка вверх-вниз, вправо-влево" - передвижение самого окна отладчика.
Просмотр кода и данных. Один способ передвижения, а значит и просмотра, Вы уже знаете: скроллинг окна кода осуществляется клавишами Ctrl+<стрелка вниз, стрелка вверх> (Alt для окна данных). Наиболее удобной является команда "U". Общий формат этой команды: U [address [length]] | [name]. Address - адрес непосредственный или определяемый через регистр, length - число выводимых байт, name - осуществлять скроллинг, пока не встретится данное имя. Например, u ebx -20 - вывести инструкции, начиная с адреса за 20 байт до адреса CS:EBX. Пустая команда u выводит коды, начиная с CS:EIP. Если вторым параметром идет l, то вывод осуществляется в командное окно. Аналогично для окна данных работает команда D, формат которой имеет вид: D [address], [length]. Например, d 100, 100 или d eax. Положим, видя команду типа MOV EAX,[EBX-10], мы можем посмотреть область данных, откуда берется значение EAX: d ebx-10.
Просмотр - модификация регистров. Переход к окну регистров, как уже было сказано, может быть осуществлено командой Alt+r. Находясь в окне. Вы можете вместе с тем менять содержимое регистров. К аналогичному результату можно прийти, просто выполнив в командной строке команду "R". Возможна также команда вида r reg=знач. Например, r еах=10. Ключ -d позволяет выводить содержимое регистров в командное окно. Команда r есх переводит курсор прямо к нужному регистру, a r fl - прямо к регистру флагов. Команда вида r fl=o+a-p — устанавливает флаги "o" и "a" и сбрасывает флаг "p".
Трассировка кода. Горячие клавиши: F8 - выполнение инструкции с заходом в процедуру, F10 - выполнение инструкции с обходом процедуры, F7 - выполнить инструкции до текущей команды (если Вы находитесь в окне кода). Нажатие клавиши F7 эквивалентно команде HERE. Клавиша F12 - выполнять код, пока не встретится команда RET (команда P с параметром RET). F11 - вернуться к последней выполненной команде CALL. В командной строке Вы можете использовать команду T, которая имеет следующий формат: Т [=address] [count]. Здесь address - адрес, с которого начинается выполнение, count - количество шагов.
Точки останова. Самое мощное и удобное средство отладки программы SoftIce.
Обычные точки останова. Список точек останова можно просмотреть при помощи команды BL. Каждой точке останова ставится в соответствие номер, который Вы увидите в списке. Удалить точку останова из списка можно командой ВС <номер или список>. Все точки останова можно удалить командой ВС *. Находясь в окне кода, Вы можете поставить точку останова, нажав клавишу F9, строка при этом будет подсвечена. Повторное нажатие клавиши F9 снимает точку останова с текущей строки. Команда BD снимает точку останова, но не удаляет ее из списка, команда BE вновь активизирует точку останова. Поставить точку останова можно также командой bpx addess.
Точки останова на функции API. Например, bpx MessageBoxA поставит точку останова на любой вызов данной функции. Для того чтобы проверить, распознает отладчик функции данной группы, выполните команду exp MessageBox, при этом в командное окно будет выведен соответствующий список.
Точки останова с условием. Общий формат команды bpx имеет вид: BPX [address] [if expression] [do "command1; command2.."]. Таким образом, точка останова сработает только при выполнении условия. При этом будет выполнена последовательность команд отладчика. Например, возможно такое выражение: BPX eip if eax=10 do "db bx".
Точки останова на сообщения. Точку останова на сообщения можно поставить при помощи команды bmsg. При этом надо знать дескриптор окна. Но на это есть специальная команда hwnd, которая дает список окон, их дескрипторов и адресов их процедур. Например, BMSG 0b0f wm_destroywindow означает установить точку останова на сообщение WM_DESTROYWTNDOW, приходящее на окно с дескриптором 0b0fh. Поскольку сообщения представляют собой определенные целые числа, можно через пробел указывать диапазон сообщений. Кроме того, точка останова может быть дополнена условием (if) и набором команда (do).
Другие команды. Команд в SoftIce неисчислимое количество, точнее около 200. Вы можете посмотреть описание их в документации, которая прилагается к пакету, либо выполнив в командной строке команду h(F1). Поражает обилие команд для получения системной информации. Думаю, что во всех этих возможностях Вы разберетесь сами.
Примеры использования программ W32Dasm и SoftIce вы найдете в последующих главах.
55
Автору не известно о возможностях использования отладчика в Windows 2000 либо новых версиях, обладающих такими возможностями.
Окно буфера обмена
Окно буфера обмена.
С помощью данного окна можно следить за содержимым буфера обмена при выполнении программы.
Окно CPU отладчика Turbo Debugger
Рисунок 4.3.2. Окно CPU отладчика Turbo Debugger.
Окно файлов
Окно файлов.
В этом окне можно просмотреть двоичный файл и исправить его при необходимости.
Окно иерархии (Hierarchy)
Окно иерархии (Hierarchy).
Выводит на дисплей иерархическое дерево для всех объектов и всех типах классов, используемых в текущем модуле. Пример окна, отображающего иерархию классов объектов, см. на Рисунок 4.3.3.
Окно истории выполнения (Execution History)
Окно истории выполнения (Execution History).
Окно содержит историю выполнения программы.
Окно модификации отлаживаемого кода
Рисунок 4.4.8. Окно модификации отлаживаемого кода.
Отладчик позволяет выдавать дополнительную информацию о выполняемых API-функциях. Чтобы воспользоваться этим, необходимо сделать следующее. В управляющем окне установите флаги: "Enable Documented API Detail", "Stop Auto On API". Далее запустите программу на выполнение клавишей F5. При прохождении API-функции будет производиться остановка и на экране будет появляться окно с информацией о данной функции,
Окно модификации регистров и ячеек памяти Дополнительные возможности для работы с API
Рисунок 4.4.9. Окно модификации регистров и ячеек памяти. Дополнительные возможности для работы с API.
К использованию программы W32Dasm мы еще вернемся в последующих главах.
54 Хотя W32Dasm работает с разного типа модулями, мы рассматриваем только модули формата РЕ.
Окно отображения памяти
Окно отображения памяти.
Дает возможность построчного просмотра памяти.
Окно переменных
Окно переменных.
Отображает все переменные, доступные в данном месте.
Окно появляющееся при запуске
Рисунок 4.6.2. Окно задержки.
После 15-ти запусков появляется окно Рисунок 4.6.3 и происходит выход из программы.
Окно потоков
Окно потоков.
Данное окно содержит информацию обо всех потоках, работающих в данный момент в программе.
Окно регистрации (Log)
Окно регистрации (Log)
. В нем хранится информация о происходящих в отладчике событиях.
Окно слежения за переменными
Окно слежения за переменными.
Окно открывается через View/Watches. Щелкнув правой кнопкой мыши, мы можем по желанию добавить в окно переменные, за значением которых мы хотим наблюдать. При пошаговом выполнении программы, либо при выполнении с точками останова можно на каждом этапе контролировать значение переменных.
Окно сообщений
Окно сообщений.
С помощью данного окна можно отслеживать все сообщения, получаемые указанным окном. На Рисунок 4.3.4 представлен пример использования окна сообщений. Можно выделить класс отслеживаемых сообщений или задать единичные сообщения. Кроме отслеживания, можно задать и другую реакцию — прервать выполнение, т.е. установить точку останова на определенное сообщение.