首页
社区
课程
招聘
[求助]如何比较简单的获取C++接口的某个方法的地址?
发表于: 2021-2-6 17:16 7350

[求助]如何比较简单的获取C++接口的某个方法的地址?

2021-2-6 17:16
7350
收藏
免费 0
支持
分享
最新回复 (38)
雪    币: 211
活跃值: (531)
能力值: ( LV9,RANK:172 )
在线值:
发帖
回帖
粉丝
26



ida打开dx的dll文件, 加载pdb。 地址减一下除以指针长度。

2021-2-8 18:29
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
27
soatm 明白了你的意思了。但是我有个疑问,有头文件,又有接口实例的指针,按照我的理解,这其实就只是没有给你源文件了。既然有了头文件,那通过这个头文件,可以很轻易的拿到函数的地址,就好比你说的IUnknown* ...

其实问题主要出在编译器,类本身和结构高度相似,且编译器对->完美兼容,
问题就是编译器强制给&IUnknown->Release这种写法报错。结构指针就无此问题。

如果编译器不报错,拿到的就是函数地址了。

最后于 2021-2-18 11:27 被sunbinjin编辑 ,原因:
2021-2-18 09:27
0
雪    币: 27
活跃值: (127)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
28

这种接口指针是通过虚表指针成员来获得函数地址,没法通过接口类型在编译时刻获取具体的偏移量。

如果编译为 C 代码,就好办多了,可以先通过虚表结构体轻松获得函数基于虚表指针的偏移量。

在运行时刻,先通过接口地址得到虚表指针的地址,再通过虚表的得到接口函数的地址。


首先,最好是使用微软提供的宏将接口声明改成 C/C++ 通用的声明语句,放在公共头文件。如:

#undef  INTERFACE
#define INTERFACE ISample

DECLARE_INTERFACE_(ISample, IUnknown)
{
    BEGIN_INTERFACE

    // *** IUnknown methods ***
    STDMETHOD (QueryInterface)(THIS_ REFIID riid, void **ppv) PURE;
    STDMETHOD_(ULONG,AddRef)(THIS) PURE;
    STDMETHOD_(ULONG,Release)(THIS) PURE;

    // *** ISample methods ***
    STDMETHOD (MethodWithSingleParam)(THIS_ LONG nArg) PURE;
    STDMETHOD (MethodWithoutParam)(THIS) PURE;
    
    END_INTERFACE
};

这里补充一下,如果是微软的头文件就没这个问题,如果是其他的建议修改一下。


将基于虚表地址的静态偏移量放在另外一个 c 文件中,通过 extern 导出偏移量给 C++ 调用。

size_t nMethodOffset0 = offsetof(ISampleVtbl, QueryInterface);
size_t nMethodOffset1 = offsetof(ISampleVtbl, AddRef);
size_t nMethodOffset2 = offsetof(ISampleVtbl, Release);
size_t nMethodOffset3 = offsetof(ISampleVtbl, MethodWithSingleParam);
size_t nMethodOffset4 = offsetof(ISampleVtbl, MethodWithoutParam);

#if defined(__cplusplus)
extern "C" {
#endif

LPBYTE GetSampleVtblAddress(LPCVOID pv)
{
    return (LPBYTE)*(ISampleVtbl **)((LPBYTE)pv + offsetof(ISample, ISampleVtbl));
}

#if defined(__cplusplus)
}
#endif

或者直接用 C 语法来写这部分功能更省事。


C++ 调用的时候,可以通过接口指针得到虚表地址,再通过虚表地址得到函数地址:

ISample *ps;

//
// 通过 union 将指针 ps 转换成标准的指针 pv,略去。
//
LPBYTE pVtbl = GetSampleVtblAddress(pv);

LPVOID pOffset0 = (LPVOID)(lpVtbl + nMethodOffset0);
LPVOID pOffset1 = (LPVOID)(lpVtbl + nMethodOffset1);
LPVOID pOffset2 = (LPVOID)(lpVtbl + nMethodOffset2);
LPVOID pOffset3 = (LPVOID)(lpVtbl + nMethodOffset3);
LPVOID pOffset4 = (LPVOID)(lpVtbl + nMethodOffset4);

仅供参考思路,代码未实测。

最后于 2021-2-20 16:25 被Xenophon编辑 ,原因:
2021-2-20 13:16
0
雪    币: 211
活跃值: (118)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
29
sunbinjin [em_10] 你这是要疯啊。。。 我本意是求简单的,你搞这么大阵仗[em_2]。 你这代码我承认是能跑出想要的结果。 但是我就只是想求一种借编译器的自动结构取成员的位置而已,不是要这么算呀。


试试: offsetof(结构体, 成员)
2021-3-3 21:47
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
30
孤山散人 试试: offsetof(结构体, 成员)

offsetof是个宏 

测试结果:

2021-3-4 09:24
0
雪    币: 211
活跃值: (118)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
31

在VS2017中,这个宏定义在stddef.h 中


#if defined _MSC_VER && !defined _CRT_USE_BUILTIN_OFFSETOF
    #ifdef __cplusplus
        #define offsetof(s,m) ((::size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
    #else
        #define offsetof(s,m) ((size_t)&(((s*)0)->m))
    #endif
#else
    #define offsetof(s,m) __builtin_offsetof(s,m)
#endif

最后于 2021-3-6 12:56 被孤山散人编辑 ,原因:
2021-3-6 12:54
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
32
孤山散人 在VS2017中,这个宏定义在stddef.h 中#if&nbsp;defined&nbsp;_MSC_VER&nbsp;&amp;&amp;&nbsp ...
我就是2017,宏定义也找到了的,只是说这种写法会编译不过。
2021-3-6 14:32
0
雪    币: 211
活跃值: (118)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
33
offsetof(IDriect3Device9, Present)  这样写,第一个参数是类名,不是变量名。 
2021-3-7 22:08
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
34
孤山散人 offsetof(IDriect3Device9, Present) 这样写,第一个参数是类名,不是变量名。

宏是有的,但是还是提示出错。

2021-3-8 09:32
0
雪    币: 12848
活跃值: (9147)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
35
一般常规做法都是用索引去虚表里强行取
2021-3-8 20:15
0
雪    币: 211
活跃值: (118)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
36
这个宏用于取成员变量的地址,Present是个成员函数。函数地址一般是固定的,直接用& 符号就行。
2021-3-8 20:17
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
37
hzqst 一般常规做法都是用索引去虚表里强行取
我现在就是这么取的,我的意思是有什么办法不用人为去算那个偏移,有时候会数错,也麻烦,而是借用编译器来知道这个偏移,本身编译器是知道的。
2021-3-9 09:32
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
38
孤山散人 这个宏用于取成员变量的地址,Present是个成员函数。函数地址一般是固定的,直接用& 符号就行。
不行的,第一贴就已经写出来的,&成员函数不支持,可能是编译器故意做了限制。
2021-3-9 09:33
0
雪    币: 27
活跃值: (127)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
39
sunbinjin 我现在就是这么取的,我的意思是有什么办法不用人为去算那个偏移,有时候会数错,也麻烦,而是借用编译器来知道这个偏移,本身编译器是知道的。
我在上面都给出来了,你不看怪谁。
2021-4-9 12:57
0
游客
登录 | 注册 方可回帖
返回
//