最近研究了下FPS游戏透视方法,市面上透视大概分为两种,一种是方框透视 就是在游戏角色模型上画一个框,另外一种就是Z缓冲区透视,这种透视原理是对指定模型(一般是人物模型)禁用Z缓冲进行深度测试,当然这两种都可以是基于D3D的,第一种方块透视也可以是不用D3D绘制方块,找到人物坐标 直接转换坐标为投影视图坐标,再用GDI绘制到屏幕上,也可以。
基本上的D3D Hook流程就完成了 ,对于上面的Hook代码需要的Dx程序 创建D3D设备之前钩住。还有种方法 是直接查找特征 获取指针。
今天我想说的是D3D Z缓冲透视,你需要一些DX基础知识,可以百度下 很多资料、下面代码以D3D9为例
首先Hook Dx,Hook Dx 有不同的方法,网上也应该能找到许多现成的代码,我就说下我常用的。
Hook Dx函数之前 我们要找到函数所在类的指针,然后再通过保存指针的内存地址+偏移定位到成员函数内存地址 ,其他的就和Hook普通函数一样。
Dx程序在调用Dx绘图之前要调用 Direct3DCreate9 获得D3D接口指针IDirect3D9, 通过IDirect3D9对象函数CreateDevice创建设备 获得设备指针、
下面代码演示如何获得这几个指针:
void OnHookInit()//CreateDevice
{
//这里只是hookDirect3DCreate9
pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
DWORD oldpro=0;
memcpy(d3dcen5bytes,pC,5);
VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
*(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
*(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
}
---------------------------------------------------------------
//当运行到Direct3DCreate9时跳转到这里
IDirect3D9 * _stdcall hookedDirect3DCreate9(
UINT SDKVersion
)
{
__asm pushad
memcpy(pC,d3dcen5bytes,5);//首先还原入口的5个字节
m_pD3D=Direct3DCreate9(SDKVersion);
if(m_pD3D){//如果成功
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
DWORD oldpro=0;
memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
*(BYTE*)pCdev=0xe9;
*(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
}
__asm popad
return m_pD3D;
}
//当运行到IDirect3D9::CreateDevice的时候跳转到这里
HRESULT _stdcall hookedCreateDevice(
LPDIRECT3D9 pDx9,
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS * pPresentationParameters,
IDirect3DDevice9 ** ppReturnedDeviceInterface
)
{
__asm pushad
memcpy(pCdev,devcen5bytes,5);//先还原入口的5个字节
HRESULT ret=pDx9->CreateDevice( //创建设备
Adapter,
DeviceType,
hFocusWindow,
BehaviorFlags,
pPresentationParameters,
ppReturnedDeviceInterface);
//x*4=68
if (ret==D3D_OK)
{
//如果创建设备成功
//执行你的HOOK D3D设备对象成员函数代码
//以从缓存区顶点渲染函数为例
LPDIRECT3DDEVICE9 m_pDevice=*ppReturnedDeviceInterface;
DWORD oldpro2=0;
VirtualProtect(Dp,5,PAGE_EXECUTE_READWRITE,&oldpro2);
*(BYTE*)Dp=0xe9;
*(DWORD*)((BYTE*)Dp+1)=(DWORD)hookDrawIndexedPrimitive-(DWORD)Dp-5;
}
__asm popad
return ret;
}
//顶点缓存绘制hook函数
HRESULT _stdcall hookDrawIndexedPrimitive(LPDIRECT3DDEVICE9 Device_Interface, D3DPRIMITIVETYPE Type, INT BaseIndex,UINT MinIndex, UINT NumVertices, UINT AAAAAAAA, UINT PrimitiveCount)
{
__asm pushad
//----------------------------
LPDIRECT3DVERTEXBUFFER9 Stream_Data;
UINT Stride = 0;
UINT iOffsetInBytes =0;
if (Device_Interface->GetStreamSource(0,&Stream_Data,&iOffsetInBytes,&Stride) == D3D_OK)
Stream_Data->Release();
if (Stride==32)
{
Device_Interface->SetRenderState(D3DRS_ZENABLE,false);
}
//-----------------------------
if(pC && pCdev && Dp)
memcpy(Dp,Dp5bytes,5);//先还原IDirect3DDevice9::DrawIndexedPrimitive入口的5字节
HRESULT retdata= Device_Interface->DrawIndexedPrimitive(Type,BaseIndex,MinIndex,NumVertices,AAAAAAAA,PrimitiveCount);
if(pC && pCdev && Dp){
//DWORD oldpro=0;
//VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
//调用完IDirect3DDevice9::DrawIndexedPrimitive后再hook一次
*(BYTE*)Dp=0xe9;
*(DWORD*)((BYTE*)Dp+1)=(DWORD)hookDrawIndexedPrimitive-(DWORD)Dp-5;
}
__asm popad
return retdata;
}
基本上的D3D Hook流程就完成了 ,对于上面的Hook代码需要的Dx程序 创建D3D设备之前钩住。还有种方法 是直接查找特征 获取指针。
何定位对象下的成员函数地址:
1.打开d3d9.h头文件
2.找到对象指针定义位置
3.下面从第一个对象成员函数开始数 直到DrawIndexedPrimitive函数的定义 函数位置是第83个函数 [ 函数顺序个数-1 * 4 = 函数内存地址]
DrawIndexedPrimitive是 (83-1)*4 = 328 16进制 0x148
本文代码测试环境 xp win7 32位 虚幻引擎系列游戏、