DotFix :: Портал разработки и защиты программ
Главная
Программы
Статьи
Разное
Форум
Контакты
Автор: GPcH. Дата публикации: 27.07.2004

Вставка ассемблерных процедур в код Visual Basic: миф или реальность? Часть 2

Использование переменных при написании Asm функций и методика работы
с API функциями


ВСТУПЛЕНИЕ

Помнится не так давно я написал статью про встраивание ассемблерных процедур в код на VB.
Оказалось, что тогда я кое чего не знал и не рассмотрел довольно важные аспекты.
Во второй части данной статьи я постараюсь раскрыть возможности объявления переменных в
ассемблерном коде, а также вызов API функций. На самом деле это возможно и не так сложно в реализации,
как кажется. Думаю, прочитав эту статью Вы без проблем разберетесь с этими возможностями,
а также научитесь их применять на практике.

Большая часть статьи опирается на ее первой части, поэтому, если Вы ее вдруг не читали -
обязательно прочтите, так как без основ, которые в ней описаны нельзя двигаться дальше.

ОБЪЯВЛЕНИЕ И РАБОТА С ПЕРЕМЕННЫМИ

Довольно неудобно писать на ассемблере не используя разного рода переменные.
Согласитесь, гораздо полезнее бывает объявить какую либо переменную, занести
в нее определенное значение и использовать потом только переменную, чем всегда
подставлять численное значение. Как не странно, используемый нами компилятор
nasm это позволяет. Для решения подобных задач используется оператор "%define",
синтаксис которого мы рассмотрим.
Основная цель данного оператора - описать имя и размещение локальной переменной в стеке,
для чего используются два основных параметра - имя и адрес.
То есть для того, чтобы объявить переменную strColor по адресу ebp-4 мы перед
кодом ассемблерной функции, то есть до директивы [BITS 32], введем следующее:

CODE NOW!
%define strColor [ebp-4]



Тем же методом можно объявить любую новую переменную.

Компилятор nasm налагает некоторые ограничения на использование переменных. Мы рассмотрим
лишь допустимые варианты использования.

Перемещение адреса в 4х байтовую переменную:

CODE NOW!
mov ebx, [eax]
mov Pic1HDC, ebx



Вызов функции, которая расположена по адресу, который находится в переменной ApiGetTickCount
(команды занесения адреса в переменную я приводить не буду) :

CODE NOW!
mov ebx, ApiGetTickCount
call ebx



То есть простейшие операции поддерживаются, более же сложные операции
требуют задания размерности переменных:

Из этого следует, что так мы записать не можем:

CODE NOW!
mov ebx, [eax + strAddressAdd]



лишь так:

CODE NOW!
mov ebx, [eax + dword[strAddressAdd]]



Как видно из предыдущего примера, использовать содержимое
переменных для математических операций требует предварительного задания
их размера, чтобы компилятор мог нормально интерпретировать команды и
создать рабочий код.

РАБОТА С API ФУНКЦИЯМИ

Прежде чем рассматривать данный вопрос, нам нужно определиться с надобностью
подобного. А она заключается в следующем. В некоторых программах есть потребность
в цикличном вызове API функций, например при работе с графикой. Функций WIN API
для работы с графикой довольно много, в вместе с тормозными циклами VB их
использование не приводит к желаемым результатам в скорости. Попробуйте
попиксельно перенести картину из одного PictureBox’а в другой. Вряд ли вы
дождетесь завершения этой операции, хотя на ассемблере это можно выполнить за
довольно небольшой промежуток времени.

Сначала рассмотрим объявление API функций в коде на VB. Откройте новый проект
и в самой верхней части кода сделайте объявление необходимых функций (включая
функцию для вызова ассемблерной процедуры, которая будет рассматриваться ниже).
Еще нам понадобятся два PictureBox’а, в один из которых необходимо поместить
любую картинку (ее мы будем перемещать попиксельно в другой PictureBox
ассемблерной функцией).

CODE NOW!
Private Declare Function SetPixel Lib "gdi32" (ByVal hdc As Long, ByVal x As Long,
ByVal y As Long, ByVal crColor As Long) As Long
Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal x As Long,
ByVal y As Long) As Long
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA"
(ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long,
ByVal lParam As Long) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long,
ByVal lpProcName As String) As Long
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA"
(ByVal lpModuleName As String) As Long
Private Declare Function GetTickCount Lib "kernel32" () As Long



