如何获取指向 COM 方法的指针以进行挂钩?
Posted
技术标签:
【中文标题】如何获取指向 COM 方法的指针以进行挂钩?【英文标题】:How to get a pointer to a COM method for hooking? 【发布时间】:2011-04-04 18:38:08 【问题描述】:我知道这似乎是一个过度回答的问题,但这个问题不同。
我有这个导出一些方法的 ActiveX 对象。我需要在它的一种方法上设置一个钩子,即Func1
,我知道如何使用VirtualProtect()
等来做到这一点。如果我打开ollydbg 并深入研究ActiveX DLL,我会看到@987654323 的地址@ 我需要设置钩子的地方。
使用 Visual Studio 2008,我正在使用预编译器指令:
#import "myactx.dll" no_namespace, named_guids, raw_interfaces_only
在我的代码中,我正在尝试这样做:
LPVOID ptr = &IMyActx::Func1;
编译时返回错误:
错误 C2440:“正在初始化”:无法从“HRESULT (__stdcall IMyActx::*)(BSTR,BSTR)”转换为“LPVOID”。
我研究并发现由于隐含的 this
参数,我无法将指向类方法的指针转换为指向函数的指针。
但是,我不打算调用这个函数。我只想知道它在内存中的地址,以便我可以将它传递给我的挂钩例程(它需要一个 LPVOID
指针)。
这对我来说似乎不可思议,我可以调用一个函数但不能在内存中获取它的原始地址。这让我想插入一些 x86 汇编代码只是为了得到我想要的指针。
非常感谢您的任何建议,感谢阅读。
编辑...
现在想起来了……LPVOID ptr = &IMyActx::Func1;
不应该工作,因为IMyActx
是一个纯虚拟类!无论如何,我在实例化一个对象之前尝试了一些组合并尝试获取指向Func1
的原始指针,但到目前为止没有任何效果。我在 ollydbg 中看到对Func1
的调用生成为:
mov edx, [eax];
call dword ptr ds:[edx + 0x1C];
所以我可以假设我需要在vptr + 0x1C
字节处读取DWORD
,然后瞧。
如果我没记错的话,组件对象模型保证它总是位于[vptr + 0x1C]
的Func1
ptr 给定相同的接口ID。永远。对吧?
我可能会用一些伪代码来做这样的事情:
#define FUNC1_ENTRY 0x1C / sizeof(LPBYTE)
CoCreateInstance(CLSID_MyActx, 0, CLSCTX_ALL, IID_IMyActxObj, (LPVOID*) &pObj);
LPVOID pToHook = (*pObj)[FUNC1_ENTRY];
【问题讨论】:
指向方法的指针和指向函数的指针不可转换,因为标准不保证(故意)方法指针可以存储在 void* 中。而一个简单的函数指针可以存储在一个 void* 中。 @Martin reinterpret_cast 编译时返回:code
错误 C2440:'reinterpret_cast':无法从 'HRESULT (__stdcall IMyActx::* )(BSTR,BSTR)' 转换为 'LPVOID' 1>没有可以进行这种转换的上下文code
抱歉,这是一时冲动,虽然我认为是错误的。我把它拿出来是因为它不应该工作。 (注意方法指针可能比普通指针大。所以它不能转换成 void*)。
问题不仅在于指针大小,还在于ptm性质。如果它指向抽象类的虚拟方法,并且仅在您提供此方法时才调用分派;)?指向成员的指针根本不是“真正的”内存指针......
您在 update 中描述的解决方案应该可以工作,但看起来很危险 :) 几年前,我使用绕道来连接 activex 对象。我刚刚使用了 ANSI C 版本的 activex 接口(你知道,midl 在一个文件中生成两个版本,一个用于 C,一个用于 C++)。
【参考方案1】:
COM 对象的方法实际上是普通的函数。这就是使 COM 客户端能够用 C 而不是 C++ 编写的原因。
我建议为 C 客户端生成头文件,然后可以轻松检索函数地址。但是,您首先需要一个要挂钩的类的实例。
【讨论】:
COM 对象的方法不是实际上是普通函数。如果将对象实现为 C++ 类,则不会有任何“常规”C 函数。使用C-client头文件,你不会只是得到一个stub方法的地址吗?然而,这对于 OP 的目的来说可能已经足够了。 @Andre:它们是使用 stdcall 调用约定的普通 C 函数。 COM 对象只不过是一个指向普通 C 函数指针结构的指针。这就是使 COM 服务器能够以非 C++ 语言编写的原因。存根仅涉及跨进程封送处理。事实上,Visual C++,当虚拟成员函数被声明为__stdcall
时,恰好生成了一个与 COM ABI 匹配的 vtable,这是一个令人高兴的巧合(尽管当然是由 Visual C++ 团队有意实现的)......所有的 COM 都定义在纯 C 的术语。【参考方案2】:
如果我理解正确,您想将指向类方法的指针转换为函数指针。
这实际上是不可能的。
简单示例:
class A
public:
void func()
this->var = 0;
int var;
;
A myObj;
A::func* ptr2 = &(myObj::func); // works
void* ptr = &(myObj::func); // doesn't: what would "this" in line 5 point to?
你甚至不能使用reinterpret_cast
。
曾经也遇到过这个问题。然后我只需要创建一个函数,将输入值映射到指向对象的指针。也许您可以创建一个包装函数,在其中通过指向它的指针来调用该方法?
【讨论】:
在使用__stdcall
调用约定时情况会发生变化,因为它适用于所有 COM 对象方法。使用 stdcall 的类成员函数是一个以this
作为第一个参数的普通函数。 (对于使用默认的 __thiscall
调用约定的成员函数,情况并非如此。)
现在,即使成员函数是普通的__stdcall
函数,指向成员函数的指针仍然与函数指针不同,这是正确的。事实上,我不认为有一个简单的语法来获取一个普通的函数指针,一个可以读取 vtable 或者通过它的错位名称获取函数的地址。以上是关于如何获取指向 COM 方法的指针以进行挂钩?的主要内容,如果未能解决你的问题,请参考以下文章