- •Системное программирование на макроассемблере masm32
- •Содержание
- •Вступление
- •Префиксы «венгерской» нотации типов данных в описаниях вызовов функций аpi Win32 (Win64)
- •Работа с консолью
- •Цель работы
- •Теоретические сведения
- •Консоль
- •Начальные настройки
- •Как в cmd.Exe выделять-копировать-вставлять текст
- •История ввода команд
- •Заменяющие символы в шаблонах имён
- •Выполнение работы
- •Навигация по дискам и папкам
- •Поиск файлов и папок по имени или шаблону
- •Копирование, перенос, переименование и удаление файлов
- •Создание и уничтожение папок
- •Создание текстового файла
- •Просмотр содержимого файлов
- •Поиск файлов, содержащих нужную строку
- •Состав отчета по работе
- •Состав пакета масм
- •Последовательность создания исполняемого ехе-модуля на масм
- •Режимы компоновки
- •Выполнение работы
- •Создание консольной программы
- •Создание программы с графическим интерфейсом
- •Использование шаблона простой программы
- •Состав отчета по работе
- •Параметризация макросов
- •Уникальность меток при макрорасширениях
- •Макроконстанты
- •Макропеременные и макровычисления
- •Увидеть работу макрогенератора
- •Расширенный листинг
- •Выполнение работы
- •Макроопределение и макрорасширение
- •Повторение строк repeat
- •Цикл for
- •Анализ расширенного листинга программы
- •Состав отчета по работе
- •Потоки вывода и ввода
- •Вывод и ввод в консольных приложениях
- •Invoke GetStdHandle,std_output_handle ; манипулятор возвращен в еах, ...
- •InputBuffer db 25 dup (0) ; вводной буфер фрагмент 3
- •Высокоуровневый консольный ввод-вывод с использованием макросов
- •Макрос консольного вывода print
- •Макросыконсольного вводаinput и inkey
- •Преобразование строки в число
- •Inkey "Жду кнопочку."
- •Преобразование числа в строку
- •Выполнение работы
- •Invoke ExitProcess,0 ; выход в Windows
- •Эксперимент 1. Ввод-вывод функциями Win32
- •Эксперимент 2
- •Эксперимент 3
- •Эксперимент 4
- •Состав отчета по работе
- •Последовательное выполнение команд
- •Передача управления в другое место кода
- •Возврат управления в основную программу
- •Метка_к5:
- •Jmp Метка_к5
- •Call метка_процедуры
- •Передача параметров в процедуру
- •Внутренние переменные в процедурах
- •Как компилятор ml.ExEпомогает организовать работу с процедурами
- •Автоматическое создание пролога и эпилога
- •Автоматическое размещение внутренних переменных
- •Повышение надежности и облегчение вызовов – макрос invoke
- •Выполнение работы
- •Invoke ExitProcess,0 ; выход в Windows
- •Неразрешённые внешние ссылки
- •Выполнение работы
- •Программные инструменты для работы
- •Изготовление файлов для сравнений
- •Восстановление структуры двоичного представления объектного файла
- •Анализ неразрешенных внешних ссылок в объектном файле
- •Состав отчета по работе
- •Исполнение программы под отладчиком
- •Оперативная память «физическая» и виртуальная
- •Выполнение работы
- •Подготовка текстовых файлов для работы
- •Исследование структуры ехе-файла
- •Исследование выполнения программы под отладчиком
- •Карта размещения программы в виртуальной оперативной памяти
- •Действительные ссылки, бывшие раньше неразрешенными
- •Состав отчета по работе
- •Вопросы для самопроверки
- •Рекомендованная литература
Автоматическое размещение внутренних переменных
Усложним нашу процедуру. Предположим, программисту в процедуре понадобились две внутренние переменные по 4 байта каждая, с именами хиу. Чтобы компилятор создал их, достаточно в процедуру сразу после заголовка вставить одну строку:
LOCAL x:dword, y:dword
И тогда в расширенном листинге после пролога мы увидим еще одну строку, вставленную в процедуру макрогенератором:
add esp, 0FFFFFFF8h
Кто не забыл компьютерную арифметику, тот во втором операнде быстро узнает число -8. Этот оператор резервирует 8 байт ниже базы, вычитая 8 (фактически прибавляя -8) из указателя стека ESP.
Как видим, резервирование ячеек стека под локальные переменные произошло тоже автоматически, это сделал макрогенератор компилятора.
Повышение надежности и облегчение вызовов – макрос invoke
Процедуры нередко имеют много параметров. Если программист предпочёл работать «вручную», самостоятельно прописывая всё, то ему нужно внимательно следить, сколько, каких и в каком порядке величин он передает в стек командами PUSHперед собственно вызовом процедуры командойCALL.
Но люди несовершенны. Предположим, наш программист не углядел. и передал параметры чуточку не в том порядке, переставил местами второй и третий. И что теперь?
А ничего хорошего. Обращение к параметрам любая процедура ведет по смещениям относительно базы. Оператор программы читает [EBP+16], программист уверен, что читается параметр 3, а на его месте стоит значение параметра 2… Ну и дальше в том же духе.
Чтобы уменьшить влияние человеческого фактора при организации вызовов процедур следует применять макрос invoke(перевод названия – «вызвать», как ни странно).
Что он делает?
Проверяет, все ли фактические параметры передаются процедуре и правильного ли они типа (байтовой ширины).
Записывает в исходный текст программы оператор PUSHдля каждого фактического параметра. Список параметров вinvoke-вызове просматривается, как правило, от последнего параметра к первому («задом наперед»). Слова «как правило» относятся к случаю, когда в начале программы записана директива .MODELFLAT,STDCALL. Если в качестве второго операнда директивы «.MODEL» указывать другие разрешенные имена (С,PASCAL,SYSCALLи некоторые другие), то в некоторых случаях это будет означать просмотр списка параметров не в обратном, а в прямом порядке. Впрочем, мы всегда будем работать с параметромSTDCALL, поэтому для нас стандартное направление просмотра списка параметров – от последнего к первому. Дополнительным полезным эффектом от параметраSTDCALLявляется то, что он заставляет компилятор применять в командеRETоперанд «число» для очистки стека от параметров. Таким образом, попутно решается и эта проблема.
Создает команду CALLимя_процедуры.
Выполнение действия (а) требует, чтобы макросу invokeбыло заранее откуда-то известно, сколько и каких параметров у каждой процедуры. Об этом тоже должен позаботиться сам программист. Он должен ДО вызова процедуры макросомinvokeразместить в исходнике так называемыйпрототип процедуры.
Это такое вот описание для нашего примера с процедурой SpendTime:
SpendTime PROTO :DWORD, :DWORD
Теперь, когда перед компилятором ставится задача вызвать такую процедуру с помощью invoke, то он сначала просматривает свой рабочий буфер (там, кстати, хранятся и текстовые описания макросов), находит прототип вызываемой функции и сравнивает фактическое количество параметров и их типы с заданными в прототипе. Если это всё совпадает, то работа продолжается, иначе возникает ошибка компиляции с соответствующим сообщением.