Assembler для Windows


Часть I. Основы 32-битного программирования в Windows

Глава 1. Средства программирования в Windows

I

В данной главе я намерен дать некоторую вводную информацию по средствам программирования на языке ассемблера. Данная глава предназначена для начинающих программирование на ассемблере, поэтому программистам более опытным ее можно пропустить без особого ущерба для себя.

Прежде всего замечу, что в названии главы есть некоторая натяжка, т.к. технологии трансляции и в MS DOS, и в Windows весьма схожи. Однако программирование в MS DOS уходит в прошлое.

Рис.1.1.1. Схема трансляции ассемблерного модуля.

Двум стадиям трансляции (Рис.1.1.1) соответствуют две основные программы: ассемблер ML.EXE и редактор связей LINK.EXE 7 (или TASM32.EXE и TLINK32.EXE в Турбо Ассемблере).

Пусть файл с текстом программы на языке ассемблера называется PROG.ASM, тогда, не вдаваясь в подробный анализ, две стадии трансляции будут выглядеть следующим образом: c:\masm32\bin\ml /c /coff PROG.ASM - в результате появляется модуль PROG.OBJ, а также c:\masm32\bin\link /SUBSYSTEM:WINDOWS PROG.OBJ - в результате появляется исполняемый модуль PROG.EXE. Как Вы, я надеюсь, догадались и /coff являются параметрами программы ML.EXE, a /SUBSYSTEM:WINDOWS является параметром для программы LINK.EXE. О других ключах этих программ более подробно см. Гл. 1.5.

Чем больше я размышляю об этой схеме трансляции, тем более совершенной она мне кажется. Действительно, формат конечного модуля зависит от операционной системы. Установив стандарт на структуру объектного модуля, мы получаем возможность: а) использовать уже готовые объектные модули, б) стыковать между собой программы, написанные на разных языках. Но самое прекрасное здесь то, что если стандарт объектного модуля распространить на разные операционные системы, то можно использовать модули, написанные в разных операционных системах8.

Чтобы процесс трансляции сделать для Вас привычным, рассмотрим несколько простых, "ничего не делающих" программ.

.386P
; плоская модель
.MODEL FLAT, STDCALL
;--------------------------------------------------
; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
_DATA ENDS
; сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
RET ; выход
_TEXT ENDS
END START

Рис.1.1.2. "Ничего не делающая" программа.

На Рис. 1.1.2 представлена "ничего не делающая" программа. Назовем ее PROG1. Сразу отмечу на будущее: команды микропроцессора и директивы макроассемблера будем писать заглавными буквами.

Итак, чтобы получить загружаемый модуль, выполним следующие команды:

ML /c /coff PROG1.ASM
LINK /SUBSYSTEM:WINDOWS PROG1

или для Турбо Ассемблера

TASM32 /ml PROG1.ASM
TLINK32 -аа PROG1.OBJ

Примем пока параметры трансляции программ как некую данность и продолжим наши изыскания.

Часто удобно разбить текст программы на несколько частей и объединять эти части еще на 1-й стадии трансляции. Это достигается посредством директивы INCLUDE. Например, один файл будет содержать код программы, а константы, данные (определение переменных) и прототипы внешних процедур помещаются в отдельные файлы. Часто такие файлы записывают с расширением .INC.

Именно такая разбивка демонстрируется в следующей программе (Рис.1.1.3).

;файл CONS.INC
CONS1 EQU 1000
CONS2 EQU 2000
CONS3 EQU 3000
CONS4 EQU 4000
CONS5 EQU 5000
CONS6 EQU 6000
CONS7 EQU 7000
CONS8 EQU 8000
CONS9 EQU 9000
CONS10 EQU 10000
CONS11 EQU 11000
CONS12 EQU 12000

;файл DAT.INC
DAT1 DWORD 0
DAT2 DWORD 0
DAT3 DWORD 0
DAT4 DWORD 0
DAT5 DWORD 0
DAT6 DWORD 0
DAT7 DWORD 0
DAT8 DWORD 0
DAT9 DWORD 0
DAT10 DWORD 0
DAT11 DWORD 0
DAT12 DWORD 0

