Суббота, 21.07.2018, 12:51
Приветствую Вас Гость

LabSoft

Меню сайта
Категории каталога
Статьи по кодингу [24]
Материалы по кодингу
КреатиFF [144]
Рассказы, истории, анекдоты...
Разное [8]
Советы, трюки, полезные рекомендации, статьи о новом и забытом старом...
Наш опрос
Как Вы узнали о нас?
Всего ответов: 27
Главная » Статьи » Разное

Стек + DLL + *.EXE
Стек:
Для работы программы часто необходим стек - структура в памяти, в которую можно помещать значения и "вынимать" их оттуда в обратном порядке. Для этого выделяется отдельная область памяти (которую и называют "стек") и используется регистр ESP. Он указывает на "вершину стека", то есть на последний адрес в стеке, куда мы что-либо помещали. Для помещения значения в стек используется инструкция PUSH, которая уменьшает значение ESP на 4 и помещает ("пихает") заданное 32-битное значение по адресу [ESP]. Инструкция POP наоборот - "достаёт" ("выталкивает") значение по адресу [ESP] и затем увеличивает ESP на 4. Таким образом стек "растёт сверху вниз". Для работы со стеком используется и регистр EBP, но это сейчас не важно. Важно то, что при вызове процедуры (с помощью инструкции CALL) в стек помещается текущее значение регистра EIP, а по окончании работы процедуры (с помощью инструкции RET) - это значение восстанавливается и процессор продолжает работу с того места, где он остановился перед вызовом процедуры. Важно также и то, что в стеке хранятся локальные переменные функции, но к этому мы вернёмся позже.

*.EXE:
Наконец, о том как выполняются программы под Windows. Типичная Windows-программа хранится в файле с расширением .EXE. Типичный .EXE-файл является Portable Executable (PE) - файлом. Portable Executable - это название формата файла. Помимо собственно исполнимого кода PE-файл содержит различную служебную информацию о том, как он будет загружен, таблицы импортируемых и экспортируемых функций и проч. При запуске PE-файла Windows загружает его в память почти в том виде, в котором он хранился на диске, и запускает как отдельный процесс. Каждый процесс получает в распоряжение своё собственное 32-битное адресное пространство (например, два различных процесса могут пользоваться одним и тем же адресом 0x12345, и при этом для каждого из них это будет "его собственная" память. Они не будут замечать друг друга). То, по какому адресу в этом пространстве будет загружен сам PE-файл, называется по английски Image Base и записано в одном из заголовков PE-файла. Обычно Image Base = 0x400000. Все прочие значения в служебных заголовках файла даны как смещения относительно этого адреса. Так, например Code Start (начало исполняемого кода), равное 0x1000 означает, что после загрузки файла в память, исполнение программы начнётся с адреса 0x400000 + 0x1000 = 0x401000.

DLL:
Практически каждая Windows-программа пользуется функциями из динамически загружаемых библиотек (Dynamic-Link-Libraries, DLL). Важнейшими из них являются KERNEL32.DLL и USER32.DLL, которые предоставляют основные системные процедуры. Тогда как функции внутри программы вызываются просто инструкцией "CALL func" где func - адрес вызываемой функции, функцию из DLL (т. н. импортируемую функцию) таким путём вызвать нельзя, т. к. во время компиляции программы адрес её не известен. Использование DLL происходит следующим образом:
Во-первых, в заголовке PE-файла записано имя DLL, функции из которой используются в программе. При загрузке программы в память, Windows загружает в её адресное пространство и все используемые ей DLL. DLL представляет из себя такой же PE-файл, как и сама программа, но так как DLL в отличие от EXE загружается в "чужое" адресное пространство, то адрес, по которому она "хотела бы" быть загружена (её Image Base), может оказаться занят. Тогда Windows переносит её по своему усмотрению.
Во-вторых, в EXE файле записано имя каждой импортируемой функции и оставлено место, куда Windows после загрузки соответствующей DLL проставит действительный адрес функции. Это называется таблицей импортов PE-файла. Во время компиляции местонахождение таблицы импортов известно, и поэтому можно вызывать процедуры из DLL "косвенно", указывая место, где должен быть адрес вызываемой процедуры.
Например, положим что таблица адресов функций, импортируемых из KERNEL32.DLL начинается в нашем PE-файле с адреса 0xe0d8 и содержит три функции - CloseHandle, CreateFileA и ExitProcess. Положим также, что Image Base нашего файла - 0x400000. Значит адрес процедуры CloseHandle будет находиться в загруженной программе по адресу 0x400000 + 0xe0d8 = 0x40e0d8, адрес CreateFileA - прямо за ним по адресу 0x40e0d8 + 4 = 0x40e0dc, и адрес ExitProcess - по адресу 0x40e0dc + 4 = 0x40e0e0.

Категория: Разное | Добавил: Jimmy (22.02.2008) | Автор: Labsoft
Просмотров: 841 | Рейтинг: 0.0/0 |
Всего комментариев: 0
Имя *:
Email *:
Код *:
Форма входа
Поиск
Друзья сайта
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0