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

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

2021-2-6 17:16
5584

问这个问题主要是Hook接口时使用

比如ITest里面有上百个方法,要获取其中某个方法的地址时,我现在就是打开头文件,从第一个方法向后数,方法地址就是函数序号*指针长度(4或8字节)


这种方法可行。因为我们都知道要hook哪个函数,名字都是知道的,但老数顺序感觉很笨。而且容易数错


我本来是想:

void* p = ITest->Methon888,按理说这就是个地址。但编译时,VC提示类型不匹配。

void* p =  (void*)ITest->Methon888,强转也不行。

void* p =  (void*)&ITest->Methon888,取地址也不行,“&”: 绑定成员函数表达式上的非法操作



有大神按这思路很快能取出地址来不?




看雪2022 KCTF 秋季赛 防守篇规则,征题截止日期11月12日!(iPhone 14等你拿!)

最后于 2021-2-7 09:43 被sunbinjin编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (38)
雪    币: 4283
活跃值: 活跃值 (1282)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
htpidk 活跃值 2021-2-6 18:37
2
0

雪    币: 6873
活跃值: 活跃值 (1813)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
lononan 活跃值 2021-2-6 19:59
3
0
配合1楼的,你想要的是这个吧.
template <typename T>
void* fn(T k)
{
    union MyUnion
    {
        void* kvoid;
        T kt;
    }u;
    u.kt = k;
    return u.kvoid;
}
ULONG64 get_classfun_x64(void* ptr, void* jmpadd)
{
    return ((ULONG64**)ptr)[0][((PCHAR)jmpadd)[5] / 8];
}
printf("%p,%p,%p", ((ULONG64**)g_pSwapChain)[0][0x40 / 8], get_classfun_x64(g_pSwapChain, fn(&IDXGISwapChain::Present)), &IDXGISwapChain::Present);
雪    币: 1972
活跃值: 活跃值 (2879)
能力值: ( LV6,RANK:97 )
在线值:
发帖
回帖
粉丝
fjqisba 活跃值 2021-2-6 21:11
4
0
万能的取地址符&
雪    币: 1714
活跃值: 活跃值 (1915)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Mxixihaha 活跃值 2021-2-6 21:15
5
1
auto FunPtr = &Connection15::Open;  //域+名字

CString str;
str.Format(L"%p",FunPtr);
AfxMessageBox(str);


他们的都太复杂了... 直接用我这个.

雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 09:19
6
0
htpidk
都不是,我说的是接口,不是普通的类,你们用的ITest::Fun1这种是取的函数地址,不是基于纯虚类的方法。
因为你们这种函数地址在编译时本身就是知道的。
而基于纯虚接口的对象,编译时根本不知道所指向的函数地址在哪个模块。
所以得尝试IUnknow->Release这种去取。
雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 09:22
7
0
fjqisba 万能的取地址符&[em_21]

基于接口你这方法不行的