;файл PROG1.ASM
.386P
;плоская модель
.MODEL FLAT, STDCALL
;подключить файл констант
INCLUDE CONS.INC
;--------------------------------------------------
;сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
;подключить файл данных
INCLUDE DAT.INC
_DATA ENDS
;сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
MOV EAX,CONS1
SHL EAX,1 ; умножение на 2
MOV DAT1,EAX
;-----------------------------------
MOV EAX,CONS2
SHL EAX,2 ; умножение на 4
MOV DAT2,EAX
;-----------------------------------
MOV EAX, CONS3
ADD EAX, 1000 ; прибавим 1000
MOV DAT3, EAX
;-----------------------------------
MOV EAX, CONS4
ADD EAX, 2000 ; прибавим 2000
MOV DAT4, EAX
;-----------------------------------
MOV EAX, CONS5
SUB EAX, 3000 ; вычесть 3000
MOV DAT5, EAX
;-----------------------------------
MOV EAX, CONS6
SUB EAX, 4000 ; вычесть 4000
MOV DAT6, EAX
;-----------------------------------
MOV EAX, CONS7
MOV EDX, 3
IMUL EDX ; умножение на 3
MOV DAT7, EAX
;-----------------------------------
MOV EAX, CONS8
MOV EDX, 7 ; умножение на 7
IMUL EDX
MOV DAT8, EAX
;-----------------------------------
MOV EAX, CONS9
MOV EBX, 3 ; деление на 3
MOV EDX, 0
IDIV EBX
MOV DAT9, EAX
;-----------------------------------
MOV EAX, CONS10
MOV EBX, 7 ; деление на 7
MOV EDX, 0
IDIV EBX
MOV DAT10, EAX
;-----------------------------------
MOV EAX, CONS11
SHR EAX, 1 ; деление на 2
MOV DAT11, EAX
;-----------------------------------
MOV EAX, CONS12
SHR EAX, 2 ; деление на 4
MOV DAT12, EAX
;-----------------------------------
RET ; выход
_TEXT ENDS
END START

Рис. 1.1.3. Пример использование директивы INCLUDE.

Программа на Рис. 1.1.3 также достаточно бессмысленна (как и все программы данной главы), но зато демонстрирует удобства использования директивы INCLUDE. Напомню, что мы не останавливаемся в книге на очевидных командах микропроцессора. Замечу только по поводу команды IDIV. В данном случае команда IDIV осуществляет операцию деления над операндом, находящемся в паре регистров EDX:EAX. Обнуляя EDX, мы указываем, что операнд целиком находится в регистре EAX.

Трансляция программы осуществляется так, как это было указано ранее для ассемблеров MASM и TASM.

Замечание о типах данных. В данной книге Вы встретитесь в основном с тремя типами данных (простых): байт, слово, двойное слово. При этом используются следующие стандартные обозначения. Байт - BYTE или DB, слово - WORD или DW, двойное слово - DWORD или DD. Выбор, скажем, в одном случае DB, а в другом BYTE, продиктован лишь желанием автора несколько разнообразить изложение.


7 Программу LINK.EXE называют также компоновщиком или просто линковщиком.

8 Правда, весьма ограниченно, т.к. согласование системных вызовов в разных операционных системах может весьма сильно различаться.


II

Перейдем теперь к вопросу о подсоединении других объектных модулей и библиотек во второй стадии трансляции. Прежде всего замечу, что, сколько бы ни подсоединялось объектных модулей, один объектный модуль является главным. Смысл этого весьма прост: именно с этого модуля начинается исполнение программы. На этом различие между модулями заканчивается. Условимся далее, что главный модуль всегда в начале сегмента кода будет содержать метку START, ее мы указываем после директивы END - транслятор должен знать точку входа программы, чтобы указать ее в заголовке загружаемого модуля (см. Гл.5.1).

