Примеч. ред.
372 Глава
Создание
на ассемблере
Листинг
push
DWORD
eax
Строка 18
push 0
call DWORD
mov DWORD PTR
eax
; Строка 24
lea
push
call
test
jne
; Строка 25
xor
; Строка 27
; Строка 39
push
mov
push
push
push
push
push
push
push
push
push
push
push
call
mov
Строка 41
mov
mov
push
call
; Строка 42
mov
push
call
; Строка 44
push
push
push
lea
push
call
test
je
Строка 46
lea
push
call
; Строка 47
eax, DWORD
eax
DWORD
eax, ax
eax, eax
$L29705
eax, eax
0
eax, DWORD
eax
0
0
;80000000H
;80000000H
-2147483648
-2147483648
-2147483648
-2147483648 ;80000000H
OFFSET
OFFSET
0
DWORD
DWORD
eax
eax, DWORD
eax, DWORD
eax
DWORD
eax, DWORD
eax
DWORD
0
0
0
eax, DWORD
eax
DWORD
eax, eax
eax, DWORD
eax
DWORD
Каркасное Windows-приложение на C/C++ 373
lea eax,
push eax
; Строка 48
; Строка 49
raov
jmp
; Строка 50
pop edi
pop esi
pop ebx
leave
ret 16
ENDP
DWORD PTR_lpMsg$[ebp]
DWORD
eax, DWORD
PROC NEAR
; Строка 55
push ebp
ebp, esp
sub esp, 4
ebx
edi
eax, DWORD
DWORD PTR
eax
push
push
push
; Строка 56
mov
mov
jmp
; Строка 58
; Строка 59
push 0
call DWORD
; Строка 60
jmp
; Строка 61
Строка 64
mov eax,
push eax
mov
push
DWORD
DWORD
DWORD
mov
push
mov
push
call
jmp
; Строка 65
jmp
jmp
$L29720:
; Строка 66
xor
jmp
; Строка 67
$L29718:
pop
pop
DWORD
eax
eax
eax
DWORD
DWORD PTR
2
eax, eax
edi
esi
374 Глава
Создание Windows-приложений на ассемблере
Листинг
pop ebx
leave
ret 16 ;00000010H
_TEXT ENDS
END
Беглый взгляд на код листинга 16.2 показывает, что он оформлен как полно-
ценная программа на ассемблере. Код программы был сгенерирован компилято-
ром Visual C++ 6.0 от Microsoft, поэтому транслятор
может сделать из него
загрузочный модуль без дополнительного редактирования. Для этого компания
Microsoft даже подготовила специальный включаемый файл
(он нахо-
дится в каталоге пакета VC++
а также среди файлов, прилага-
емых к книге).
Таким образом, имея исходный файл Windows-приложения на языке C/C++,
можно получить текст на языке ассемблера. Теоретически, на его основе впослед-
ствии можно сформировать функционально эквивалентный исполняемый модуль.
Более того, если задаться такой целью, то не составит большого труда переделать
его для обработки транслятором TASM 5.0.
Но не все так просто. Готов расстроить читателя, но мы изложили очень упро-
щенный подход к разработке Windows-приложения на ассемблере. Полученный
код можно рассматривать лишь как схему (шаблон) такого приложения. Реально
все несколько сложнее. Прежде чем мы объясним имеющиеся проблемы, прове-
дем еще один эксперимент — дизассемблируем исполняемый модуль программы
Причем сделать это нужно тем дизассемблером, который «понимает»
интерфейс Win32 API. В данной книге для этой цели был использован дизассемб-
лер IDA.
с его помощью файл можно сохранить как лис-
тинг
и как исходный текст ассемблера
Это говорит о том, что IDA
также пытается по загрузочному модулю построить файл, пригодный для повтор-
ной трансляции. В начале
им текста даже приводятся ре-
комендации по использованию соответствующих параметров транслятора TASM.
В контексте нашего изложения наибольший интерес представляет файл лис-
тинга
формируемый IDA. Анализ его содержимого позволяет изнутри по-
смотреть на результаты процесса формирования исполняемого модуля трансля-
тором и редактором связей. Файл листинга в своей левой части содержит колонку
с адресными смещениями команд. Все метки и символические имена в дизассем-
блированном тексте формируются с учетом этих смещений, поэтому данный файл
удобно использовать для анализа.
Файл листинга IDA имеет большой размер, поэтому привести весь его текст
в книге невозможно, да это и не нужно. Для нас интерес представляют лишь от-
дельные фрагменты этого файла (листинг 16.3). Полностью Дизассемблированный
код программы
находится среди фалов, прилагаемых к книге.
Листинг
Фрагменты дизассемблированного кода каркасного
Windows-приложения (дизассемблер IDA)
Этот файл сгенерирован интерактивным
Copyright (с) 1997 by
Каркасное Windows-приложение на C/C++ 375
00401000
файл должен компилироваться следующей командной строкой:
00401000 р386
00401000 model flat
proc near ;CODE XREF: start+146_p
00401000
00401001
00401003
00401006
00401007
00401008
00401037
00401039
0040103F
00401042
00401047
00401049
00401072
00401075
00401076
0040107C
0040108E
0040108E
00401090
00401093
00401094
00401096
00401098
0040109D
004010A2
004010A7
004010AC
004010B1
004010BB
004010BD
00401004
004010D7
004010D8
004010DE
004010DE
004010DE
004010E0
004010E2
004010E4
004010E7
004010E8
004010EE
004010F0
004010F6
004010F9
004010FA
00401100
00401103
00401104
0040110A
push
mov
sub
push
push
push
push
call
mov
push
push
call
lea
push
call
push
mov
push
push
push
push
push
push
push
push
push
push
push
call
mov
push
call
push
push
push
lea
push
call
test
lea
push
call
lea
push
call
ebp
ebp, esp
esp, 50h
ebx
esi
0
[ebp+var
eax
7F00h
0
eax, [ebp+var_50]
eax
ds:RegisterClassExA
eax, ax
: ;CODE XREF:
j
0
eax, [ebp+arg_0]
eax
0
0
80000000h
80000000h
offset unk 404034
offset unk 404020
0
ndow
eax, [ebp+var_4]
eax
: ; CODE XREF:
j
0
0
0
eax, [ebp+var_20]
eax
eax, eax
loc_40110F
eax, [ebp+var_20]
eax
eax, [ebp+var_20]
eax
ds
loc_4010DE
продолжение
376 Глава
Создание Windows-приложений на ассемблере
Листинг
00401117
;CODE
00401117 pop
00401118 pop esi
00401119 pop ebx
0040111A
retn
endp
;оконная процедура
00401132
00401132
00401134
0040113A
0040114F
00401175
00401176
00401177
00401178
00401179
loc_401132:
push 0
call
loc_40116E
;CODE XREF: .
push
call
eax
pop edi
pop esi
pop ebx
leave
retn 10h
00401180
00401180 public start
00401180 start proc near
004011A6 call
;получить текущую версию Windows
004011A6
информацию о текущей операционной платформе
00401205 call
0040120В
eax
00401210 call
call
004012BF call
004012C5 push eax
004012C6 call
004012CB push eax
004012CC call _exit
pop esi
0040130F pop ebx
00401310 mov esp, ebp
00401312 pop ebp
00401313 retn
00401313 start endp;sp = -88h
00401313
proc near ;CODE
00401380 ; start+14C_p
00401380
XREF: start+A9_p
00401389 call sub_4013C0
00401391 _exit endp
004013C0 ; п о д п р о г р а м м а doexit
004013C0 ; Атрибуты: Статическая библиотечная функция
Каркасное Windows-приложение на C/C++ 377
sub_4013C0 proc near ; CODE
004013D1
004013D7
00401308
00401458
00401459
push
call
push
call
eax
esi
00401462 retn
00401462 sub_4013C0 endp
Импорт из KERNEL32.dll
0040610C
0040610C extrn
; DATA XREF:_setargv+12_r
0040610C ;_NMSG_WRITE+77_r
00406188 Импорт из USER32.dll
00406188 extrn
;DATA XREF:
0040618C extrn
end start
Текст листинга 16.3 дает богатую
для размышлений. Его обсуждение нуж-
но начать с последней строки. По правилам ассемблера, в последней директиве
программы
должна стоять метка первой исполняемой команды. В нашем слу-
чае это метка start. Если теперь посмотреть на место в тексте, куда она ссылается,
то мы увидим, что в данной строке содержится директива PROC, обозначающая на-
чало процедуры с именем start. Можно сделать вывод, что исполнение программы
начинается именно с этого места. Обратите внимание на то, что в теле процедуры
start содержатся многочисленные вызовы функций.
В листинге 16.3 показаны только вызовы функций API, но если вы посмотрите
полный вариант листинга
находящийся среди файлов к книге, то увидите,
что в нем содержатся вызовы множества других функций. Названия этих функ-
ций начинаются символом подчеркивания (_), в отличие от названий функций
API, которым предшествует префикс переопределения сегмента, например:
Задержимся немного и посмотрим внимательно на листинг
в ча-
стности, нас интересуют функции, к которым идет обращение. Видно, что в исход-
ном тексте Windows-приложения, написанном на C/C++, нет и намека на какие-
либо функции, за исключением функций API. Более того, вызов многих из функций
API, которые присутствуют в дизассемблированном тексте, отсутствует в листин-
ге 16.1. Отсюда следует естественный вывод о том, что их вставил компилятор
C/C++ и, очевидно, они предназначены для инициализации среды, в которой бу-
дет функционировать программа. А так ли они необходимы? Забегая вперед, ска-
жем, что нет. Но и отказаться от них нельзя, так как они фактически «навязывают-
ся» нам компилятором C/C++. В этом и состоит одна из главных причин того, что
исполняемые модули на ассемблере по размеру в несколько раз меньше функцио-
нально эквивалентных модулей на языках высокого уровня. Можно сказать, что
приложение, написанное на ассемблере, не содержит избыточного кода. Отметьте
еще один характерный момент относительно функций API. Кодовый сегмент ис-
полняемого модуля содержит только команды дальнего перехода к этим функци-
ям. Сами же функции находятся в отдельном файле — библиотеке DLL. В конце
378 Глава
Создание Windows-приложений на ассемблере
листинга 16.3 содержится таблица, в которой описано местонахождение всех им-
портируемых функций API.
Проследим за действиями, которые производит код исполняемого модуля, фор-
мируемый компилятором Visual C++ для инициализации Windows-приложения.
Для этого откройте файл
с
текстом программы
на C/C++, находящийся среди прилагаемых к книге файлов, и переместитесь на
начало процедуры start.
1. Вызов функции API GetVersion
1
для определения текущей версии Windows и не-
которой другой информации о системе. В настоящее время существует более
информативная версия данной функции —
2. Инициализация кучи. Используется как для динамического выделения памя-
ти функциями языков C/C++, так и для организации ввода-вывода на низком
уровне.
3. Вызов функции API
для получения указателя на командную
строку, с помощью которой было вызвано данное приложение.
4. Вызов функции API
для получения указателя на блок
с переменными окружения.
5. Инициализация глобальных переменных периода выполнения.
6. Вызов функции API
для заполнения структуры, поля которой
определяют свойства основного окна приложения.
7. Вызов функции API
для получения базового адреса, по кото-
рому загружен данный исполняемый модуль.
8. Вызов функции
Как можно судить по способу вызова и внутреннему
названию, _WinMain@16 не является функцией API. Это локальная функция
данного исполняемого модуля. Территориально она расположена в начале диз-
ассемблированного текста (см. листинг 16.3). Позже обсудим ее подробнее.
Главный вывод приведенных рассуждений — текст процедуры start исполняе-
мого модуля не соответствует исходному тексту программы на C/C++. В него до-
бавлены локальные функции и функции Win32 API, которые вызываются в ис-
полняемом модуле, например GetVersion, GetCommandLineA и т. д. Это и есть так
называемый стартовый код программы. Если продолжить изучение этого стар-
тового
в файле
то можно увидеть вызов
Найдем
теперь тело этой функции в тексте. Даже беглое сравнение
(см. листинг 16.3) и функции WinMain (см. листинг 16.1) показывает, что мы нашли
место, где содержится код ассемблера, функционально эквивалентный коду на
C/C++. В частности, хорошо видно, что в
вызываются именно те
функции (и никакие другие), что и в функции WinMain программы на C/C++.
Мы не будем сейчас обсуждать порядок и цель вызова каждой из этих функ-
ций, как это делалось для стартовой процедуры, по той причине, что следующий
Описания всех
в книге функций и структур данных Win32 API приводятся лишь час-
тично. Полную информацию о них вы можете получить в многочисленных источниках, которых
рассматриваются вопросы разработки Windows-приложений. Предпочтительно использовать перво-
находящийся в Интернете по адресу: http://www.microsoft.com/msdn/.
Каркасное Windows-приложение на ассемблере 379
наш шаг — это создание программы на языке ассемблера. В процессе ее разработки
мы и рассмотрим эти вопросы достаточно подробно. Сейчас нам важно сделать
вывод, что функция
не имеет прямого отношения к функциям Win32 API.
Эта функция используется лишь для того, чтобы компилятор
сгенерировать
код, выполняющий инициализацию приложения.
Кстати, если вы внимательно посмотрите на листинг 16.3, то без труда обнару-
жите другие фрагменты кода исполняемого файла, прямо соответствующие тек-
сту исходного файла на C/C++. Например, в качестве упражнения найдите текст
оконной функции.
В заключение обсуждения обратите внимание на код завершения программы:
004012С6
004012СВ push eax
004012СС
_exit
Видно, что для завершения приложения вызывается процедура
Код, ко-
торый она содержит, является обязательным для корректного завершения любого
Windows-приложения. Более подробно мы его обсудим при разработке каркасно-
го Windows-приложения на языке ассемблера.
Do'stlaringiz bilan baham: |