最后于 2021-2-7 09:23 被sunbinjin编辑 ,原因:
雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 09:23
8
0
lononan 配合1楼的,你想要的是这个吧. template void* fn(T k) {    union MyUnion    {        void* kvoid;       ...

你在搞DXGI,我们差不多
这是我现在的:
g_pFunc = (*(void***)d3d9Device)[17];//Present


你的方法看上去也不简单啊

d3d9Device->Present本身就是个地址。

但就是没办法取出来,强转成void*时,vc提示类型不匹配

取地址时:error C2276: “&”: 绑定成员函数表达式上的非法操作



最后于 2021-2-7 09:26 被sunbinjin编辑 ,原因:
雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 09:33
9
0
htpidk

不行的,取地址和不取地址,都试了,错误如图:

最后于 2021-2-7 09:36 被sunbinjin编辑 ,原因:
雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 09:38
10
0
Mxixihaha auto&nbsp;FunPtr&nbsp;=&nbsp;&amp;Connection15::Open;&nbsp;&nbsp;//域+名字 C ...
换成IUnknow你试下,就知道这方法不行的。
你这个只能取函数地址。
雪    币: 0
活跃值: 活跃值 (233)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
soatm 活跃值 2021-2-7 09:42
11
0
楼主的意思应该是想通过获取到函数的地址判断是否是否是想要的那个函数吧?个人愚见:C++代码编译后,没有了符号文件,所以你想通过这种方式来判断获取到的地址是不是某个函数,是不可行的。但是如果有PDB文件,又是另一回事了。
雪    币: 1714
活跃值: 活跃值 (1915)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Mxixihaha 活跃值 2021-2-7 10:05
12
0
我最不喜欢动不动就是别人不行的了  自己不动脑筋就是第一句来个    你这个不行的  你这个咋的.
	auto FunPtr = &IDXGISwapChain::Present;
	
	char szBuf[10] = { 0 };
	sprintf_s(szBuf, "%p", FunPtr);

	DWORD FuncAddr = strtoul(szBuf, NULL, 16);

	printf("IDXGISwapChain::Present接口地址为:  %08X\n", FuncAddr);


最后于 2021-2-7 10:05 被Mxixihaha编辑 ,原因:
雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 13:48
13
0
Mxixihaha 我最不喜欢动不动就是别人不行的了&nbsp; 自己不动脑筋就是第一句来个&nbsp; &nbsp; 你这个不行的&nbsp; 你这个咋的. auto&nbsp; ...

我错了,你这个确实是编译得过。 

但是,我仔细分析了下,你这个很奇怪,虽说VC能编译过,但理论上说不过去啊 

  1. 在第三方DLL未加载时,它怎么可能得到到真实的地址?理论上就不可能知道啊。

  2. 实际测试后,FunPtr所指向的汇编内容并非D3D DLL里的汇编代码,仅仅是一个跳转指令,根本不可能用于HOOK。

  3. 所以此地址不是真正的函数地址。


再看地址所指向的汇编(我的pFun,明显是D3D自己的代码):

FunPtr,指向的仅仅是一个跳转,里面有个88h,确实是个+17个方法。等于VC计算出来了偏移。但此函数并非真实的方法地址。

雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 13:53
14
0
soatm 楼主的意思应该是想通过获取到函数的地址判断是否是否是想要的那个函数吧?个人愚见:C++代码编译后,没有了符号文件,所以你想通过这种方式来判断获取到的地址是不是某个函数,是不可行的。但是如果有PDB文件 ...
不是这个意思,是根据第三方提供的.h里,配合手上拿到的接口实例指针。
获取接口某个方法的真实函数地址,用于Hook。
VC其实是知道接口中某个方法相对接口的偏移的,就是虚表。我是想借编译器快速定位,比如IUnknow的Release一定是第3个方法,如何我不用自己去数这个3,而依赖Ixxx->Release直接求出任何方法的位置。
雪    币: 6873
活跃值: 活跃值 (1813)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
lononan 活跃值 2021-2-7 14:47
15
0
sunbinjin 不是这个意思,是根据第三方提供的.h里,配合手上拿到的接口实例指针。 获取接口某个方法的真实函数地址,用于Hook。 VC其实是知道接口中某个方法相对接口的偏移的,就是虚表。我是想借编译器快速定位 ...
我给你那个思路就是获取偏移的,坏处就是,源码中会添加跳转函数,还有,我也只简单实现,我测试的时候跳转指令中偏移数据占1字节,所以直接char*获取偏移,然后计算,这个你自己具体情况优化就行了.
取地址用的是类名::函数名,不是变量::函数名.
测试的时候发现,成员函数取地址,不能强制转换,所以必须用2楼的方法来转换.
雪    币: 8
活跃值: 活跃值 (120)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
duanqiang 活跃值 2021-2-7 15:44
16
0
C++11的bind 和 function.就可以解决了。因为C++有类机制,所以函数是需要和this指针关联的。如果你直接强制转,其实会出错的。所以。。。。。。。
雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 16:05
17
0
lononan 我给你那个思路就是获取偏移的,坏处就是,源码中会添加跳转函数,还有,我也只简单实现,我测试的时候跳转指令中偏移数据占1字节,所以直接char*获取偏移,然后计算,这个你自己具体情况优化就行了. 取地 ...
嗯,你其实是取汇编代码的字节码吧?
我的测试结果,表示值不正确,是不是因为是x64的原因。
00007FFFE361A010,0000017E4890EA20,00007FF73699220C
雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 16:46
18
0
lononan 我给你那个思路就是获取偏移的,坏处就是,源码中会添加跳转函数,还有,我也只简单实现,我测试的时候跳转指令中偏移数据占1字节,所以直接char*获取偏移,然后计算,这个你自己具体情况优化就行了. 取地 ...

如果按字节码强算的话,就不需要2楼那么麻烦搞模板函数了:

这种写法严重依赖生成的机器码,太恶心了。可能换个编译器版本都不是这样。

雪    币: 1714
活跃值: 活跃值 (1915)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Mxixihaha 活跃值 2021-2-7 16:51
19
1

还有另外一种异常获取方式 让其得到地址 = 0 + 偏移 的方式  配合反汇编引擎

				auto p1 = &IDirect3DDevice9::Present;
				DWORD * pfc = (DWORD *)&p1;

				DWORD pthis = 0;

				typedef void (__stdcall *pfnBadCall)(DWORD *ZeroThis);

				pfnBadCall haha = (pfnBadCall)pfc[0];
				__try
				{
					haha(&pthis); //随便构造 反正在 0+偏移要异常的!
				}
				__except (GetPresentfilter(GetExceptionCode(), GetExceptionInformation()))
				{
					printf("%d\n", g_RealPresentIdx);//在异常里面获取偏移
				}
int GetPresentfilter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {

	puts("in filter.");

	if (code == EXCEPTION_ACCESS_VIOLATION) {

		printf("disasm(ep->ExceptionRecord->ExceptionAddress) %08X\n", ep->ExceptionRecord->ExceptionAddress);
		g_RealPresentIdx = disasm(ep->ExceptionRecord->ExceptionAddress);
		//分析当前异常指令得到 偏移!
		//如地址停在: JMP DWORD PTR DS:[EAX+0x44]     EAX=0  所以会异常.反汇编引擎解析得到 0x44
		return EXCEPTION_EXECUTE_HANDLER;

	}

	return EXCEPTION_CONTINUE_SEARCH;


}


最后于 2021-2-7 23:59 被Mxixihaha编辑 ,原因:
雪    币: 154
活跃值: 活跃值 (527)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
sunbinjin 活跃值 1 2021-2-7 17:11
20
0
Mxixihaha 依旧很简单. 看我的脑洞大开版.&nbsp;&nbsp;//编译去掉GS(安全检查) &nbsp;&nbsp;&nbsp;&nbsp;&n ...

你这是要疯啊。。。
我本意是求简单的,你搞这么大阵仗
你这代码我承认是能跑出想要的结果。
但是我就只是想求一种借编译器的自动结构取成员的位置而已,不是要这么算呀。
雪    币: 1714
活跃值: 活跃值 (1915)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Mxixihaha 活跃值 2021-2-8 00:00
21
0
sunbinjin [em_10] 你这是要疯啊。。。 我本意是求简单的,你搞这么大阵仗[em_2]。 你这代码我承认是能跑出想要的结果。 但是我就只是想求一种借编译器的自动结构取成员的位置而已,不是要这么算呀。

异常法也可以. 要通用的话这个构造异常肯定是可行的.


  1. 申请一段内存,把全部接口填充返回索引.当调用指针方法时,mov eax,索引值

  2. 特征码搜索

  3. 版本固定直接硬编ID

  4. 用异常法抓this=0获取偏移

  5. pdb文件获取(一般不会用)

  6. 空函数反汇编获取. 在空函数中调用 d3d9Device->Present(NULL, NULL, (HWND)0x123, NULL); 用反汇编引擎找偏移

  7. 直接把头文件的函数直接做成 结构体找偏移(很方便查找多个函数 和 方法1相似)

......

    方法总比困难多吧.因为虚函数不固定 可能考虑一些问题无法取得指针吧,需要变通的方法. 

通过下面的汇编可以看到不让直接用 &d3d9Device->Present 的原因:

013B140B >  8B4424 04       MOV EAX,DWORD PTR SS:[ESP+0x4]           ; & 只在这里.编译无法通过 *(*(&)+0x44)
013B140F    8B00            MOV EAX,DWORD PTR DS:[EAX]
013B1411    FF60 44         JMP DWORD PTR DS:[EAX+0x44]

因为那个接口是动态的 所以 & 符号做不到 

MOV EAX,DWORD PTR DS:[EAX]

和 

JMP DWORD PTR DS:[EAX+0x44]   //即使&取到了这里  他也无法保证上面这行的地址


最后于 2021-2-8 00:25 被Mxixihaha编辑 ,原因:
雪    币: 0
活跃值: 活跃值 (233)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
soatm 活跃值 2021-2-8 09:40
22
0
sunbinjin 不是这个意思,是根据第三方提供的.h里,配合手上拿到的接口实例指针。 获取接口某个方法的真实函数地址,用于Hook。 VC其实是知道接口中某个方法相对接口的偏移的,就是虚表。我是想借编译器快速定位 ...
明白了你的意思了。但是我有个疑问,有头文件,又有接口实例的指针,按照我的理解,这其实就只是没有给你源文件了。既然有了头文件,那通过这个头文件,可以很轻易的拿到函数的地址,就好比你说的IUnknown*->Release()一样,所以你说的这种情况,除非是派生类自己的实现的那部分函数,否则没理由获取不到啊。但如果是这种情况,就看看第三方有没有提供lib文件,如果有,也是可以的,可以拿到函数的RVA值,完了基址+RVA就可以了。
雪    币: 9610
活跃值: 活跃值 (1767)
能力值: ( LV5,RANK:73 )
在线值:
发帖
回帖
粉丝
Sprite雪碧 活跃值 1 2021-2-8 10:40
23
0


最后于 2021-2-8 10:50 被Sprite雪碧编辑 ,原因:
雪    币: 4370
活跃值: 活跃值 (897)
能力值: ( LV7,RANK:113 )
在线值:
发帖
回帖
粉丝
Adventure 活跃值 2021-2-8 14:53
24
0
你这是想hook com 对象接口的虚函数把
雪    币: 29905
活跃值: 活跃值 (492)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
bestbird 活跃值 2021-2-8 17:49
25
0
有两个方法:
1、整个HOOK,不容易数错,但代码量大N倍。
2、写个工具计算索引号。代码量很小,不会出错。
游客
登录 | 注册 方可回帖
返回