Обычно во второстепенные модули помещаются процедуры, которые будут вызываться из основного и других модулей. Рассмотрим такой модуль. Этот модуль Вы можете видеть на Рис. 1.4.

.386P
;модуль PROG2.ASM
;плоская модель
.MODEL FLAT, STDCALL
PUBLIC PROC1
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
PROC1 PROC
MOV EAX, 1000
RET
PROC1 ENDP
_TEXT ENDS
END

Рис. 1.1.4. Модуль PROG2.ASM, процедура которого PROC1 будет вызываться из основного модуля.

Прежде всего, обращаю Ваше внимание на то, что после директивы END не указана никакая метка. Ясно, что это не главный модуль, процедуры его будут вызываться из других модулей.

Второе, на что я хотел бы обратить Ваше внимание, это то, что процедура, которая будет вызываться, должна быть объявлена как PUBLIC. В этом случае это имя будет сохранено в объектном модуле и далее может быть связано с вызовами из других модулей.

Итак, выполняем команду ML /coff /c PROG1.ASM. В результате на диске появляется объектный модуль PROG2.OBJ.

А теперь проведем маленькое исследование. Просмотрим объектный модуль с помощью какого-нибудь простого Viewer'a, например того, что есть у программы Far. И что же мы обнаружим: вместо имени PROC1 мы увидим имя _PROC1@0. Это особый разговор — будьте сейчас внимательны! Во-первых, подчеркивание спереди отражает стандарт ANSI, предписывающий всем внешним именам (доступным нескольким модулям) автоматически добавлять символ подчеркивания. Здесь ассемблер будет действовать автоматически, и у нас по этому поводу не будет никаких забот.

Сложнее с припиской @0. Что она значит? На самом деле все просто: цифра после знака @ означает количество байт, которые необходимо передать в стек в виде параметров при вызове процедуры. В данном случае ассемблер понял так, что наша процедура параметров не требует. Сделано это для удобства использования директивы INVOKE. Но о ней речь пойдет ниже, а пока попытаемся сконструировать основной модуль PROG1.ASM.

.386P
; плоская модель
.MODEL FLAT, STDCALL
;--------------------------------------------------
; прототип внешней процедуры
EXTERN PROC1@0:NEAR
; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
_DATA ENDS
; сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
CALL PROC1@0
RET ; выход
_TEXT ENDS
END START

Рис. 1.1.5. Модуль PROG1.ASM с вызовам процедуры из модуля PROG2.ASM.

Как Вы понимаете, процедура, вызываемая из другого модуля, объявляется как EXTERN. Далее, вместо имени PROC1 нам приходится использовать имя PROC1@0. Здесь пока ничего нельзя сделать. Может возникнуть вопрос о типе NEAR. Дело в том, что в операционной системе MS DOS тип NEAR означал, что вызов процедуры (или безусловный переход) будет происходить в пределах одного сегмента. Тип FAR означал, что процедура (или переход) будет вызываться из другого сегмента. В операционной системе Windows реализована так называемая плоская модель, когда всю память можно рассматривать как один большой сегмент. И здесь логично использовать тип NEAR.

Выполним команду ML /coff /c PROG1.ASM, в результате получим объектный модуль PROG1.OBJ. Теперь можно объединить модули и получить загружаемую программу PROG1.EXE:

LINK /SUBSYSTEM:WINDOWS PROG1.OBJ PROG2.OBJ

При объединении нескольких модулей первым должен идти главный, а остальные — в произвольном порядке.

III

Обратимся теперь к директиве INVOKE. Это довольно удобная команда, правда, по некоторым причинам (которые станут понятными позже) я почти не буду употреблять ее в своих программах.

Удобство ее заключается, во-первых, в том, что мы сможем забыть о добавке @N. Во-вторых, эта команда сама заботится о помещении передаваемых параметров в стек. Последовательность команд

PUSH par1
PUSH par2
PUSH par3
PUSH par4
CALL NAME_PROC@N ; N-количество отправляемых в стек байт

заменяется на

INVOKE NAME_PROC, par4, par3, par2, par1

