Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
assmblr.docx
Скачиваний:
218
Добавлен:
16.02.2016
Размер:
366.71 Кб
Скачать

4. Выполнение (запуск) программы

Запуск .EXE-программы может быть осуществлён с помощью двойного щелчка мышью.

Если вы создали программу, которая ничего не выводит на экран, то за её работой можно наблюдать при помощи программы-отладчика, например, OllyDbg. Отладчики позволяют наблюдать за изменением содержимого регистров и флагов. Подробнее работа в отладчике OllyDbg будет описана ниже.

2.4. Инструментальный пакет masm32

В п.2.3 отмечено, что для создания программ на ассемблере в Windows, необходим текстовый редактор и компилятор. Реальные программы Win32 используют также внешние функции, стандартные константы и переменные, ресурсы и много другое. Всё это требует дополнительных файлов, которые есть в инструментальном пакете MASM32. Важно понять, что MASM32 не компилятор, а сборник для программирования под Win32, в который входит 32-битный компилятор MASM.

Инструментальный пакет MASM32 предназначен для создания приложений Windows на языке ассемблера и содержит все необходимые инструменты, к тому же он распространяется бесплатно.

Основные сведения и порядок работы в пакете masm32:

1. Для создания исходных текстов программ рекомендуется использовать текстовый процессор пакета MASM32 под названием QEDITOR (от Quick Editor, быстрый редактор):

После набора сохранить текст программы командой FileSave, указать папку BIN и расширение .ASM, например MYPROG.ASM.

2. Командой ProjectBuild All создать объектный и исполнимый файлы:

Если исходный текст программы набран без ошибок, то в папке, где он хранился, увидим два новых файла: MYPROG.OBJ и MYPROG.EXE.

3. Когда объектный и исполнимый файлы созданы, запустите программу на выполнение. Для этого можно дважды щелкнуть мышью по названию исполнимого файла (в папке BIN) или запустить программу через редактор QEDITOR командой ProjectRun Program.

2.5. Примеры

Пример 0. «Скелет» стандартной программы

.386

.model flat, stdcall

option casemap :none

;подключение необходимых библиотек

include \MASM32\INCLUDE\windows.inc

include \MASM32\INCLUDE\masm32.inc

include \MASM32\INCLUDE\gdi32.inc

include \MASM32\INCLUDE\user32.inc

include \MASM32\INCLUDE\kernel32.inc

includelib \MASM32\LIB\masm32.lib

includelib \MASM32\LIB\gdi32.lib

includelib \MASM32\LIB\user32.lib

includelib \MASM32\LIB\kernel32.lib

;раздел, где объявляются все константы

.const

;раздел, где объявляются переменные, уже имеющие какое-то значение

.data

;раздел, где объявляются переменные, еще не имеющие значения

.data?

.code

start: ;с этого слова начинается код программы

invoke ExitProcess,0

end start ;с этого слова заканчивается код программы

Сохраните этот «скелет» в отдельном файле для удобства и используйте как заготовку.

Пример 1. Структура программы и основные директивы

Построчно разберём простейшую программу.

Текст программы на ассемблере содержит кроме инструкций процессору еще и служебную информацию (в виде директив), предназначенную для программы-ассемблера.

Начнем с простого. В первой программе не будет вызовов API-функций, ее цель – понять саму структуру программы на языке ассемблера для Windows. Поэтому программа, прибавляющая к 2 число 3, будет выглядеть следующим образом:

.386

.model flat, stdcall

.code

start:

mov eax, 8

add eax, 8

ret

end start

В ней инструкции процессора mov, add, ret окружены директивами. Первые три директивы начинаются с точки.

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

Вторая директива .model flat, stdcall показывает, в какой среде будет работать программа. Все программы работают под управлением операционной системы, которая их запускает и обеспечивает взаимодействие с внешней средой. Директива .model задаёт модель памяти flat (плоская или сплошная) для нашей программы. Эта модель памяти используется для программирования под Windows, т.е. директива говорит о том, что именно для операционных систем семейства Windows 952предназначена программа.

Stdcall - это "уговор" о том, кто будет чистить параметры (функция, которую вызвали, или сам вызывающий). Мы всегда будем использовать вариант "функция чистит свои параметры". Он и называется stdcall. Однако такое объяснение не полное, и мы вернемся к параметру stdcall при объяснении вызова функций. К этому моменту вы уже будете знать, что такое стек.

Третья директива .code показывает, где начинаются сами команды процессора. Когда операционная система пытается запустить программу, она ищет в ней инструкцию, с которой нужно начать, и отправляет ее процессору. Когда же инструкции кончаются, операционная система «подхватывает» программу и помогает ей правильно завершиться, чтобы освободить место другим, ведь Windows – многозадачная операционная система, способная выполнять одновременно несколько программ. Уйти из-под «опеки» операционной системы помогает инструкция ret.

