Статистика |
Онлайн всего: 1 Гостей: 1 Пользователей: 0 |
|
Вызов функций по указателю в Visual Basic (часть 5)
Теперь поговорим немного <о Сусанине>. Если по каким-либо причинам переадресация, предложенная мною в откомпилированном виде кажется нежелательной (например, требуется большая скорость и не хочется терять даже десятка полтора тактов процессора на приведенный выше вариант, которые при частом обращении (например, в цикле!) могут сыграть значительную роль). Тогда можно пойти как тем поправленным вариантом предложенным выше мною, так и авторским. И всё же. Если уж многократное обращение происходит в цикле (а <потерянное> время бывает критичным только в этом случае), тогда можно извратиться несколько больше, чем предложил автор.
Дело в том, что для обращения к какой-либо <собственной> функции используется стандартная инструкция: Если взять авторский вариант, то у нас получится последовательность вызовов
Код ASM | 1
2
| call rel32=Offset PlaceholderFor1ParamFunction
jmp rel32=Offset <Вызываемая функция> |
| или в поправленном мною выше варианте
Код ASM | 1
2
| call rel32=Offset PlaceholderFor1ParamFunction
jmp dword ptr ds:[imm32=VarPtr(vCallAddress)] функция> |
| Ну, почему бы нам вообще не избавиться от <лишнего> jmp, коли уж так сильно поджимает нас время? И избавимся! Да ещё и будем ходить по условному адресу. Для этого немного напишем функцию переключателя
Код Visual Basic | 1
2
3
4
5
6
7
8
9
10
11
12
13
| #If ReleaseBuild Then
Private Ra() as Byte
Public Function ReDirect(Optional ByVal vP0 As Long, Optional ByVal vP1 As Long) As Long
vESP=VarPtr(vP0)-4
CopyMem VarPtr(pRet), vESP, 4
ReDim Ra(1 to 7)
'call dword ptr ds:[imm32=VarPtr(vCallAddress]
Ra(1)=&H2E: Ra(2)=&HFF: Ra(3)=&H15: PutMem VarPtr(Ra(4)),
VarPtr(vCallAddress)
WriteProcessMemory GetCurProcess, pRet-7, VarPtr(Ra(1)), 7, 0&
PutMem4 vESP, pRD
End Function
#End If |
| а обращаться к этой функции будем
Код Visual Basic | 1
2
| vCallAddress=vNewAddress
vReturnValue=ReDirect(,vParamValue) |
| <концовка> подобного обращения (из любого места VB-программы!) в маш. кодах выглядит
Код ASM | 1
2
| push imm8=0
call rel32 |
| что занимает семь байт. Но инструкция
Код ASM | 1
| call dwrod ptr ds:[imm32] |
| На самом деле занимает шесть байт. Что делать? Не вставлять же nop'ы в конце? Ведь это опять получится дополнительная, хотя и пустая (один такт) операция процессора. Есть простое решение указанная выше инструкция по умолчанию адресуется относительно регистра ds, достаточно приписать перед этой инструкцией <уточняющий> префикс &H2E, который будет делать тоже самое, но в явном виде. Что в итоге? В итоге имеем те самые семь байт.
Что же на самом деле делает функция ReDirect? При обращении к этой функции, она вычисляет адрес возврата уже описанным выше способом. Затем меняет предшествующие адресу возврата семь байт. Таким образом, если обращение из этого места происходит либо в цикле, либо просто часто, то в следующий раз обращение по нужному адресу (vCallAddress) будет происходить прямо оттуда! Кстати говоря, несмотря на то, что функция ReDirect, вроде бы имеет два параметра, при повторных обращениях задержки из-за пустого параметра всё равно не происходит, так как мы затираем при первом обращении.
Есть ещё один не менее изящный, на мой взгляд, вариант.
Код Visual Basic | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| #If ReleaseBuild Then
Private REa() as Byte
Public Function REx(ByVal vP0 As Long, ByVal vP1 As Long) As Long
vESP=VarPtr(vP0)-4
CopyMem VarPtr(pRet), vESP, 4
ReDim REa(1 to 5)
'pop eax
REa(1)=&H67: REa(2)=&H28
'call eax
REa(3)=&H67: REa(4)=&HFF: REa(5)=&HD0
WriteProcessMemory GetCurProcess, pRet-5, VarPtr(REa(1)), 5, 0&
PutMem4 vESP, pRD
End Function
#End If |
| В этом случае обращение к функции REx должно выглядеть следующим образом:
Код Visual Basic | 1
| vReturnValue=REx(vCallAddress,vParamValue) |
| То есть теперь инструкция заменяется инструкциями Как мы помним инструкция занимает пять байт, в то время, как наша замена - всего три. Как и выше, <недостающие байты> мы <добираем> за счёт <уточняющих> префиксов. В данном случае это префикс &H67. Его <безболезненно> можно ставить перед любой из наших инструкций. В итоге получаем искомые пять байт. Заметьте, что последний вариант при передаче управления по условному адресу выполняется максимально быстро. Если же, мы знаем, что обращение будет происходить на один и тот же адрес, то максимально быстро будет работать вот такой вариант
Код Visual Basic | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #If ReleaseBuild Then
Public vNewAddress as Long
Private Function GetPA(ByVal vProcAddress as Long)as Long
GetPA=vProcAddress
End Function
Public Function ReD(ByVal vP1 As Long) As Long
vESP=VarPtr(vP0)-4
CopyMem VarPtr(pRet), vESP, 4
'до обращения в vNewAddress должен содержаться <абсолютный> адрес
'после вычисления там будет содержаться относительный
vNewAddress=pRet-GetPA(AddressOf ReD)+vNewAddress
WriteProcessMemory GetCurProcess, pRet-4, VarPtr(vNewAddress), 4, 0&
PutMem4 vESP, pRD
End Function
#End If |
| Теперь обращение к этой функции обычное.
Код Visual Basic | 1
2
3
| vNewAddress=<адрес вызываемой процедуры>
:
vReturnValue=ReD(vParamValue) |
| А что происходит в функции ReD? Просто в инструкции перезаписывается само значение rel32.
Вот, собственно говоря, и все замечания. В прочих случаях, не требующих столь изощрённых методов оптимизации предлагаю пользоваться переадресацией альтернативной (если честно, то не очень-то поворачиваются пальцы на клавиатуре их сравнивать) CallWindowProc.
Автор: Approximator Источник: vbnet.ru
|
Категория: Basic | Добавил: Admin (14.06.2012)
|
Просмотров: 2343
| Рейтинг: 0.0/0 |
Добавлять комментарии могут только зарегистрированные пользователи. [ Регистрация | Вход ]
|
|
|