Причем параметрами могут являться регистр, непосредственно значение или адрес. Кроме того, для адреса может использоваться как оператор OFFSET, так и оператор ADDR. Видоизменим теперь модуль PROG1.ASM (модуль PROG2.ASM изменять не придется).

.386P
; плоская модель
.MODEL FLAT, STDCALL
;-----------------------------------
; прототип внешней процедуры
PROC1 PROTO
; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
_DATA ENDS
; сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
INVOKE PROC1
RET ; выход
_TEXT ENDS
END START

Рис. 1.1.6. Пример использования оператора INVOKE.

Как видите, внешняя процедура объявляется теперь при помощи директивы PROTO. Данная директива позволяет при необходимости указывать и наличие параметров. Например, строка

PROC1 PROTO :DWORD, :WORD

будет означать, что процедура требует два параметра длиной в четыре и два байта (всего 6, т.е. @6).

Как уже говорилось, я буду редко использовать оператор INVOKE. Теперь я назову первую причину такого пренебрежения к данной возможности. Дело в том, что я сторонник чистоты языка ассемблера и любое использование макросредств вызывает у меня чувство несовершенства. На мой взгляд, и начинающим программистам не стоит увлекаться макросредствами, иначе не чувствуется вся красота этого языка. О второй причине Вы узнаете ниже.

На нашей схеме, на Рис. 1.1, говорится не только о возможности подсоединения объектных модулей, но и библиотек. Собственно, если объектных модулей несколько, то это по понятным причинам вызовет неудобства. Поэтому объектные модули объединяются в библиотеки. Для подсоединения библиотеки в MASM удобнее всего использовать директиву INCLUDELIB, которая сохраняется в объектном коде и используется программой LINK.EXE.

Но как создать библиотеку из объектных модулей? Для этого имеется специальная программа, называемая библиотекарем. Предположим, мы хотим создать библиотеку LIB1.LIB, состоящую из одного модуля - PROG2.OBJ. Выполним для этого следующую команду: LIB /OUT:LIB1.LIB PROG2.OBJ.

Если необходимо добавить в библиотеку еще один модуль (MODUL.OBJ), то достаточно выполнить команду: LIB LIB1.LIB MODUL.OBJ.

Вот еще два полезных примера использования библиотекаря:
LIB /LIST LIB1.LIB - выдает список модулей библиотеки.
LIB /REMOVE:MODUL.OBJ LIB1.LIB - удаляет из библиотеки модуль MODUL.OBJ.

Вернемся теперь к нашему примеру. Вместо объектного модуля мы используем теперь библиотеку LIB1.LIB. Видоизмененный текст программы PROG1.ASM представлен на Рис. 1.7.

.386P
; плоская модель
.MODEL FLAT, STDCALL
;--------------------------------------------------
; прототип внешней процедуры
EXTERN PROC1@0:NEAR
;--------------------------------------------------
INCLUDELIB LIB1.LIB
;--------------------------------------------------
; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
_DATA ENDS
; сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
CALL PROC1@0
RET ; выход
_TEXT ENDS
END START

Рис. 1.1.7. Пример использование библиотеки.

IV

Рассмотрим теперь менее важный (для нас) вопрос об использовании данных (переменных), определенных в другом объектном модуле. Здесь читателю, просмотревшему предыдущий материал, должно быть все понятно, а модули PROG2.ASM и PROG1.ASM, демонстрирующие технику использования внешних9 переменных, приводятся на Рис.1.8-1.9.

.386P
; модуль PROG2.ASM
; плоская модель
.MODEL FLAT, STDCALL
PUBLIC PROC1
PUBLIC ALT
; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
ALT DWORD 0
_DATA ENDS
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
PROC1 PROC
MOV EAX,ALT
ADD EAX,10
RET
PROC1 ENDP
_TEXT ENDS
END

Рис. 1.1.8. Модуль, содержащий переменную ALT, которая используется в другом модуле (PROG1.ASM).

