Часть IV. Отладка, анализ кода программ, драйверы
Вопросы, затронутые в данной части, относятся к категории сложных. Отладка и анализ кода программ невозможен без хорошего знания ассемблера. Однако если Вы дошли до этого места книги, значит Вам по плечу и такой материал.
Глава 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*? смещ. сегмент ... смещ.
сегмент |
Рис. 4.1.1. Структура ЕХЕ-программы для MS DOS.
Более подробно разбор заголовка DOS-программы можно найти в [1]. Добавлю только, что сразу за таблицей перемещения начинается исполняемая часть модуля. Таблица же перемещения используется для того, чтобы при загрузке настроить адреса. Но это лишь в том случае, если в программе используются адреса сегментов. В противном случае таблица перемещения не содержит элементов, и исполняемый код начинается сразу за форматированной частью заголовка. Перейдем теперь к общей структуре РЕ-модуля.
I
Общая структура РЕ-модуля. Начало заголовка ЕХЕ-файлов в 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
Рис. 4.1.3. Фрагмент РЕ-заголовка.
На рис. 4.1.3 показан фрагмент PE
-заголовка. Обратите внимание,
что по смещению 3CH
действительно находится адрес начала основного
заголовка (символы PE
).
II
В таблице, представленной ниже мы даем описание заголовка РЕ
.
Смещение | Длина поля | Название поля | Описание поля |
---|---|---|---|
00h | DWORD | Signature Bytes | Сигнатура. Первые два байта "PE " 4550Н . Еще два
байта обязательно должны быть равны нулю. |
04h | WORD | CPU Type | Данное поле указывает на процессор, который следует предпочесть при запуске
программы. Вот возможное значение этого поля:0000h -
неизвестный процессор.014Ch - i386014Dh -
i486014Eh - i5860162h - MIPS Mark I (R2000,
R3000)0163h - MIPS Mark II (R6000)0166h - MIPS
Mark III (R4000)Чаще всего данное поле указывает на процессор 386. |
06h | WORD | Num of Objects | Поле указывает на число реальных входов в Object Table (см. таб. ниже). |
08h | DWORD | Time/Date Stamp | Дата и время, которые устанавливаются при компоновке программы. |
0Ch | DWORD | Pointer to COFF table | Дополнительный указатель, определяющий местонахождение отладочной COFF-таблицы в файлах. Это поле используется только в OBJ-файлах и РЕ-файлах, содержащих отладочную COFF-информацию. |
10h | DWORD | COFF table size | Количество символов в COFF-таблице. |
14h | WORD | NT Header Size | Размер заголовка РЕ-файла, начиная с поля Magic - таким
образом, общий размер заголовка РЕ-файла составляет NT Header Size +
18h . |
16h | WORD | Flags | Указывает на предназначение программы. Значение
флагов:0000h - это программа;0001h - файл не
содержит перемещений и таблицы перемещаемых элементов;0002h -
образ в файле можно запускать. Если этот бит не установлен, то это обычно
указывает на ошибку, обнаруженную на этапе линковки, или же на то, что код был
инкрементально отлинкован (инкрементальная линковка - частичная линковка кода
при изменении участка программы, а не тотальная перекомпиляция
проекта);0200h - загружать в память фиксированно. Указывает на
то, что программу можно грузить только по адресу, записанному в Image Base, если
это невозможно, то такой файл лучше вообще не запускать.2000h -
это библиотека. |
18h | WORD | Magic | Слово-сигнатура, определяющее состояние отображенного файла. Определены
следующие значения:107Н - отображение ПЗУ.10BH
- нормально исполняемое отображение. |
1Ah | BYTE | Link Major | Старший номер версии использовавшегося при создании модуля компоновщика. Десятичный вид. |
1Bh | BYTE | Link Minor | Младший номер версии использовавшегося при создании модуля компоновщика. Десятичный вид. |
1Ch | DWORD | Size of Code | Размер именно программного кода в файле. KERNEL использует это значение для
фактического отведения памяти под загружаемую программу. Установка этого
значения слишком маленьким приведет к выдаче сообщения о нехватке памяти. Обычно
большинство модулей имеют только одну программную секцию
.text . |
20h | DWORD | Size of Init Data | Размер секции инициализированных данных, очевидно, не используется в Windows 95, но используется в Windows NT. Назначение аналогично приведенному выше. |
24h | DWORD | Size of UnInit Data | Размер секции неинициализированных данных. Неинициализированные данные
обычно содержатся в секции .bss . Данная секция не занимает на диске
никакого места, но при загрузке модуля в память загрузчик отводит под нее
память. |
28h | DWORD | Entry point RVA | Адрес относительно Image Base , no которому передается
управление при запуске программы или адрес инициализации/завершения
библиотеки. |
2Ch | DWORD | Base of Code | Адрес секции относительно базового адреса (40000Н ), содержащей
программный код. Этот адрес обычно равен 1000Н для компоновщика
Microsoft и 10000H для компоновщика Borland. |
30h | DWORD | Base of Data | Адрес относительно базового (40000H ), с которого начинаются
секции данных файла. Секции данных обычно идут последними в памяти, после
заголовка РЕ и программных секций. |
34h | DWORD | Image Base | При создании компоновщик помещает сюда адрес, куда будет отображен исполняемый файл в памяти. Если загрузчик отобразит файл именно по этому адресу, то дополнительной настройки не потребуется. |
38h | DWORD | Object align | Выравнивание программных секций. После отображения в память каждая секция будет обязательно начинаться с виртуального адреса, кратного данной величине. |
3Ch | DWORD | File align | В случае РЕ-файла исходные данные, которые входят в состав каждой секции,
будут обязательно начинаться с адреса, кратного данной величине. Значение по
умолчанию составляет 200Н . |
40h | WORD | OS Major | Старший номер версии операционной системы, необходимый для запуска программы. |
42h | WORD | OS Minor | Младший номер версии операционной системы. |
44h | WORD | USER Major | Пользовательский номер версии, задается пользователем при линковке программы. Старшая часть. |
46h | WORD | USER Minor | Пользовательский номер версии, младшая часть. |
48h | WORD | SubSys Major | Старший номер версии подсистемы. |
4Ah | WORD | SubSys Minor | Младший номер версии подсистемы. Типичное значение версии 4.0, что означает Windows 95. |
4Ch | DWORD | Reserved | Зарезервировано. |
50h | DWORD | Image Size | Представляет общий размер всех частей отображения, находящихся под контролем загрузчика. Эта величина равна размеру области памяти, начиная с базового адреса отображения и заканчивая адресом конца последней секции. Адрес конца секции выровнен на ближайшую верхнюю границу секции. |
54h | DWORD | Header Size | Общий размер всех заголовков: DOS Stub + РЕ Header + Object
Table |
58h | DWORD | File CheckSum | Контрольная сумма всего файла. Как и в операционной системе MS DOS, ее никто
не контролирует, а программа редактирования связей устанавливает ее в
0 . Предполагалось ее рассчитывать как инверсию суммы всех байтов
файла. |
5Ch | WORD | Subsystem | Операционная подсистема, необходимая для запуска данного файла. Вот значения
этого поля:1 - подсистема не требуется
(NATIVE).2 - запускается в подсистеме Windows
GUI.3 - запускается в подсистеме Windows character
(терминальное или консольное приложение).5 - запускается в
подсистеме OS/2.7 - запускается в подсистеме Posix. |
5Eh | WORD | DLL Flags | Указывает на специальные потребности при загрузке, начиная с операционной системы NT 3.5. Устарел и не используется. |
60h | DWORD | Stack Reserve Size | Память, требуемая для стека приложения. Память резервируется, но выделяется
только Stack Commit Size байтов. Следующая страница является
охранной. Когда приложение достигает этой страницы, то она становится доступной,
а следующая страница - охранной, и так до достижения нижней границы, после чего
Windows 95 убивает программу. |
64h | DWORD | Stack Commit Size | Объем памяти, отводимой для стека немедленно после загрузки. |
68h | DWORD | Heap Reserve Size | Максимально возможный размер локальной кучи. |
6Ch | DWORD | Heap Comit Size | Отводимый размер кучи при загрузке. |
70h | DWORD | Loader Flags | Начиная с Windows NT 3.5 объявлено неиспользуемым, назначение неясно, но в целом связано с поддержкой отладки. |
74h | DWORD | Num of RVA and Sizes | Указывает размер массива VA/Size , который следует ниже, данное
поле зарезервирована под будущие расширения формата. В данный момент его
значение всегда равно 10h . |
78h | DWORD | Export Table RVA | Относительный адрес (относительно базового адреса) таблицы экспорта. |
7Ch | DWORD | Export Data Size | Размер таблицы экспорта. |
80h | DWORD | Import Table RVA | Относительный адрес (относительно базового адреса) таблицы импорта. |
84h | DWORD | Import Data Size | Размер таблицы импорта. |
88h | DWORD | Resource Table RVA | Относительный адрес (относительно базового адреса) таблицы ресурсов. |
8Ch | DWORD | Resource Data Size | Размер таблицы ресурсов. |
90h | DWORD | Exception Table RVA | Относительный адрес таблицы исключений. |
94h | DWORD | Exception Data Size | Размер таблицы исключений. |
98h | DWORD | Security Table RVA | Адрес таблицы безопасности. По-видимому, не используется. |
9Ch | DWORD | Security Data Size | Размер таблицы безопасности. |
A0h | DWORD | Fix Up's Table RVA | Относительный адрес таблицы настроек. |
A4h | DWORD | Fix Up's Data Size | Размер таблицы настроек. |
A8h | DWORD | Debug Table RVA | Относительный адрес таблицы отладочной информации. |
ACh | DWORD | Debug Data Size | Размер таблицы отладочной информации. |
B0h | DWORD | Image Description RVA | Относительный адрес строки описания модуля. |
B4h | DWORD | Description Data Size | Размер строки описания модуля. |
B8h | DWORD | Machine Specific RVA | Адрес таблицы значений, специфичных для микропроцессора. |
BCh | DWORD | Machine Data Size | Размер таблицы значений, специфичных для микропроцессора. |
C0h | DWORD | TLS RVA | Указатель на локальную область данных цепочек. |
C4h | DWORD | TLS Data Size | Размер области данных цепочек. |
C8h | DWORD | Load Config RVA | Предназначение неизвестно. |
CCh | DWORD | Load Config Data Size | Предназначение неизвестно. |
D0h | 08h | Reserved | Зарезервировано. |
D8h | DWORD | IAT RVA | Используется в NT. В Windows 95, судя по всему, нет. |
DCh | DWORD | IAT Data Size | Размер описанного поля. |
E0h | 08h | Reserved | Зарезервировано. |
E8h | 08h | Reserved | Зарезервировано. |
F0h | 08h | Reserved | Зарезервировано. |
Между заголовком РЕ и данными для секций расположена таблица секций. Вот элемент этой таблицы.
Элемент таблицы секций содержит полную базу данных об одной секции.