![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Проблемы при перехвате IDispatch::Invoke
Ihor Osov'yak © (2004-06-22 23:22:00)При иследовании одной проблемы пришел к выводу о необходимости перехвата Invoke некоего наследника от IDispatch, если конкретно - IHtmlDocument2 (поначалу с целью протоколирования).
В общем-то с реализацией особых проблем не было, но.. После вызова старого обработчика Invoke, после выхода со своего перехватчика - довольно таки странный эксепшен. Причем корректноость постановки перехватика проверялась с помощью иследования стека вызовов - входные параметры старому методу передаются идентичные тем, что идут на вход перехватчика (обнаружен некий интересный момент при передачи word - старшие 16 бит в стеке захламляются - но это отношения к проблеме не имеет - делался ручной сброс). Проверка делалась и при установленом перехватчике, и при отключеном - в том и том случае на вход штатного метода Invoke идут одинаковые параметры.
Эксепшен возникает после выхода с кода перехватчика, где-то в глубине вызвавшего кода - довольно далеко, пока не хватило терпения детально протрассировать.
Пока есть две версии.
Первая, более вероятная. Возможно, что вызывавший код ставит более жеские требования по сохранению регистров, чем делфи. Вызывавший код, вероятнее всего написан в недрах майкрософт :-), может и на cипласплас, хотя возникают некие сомнения - уж очень не рыхлый там код, хотя вероятнее всего мною трасировалась пока только прокси соотв. ком-обьекта, а не сам обьект...
Итак, первый вопрос - какие требования по сохранению регистров в коде, написанном на msVC, с учетом того, что код имеет прямое отношение к COM, да и с поправкой на то, что разработчик мог делать предпосылку, что его вызов соотв. метода ну никак не будет перехвачвсться..
Вторая предпосылка, более с разряда фантастики. Используется что-то похожжее на технику вложенных процедур в паскале, то есть там делается попытка работать с локальными переменными в стеке обрамляющих процедур. Естественно, при этом делаются некоторые предпосылки о структуре стека. Мною же в стек добавлена одна ступень...
Ах, в чем вопрос. Хочу услышать критику этой второй версии как несостоятельной, чтобы не спешить ее прорабатывать..
Ihor Osov'yak (2004-06-25 13:07:00) [21]2 [25] evvcom © (25.06.04 10:11)
если OleVariant передается по значению - в стек загонится 4 32 битных значения, то есть тело OleVariant. Проверялось. Думаю, это не фича делфи, так как Olevariant не есть делфийский тип. Наверное здесь имеет место следование рекомендациям по работе с OleVariant. Фрейм начала процедуры анализирует тип OleVariant, видит, что там ссылка на строку. Раз ссылка - значит нужно снять копию (ведь мы работаеи по значению) с этой строки - что вполне правильно. Естественно, что тело OleVariant будет модифицироваться с целью указания на новую копию строки. И это правомерно, так как тело нашего OleVariant - в области копии фактических параметров, то есть в области, куда, по идее кроме кода процедуры никто не должен вклиниваться. Я сначала был немного в недоумении - но поразмыслив - все верно, в этом случае компилятор поступает абсолютно правомерно и абсолютно корректно. Это наша проблема, что мы делаем такие па вокруг стека.
2 [26] GuAV © (25.06.04 12:47)
> Ого! Ни фига себе! Какое он имеет право?
См. пред. абзац.
> А что если из cdecl-заглушки вызывать stdcall/pascal-процедуру? может тогда он перестанет трогать параметры cdecl.
Не думаю. Код для снятия копии генерируется даже для случая, когда соотв. параметр вообще не используется. Собственно, с этого повода можно было бы немного ворчать, но не более. Хотя даже и ворчать нельзя. А вдруг в нашем коде какие-то манипуляции вокруг указателей, ассемюлерных вставок, етс - и вследствии на этапе компиляции невозможно в общем случае определить, используется ли соотв. параметр. Так что тот факт что копия снимается по любому - наверное есть единственно правильным решением.
GuAV (2004-06-25 12:47:00) [22]
> Дело в том, что заглушка имеет все основания считать, что
> у нее в стеке - локальные параметры, и может делать с ними
> все, что ей заблагорассудится. Собственно
Ого! Ни фига себе! Какое он имеет право?
*****
А что если из cdecl-заглушки вызывать stdcall/pascal-процедуру?может тогда он перестанет трогать параметры cdecl.
Он же имхо должен сделать копию в стеке тока и всего. А для тех параметров, которые должны менятся в заглушке, дописать var.
evvcom (2004-06-25 10:11:00) [23]Я не разбирался, как передается именно OleVariant, но другие сложные структуры данных, с которыми разбирался, все передавались как указатели. Только если не указывался var или const, в стеке выделялось место и копировалось туда содержимое по указателю. Дальнейшая работа естественно шла уже с локальной копией. Я не думаю, что OleVariant здесь будет исключением.
Ihor Osov'yak (2004-06-25 09:18:00) [24]> Для пущей надежности перечислить параметры через const.
Тогда некоторые параметры будут в стеке будут интрепритироваться как указатель. Для примера, в нашем случае вот такой зверь
name: OleVariant; - в стеке 4 32-битных, если же const name: OleVariant; - содержимое стека будет интерпритироваться как один поинтер (одно 32-битное). Но, впрочем, для тех параметров, где не происходит замены значения на поинтер в случае декларации их как const - вполне можно. Это, наверное, истинно для тех параметров, которые в стеке как одиночное 32- битное (integer, word, ets), нужно будет проверить и/или книжки почитать.
evvcom (2004-06-25 08:22:00) [25]
> Но там, естественно, также нужно ухо востро держать по отношению
> "локальных" параметров той процедуры, что вклиниваем перед
> вызовом оригинального метода. Собственно, в этом большой
> минус варианта от GuAV.
Для пущей надежности перечислить параметры через const.
А вообще мне тоже вариант с cdecl понравился. Я делал перехваты функций из mfc42 (в терминологии Delphi методов классов, не знаю как это в Сях называется). Соглашения о вызове thiscall (во всяком случае IDA Pro их так обзвал). Там тоже надо было сохранять регистры, которые Delphi портил: ebx и ecx - указатель на объект класса. Пришлось извращаться с повторной передачей параметров в мою Hook процедуру, а вызов перехваченной функции из mfc тоже делал через jmp. А этот вариант с cdecl, конечно, намного элегантнее! Срочно переделывать! Спасибо за идею.
Ihor Osov'yak (2004-06-25 01:58:00) [26]Зы.
[10] Digitman © (23.06.04 16:20)
Мое графоманство немного cпровоцировано Вашим постигом :-). Был-бы признателен за маленький коментарий, если есть возможность.
2 GuAV ©
Вам спасибо за некоторые наводки.
Ps
> Собственно, для Invoke проблем не возникло.
Пока не возникало, для текущей реализации. Но там, естественно, также нужно ухо востро держать по отношению "локальных" параметров той процедуры, что вклиниваем перед вызовом оригинального метода. Собственно, в этом большой минус варианта от GuAV. Но впрочем, я не уверен, что откажусь от такого подхода. Все может быть.
Ihor Osov'yak (2004-06-25 01:50:00) [27]Первый модуль содержит всего-навсего только процедуру модификации адреса соотв. метода в таблице методов интерфейса, там должно быть все очевидно.
Интерфейс передаем как обычный поинтер, чтобы компилятор не делал лишних движений по подсчету ссылок.
Второй модуль. Собственно реализация ловушек. Сделал отказ от классовой реализации в пользу процедурной. Как минимум - более понятно.
Здесь пока пример перехвата IHTMLDocoment2::Invoke и IHTMLDocoment2::Open
В переменных old_Open_Entry: pointer;
old_Invoke_Entry: pointer; - адреса оригинальных методов.
Вместо их в таблицы подставлено процедуры-хуки IDoc_Open_Hook, IDoc_Invoke_Hook.
В конце каждой процедуры-хука - джамп на оригинальные методы интерфейса. Поскольку мы сохраняем структуру стека – то по ret в оригинальном методе - возврат уже не на наший код, а код, который вызывал перехваченный метод. То есть туда, куда нужно.
Посреди хука - вызов нашей заглушки. Да, здесь я уже не стал играть с тем, чтобы self был на первом месте. Здесь все же не классовая реализация, а процедурная - то есть это уже не важно.
А теперь о граблях. Дело в том, что заглушка имеет все основания считать, что у нее в стеке - локальные параметры, и может делать с ними все, что ей заблагорассудится. Собственно, для Invoke проблем не возникло. А вот для Open (IDoc_Open_Hook)- таки да. Даже если IDoc_Open_Hook был полностью пустой.
Здесь несколько моментов. Первым делом, о тех, которые провоцируются нашей реализацией. Дело в том, что формальные параметры - OleVariant, передаваемые по значению. И естественно, компилятор генерирует код, который снимает копию с этого параметра. И имеет все право расположить копию в стеке, так как там как бы локальная переменная. Но он ведь не знает, что эту с его точки зрения локальную переменную мы потом передаем оригинальному методу.. :-(.
Отказываться напрочь от идеи GuAV с cdecl; пока не хочется, уж очень красиво в большинстве случаев получается.
Посему я пока пошел по пути немного надурить компилятор, объявив соотв. параметры не как OleVariant, а в случае необходимости сделать необходимые преобразования ручками. Собственно прообраз таких телодвижений в IDoc_Open_Hook на примере features.. Может в конечном итоге откажусь от такого подхода, но тока есть так.
А теперь еще одни грабли. Уже не зависящие от нашей реализации.
Дело в том, что штатно Open должен возвращать IDispatch. ячейка для возврата - в стеке. И поскольку там якобы интерфейс - то поскольку он будет замещен - то компилятор делфи предпринимает некоторые телодвижения для очистки этого интерфейса. Что, в общем - то и правильно. Но проблема в том, что с недр вызывающего кода в той ячейке приходит какой-то мусор, а не nil. Понятно, что получается из-за попытки очистить интерфейсную ссылку в данном случае. Обход этой проблемы и объясняет почему IDoc_Open_Hook декларирован как процедура с еще одним параметром (result: pointer;), который я пока не трогаю. Трогать придется потом, так как не исключено, что в некоторых случаях в реальной задаче оригинальный метод не будет нужно вызывать, а результат нужно будет формировать самому...
зы. Задача оказалась значительно интереснее и более тяжелой, чем казалось поначалу.
зы2. Не знаю, будет ли толк с этого проекта, но материал на очень уж хорошую статью соберу точно :-).
Ihor Osov'yak (2004-06-25 01:47:00) [28]В общем-то тема оказалась более интересной и более "чреватой". Есть несколько довольно таки интересных вещей, которые элементарно превращаются на грабли..
Но первым делом немного причесанный код, который не чень "граблеустойчив", но который при известной степени осторожности можно брать за основу. После кода - немного комметнариев об этих граблях. Некоторые из них обойдены, некоторые - хорошо травкой притрушенны.
unit e0_ihook;
{-$DEFINE DEBUG - moved to project options}
interface
function SetHookForMethod(aIntrf: pointer; aMethodDisp: integer;
aNewMethod: pointer;
var aOldIVMT, aOldMethod: pointer): integer;
implementation
uses windows,
sysutils,
{$IFDEF DEBUG}
e0_log
{$ENDIF}
;
function PointerToHex(p: pointer): string;
begin
result := IntToHex(integer(p), 8);
end;
function SetHookForMethod(aIntrf: pointer; aMethodDisp: integer;
aNewMethod: pointer;
var aOldIVMT, aOldMethod: pointer): integer; {-1 - ошибки при работе
0 - без изменений;
1 - изменения проведены}
var
wp: pointer;
pIVMT: pointer; // адресс таблицы методов
pMethod: pointer; // адресс метода
op, op2: DWORD;
begin
result := 0;
if not assigned(aIntrf) then
exit;
move(integer(aIntrf), wp, sizeof(pointer));
move(wp^, pIVMT, sizeof(pointer));
{$IFDEF DEBUG}
if pIVMT <> aOldIVMT then
begin
Log(PointerToHex(aIntrf) + ': iVTM : '
+ PointerToHex(aOldIVMT) + ' -> ' + PointerToHex(pIVMT));
end;
{$ENDIF}
if pIVMT <> aOldIVMT then
aOldIVMT := pIVMT;
wp := pointer(integer(pIVMT) + aMethodDisp);
move(wp^, pMethod, sizeof(pointer));
if aNewMethod <> pMethod then
begin
if VirtualProtect(wp, sizeof(pointer), PAGE_READWRITE, @op) then
begin
move(aNewMethod, wp^, sizeof(pointer));
VirtualProtect(wp, sizeof(pointer), op, @op2);
{$IFDEF DEBUG}
if aOldMethod <> pMethod then
begin
Log(PointerToHex(aIntrf) + ': mthd :'
+ PointerToHex(aOldMethod) + ' -> ' + PointerToHex(pMethod));
end;
{$ENDIF}
aOldMethod := pMethod;
result := 1;
end
else
begin
{$IFDEF DEBUG}
Log(PointerToHex(aIntrf) + ': can''t replace method...');
{$ENDIF}
result := -1;
end
end;
end;
end.
=====
unit hook2_HTMLDoc;
interface
uses mshtml;
procedure SetHook_IHtmlDocument2(iDoc: IHtmlDocument2);
implementation
uses windows,
sysutils,
e0_ihook,
e0_log;
const
offset_Open_IHTMLDocument2 = 61 * sizeof(DWORD); // offset to open method, open is the ..th entry in the vtbl
offset_Invoke_IHTMLDocument2 = 6 * sizeof(DWORD); //$18 offset to Invoke method
var
lastHooked_IVMT: pointer; {таблица методов последнего перехваченного IHTMLDocument2;
для текущей задачи запоминать не нужно, просто любопытно, есть ли сия вещь постоянная -
для определления есть ли постоянство или нет храним последнее значение}
old_Open_Entry: pointer;
old_Invoke_Entry: pointer;
var
oldDispID: integer;
countOldDispID: integer;
function IDoc_Invoke_Hook(self: pointer; dummy1, dummy2, dummy3: LongInt;
DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; cdecl;
begin
if oldDispID <> DispID then
begin
Log('== invoke ==> ' + IntToStr(DispID) + ' self = ' + IntToHex(integer(self), 8) +
' coutOld = ' + IntToStr(countOldDispID));
countOldDispID := 1;
oldDispID := DispID;
end
else
inc(countOldDispID);
result := E_NOTIMPL;
end;
procedure IDoc_Invoke_Hook_Wraper; assembler;
asm
mov eax, [esp+4] // self
PUSH EBX
PUSH ECX
PUSH EDX
push eax
//CALL IDoc_Invoke_Hook
pop eax
POP EDX
POP ECX
POP EBX
jmp old_Invoke_Entry
end;
procedure IDoc_Open_Hook(
//self: pointer;
result: pointer;
d0, dummy1, dummy2, dummy3 {, dextr}: LongInt;
self: pointer;
const url: WideString;
(*
name: OleVariant;
features: OleVariant;
replace: OleVariant);
*)
nameL, nameH: int64;
featuresL, featuresH: int64;
replaceL, replaceH: int64);
cdecl;
var
featuresCopy: OleVariant;
begin
move(featuresL, featuresCopy, sizeof(OleVariant));
Log('== open ==> URL = ' + url + ' ' + featuresCopy + ' self = ' + IntToHex(integer(self), 8));
fillChar(featuresCopy, 0, sizeof(OleVariant));
end;
procedure IDoc_Open_Hook_Wraper; assembler;
asm
push eax
PUSH EBX
PUSH ECX
PUSH EDX
//push $55AA1122
CALL IDoc_Open_Hook
//pop eax
POP EDX
POP ECX
POP EBX
pop eax
jmp old_Open_Entry
end;
procedure SetHook_IHtmlDocument2(iDoc: IHtmlDocument2);
var
interfaceAsPointer: pointer absolute iDoc;
begin
if not assigned(iDoc) then
exit;
case SetHookForMethod(interfaceAsPointer, offset_Invoke_IHTMLDocument2,
@IDoc_Invoke_Hook_Wraper,
lastHooked_IVMT,
old_Invoke_Entry) of
-1:
begin
{$IFDEF DEBUG}
Log('**** error in SetHookForMethod for Invoke');
{$ENDIF}
end;
0: ;
1:
begin
{$IFDEF DEBUG}
Log('Invoke for IHTMLDocument2 replaced....');
{$ENDIF}
end;
end;
case SetHookForMethod(interfaceAsPointer, offset_Open_IHTMLDocument2,
@IDoc_Open_Hook_Wraper,
lastHooked_IVMT,
old_Open_Entry) of
-1:
begin
{$IFDEF DEBUG}
Log('**** error in SetHookForMethod for Open');
{$ENDIF}
end;
0: ;
1:
begin
{$IFDEF DEBUG}
Log('Open for IHTMLDocument2 replaced....');
{$ENDIF}
end;
end;
end;
end.
комментарии далее.
GuAV (2004-06-23 23:24:00) [29]Я как те хирурги, что сначала режут, а потом считают :(
mov edx, [esp+4+12] // self
push edx
GuAV (2004-06-23 23:12:00) [30]
> push eax
> mov edx, [esp+4+12+4] // self
> mov [esp], edx
имелось ввиду:
mov edx, [esp+4+12+4] // self:)
push edx
GuAV (2004-06-23 23:00:00) [31]Вы правы. Да, и EAX то можно и не изменять если он нужен будет:
procedure SimpleHook2; assembler;
asm
PUSH EBX //зачем?
PUSH ECX
PUSH EDX
push eax
mov edx, [esp+4+12+4] // self
mov [esp], edx
CALL TOpenHookIHTMLDocumentHolder.Invoke_Hook2
pop EDX
POP EDX
POP ECX
POP EBX //зачем?
jmp oldIHTMLDocument2_InvokeProc
end;
зы - опять тут наверное баг - не в Дельфе же пишу, и не проверяю :)
Ihor Osov'yak (2004-06-23 22:48:00) [32]2 [15] GuAV © (23.06.04 21:00)
Снова спасибо за подсказку (еще руки не дошли нарваться на соотв. баг), но снова решение не совсем так :-).
Дело в том, что в момент выполнения первого POP EAX в стеке в текущей позиции будет адрес возврата, а не значение self.
Корректным будет такое решение (уже проверялось отладчиком на предмет корректной передачи self):
procedure SimpleHook2; assembler;
asm
mov eax, [esp+4] // self
PUSH EBX
PUSH ECX
PUSH EDX
push eax
CALL TOpenHookIHTMLDocumentHolder.Invoke_Hook2
pop eax
POP EDX
POP ECX
POP EBX
jmp oldIHTMLDocument2_InvokeProc
end;
GuAV (2004-06-23 21:00:00) [33][14] - если есть такой момент, то исправить imho можно:
POP EAX
PUSH EBX
PUSH ECX
PUSH EDX
PUSH EAX
CALL TOpenHookIHTMLDocumentHolder.Invoke_Hook2
POP EAX
POP EDX
POP ECX
POP EBX
PUSH EAX
GuAV (2004-06-23 19:43:00) [34]Кстати а в stdcall self слева или справа? я к тому, что если слева, то реальный self в dummy3 а в self - лажа!
Ihor Osov'yak (2004-06-23 17:36:00) [35]2 [11] GuAV © (23.06.04 17:10)
>jmp oldIHTMLDocument2_InvokeProc
>Хорошо придумано,
ну это.. детство в районах машинных кодов 8080, досовская юность :-).
Там такая техника вдоль и впоперек использовалась..
Ihor Osov'yak (2004-06-23 17:32:00) [36]да какой. Самый очевидный :-).
Сейчас.. Пока код ну совершенно неопрятен, так как стадия исследований в условиях цейтнота, и в перемешку с другой задачей, так что не обезсудь..
Ну не предначался он для обнародывания.. А облогораживать его я начну часов через три - поэтому даю как есть..
Надеюсь, реализацию можно отследить. Ставит перехватчик doTestHtmlDoc..
e0_log, e0_log.Log - запись в лог файл, с целью отладки.. Правда, пищу я туда пока почти что бред сумашедшего..
unit hook_HTMLDoc;
interface
uses mshtml;
procedure doTestHtmlDoc(iDoc: IHtmlDocument2);
implementation
uses windows,
sysutils,
e0_log;
const
dfOffsetOpenIHTMLDocument2 = 61 * sizeof(DWORD); // offset to open method, open is the ..th entry in the vtbl
dfOffset_InvokeIHTMLDocument2 = 6 * sizeof(DWORD); //$18 offset to Invoke method
var
hookedIHTMLDocument2VMT: pointer;
oldIHTMLDocument2OpenProc: pointer;
oldIHTMLDocument2_InvokeProc: pointer;
type
TOpenHookIHTMLDocumentHolder = class
private
class function OpenIHTMLDocument2_Hook(const url: WideString; name: OleVariant; features: OleVariant;
replace: OleVariant): IDispatch; safecall;
class function GetAddr_OpenIHTMLDocument2_Hook: pointer;
class function Invoke_Hook(DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
class function Invoke_Hook2(dummy1, dummy2, dummy3: LongInt; DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; cdecl;
class function GetAddr_Invoke_Hook: pointer;
end;
{ TOpenHookIHTMLDocumentHolder }
class function TOpenHookIHTMLDocumentHolder.OpenIHTMLDocument2_Hook(const url: WideString; name: OleVariant; features: OleVariant;
replace: OleVariant): IDispatch; safecall;
var
oldDoer: function(const url: WideString; name: OleVariant; features: OleVariant;
replace: OleVariant): IDispatch of object; safecall;
begin
TMethod(oldDoer).Code := oldIHTMLDocument2OpenProc;
TMethod(oldDoer).Data := self; //pointer(intData);
result := oldDoer(url, name, features, replace);
end;
class function TOpenHookIHTMLDocumentHolder.GetAddr_OpenIHTMLDocument2_Hook: pointer;
var
hop: function(const url: WideString; name: OleVariant; features: OleVariant;
replace: OleVariant): IDispatch of object; safecall;
begin
hop := self.OpenIHTMLDocument2_Hook;
Result := TMethod(hop).Code;
end;
procedure SimpleHook; assembler;
asm
mov eax, oldIHTMLDocument2_InvokeProc
jmp eax
end;
procedure SimpleHook2; assembler;
asm
PUSH EBX
PUSH ECX
PUSH EDX
CALL TOpenHookIHTMLDocumentHolder.Invoke_Hook2
POP EDX
POP ECX
POP EBX
jmp oldIHTMLDocument2_InvokeProc
end;
procedure doTestHtmlDoc(iDoc: IHtmlDocument2);
var
p: pointer;
hdocIVMT: pointer;
hop: pointer;
op, op2: DWORD;
//iDisp: IDispatch;
tmpP: pointer;
begin
if not assigned(iDoc) then
exit;
move(integer(iDoc), p, sizeof(pointer));
move(p^, hDocIVMT, sizeof(pointer));
Log(' p = ' + IntToHex(integer(p), 8) + '; hDocIVMT = ' + IntToHex(integer(hDocIVMT), 8));
if not assigned(hookedIHTMLDocument2VMT) then
begin
Log(' **** Set hookedDocIVMT: ' + IntToHex(integer(hDocIVMT), 8));
hookedIHTMLDocument2VMT := hDocIVMT;
p := pointer(integer(hookedIHTMLDocument2VMT) + dfOffsetOpenIHTMLDocument2);
move(p^, oldIHTMLDocument2OpenProc, sizeof(pointer));
hop := TOpenHookIHTMLDocumentHolder.GetAddr_OpenIHTMLDocument2_Hook;
if VirtualProtect(p, sizeof(pointer), PAGE_READWRITE, @op) then
begin
move(hop, p^, sizeof(pointer));
VirtualProtect(p, sizeof(pointer), op, @op2);
end
else
hookedIHTMLDocument2VMT := nil; // ? or generate raise
p := pointer(integer(hookedIHTMLDocument2VMT) + dfOffset_InvokeIHTMLDocument2);
move(p^, oldIHTMLDocument2_InvokeProc, sizeof(pointer));
hop := @SimpleHook2; //{@InvokeHookWraper; }//{@Skip;} TOpenHookIHTMLDocumentHolder.GetAddr_Invoke_Hook;
if VirtualProtect(p, sizeof(pointer), PAGE_READWRITE, @op) then
begin
move(hop, p^, sizeof(pointer));
VirtualProtect(p, sizeof(pointer), op, @op2);
end
else
hookedIHTMLDocument2VMT := nil; // ? or generate raise
end
else
begin
if hookedIHTMLDocument2VMT <> hDocIVMT then
begin
Log(' ************ ??????? Other iVTM: ' + IntToHex(integer(hDocIVMT), 8));
end
else
begin
p := pointer(integer(hookedIHTMLDocument2VMT) + dfOffset_InvokeIHTMLDocument2);
move(p^, tmpP, sizeof(pointer));
if tmpP <> oldIHTMLDocument2_InvokeProc then
Log(' $$$$$$$$$ ??????? Other iVTM: ' + IntToHex(integer(hDocIVMT), 8));
end;
end;
end;
class function TOpenHookIHTMLDocumentHolder.GetAddr_Invoke_Hook: pointer;
var
hop: function(DispID: Integer;
const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult,
ExcepInfo, ArgErr: Pointer): HResult of object; stdcall;
begin
hop := self.Invoke_Hook;
Result := TMethod(hop).Code;
end;
class function TOpenHookIHTMLDocumentHolder.Invoke_Hook(DispID: Integer;
const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult,
ExcepInfo, ArgErr: Pointer): HResult; stdcall;
var
oldDoer: function(DispID: Integer;
const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult,
ExcepInfo, ArgErr: Pointer): HResult of object; stdcall;
begin
TMethod(oldDoer).Code := oldIHTMLDocument2_InvokeProc;
TMethod(oldDoer).Data := self;
result := oldDoer(DispID, IID, LocaleID, Flags, Params,
VarResult, ExcepInfo, ArgErr);
end;
class function TOpenHookIHTMLDocumentHolder.Invoke_Hook2(dummy1, dummy2, dummy3: LongInt; DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; cdecl;
begin
Log('=========> ' + IntToStr(DispID));
result := E_NOTIMPL;
end;
end.
Ключевой момент - получение указателя на таблицу методов интерфейса
move(integer(iDoc), p, sizeof(pointer));
move(p^, hDocIVMT, sizeof(pointer));
ну а далее дело техники.
Еще один некрасивый момент - получение нужного смешения в таблице.
Копаться в премудностях IDL и вокруг него терпения не хватило - поэтому просто смотрю, какой код генерирует компилятор при вызове соотв. метода, далее прописываю как константу - см. к примеру dfOffset_InvokeIHTMLDocument2
В приведенном примере с OpenIHTMLDocument2_Hook проблем не возникало, возникли проблемы с Invoke_Hook - см. выше. Посему сейчас работает парочка
SimpleHook2 + Invoke_Hook2.. Вернее пока придерживаюсь мнения, что реализация будет идти в этом направлении.
GuAV (2004-06-23 17:10:00) [37]Ihor Osov'yak © (23.06.04 05:27) [6]
jmp oldIHTMLDocument2_InvokeProc
Хорошо придумано, я бы возможно долго к этому шел. ihmo почти универсальный подход - ничего кроме EAX не трогает но позволяет писать на Delphi а не на asm.
теперь, кстати и PUSH EBX/POP EBX не нужны.
> мне любопытно, какой механизм перехвата интерфейсного вызова
> ты используешь в наст.момент ... поделись ...
мне, кстати, тоже.
Digitman (2004-06-23 16:20:00) [38]
> Ihor Osov'yak © (23.06.04 12:41) [9]
мне любопытно, какой механизм перехвата интерфейсного вызова ты используешь в наст.момент ... поделись ...
Ihor Osov'yak (2004-06-23 12:41:00) [39]2 [8] Digitman © (23.06.04 08:14)
> а не проще ли перехватить все обращения к интересующему интерфейсу, воспользовавшись станд.механизмом делегирования интерфейсов ?
Обьекты моего внимания - не мои обьекты (в смысле, не мой исходный код, это можно считать экземпляры несклольких "стандартных" inproc виндозных ком-серверов). При создании некоторого ком-обьекта он создает еще несколько иного типа ком-обьектов для поддержки своей функциональности, некоторые интерфейсы к которым также можно получить. Вот меня интересует и взаимодействие между этими обектами (как видите, часть из них мною даже не создаются). То есть я никак не могу вклиниться во внутреннию логику этих обьектов, чтобы в делегирование поиграть...
.. Меня интересует на даном этапе протоколирование некоторых взамодействий между этими обьектов , которые они осуществляют через вызовы методов интерфейсов друг друга. В перспективе - возможно иммуляция завершения с отрицательным результатом некоторых вызовов при некоторых обстоятельствах.
Штатные нотификационные интерфейсы, которые поддерживаются этими ком-обьектами не могут полностью удовлетворить моего любопытства и потребностей. Пока я ничего лучшего, чем перехват некоторых методов интерфейсов не придумал.
Digitman (2004-06-23 08:14:00) [40]
> Ihor Osov'yak © (22.06.04 23:22)
> необходимости
> перехвата Invoke некоего наследника от IDispatch
а не проще ли перехватить всеобращения к интересующему интерфейсу, воспользовавшись станд.механизмом делегирования интерфейсов ?