- •Системное программирование на макроассемблере 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
- •Неразрешённые внешние ссылки
- •Выполнение работы
- •Программные инструменты для работы
- •Изготовление файлов для сравнений
- •Восстановление структуры двоичного представления объектного файла
- •Анализ неразрешенных внешних ссылок в объектном файле
- •Состав отчета по работе
- •Исполнение программы под отладчиком
- •Оперативная память «физическая» и виртуальная
- •Выполнение работы
- •Подготовка текстовых файлов для работы
- •Исследование структуры ехе-файла
- •Исследование выполнения программы под отладчиком
- •Карта размещения программы в виртуальной оперативной памяти
- •Действительные ссылки, бывшие раньше неразрешенными
- •Состав отчета по работе
- •Вопросы для самопроверки
- •Рекомендованная литература
Возврат управления в основную программу
Итак, мы худо-бедно ушли в процедуру, выполнили ее код (команды к31 – к34) и потом надо вернуться на команду к5 и продолжить выполнение основной программы. Как это можно сделать?
Ну, «лобовое» решение состоит в повторении того же подхода, что и при вызове процедуры - выполнить после к34 команду JMPна адрес 142. Но это решение плохое по таким причинам.
Дело в том, что оно (такое решение) излишне жёсткое. Да и как на этапе программирования обеспечить такой переход? Решение, конечно, возможно. Например, перед командой к5 поставить явным образом какую-нибудь метку:
Метка_к5:
К5
А в процедуре после к34 написать обратный возврат
Jmp Метка_к5
Возврат состоится. Но он будет производиться всегда только на метку Метка_к5 .
Негодность такого решения становится понятной, если представить, что процедура Р55 в основной программе вызывается несколько раз, из разных точек кода. Тогда и возврат в основную программу для ее продолжения потребуется производить тоже в разные точки. А Jmp Метка_к5 эту проблему не решает никак. Жёстко – только на к5 и всё. Не годится.
Как же обеспечить возвраты всегда на правильное место продолжения, откуда бы ни была вызвана процедура?
Идея правильного решения проста. Надо перед передачей управления адрес возврата куда-то записать (сохранить), а в конце работы процедуры восстановить EIPиз этого сохранённого места.
Но тут мы опять «наступаем на те же грабли» в виде невозможности прямого присвоения регистру EIPтребуемого значения. Это значит, что если мы даже и сохранимEIPвозврата в какую-то переменнуюMyVar, то возврат операторомmov EIP, MyVarне может быть откомпилирован как недопустимый. Так что же, безвыходная ситуация?
Отнюдь. Предусмотрительные инженеры Intelвключили в систему команд специальную команду возврата из процедурыRET.
Она читает число с вершины системного стека и записывает его в EIP. Старое значениеEIP, естественно, перетирается. Это и есть передача управления куда надо.
Значит, задача программиста состоит, во-первых, в сохранении EIPвозврата в стек и, во-вторых в том, чтобы обеспечить положение указателя стекаESPточно на сохраненном адресе возврата в момент выполнения в процедуре командыRET.
Ну, первая подзадача решается просто:
Push EIP; кто не помнит:Pushзаносит операнд в стек.
Втораяже подзадача может быть решена только дисциплиной программиста. Само собой (типа автоматически) этого не сделать никак. То есть, работая со стеком внутри процедуры, программист должен позаботиться о том, чтобы когда дело дойдет до выполнения RET, указатель стекаESPдолжен обязательно указывать именно на ту ячейку стека, в которую было занесеноEIPвозврата во время вызова процедуры.
Подведем итог.
Для корректного обеспечения возврата надо уходить в процедуру примерно так:
Push (EIP + длина_команды_передачи_управления)
JMP P55
Странный операнд команды Pushобъясняется тем, что мы должны занести в стек адрес команды, следующей заJMPP55. Мы этот оператор написали, но откомпилировать его невозможно, потому что выражения как операнд командыPushкомпилятор не пропустит. В ассемблере допускаются только такие выражения, которые сводятся к константе, а тут не этот случай. Неужели опять безвыходная ситуация?
Конечно же, нет. В системе команд процессора имеется специальная команда, специально сконструированная для вызова процедур –