.386P
; модуль PROG1.ASM
; плоская модель
.MODEL FLAT, STDCALL
;-----------------------------------
; прототип внешней процедуры
EXTERN PROC1@0:NEAR
; внешняя переменная
EXTERN ALT: DWORD
; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
_DATA ENDS
; сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
MOV ALT, 10
CALL PROC1@0
MOV EAX, ALT
RET ; выход
_TEXT ENDS
END START

Рис. 1.1.9. Модуль, использующий переменную ALT, определенную в другом модуле (PROG2.ASM).

Заметим, что в отличие от внешних процедур, внешняя переменная не требует добавки @N, поскольку размер переменной известен.


9 Термин "внешняя переменная" используется нами по аналогии с термином "внешняя процедура".


V

А теперь проверим все представленные в данной главе программы на предмет их трансляции средствами пакета TASM.

С программами на Рис. 1.2-1.3 дело обстоит просто. Для их трансляции достаточно выполнить команды:

TASM32 /ml PROG1.ASM
TLINK32 -аа PROG1.OBJ

Обратимся теперь к модулям PROG2.ASM и PROG1.ASM, приведенным на Рис. 1.4 и 1.5 соответственно.

Получение объектных модулей происходит без каких-либо трудностей. Просматривая модуль PROG2.OBJ, мы увидим, что внешняя процедура представлена просто именем PROC1.

Следовательно, единственное, что нам следует сделать, это заменить в модуле PROC1.ASM имя PROC1@0 на PROC1.

Объединение модулей далее производится элементарно:

TLINK32 -аа PROG1.OBJ PROG2.OBJ

Для работы с библиотекой в пакете TASM имеется программа — библиотекарь TLIB.ЕХЕ. Создание библиотеки, состоящей из модуля PROG2.OBJ, производится по команде

TLIB LIB1.LIB+PROG2.OBJ

В результате на диске появится библиотека LIB1.LIB. Далее компонуем модуль PROG1.OBJ с этой библиотекой:

TLINK32 -аа PROG1,PROG1,PROG1,LIB1

В результате получается загружаемый модуль PROG1.EXE.

Вообще, стоит разобраться с командной строкой TLINK32 более подробно. В расширенном виде11 эта строка выглядит следующим образом:

TLINK32 -аа OBJFILES, EXEFILE, MAPFILE, LIBFILES

OBJFILES - один или несколько объектных файлов (через пробел). Первый главный модуль.

EXEFILE - исполняемый модуль.

MAPFILE - МАР-файл, содержащий информацию о структуре модуля.

LIBFILES - одна или несколько библиотек (через пробел).

В TASM отсутствует директива INVOKE, поэтому в дальнейшем я буду избегать ее использования12.

В начале книги я объявил о своем намерении примирить два ассемблера. Поскольку различие между ними заключается в директивах и макрокомандах (см. Гл.1.5), то напрашивается вывод, что совместимости можно добиться, избегая таких директив и макрокоманд. Основой программы для Windows является вызов API-функций (см. Гл. 1.2), а мы знаем, что различие в вызове внешней процедуры заключается в том, что в именах для MASM в конце есть добавка @N. И здесь не обойтись без макроопределений, и начинается самое интересное. Но об этом, дорогой читатель, Вы узнаете в свое время.

И MASM и TASM поддерживают так называемую упрощенную сегментацию. Я являюсь приверженцем классической структуры ассемблерной программы и должен признаться, что упрощенная сегментация довольно удобная штука, особенно при программировании под Windows. Суть такой сегментации в следующем: начало сегмента определяется директивой .CODE, а сегмента данных - .DATA13. Причем обе директивы могут появляться в тексте программы несколько раз. Транслятор затем собирает код и данные вместе, как положено. Основной целью такого подхода, по-видимому, является возможность приблизить в тексте программы данные к тем строкам, где они используются. Такая возможность, как известно, в свое время была реализована в C++. На мой взгляд, она приводит к определенному неудобству при чтении текста программы. Кроме того, не сочтите меня за эстета, но когда я вижу данные, перемешанные в тексте программы с кодом, у меня возникает чувство дискомфорта.

