
- •Лабораторная работа №1 работа с машинными командами и командами ассемблера с помощью отладчика debug
- •1. Цель работы
- •2. Основные сведения
- •2.1. Машинные команды
- •2.2. Команды ассемблера
- •Некоторые команды ассемблера
- •2.3. Отладчик Debug
- •3. Методические указания к выполнению лабораторной работы
- •3.1. Арифметика при помощи команд Debug и просмотр регистров
- •3.2. Машинные команды
- •3.3. Команды ассемблера
- •4. Контрольные вопросы
- •Лабораторная работа №2 основы программирования на ассемблере в windows
- •1. Цель работы
- •2. Основные сведения
- •2.1. Особенности программирования на ассемблере для Windows
- •2.2. Вызов WinApi функций
- •2.3. Создание программ на ассемблере
- •1. Получение ассемблерного листинга
- •2. Ассемблирование (трансляция)– получение объектного модуля
- •3. Компоновка (линковка) – создание исполняемого модуля
- •4. Выполнение (запуск) программы
- •2.4. Инструментальный пакет masm32
- •Основные сведения и порядок работы в пакете masm32:
- •2.5. Примеры
- •3. Методические указания к выполнению лабораторной работы
- •4. Контрольные вопросы
- •Литература
- •Лабораторная работа №3 представление данных в памяти компьютера
- •1. Цель работы
- •2. Основные сведения
- •2.1. Типы данных
- •2.2. Директивы определения данных
- •2.3. Взаимодействие ассемблерных программ с памятью
- •2.4. Примеры
- •3. Методические указания к выполнению лабораторной работы
4. Выполнение (запуск) программы
Запуск .EXE-программы может быть осуществлён с помощью двойного щелчка мышью.
Если вы создали программу, которая ничего не выводит на экран, то за её работой можно наблюдать при помощи программы-отладчика, например, OllyDbg. Отладчики позволяют наблюдать за изменением содержимого регистров и флагов. Подробнее работа в отладчике OllyDbg будет описана ниже.
2.4. Инструментальный пакет masm32
В п.2.3 отмечено, что для создания программ на ассемблере в Windows, необходим текстовый редактор и компилятор. Реальные программы Win32 используют также внешние функции, стандартные константы и переменные, ресурсы и много другое. Всё это требует дополнительных файлов, которые есть в инструментальном пакете MASM32. Важно понять, что MASM32 не компилятор, а сборник для программирования под Win32, в который входит 32-битный компилятор MASM.
Инструментальный пакет MASM32 предназначен для создания приложений Windows на языке ассемблера и содержит все необходимые инструменты, к тому же он распространяется бесплатно.
Основные сведения и порядок работы в пакете masm32:
1. Для создания исходных текстов программ рекомендуется использовать текстовый процессор пакета MASM32 под названием QEDITOR (от Quick Editor, быстрый редактор):
После набора сохранить текст программы командой File – Save, указать папку BIN и расширение .ASM, например MYPROG.ASM.
2. Командой Project – Build All создать объектный и исполнимый файлы:
Если исходный текст программы набран без ошибок, то в папке, где он хранился, увидим два новых файла: MYPROG.OBJ и MYPROG.EXE.
3. Когда объектный и исполнимый файлы созданы, запустите программу на выполнение. Для этого можно дважды щелкнуть мышью по названию исполнимого файла (в папке BIN) или запустить программу через редактор QEDITOR командой Project – Run 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.