Инструкция, с которой начинается программа, обычно помечается последовательностью символов с двоеточием на конце (меткой). В нашем случае это start:. Там, где оканчивается последовательность команд процессора, в программе должна стоять директива end <метка первой инструкции программы>, в нашем случае это end start. Эта директива, а также сама метка не переводятся в инструкции ассемблера, а лишь помогают получить программу, которую способен выполнить процессор. Без них программа-ассемблер не поймет, с какой инструкции процессор начнет работу.

Отладка

Программа ничего не выводит на экран, поэтому за для изучения её работы воспользуемся программой-отладчиком OllyDbg. Чтобы открыть программу в отладчике, достаточно загрузить OllyDbg и открыть программу как обычный документ – File-Open.

В верхней левой части отладчика можно увидеть свою программу. Вверху справа – регистры процессора.

Внизу слева - байты памяти (OllyDbg сразу же показывает секцию данных программы). Внизу справа отображается содержимое стека (работа со стеком будет описана ниже).

Необходимо помнить, что Ollydbg это отладчик и у него есть свои ограничения. Рекомендуется закрывать его каждый раз перед загрузкой новой программы.

С помощью клавиши F8 можно выполнить программу по шагам и просмотреть, как меняется содержимое регистров. Ответьте на вопрос, почему в результате сложения 8+8 регистр EAX стал равен 10? Состояние флагов также меняется. Мы видим, что после выполнения первой команды флаг Z опустился (обратился в ноль), потому что результат выполнения операции не равен нулю.

Пример 2. Использование функций API

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

API – это стандартные функции, на основе которых и пишут все программы для Windows. Например MessageBox выдаёт сообщение на экран, PostQuitMessage сообщит Windows, что программа хочет закончить работу и т.д. Все они уже готовы – остается только вызывать их. Получается, что используя API-функции, мы применяем элементы программирования высокого уровня.

Находятся API-функции обычно в динамически загружаемых библиотеках – файлах с расширением .DLL.

При вызове каждой API-функции надо передавать параметры, т.е. аргументы, с которыми ей предстоит работать. Раньше это делалось так: параметры заталкивались в стек (команда push) задом наперед – сначала последний, потом предпоследний и т.д., а затем вызывалась сама программа (команда call). Например:

push addr Text2

push addr Text1

push hWnd

call MessageBox

Такая запись допускается и сейчас, но существует и более компактная: invoke MessageBox, hWnd, Text1, Text2

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

Рассмотрим программу с использованием функций API. Прежде чем приступить к выводу на экран, изучим более простую процедуру ExitProcess. Её вызывает каждая Windows-программа, чтобы завершить свою работу. В ассемблере под DOS мы пользовались инструкцией возврата ret. Но ExitProcess действует правильнее, не только возвращая управление операционной системе, но и освобождая занятые программой ресурсы.

В следующем листинге показана программа для Windows, которая только и делает, что правильно завершается.

.386

.model flat, stdcall

option casemap:none

includelib C:\MASM32\LIB\kernel32.lib

ExitProcess proto :DWORD

.code

start:

push 0

call ExitProcess

end start

Вызываемая в ней процедура ExitProcess требует одного параметра – это код завершения, возвращаемый операционной системе. Он передается процедуре командой push 0. Число 0 считается признаком удачного завершения.

Поскольку ExitProcess – «чужая» процедура, не определенная в нашей программе, ассемблер должен знать, где она находится, а также (для проверки – она ли это) число и размер ее параметров.

Сведения об адресе и параметрах процедуры хранятся в файле библиотеки kernel32.lib, который подключается к ассемблерному тексту директивой includelib C:\MASM32\LIB\kernel32.lib.

Перед тем как создать инструкцию вызова этой процедуры компоновщик сравнивает сведения из библиотеки с прототипом ExitProcess proto :DWORD, и если все совпадает, создает пригодный к исполнению файл с расширением .EXE. Прототип процедуры очень прост и состоит из имени, слова proto и параметров. В нашем случае параметр один – это двойное слово (то есть 4 байта) DWORD. Если параметров несколько, они разделяются запятой.

Пример 3. Вывод строки на экран

Создадим программу, выводящую на экран фразу “Hello, world!”:

.386

.model flat, stdcall

option casemap:none

ExitProcess proto :dword

GetStdHandle proto :dword

WriteConsoleA proto :dword, :dword, :dword, :dword, :dword

includelib C:\MASM32\LIB\kernel32.lib

.data

stdout dd ?

msg db “Hello, world!”, 0dh, 0ah

cWritten dd ?

.code

start:

invoke GetStdHandle, -11

mov stdout, eax

invoke WriteConsoleA, stdout, ADDR msg, sizeof msg, ADDR cWritten, 0

invoke ExitProcess, 0

end start

В программе вызываются три процедуры: GetStdHandle, WriteConsoleA и ExitProcess.