Ниже представлена программа, демонстрирующая упрощенный режим сегментации.

.386P
; плоская модель
.MODEL FLAT, STDCALL
;--------------------------------------------------
; сегмент данных
.DATA
SUM DWORD 0
; сегмент кода
.CODE
START:
; сегмент данных
.DATA
A DWORD 100
; сегмент кода
.CODE
MOV EAX,A
; сегмент данных
.DATA
B DWORD 200
; сегмент кода
.CODE
ADD EAX,B
MOV SUM,EAX
RET ; выход
END START

Рис. 1.1.10. Пример программы, использующей упрощенную сегментацию.


11 Все же в несколько упрощенном виде.

12 Честно говоря, не знаю ничего лучшего, чем собственноручно отправлять параметры в стек.

13 Разумеется, есть директива и для стека — это .STACK, но мы ее почти не будем использовать.


VI

В заключительной части главы я хотел дать краткий обзор ряда других программ, которые часто используются при программировании на ассемблере. С некоторыми из перечисленных программ мы познакомимся в дальнейшем более подробно, а некоторые более упоминаться не будут.

Редакторы. Хотя сам я никогда не использую специализированные редакторы для написания программ на ассемблере, полноты ради кратко расскажу о двух известных мне редакторах. Начну с редактора QEDITOR.EXE, который поставляется вместе с пакетом MASM32. Сам редактор и все сопутствующие ему утилиты написаны на ассемблере. Анализ их размера и возможностей действительно впечатляет. Например, сам редактор имеет длину всего 27 Кб, а утилита, используемая для просмотра отчетов о трансляции — всего 6 Кб. Редактор вполне годится для работы с небольшими одномодульными приложениями. Для работы с несколькими модулями он не очень удобен. Работа редактора основана на взаимодействии с различными утилитами посредством пакетных файлов. Например, трансляцию программ осуществляет пакетный файл ASSMBL.BAT, который использует ассемблер ML.EXE, а результат ассемблирования направляется в текстовый файл ASMBL.TXT. Далее для просмотра этого файла используется простая утилита THEGUN.EXE. Аналогично осуществляется редактирование связей. Для дизассемблирования исполняемого модуля используется утилита DUMPPE.EXE, результат работы этой утилиты помещается в текстовый файл DISASM.TXT. Аналогично осуществляются и другие операции. Вы легко сможете настроить эти операции, отредактировав соответствующий пакетный файл с модификацией (при необходимости) используемых утилит (заменив, например, ML.EXE на TASM32.EXE и т.п.).

Вторая программа, с которой я хочу познакомить читателя, это EAS.EXE (Easy Assembler Shell). Редактор, а точнее оболочка, позволяет создавать и транслировать довольно сложные проекты, состоящие из ASM-,OВJ-,RC-,RES-,DEF-файлов. Программа позволяет работать как с TASM, так и MASM, а также с другими утилитами (отладчиками, редакторами ресурсов и т.д.). Непосредственно в программе можно настроить компиляторы и редакторы связей на определенный режим работы путем задания ключей этих утилит.

Отладчики.

Отладчики позволяют исполнять программу в пошаговом режиме. В IV части книги мы более подробно будем рассматривать отладчики и дизассемблеры. Приведу несколько наиболее известных отладчиков14: CodeView (Микрософт), Turbo Debugger (Borland), Ice.

Дизассемблеры.

Дизассемблеры переводят исполняемый модуль в ассемблерный код. Примером простейшего дизассемблера является программа DUMPPE.EXE, работающая в строковом режиме. Пример работы программы DUMPPE.EXE представлен на Рис. 1.1.11. Здесь дизассемблируется программа, приведенная на Рис.1.1.5. Ну как, узнали нашу программу? Смысл обозначений будет ясен из дальнейшего изложения.

