PerfHUD是NVidia提高的免费DX图形程序性能分析工具,目前最新版本为6.1。PerfHUD使用起来也很方便,只需在CreateDevice时将设备ID设置为PerfHUD虚拟设备的ID(如果只有一个显卡,该ID通常为1),类型设置为D3DDEVTYPE_REF,然后将编译好的可执行文件通过右键菜单的“Send to”或者直接拖到PerfHUD图标上的方式就可以运行该分析工具。
然而对于手上没有源码的图形程序,如发行的游戏,我们就无法直接使用PerfHUD了。当遇到这个问题时,我想了两种办法:1)使用D3D Hook;2)逆向分析直接修改调用CreateDevice传入的前两个参数。
对于第一种方案,我使用了DLL function forwarders的方式只hook了Direct3DCreate9,然后将d3d9.Direct3DCreate9的返回值保存后替换成自己的实现的IDirect3D9接口指针,这样我几乎可以监视所有d3d9提供的接口方法。理论上,采用第一种方案,只需在CreateDevice方法中将传入的前两个参数修改让后调用d3d9.CreateDevice即可。然而,事实却往往不如人意,后来仔细分析了一下PerfHUD的实现原理:实际上PerfHUD也是一个D3D Hook,当程序作为PerfHUD参数启动时,PerfHUD判断前两个参数,然后如果允许分析,就载入几个包含分析功能的DLL,最终PerfHUD还是会将真正创建D3D设备的前两个参数恢复成应用程序使用的参数,因为PerfHUD仅仅是虚拟了一个设备和Hook了一下D3D。然而,使用第一种方案你自己就可以写出另外一个PerfHUD来了(不过就得花些功夫了),而且普适性很强。
对于第二种方案,可使用动态分析软件,先定位d3d39.Direct3DCreate9,然后根据其返回值定位CreateDevice入口,最后找到调用CreateDevice的地方,修改前两个参数就OK了。笔者用此方法,测试了Wow和Call of Duty-World at War,其中Wow由于直接使用立即数作为CreateDevice前两个参数,所以不需要调整代码,而Call of Duty-World at War采用了直接寻址来得到第1个参数,修改起来就有点麻烦了。 此方案不具有普适性,针对每个图形程序,需要去修改其二进制代码。