Гергерт Сергей
Ассемблер в VB
Эта статья предполагает, что вы знакомы с функциями Get/PutMem и принципом получения параметров по их смещению с стеке
(Подробнее...). Да, возможно и это :)
Когда-то попался мне на глаза класс cFuncCall, реализующий вызов динамически загружаемой библиотеки. Не могу не вставить то, что было в нём:
'***********************************************
'* This module use excelent solution from
'* http://www.vbdotcom.com/FreeCode.htm
'* how to implement assembly calls directly
'* into VB code.
'***********************************************
Но и не могу не отметить, что очень сильно я этот класс изменил. От первоначального варианта осталась только самая общая идея.
Итак, что нам нужно? Нам нужно вызвать функцию по указателю (ключевой момент именно этот, из него следует всё остальное).
VB не предоставляет нам средств передачи точки исполнения туда, куда мы хотим. Но в
Windows есть функция CallWindowProc, она умеет передавать управление, а мы умеем её
Declare :) Но у этой функции ещё 4 параметра, а вдруг мы хотим вызывать функцию с 5 параметрами? Придётся, следовательно, изыскать обход этого ограничения. Отказаться от
CallWindowProc мы не можем, следовательно, ...
... следовательно, мы должны создать для неё подходящую среду обитания. Этой средой будет маленький кусочек динамически генерируемого кода. Именно на него (а не на указатель функции) мы будем передавать управление.
Что такое вызов функции, по сути? Это упаковка параметров в стек в нужном порядке, помещение в верхушку стека адреса возврата и передача управления в нужную точку. Так что наш маленький кусочек должен будет запихивать наши параметры (сколько угодно) в стек, вызывать функцию по указателю (эта функция выпихнет параметры сама), после чего возвращать управление в
CallWindowProc с одновременным выпихиванием из стека тех самых 4 параметров
CallWindowProc (которые там, в стеке, всё это время спокойно лежали, никому не нужные).
Вооружившись мануалом по OpCodes (а ещё лучше - asm-компилятором и hex-viewer'ом), находим нужные машинные коды и правила их применения. После этого используем функцию
GlobalAlloc, которая возвращает нам указатель на размещённый для нас кусочек памяти, выровненный по 8-байтовой границе, и пишем в этот кусочек наш код (машинные константы будут перемежаться с динамически подставляемыми значениями). Несколько десятков крашей1 IDE - и вот, всё работает :)
А раз мы научились вызывать по указателю - от тут уж свобода полная.
LoadLibrary вкупе с GetProcAddres возвращают что? Указатель. Если мы откомпилируем процедурину, и засунем двоичный код в ресурсы, а потом вызовем
LoadResData, получим что? Указатель. Ура :) Ещё
один часто используемый класс :) Код прилагается.
Скачать проект
DynamicCalls.
1 От английского crash
(прим. редактора).