Объявили? Отлично! Переходим далее.
Для начала нам необходимо определить адрес данных функций в памяти, чтобы передать их
в ассемблерную программу. Для этого мы используем API функции GetModuleHandle и
GetProcAddress. Теперь создадим массив параметров (то есть параметр, передаваемый
в API функцию будет один - адрес на первую ячейку массива) и занесем все
важные параметры PictureBox’ов: hdc, left, top, width и height, не забыв перевести
все линейные размеры PictureBox’а в пиксельное представление (данная операции
заключается в делении стандартных размеров на 15).

Подготовительно заключительные операции мы сделали, теперь перейдем непосредственно к
написанию ассемблерной функции. Чтобы не было вопросов по приведенному мною коду,
скажу, что для вызова API функции необходимо занести все ее параметры в обратном
порядке в стек, а затем переместить адрес API функции в какую либо переменную,
после чего вызвать эту переменную. Пример работы с API функциями может выглядеть так:

CODE NOW!
;заносим 4 параметра в стек
mov edx, Y
push edx
mov ecx, X
push ecx
mov ebx, Pic1HDC
push ebx
;вызываем API функцию
mov ebx, ApiGetPixel
call ebx



Напомню, что результат работы функции всегда будет помещен в регистр eax.

Вот сам код, выполняющий копирование картинки:

;создаем переменные в стеке

CODE NOW!
%define Pic1HDC [ebp-4]
%define Pic2HDC [ebp-8]
%define Pic1Width [ebp-12]
%define Pic1Height [ebp-16]
%define Pic2Width [ebp-20]
%define Pic2Height [ebp-24]
%define ApiSetPixel [ebp-28]
%define ApiGetPixel [ebp-32]
%define ApiGetTickCount [ebp-36]
%define X [ebp-40]
%define Y [ebp-44]
%define Time [ebp-48]

;даем компилятору понять, что все регистры 32х битные

[BITS 32]

;изменяем указатель на вершину стека и заносим содержимое
;изменяемых нами регистров в стек

push ebp
mov ebp, esp
sub esp, 48
push ebx
push esi
push edi
push edx

;заносим в eax адрес первой ячейки массива

mov eax, [ebp+8]

;задаем значения переменных

mov ebx, [eax]
mov Pic1HDC, ebx
mov ebx, [eax+4]
mov Pic1Width, ebx
mov ebx, [eax+8]
mov Pic1Height, ebx
mov ebx, [eax+12]
mov Pic2HDC, ebx
mov ebx, [eax+16]
mov Pic2Width, ebx
mov ebx, [eax+20]
mov Pic2Height, ebx
mov ebx, [eax+24]
mov ApiSetPixel, ebx
mov ebx, [eax+28]
mov ApiGetPixel, ebx
mov ebx, [eax+32]
mov ApiGetTickCount, ebx

;вызываем API функцию счетчика времени для того, чтобы
;засечь время начала работы функции

mov ebx, ApiGetTickCount
call ebx

;заносим в переменную Time начальный показатель счетчика времени

mov Time, eax

;далее приступаем к цикличному копированию содержимого
;одного PictureBox’а в другой. Данный код не требует
;комментариев для человека, хотя бы поверхностно знающего ассемблер

mov ecx, 0
mov Y, ecx
for:

mov eax, 0
mov X, eax
for1:
mov edx, Y
push edx
mov ecx, X
push ecx
mov ebx, Pic1HDC
push ebx
mov ebx, ApiGetPixel
call ebx

push eax
mov edx, Y
push edx
mov ecx, X
push ecx
mov ebx, Pic2HDC
push ebx
mov ebx, ApiSetPixel
call ebx

mov eax, X
inc eax
mov ebx, Pic1Width
cmp eax, ebx
mov X, eax
jne for1

mov eax, Y
inc eax
mov ebx, Pic1Height
cmp eax, ebx
mov Y, eax
jne for

;вызываем API функцию счетчика времени для того, чтобы
;засечь время окончания работы функции

mov ebx, ApiGetTickCount
call ebx

;заносим в eax время работы функции

mov edx, Time
sbb eax, edx

;считываем ранее сохраненные переменные из стека
;конечно рациональнее было бы использовать popad и pushad,
;но я решил сохранять и восстанавливать только изменяемые
;регистры, что технически более правильно

pop edx
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp

;возвращаем управление программе

ret



Как видите, ничего особо сложного тут нет.
Все делается почти также, как если бы Вы писали весь код на ассемблере.

Теперь для проверки работы написанной функции нам необходимо
ее откомпилировать и перевести в HEX последовательность, что
с легкостью можно выполнить с помощью программы Asm2VB, которую
я написал для упрощения данной задачи. Поле этого нам необходимо присвоить
полученную HEX последовательность любой переменной в VB и занести все ее
содержимое в байтовый массив используя функцию, приведенную в первой части данной статьи.
Далее останется только вызвать эту функцию из VB, предварительно передав ей параметры.
Результат работы функции мы будем передавать в eax (если вы помните - это время
работы функции), сами же операции функция производит непосредственно в памяти
и результат своей работы не возвращает.

