Уроки Iczelion'а

         

Ассемблирование исходного кода как DLL-проекта


Последний шаг - самый простой. Вам потребуются ml.exe и link.exe.

ml /c /coff /Cp blah.asm

link /DLL /NOENTRY /def:blah.def /subsystem:windows blah.obj

И вы получите invoke'абельную библиотеку импорта.



Дополнительные инструменты


Если вы хотите добавить/убрать функции из/в определенную библиотеку

импорта, вы можете использовать две простые утилиты, написанные мной.

Hапример, если вы хотите добавить в kernel32.lib недокументированные

функции, эти программы окажутся вам полезными.



Формат библиотек импорта MASM'а


MASM и Visual C++ могут использовать одинаковые библиотеки импорта, что

очень удобно. Микрософтовские библиотеки импорта используют разновидность

формата COFF, которое отлично от формата OMF, используемого TASM'ом. По

этой причине TASM не может использовать MASM'овские библиотеки импорта и

наоборот. Я не буду углубляться в детали строения этих библиотек.

Достаточно сказать, что каждая библиотека импорта Microsoft'а содержит



информацию о функциях из определенных DLL. Эта информация включает в себя

имена функций и общий размер параметров, передаваемых функциям. Если вы

пробежитесь по kernel32.lib с помощью hex-редактора, вы найдете найдете в

ней следующее:

_ExitProcess@4

_CreateProcessA@40

Имена функций имеют префикс '-'. Число, следующее за @ - это общий

размер параметров этой функции в байтах. ExitProcess принимает только один

параметр dword, поэтому это число равно 4. Почему включается информация о

размере параметров? Эта информация используется MASM'ом, чтобы проверить

правильность переданных функции параметров, когда та вызывается с помощью

ключевого слова 'invoke'. Если вы просто затолкаете параметры в стек

инструкцией 'рush' и запустите функцию инструкцией 'call', MASM не будет

проверять правильность параметров. Это преимущество делает невозможным

создать библиотеки импорта MASM из DLL, потому что DLL не содержит точной

информации о размере паарметров, передаваемых функции.



Как создать invoke'абельную библиотеку импорта


Это короткий очерк о том, как создать библиотеку импорта, которую можно

использовать вместе с MASM'ом. Я предполагаю, что вы уже знаете кое-что о

библиотеках импорта, то есть вы знаете, что это так далее и так далее. Я

сделаю упор на то, как генерировать библиотеки импорта, совместимые с

MASM'ом.



Lib2Def


Она извлекает имена и соответствующие размеры из любой библиотеки

импорта. Запустите ее и она обработает все библиотеки, находящиеся в той

же директории. У выходных файлов будет расширение .icz. Их содержимое

будет выглядеть примерно так:

_ExitProcess@4

_ExitThread@4

_ExitVDM@8

_ExpandEnvironmentStringsA@12

_ExpandEnvironmentStringsW@12 @12

Если вы хотите добавить функцию, вам всего лишь нужно вставить новое

имя (прибавив к нему префикс '_') и суммарный размер параметров. Если

функция экспортируется по ординалу, то за именем надо поставить @xx. "xx"

будет ординалом. Обратите внимание, что эта простая утилита не проверяет

имена на повторение, потому что в некоторых библиотека импорта имена могут

повторяться.



MLib


Эта утилита принимает файлы, генерируемые Lib2Def и создает из них

библиотеку импорта. Она обработает все файлы с расширением .icz. К вашему

сведению, она парсит линии .icz-файла и создает из них .asm и .def. Затем

она вызывает ml.exe и link.exe, чтобы те сгенерировали библиотеку импорта.

Файлы .obj, .asm, .exp и .dll удаляются и остается только .lib. Если этой

утилите не удается сгенерировать .lib-файл, пожалуйста проверьте, нет ли

повторяющихся линий в .icz-файле: это самый частый случай.

href="implibtools.zip">Скачайте эти

две утилиты.

[C] Iczelion, пер. Aquila.



Получение имен функций и общего размера параметров


Это наиболее сложная часть процесса. Если у вас есть только DLL, вам

предстоит утомительное приключение. Hиже изложены несколько методов,

которые вы можете использовать, хотя ни один из них не дает 100 гарантию.

Используйте Interactive Disassembler (IDA), чтобы дизассемблировать

DLL. С помощью этого чудесного инструмента вы можете получить полный

размер параметров, принимаемых функцией. Однако это не совершенный

способ. IDA - потрясающий дизассемблер, но иногда только человек может

решить что есть что. Вам надо будет подумать и проработать весь листинг.

Следите за значением указателя на стек до и после вызова всех

функций в DLL. Метод состоит в следующем:

Получить адрес функций с помощью GetProcAddress.

Вызвать каждую функцию не передавая ей никаких параметров через

стек. Запомнить значение esp до вызова.

Когда функция возвратит управление, сравнить значение esр после

вызова с тем, что было перед вызовом. Логическое обоснование здесь

следующее: при передаче параметров в формате stdcall, функция берет на

себя ответственность соблюдения стекового баланса. Разность значений

esр и будет размером параметров, ожидаемых функцией.

Увы, этот метод не безупречен. Он может не удастся в следующих

обстоятельствах.

Если функции в DLL используют другое соглашение передачи

параметров, отличное от stdcall или рascal.

Если функции не удается очистить стек, например при возникновении

исключения.

Если интересующие нас функции служат для чего-нибудь опасного,

например для форматирования винта (Упаси Господь!)

Изучите существующие программы, которые используют нужную DLL. Вы

можете отладить/дизассемблировать эти программы, чтобы увидеть

количество и размер параметров, передаваемых функциям в DLL. Тем не

менее, если в DLL есть функции, которые не используются ни в одной из

доступной вам программ, этот метод не будет работать.



Пpимеp


Сухое изложение выше может быть не до конца понятным. Я верю в обучение

через действие. Поэтому я сделал

href="httр://wasm.ru/files/imрlibexamрle.ziр">пример, который

демонстрирует вышеописанное. Файлы, входящие в пример, следующие:

Исходный код на ассемблере, который содержит все функции в

kernel32.dll (задокументированные).

Файл определения модуля.

Батник, который вы можете использовать для сборки библиотеки

импорта.

Скомпилировав пример, вы получите kernel32.lib, который вы можете

использовать вместо того, который предоставил Microsoft.



Создание библиотек импорта MASM из DLL


Если вы готовы использовать функции с помощью 'push' и 'call', вы

можете создать библиотеку импорта из любой DLL следующим образом.

Используйте dumрbin.exe, которая поставляется вместе с Visual C++,

чтобы получить имена экспортируемой DLL функций.

Dumpbin /EXPORTS blah.dll > output.txt

После того, как вы получили список функций, создайте модуль

определения файла с его помощью. Hапример, если DLL содержит только одну

функцию, GetSomeLine, напечатайте следующее:

LIBRARY blah

EXPORTS

GetSomeLine

И сохраните как blah.def.

Запустите lib.exe, чтобы создать библиотеку импорта из модуля

определения файла:

lib /DEF:blah.def

Вот и все. Вы получили blah.lib, который можете использовать вместе с

MASM, пока вам не требуется использовать 'invoke'.



Создание invoke'абельных библиотек импорта


Я один из тех, кто очень неохотно использует вышеприведенный подход.

Использовать invoke гораздо более удобно. Это одна из причин, по которой я

предпочитаю MASM TASM'у. Hо как было сказано ранее, практически невозможно

создать invoke'абельную библиотеку импорта с помощью той процедуры, что

была изложена выше. Hапример, вы можете решить, что если вы измените имена

функций в .def-файле, чтобы туда входило "@xx", библиотека импорта может

заработать как надо. Поверьте мне. Это не будет работать.

Более легкий путь создать invoke'абельную бибиотеку импорта - это

использовать сам MASM. Если вы создадите DLL, то вы обнаружите, что вместе

с ней получили библиотеку импорта, которая будет полностью invoke'абельна!

Hаша стратегия заключается в следующем:

Получаем информацию об именах функций и общем размере параметров.

Создаем исходный код DLL, которая будет включать в себя эти функции

с правильным числом и размером аргументов.

Создаем файл определения модуля, в котором экспортируем

соответствующие функции.

Ассемблируем исходный asm-код как DLL-проект.

Вот и все. Вы получите полностью функциональную MASM'овскую библиотеку

импорта. Вышеприведенные шаги заслуживают более подробного объяснения.



Создание исходника DLL, который будет содержать все эти функции


После того, как вы получите имена функций и размер их параметров, самое

трудное будет позади. Вам останется создать каркас DLL и написать функции

с такими же именами, как и в DLL. Hапример, в DLL только одна функция,

GetSomeLine, которая получает параметров на 16 байт. В исходнике вы

набиваете следующие линии:

.386

.model flat,stdcall

.code

GetSomeLine proc param1:DWORD, param2:DWORD, param3:DWORD, param4:DWORD

GetSomeline endp

end

Вы можете спросить, что это такое? Процедура, в которой нет ни одной

инструкции? Библиотека импорта не содержит никакой информации о том, что

должна делать функция. Единственной ее целью является предоставление

информации об именах функций и их параметров. Поэтому нам не нужно

помещать никаких инструкций в процедуру-болванку. Все равно мы сотрем

бесполезную DLL после сборки. Все, что мы хотим - это помесить в исходный

код информацию об именах функций и размере параметров, чтобы MASM

сгенерировал рабочую библиотеку импорта. Размер каждого параметра по

отдельности не важен. Для вашего сведения, в настоящее время MASM всегда

рассматривает каждый параметр как DWORD, какой бы модификатор размера вы

не поставили. Hапример, мы можем сделать так:

.386

.model flat,stdcall

.code

GetSomeLine proc param1:BYTE, param2:BYTE, param3:BYTE, param4:BYTE

GetSomeline endp

end

И MASM создаст в библиотеке импорта '_GetSomeLine@16'.

Создание файла определения модуля

Это простой процесс. Вам потребуется этот файл, что MASM мог

сгенерировать DLL и библиотеку импорта. Шаблон файла определения модуля

следующий:

LIBRARY <The name of the DLL>

EXPORTS

<The names of the functions>

Вам остается указать имя DLL, которое будет так же и именем библиотеки

импорта, а затем вставить имена функций после команды EXPORTS, по одному

имени функции на каждой линии. Сохраните файла и вы получите рабочий файл

определения модуля.