关于C++虚函数Hook的一些小事儿

最近在做C++虚函数Hook的时候想起了一点比较有趣的东西,不是什么高深的技术,就是简单的记录一下。

做C++的对于虚函数和纯虚函数应该不陌生,简单来说就是用虚表的形式实现的,网上关于虚函数的理论知识和图太多,我这里就不贴了,直接用一段测试代码上调试器实际看一下。

类定义如下:

通过调试的结果可以直观的看出,对象this指针所指向的(*this)正好是虚表的地址,拿到了虚表的地址,我们就可以对虚表中的函数地址进行修改,或者直接调用里面的函数,反正能想到什么都能做。

先搞一个有意思的东西,直接调用一下虚表中的函数。

在这里要插一嘴,关于三种常见函数调用约定的东西,虽然这个也是老生常谈了。

__cdecl: C/C++默认方式,参数从右向左入栈,主调函数负责栈平衡。

__stdcall: Windows API默认方式,参数从右向左入栈,被调函数负责栈平衡。

__thiscall : C++成员函数调用方式,参数从右到左入栈,被调函数负责栈平衡,this指针存放于CX/ECX寄存器中。

C++成员函数都是__thiscall调用约定,又需要特别提到的,成员函数默认第一个参数是this指针【其实这都是基础我也不想多说

所以按下面的方式声明函数类型:

main中代码如下:

执行结果如下:

如果想要Hook虚表中的函数,这样是不够的,C风格函数无法定义__thiscall调用约定,所以要构造的函数第一个参数必须是this指针,但是__thiscall调用约定this指针是通过ECX传入的,我们通过栈传入this指针,实际上相当于多传了一个参数。如果对于无其他参数的函数,可以用__cdecl声明替换的函数,在函数外把多传的this指针平衡调;但是对于多参数的函数,无论用__cdecl还是__stdcall调用约定,都无法正常平衡堆栈,在Debug模式编译CheckEsp就会直接报栈不平,在Release编译模式下虽不会报错,但实际上栈是没有平衡的。所以我在这里选择构造一个类,在类中实现一个__thiscall调用约定的函数,然后替换进虚表中。

 

有了方法之后怎么利用就根据场景去做变通吧,其实这片真的很水,只是遇到了随便写一下记录一下。

屏幕划词的实现

最近朋友让我写一个屏幕取词的小工具,考虑了一下主要步骤大致如下:

1.下全局鼠标钩子,Hook鼠标WM_MOUSEMOVE消息

2. WM_MOUSEMOVE消息函数中设定时器,判断鼠标停留时间

3.触发定时器事件,注DLL到目标进程(鼠标停留进程),可根据鼠标位置判断目标进程(GetWindowThreadProcessId),DLL中Hook目标进程的某些函数,如ExtTextOutW等;

4.使目标进程控件强制刷新,以此触发字符串显示函数

5.将Hook到的字符串通过(管道、共享内存、消息)等方式传回屏幕取词进程

6.卸载目标进程注入的DLL(取消函数钩子)

理论上并不复杂,但在实现时发现不同窗体的绘制函数都不一样,Hook和强制刷新存在一定难度,效率也比较低,甚至可能都不如暴力搜索内存效率高,就退而求其次先写了一个屏幕划词的小东西。

屏幕划词比屏幕取词难度就小很多,设置某个热键,触发热键按下事件时通过剪贴板过度选中的词,然后显示出来,我实现的主要步骤如下:

继续阅读屏幕划词的实现

关于列举进程Module的那点小事儿

列举进程对应Module其实是个比较常见的问题,最开始想到的是TlHelp32.h头文件里的Module32First和Module32Next函数,代码很简单:

但是运行起来会发现,通过这套函数在编译成64位的时候,读32位的程序的Module会出错

继续阅读关于列举进程Module的那点小事儿