13.4 DirectX内部劫持绘制
相对于外部绘图技术的不稳定性,内部绘制则显得更加流程与稳定,在Dx9
环境中,函数EndScene
是在绘制3D
场景后,用于完成将最终的图像渲染到屏幕的一系列操作的函数。它会将缓冲区中的图像清空,设置视口和其他渲染状态,执行顶点和像素着色器,最后在后台缓冲区中生成一张完整的渲染图像,然后将其呈现到屏幕上,完成一次绘制操作。
而EndScene
是IDirect3DDevice9
第43
个函数,我们通过对该函数进行挂钩,并将该函数绘制之前的流程劫持到自身进程内的MyEndScene
函数内做图形的增加工作,当我们增加好所需功能后再将该函数指向原来的函数入口,此时EndScene
函数再次渲染则会出现我们所新增的功能,利用这种方式即可实现屏幕图形绘制效果,至于笔者是如何确定该函数是第43个的,读者可以在IDirect3DDevice9
上面右键查看定义,至此即可看到函数所在位置;
13.4.1 封装Hook劫持功能
首先要实现劫持需要封装钩子函数,如下代码片段则是一个简单通用的钩子结构体的封装,该结构体在此处其实是当作类来使用了,其中读者只需要调用JmpCode()
函数则可自动将需要跳转的内存地址与JMP指令相结合,当有了跳转指令的机器码后,则我们只需要通过VirtualProtect
设置内存属性为可写,并通过调用memcpy
函数即可实现对特定内存的地址替换功能,如下代码中hook()
函数用于挂钩,unhook()
函数则用于摘除,代码比较通用读者可应用于任何一个领域。
// --------------------------------------------------------------------------------- |
13.4.2 定制MyEndScene
接着就是自定义绘图部分,此处第一个DrawBox
绘图函数我们仅仅提供一个方框的绘制,如果需要更多绘制技巧读者可自行尝试实现,这里我们重点看一下MyEndScene
函数,该函数是我们的自定义函数,当进程绘图函数被挂钩后,所有调用原函数的请求都会被路由到此函数内,进入此函数内首先通过g_font == NULL
判断函数是不是第一次被调用如果是第一次被调用则对当前模块的字体绘制设备等进行初始化,而如果不是第一次绘制则自动流转到else
片段内,此块区域内则是我们自己自由发挥的位置,如下代码中我们仅仅是绘制了一段话,并绘制出了两个方框,并没有做其他功能扩展。
void* endSceneAddr = NULL; |
13.4.3 初始化与绘制图形
继续向下则是initHookThread
函数,该函数内我们自行创建了一个具有空类名的隐藏窗口,并通过调用Direct3DCreate9
实现了对Dx9引擎的初始化,通过调用(*(void***)device)[42]
的方式我们即可获取到当前内存中endSceneAddr
的原始地址,有了这个地址则直接对其进行Hook替换,此时当有新的请求访问该函数时则会自动路由到MyEndSceneAddr
函数内。
// 初始化Hook线程 |
有了上述代码基础,接着读者只需要增加一个DLL头,在入口处通过DuplicateHandle
得到当前线程的线程ID,并调用CreateThread
创建新线程,此时劫持也就正式生效了。
// dll入口 |
至此,读者可使用任意一款注入软件将编译好的hook.dll
文件注入到目标进程内,此时会发现窗体上新增加了一行文字和两个方框,至此绘制实现;