用detours对C++类成员函数加钩子

背景:Flash的RTMP协议采用了AES加密,而密钥是通过Diffie-Hellman算法生出来的,于是想通过直接抓包的方式分析协议就不可能了。解决办法之一是利用Diffie-Hellman算法的漏洞,写一个中间人。方法二是,做个外挂,注入到flash player的进程中,把key打印出来。

我所遇到的问题是,我想给一个flash player中的某个函数加钩子,我知道这个函数的地址,但是它是一个C++的类成员函数。

然后我在detours的安装目录下的samplesmember找到了一个如何对C++的类成员函数加hook的例子。但是它没有解决一个问题:“如何把一个整数转换成成员函数指针”。

假设原来的函数声明是:

class CMember {
public:
void Target(const char* str);
};

一般来说,类成员函数都是遵守__thiscall。但是我们不能强行给一个全局函数加上__thiscall的修饰符。所以,首先,必须得先写一个虚假的类,然后重新声明这个函数。

class CDetour {
public:
void Mine_Target(const char* str);
};

然后我做了一点小技巧,把以前的函数声明成__stdcall

typedef void (__stdcall *pfunc)(const char* str);
pfunc oldfunc=(pfunc)0×00401040;

然后还是按以前那样attach,但是此时成员函数指针的转换很巧妙

DetourAttach( &(PVOID &)oldfunc,(PVOID)(&(PVOID&) A::func));

你不能把一个成员函数转换成void*,但是你可以把它转换成void*&。

顺便今天发现一个技巧,这样输出的结果是一个index,如0、1、2

void CMember::Target(const char* str){
std::cout<<&CMember::Target<<std::endl;
}

但是这样输出的就是函数地址:

void CMember::Target(const char* str){
std::cout<<(void*&)CMember::Target<<std::endl;
}

嗯,继续说,Mine_Target的实现

我想到的第一个方法是,利用__stdcall模拟__thiscall。

先保存ecx,并用this给ecx赋值。p.s.如果不是this,而是一个栈变量,mov就要换成lea。

__asm{
push ecx;
mov ecx,this;
}

然后调用以前的函数:

oldfunc(str);

然后恢复ecx:

__asm{
pop ecx;
}

这里利用的原理就是,__stdcall和__thiscall的区别仅在于后者多传一个ecx。但是这种方法有时候不成功。因为在调用oldfunc的时候,需要传递参数,此时有可能会利用ecx作临时寄存器,先把参数mov到ecx中,然后push。

我最后的办法是用__fastcall来模拟__thiscall,虽然多了一个参数dummy,但是忽略就好了,这种方法很优雅,不需要内联汇编。原来的函数定义就变成了

typedef char  (__fastcall *pFunc)(void* pthis,int dummy,const char *str);

按照__fastcall的规范,最前面2个参数应该通过ecx,edx传递,后面的从右向左依次push。相当于我多传递了一个edx,无所谓啦。

参考链接:http://m.udpwork.com/item/7338.html

原文地址:https://www.cnblogs.com/2018shawn/p/13487957.html