Ассемблер под Windows №15

Процедура в Windows


Доброго времени суток, уважаемые подписчики.


Сегодняшний урок будет больше о теоретическим программировании, чем о практическом. Завершая тему Controls, хочу сказать, что Controls могут быть разделены на два типа, Common Controls и просто Controls (далее контролы), в предыдущем уроке мы рассмотрели работу именно контрола, так как именно эта категория создаётся функцией CreateWindowsEx и обслуживается путем обмена сообщений. Common controls (которые можно перевести как обычные "контролы") отличаются тем, что из-за их многофункциональности простых сообщений иногда бывает недостаточно. Подробную информацию о контролах и процедурах можно получить, скачав 5 мегабайтный справочник WinAPI. Все контролы работают примерно одинаково, поэтому я пока не вижу смысла рассматривать отдельно каждый из них.

Следующая тема, которую я хотел бы рассмотреть в сегодняшнем уроке, это построение процедуры в Windows. Наверняка многие из вас уже слышали о таких вещах, как стековой кадр или локальные переменные. Пришло время узнать, что это такое, и с чем его едят! Итак, начнём, насколько это ни удивительно, с самого начала: Что есть процедура!? Процедура это определённая часть кода, которая вызывается командой call, при этом в стеке ей передаётся адрес следующей после call команды, что и позволяет командой ret перейти на эту метку. В досе каждая программа имела собственный сегмент, поэтому при вызове функций через прерывания параметры передавались в регистрах. Однако в Windows стек наследуем, то есть при вызове определённой функции она получает в своё распоряжение тот же стек, что и вызывающая его программа. Это даёт возможность передавать параметры через стек. И тут возникает вопрос как к ним обращаться, если единственная возможность обратиться к ним является обращением через esp. Но там где одна проблема там и другая, так как при адресации через определённый регистр мы должны сохранять его значение, в нашем случае не пользоваться стеком. Тогда самым простым решением является создание стекового кадра, что значит сохранить текущее значение стека в ebp (или любой другой регистр), предварительно сохранив его в тот же стек, и адресовать наши переменные уже через этот регистр.
push ebp
mov ebp,esp

Теперь нам нужно получить доступ к переменным переданным процедуре, так как по адресу [ebp] у нас находится сохранённый ebp, а по адресу [ebp+04h] находится адрес возврата процедуры, то первым переданным нам параметром будет [ebp+08h], вторым - [ebp+0Ch] и т.д. Причём надо заметить, что параметр, загруженный в стек последним будет именно [ebp+08h]. Сохранение ebp является обязательным, так как если вызывающая процедура уже имеет стековой кадр, то изменяя его мы разрушаем стековой кадр вызывающей процедуры. Также обязательно восстановление ebp и esp,
mov esp,ebp
pop ebp

Так выглядело бы завершение всех процедур, а так как эти две команды занимают 2 байта была придумана 1 байтная команда leave, которая совершает те же действия, что и две вышезаписаные команды занимая 1 байт. Единственной лишней (пока) вещью может показаться строка mov esp,ebp, так как мы вряд ли собираемся зачем-то запихивать в стек то, что мы потом не будем использовать! Ответ на этот вопрос таят в себе локальные переменные. Давайте посмотрим что мы знаем о локальных переменных, они могут быть использованы лишь в пределах локальной переменной и не занимают места, так как уничтожаются при завершении процедуры создателя. Что ещё может произвольно изменяться на протяжении процедуры и всегда возвращается к своему изначальному значению в конце процедуры? Конечно это стек. То есть для того чтоб создать локальную переменную нам достаточно лишь уменьшить стек на нужное нам количество байт, адресовать же переменную мы можем через ebp, создав переменные сразу после создание стекового кадра. Давайте рассмотрим простой пример процедуры с двумя параметрами и одной локальной переменной:
procedure proc
push ebp
mov ebp,esp
sub esp,4
........
leave
ret 8
procedure endp

В этой процедуре возможно обращение к следующим переменным:
dword ptr [ebp+08h] - параметр 1
dword ptr [ebp+0Ch] - параметр 2
dword ptr [ebp-04h] - локальная переменная
Также мы видим, что в конце процедуры вместо команды ret находится команда ret 8. Дело в том, что процедура должна удалять параметры, переданные ей в стеке, поэтому цифра после ret является количеством удаляемых из стека байт после завершения процедуры. Последняя деталь, стоящая обсуждения, является получение адреса локальных переменных или параметров для передачи их процедурам. Тут, к сожалению, не обойтись без какого-либо регистра, например eax. Для вычисления адреса используется команда lea:
lea eax,[ebp+08h]

В изучении языка программирование бесспорно наиважнейший фактор - информация, однако обучение без практики - деньги на ветер, поэтому я намериваюсь давать Вам "домашнее задание" или что-то вроде этого. В этот раз я лишь хочу заставить Вас задуматься о правильном использовании команд ассемблера (если кто не в ладах с ними, то может скачать подробный хелп по опкодам, так их называют на самом деле). Итак, сегодняшнее задание написать наиболее короткую процедуру измерения длинны строки без использования API. Размер файла всегда будет одинаков, но это нас пока не интересует, так как дописав в .bat файл ключ /Fl к программе ml.exe можно получить файл листинга, который содержит код программы и машинные коды, в которые и была трансформирована программа. Файлы с процедурой (а это должна быть именно процедура, получающая адрес начала строки как параметр) присылайте мне.


На сегодня это всё. Если есть какие вопросы, пишите, обязательно отвечу. Пишите, Dark_Lord@RusFAQ.ru, Dark_Lord@land.ru.
Или свяжитесь со мной по ICQ, мой номер 126222874!

Сайт управляется системой uCoz