Ниже я приведу возможную реализацию кода-обвязки ассемблерной функции из VB
без комментариев, так как все это описано в первой части статьи. Добавлю, что Вы
должны поместить на форму CommandButton cmdRunAsm:

CODE NOW!
Const Temp = "5589E581EC30000000535657528B45088B18895DFC8B58
04895DF48B5808895DF08B580C895DF88B5810895DEC8B5814895DE88B5818895
DE48B581C895DE08B5820895DDC8B5DDCFFD38945D0B900000000894DD4B80000
00008945D88B55D4528B4DD8518B5DFC538B5DE0FFD3508B55D4528B4DD8518B5
DF8538B5DE4FFD38B45D8408B5DF439D88945D875CF8B45D4408B5DF039D88945
D475B98B5DDCFFD38B55D019D05A5F5E5B89EC5DC300"
Private AsmProc() As Byte
Private AsmAdr As Long
Private Param(8) As Long
Private ParamAdr As Long

Private Sub cmdRunAsm_Click()

t = GetTickCount
t = GetPixel(Picture1(0).hdc, 0, 0)
SetPixel Picture1(1), 0, 0, 0

Param(0) = Picture1(0).hdc
Param(1) = Picture1(0).Width / Screen.TwipsPerPixelX
Param(2) = Picture1(0).Height / Screen.TwipsPerPixelY
Param(3) = Picture1(1).hdc
Param(4) = Picture1(1).Width / Screen.TwipsPerPixelX
Param(5) = Picture1(1).Height / Screen.TwipsPerPixelY
Param(6) = GetProcAddress(GetModuleHandle("gdi32"),
"SetPixel")
Param(7) = GetProcAddress(GetModuleHandle("gdi32"),
"GetPixel")
Param(8) = GetProcAddress(GetModuleHandle("kernel32"),
"GetTickCount")

ParamAdr = VarPtr(Param(0))
n = GetTickCount
a = CallWindowProc(AsmAdr, ParamAdr, 0, 0, 0)
v = GetTickCount - n
MsgBox Str$(a), , "Время прорисовки:"
MsgBox Str$(v - a), , "Время, которое барсик вызывал функцию"
End Sub

Private Sub Form_Load()
ReDim AsmProc((Len(Temp) / 2) - 1)
For i = 1 To Len(Temp) Step 2
AsmProc(Index) = Val("&H" + Mid$(Temp, i, 2))
Index = Index + 1
Next
AsmAdr = VarPtr(AsmProc(0))
End Sub



Теперь жмем F5 и радуемся результату. Теперь Вы умеете совмещать знание ассемблера и
VB и применять эти знания для решения сложных ресурсоемких задач, требующих
высокой скорости работы.

Исходник к статье можно скачать отсюда

Отдельное спасибо хочу выразить Shacks’у, так как именно он
практиковался на вызове API функций и именно он посоветовал мне
описать эту возможность в статье. Также хочется поблагодарить [RU].Ban0K!’а
за рецензирование.


Комментарии
отсутствуют

Добавление комментария

Ваше имя (на форуме):

Ваш пароль (на форуме):

Комментарии могут добавлять только пользователи,
зарегистрированные на форуме данного сайта. Если Вы не
зарегистрированы, то сначала зарегистрируйтесь тут

Комментарий:



04.09.2011 Долгожданный релиз VB Decompiler. Масса улучшений декомпиляции Native Code. Значительно расширенна и обновлена справочная система на русском и английском языках.
20.12.2010 DotFix Software поздравляет наших клиентов и посетителей сайта с наступающим Новым Годом и рождеством! Желаем приятно провести праздники и успехов в новом году!
28.11.2010 Выпущена новая версия защиты DotFix NiceProtect. Основные изменения коснулись обфускатора Delphi программ. Теперь имеется полная поддержка Tab и Page контролов на формах, что обеспечивает максимальную совместимость обфускации с Delphi XE программами.
21.10.2010 Обновлен декомпилятор Visual Basic программ до версии 8.1. Декомпиляция P-Code программ становится все более идеальной, также проделана большая работа по улучшению анализа Native Code и .NET приложений.
16.09.2009 Полностью обновлен движок сайта! Теперь все ссылки имеют читаемый понятный вид, разного рода глюки на страницах убраны. И теперь сайт полноценно работает на второй версии нашего движка.
Архив новостей
Яндекс цитирования

Движок сайта: DotFix Engine v0.2
Администрация сайта: