Часть 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) Простые ресурсы можно создавать в обычном текстовом редакторе. Язык описания ресурсов будет подробно рассмотрен далее.