knia.exe (hex) (dec)
.EXE size (bytes) 490 1168
Minimum load size (bytes) 450 1104
Overlay number 0 0
Initial CS:IP 0000:0000
Initial SS:SP 0000:OOB8 184
Minimum allocation (para) 0 0
Maximum allocation (para) FFFF 65535
Header size (para) 4 4
Relocation table offset 40 64
Relocation entries 0 0
Portable Executable starts at a8
Signature 00004550 (РЕ)
Machine 014C (Intel 386)
Sections 0001
Time Date Stamp 3AE6D1B1 Wed Apr 25 19:31:29 2001
Symbol Table 00000000
Number of Symbols 00000000
Optional header size OOEO
Characteristics O1OF
Relocation information stripped
Executable Image
Line numbers stripped
Local symbols stripped
32 bit word machine
Magic 010B
Linker Version 5.12
Size of Code 00000200
Size of Initialized Data 00000000
Size of Uninitialized Data 00000000
Address of Entry Point 00001000
Base of Code 00001000
Base of Data 00002000
Image Base 00400000
Section Alignment 00001000
File Alignment 00000200
Operating System Version 4.00
Image Version 0.00
Subsystem Version 4.00
Reserved 00000000
Image Size 00002000
Header Size 00000200
Checksum 00000000
Subsystem 0002 (Windows)
DLL Characteristics 0000
Size Of Stack Reserve 00100000
Size Of Stack Commit 00001000
Size Of Heap Reserve 00100000
Size Of Heap Commit 00001000
Loader Flags 00000000
Number of Directories 00000010
Directory Name VirtAddr VirtSize
----------------------------------- -------- --------
Export 00000000 00000000
Import 00000000 00000000
Resource 00000000 00000000
Exception 00000000 00000000
Security 00000000 00000000
Base Relocation 00000000 00000000
Debug 00000000 00000000
Decription/Architecture 00000000 00000000
Machine Value (MIPS GP) 00000000 00000000
Thread Storage 00000000 00000000
Load Configuration 00000000 00000000
Bound Import 00000000 00000000
Import Address Table 00000000 00000000
Delay Import 00000000 00000000
СОМ Runtime Descriptor 00000000 00000000
(reserved) 00000000 00000000
Section Table
-------------
Virtual Address 0001000
Virtual Size OOOOOE
Raw Data Offset 000200
Raw Data Size 0000200
Relocation Offset 000000
Relocation Count 000
Line Number Offset 0000000
Line Number Count 000
Characteristics 0000020
Code
Executable
Readable
Disassembly
00401000 start:
00401000 E803000000 call fn_00401008
00401005 C3 ret
00401006 CC int 3
00401007 CC int 3
00401008 fn_00401008:
00401008 B8E8030000 mov eax,3E8h
0040100D C3 ret

Рис. 1.1.11. Пример дизассемблированш программы с помощью DUMPPE.EXE.

Отмечу также дизассемблер W32Dasm, который подробно будет описан в последней части книги, и знаменитый дизассемблер IDA Pro. В части IV мы будем подробно рассматривать и сами дизассемблеры, и методику их использования.

Нех-редакторы

Нех-редакторы позволяют просматривать и редактировать загружаемые модули в шестнадцатеричном виде. Их великое множество, к тому же отладчики и дизассемблеры, как правило, имеют встроенные НЕХ-редакторы. Отмечу только, весьма популярную в хакерских кругах программу HIEW.EXE. Эта программа позволяет просматривать загружаемые модули как в шестнадцатеричном виде, так и в виде ассемблерного кода. И не только просматривать, но и редактировать.

Компиляторы ресурсов

В пакетах MASM32 и TASM32 есть компиляторы ресурсов, которые будут описаны ниже. Это программы RC.EXE и BRC32.EXE соответственно.

Редакторы ресурсов

Обычно я пользуюсь редактором ресурсов из пакета ВС5 (Borland C++ 5.0) Простые ресурсы можно создавать в обычном текстовом редакторе. Язык описания ресурсов будет подробно рассмотрен далее.


14 Программа DEBUGER.EXE все еще поставляется с операционной системой Windows, но этот отладчик не поддерживает новый формат исполняемых файлов.