如何获取用于挂钩的COM方法指针?

3
我知道这似乎是一个被过度回答的问题,但这个问题是不同的。我有一个导出一些方法的ActiveX对象。我需要在其中一个方法上设置钩子,即Func1。我知道如何使用VirtualProtect()等方法来做到这一点。如果我打开ollydbg并深入研究ActiveX DLL,我会看到我需要设置钩子的Func1的确切地址。
使用Visual Studio 2008,我正在使用预编译指令:
#import "myactx.dll" no_namespace, named_guids, raw_interfaces_only

在我的代码中,我正在尝试做到这一点:

LPVOID ptr = &IMyActx::Func1;

当编译时出现错误:

错误 C2440:'initializing' : 无法将类型为 'HRESULT (__stdcall IMyActx::* )(BSTR,BSTR)' 的数据转换为类型为 'LPVOID' 的数据。

我研究后发现,由于隐式的 this 参数,我无法将一个指向类方法的指针转换为函数指针。

但是,我并不打算调用这个函数。我只想知道它在内存中的地址,以便将其传递给我的 hooking 程序(该程序期望一个 LPVOID 指针)。

我觉得这似乎是不可思议的,我可以调用一个函数却无法获得它在内存中的原始地址。这让我想插入一些 x86 汇编代码以获取所需的指针。

非常感谢您的阅读,如果有任何建议,请告诉我。

编辑...
现在考虑到... LPVOID ptr = &IMyActx::Func1; 不应该起作用,因为 IMyActx 是一个纯虚类!无论如何,我之前尝试过一些组合,试图实例化一个对象并尝试获取指向 Func1 的原始指针,但都没有成功。在 ollydbg 中,我看到对 Func1 的调用生成了以下代码:

mov edx, [eax];
call dword ptr ds:[edx + 0x1C];

所以我可以假设我需要读取vptr + 0x1C字节处的DWORD,然后就完成了。

如果我没记错的话,组件对象模型保证在给定相同接口ID的情况下,永远会是[vptr + 0x1C]处的Func1指针。对吗?

我可能会用类似伪代码的方式来实现:

#define FUNC1_ENTRY   0x1C / sizeof(LPBYTE)

CoCreateInstance(CLSID_MyActx, 0, CLSCTX_ALL, IID_IMyActxObj, (LPVOID*) &pObj);

LPVOID pToHook = (*pObj)[FUNC1_ENTRY];

1
指向方法的指针和指向函数的指针不能相互转换,因为标准(故意地)不保证方法指针可以存储在void中。而一个简单的函数指针可以存储在void中。 - Martin York
@Martin,reinterpret_cast在编译时返回以下错误:code error C2440: 'reinterpret_cast' : 无法将类型为 'HRESULT (__stdcall IMyActx::* )(BSTR,BSTR)' 的内容转换为类型为 'LPVOID' 的内容。 1> 不存在可以进行此转换的上下文。code - Marcus Geist
抱歉,那是我一时冲动的想法,后来意识到是错误的。我将其删除了,因为它不应该起作用。(请注意,方法指针可能比普通指针更大。因此,它不能转换为void*)。 - Martin York
1
问题不仅在于指针大小,而且在于ptm的本质。如果它指向抽象类的虚方法,并且只有在你提供这个方法时才调用分发怎么办?成员指针根本不是真正的内存指针... - tuxSlayer
你在更新中描述的解决方案应该是可行的,但看起来相当危险 :) 几年前,我使用了Detours来挂钩ActiveX对象。 我只使用了ANSI C版本的ActiveX接口(你知道,midl在一个文件中生成两个版本,一个用于C,一个用于C++)。 - tuxSlayer
2个回答

1

COM对象的方法实际上是普通函数。这就使得使用C而不是C++编写的COM客户端成为可能。

我建议为C客户端生成头文件,这样函数地址就可以轻松获取。但是,您首先需要一个要挂钩的类的实例。


COM对象的方法实际上并不是普通函数。如果您将对象实现为C++类,则不会有任何“常规”C函数。使用C客户端头文件,您只会得到存根方法的地址?这对于OP的目的可能已经足够了。 - André Caron
@Andre:它们是使用stdcall调用约定的普通C函数。COM对象只是指向普通C函数指针结构的指针。这就是使非C++语言编写的COM服务器成为可能的原因。存根仅涉及跨进程封送。当虚拟成员函数声明为__stdcall时,Visual C++生成与COM ABI匹配的vtable只是一个愉快的巧合(尽管当然是由Visual C++团队有意实现的)...所有COM都是以纯C定义的。 - Ben Voigt

0

如果我理解你的意思正确,你想将一个指向类方法的指针转换为函数指针。

实际上这是不可能的。

以下是一个简单的例子:

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调用约定的成员函数并不正确。) - Ben Voigt
现在,你说的没错,即使成员函数是一个普通的__stdcall函数,指向成员函数的指针仍然不同于函数指针。事实上,我认为没有一个简单的语法来获取一个普通的函数指针,可以通过读取虚表或者使用其符号名称来获取函数的地址。 - Ben Voigt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接