實現目的:
目前大部分游戲通過Direct3D實現3D效果,通過掛鈎相應函數,可以實現3D透視,屏幕掛字效果。而透視,屏蔽特定效果,設置透明在很多游戲(特別是FPS)中發揮着巨大的作用!
實現思路:
[D3D]
DirectX的功能都是以COM組件的形式提供的。在Direct3D中,主要通過采取以下操作來實現編程:
調用適當的函數獲取接口指針;
調用接口的方法(成員函數)來完成所需功能;
用完接口后,調用Release方法進行“釋放”,注意釋放順序應該和獲取它們的順序相反。
D3D的實現流程:
大體可以分為:設計,渲染和顯示三個部分。通過設計物體的頂點,貼圖,材質等信息,並將坐標轉換為屏幕坐標后,調用渲染方式,根據坐標變化,材質文理等計算亮度,進行背面消除,裁剪,投影和視口計算,最后在后備緩沖中繪制好圖形交換到當前緩沖區。
設計階段:
這里介紹部分概念方便大家理解
圖元
在d3d編程中,所有的圖形都是由圖元組成的,例如:
這些圖元分為點列,線列,線帶,三角形列,三角形帶,三角扇形
頂點緩存
頂點緩存通常除頂點坐標之外還包括法線,顏色,紋理等數據。
索引緩存
索引緩存就是將頂點的具體數據和代表圖元格式的頂點順序分開存儲:頂點數據仍然放到頂點緩存區中,索引緩存區則按照圖元格式,順序存放頂點的索引。
如
A,B,C,D對應的頂點緩存索引為0 1 2 3,按照三角形的列的組成順序,把頂點索引值存入索引緩存區,4個三角形分別為△ACB、△ADC、△ABD、△BCD(注意頂點排列順序和可視面的關系),則索引序列為0 2 1 0 3 2 0 1 3 1 2 3。這樣原本要用12個頂點數據構建一個三棱錐,使用索引緩存后,只需要4個。
Z緩存
在Direct3D中,使用深度緩存區(Depth Buffer)來進行消隱處理(隱藏面消除),以確保實體被遮擋的部分不被顯示。Z緩存是最常用的一種深度緩存,它因為用Z坐標作為判斷深度(遠近)的依據而得名,其工作原理如圖14所示,圖中的渲染表面相當於Direct3D窗口,Z緩存用來保存窗口中各個像素的深度。在消隱時,Direct3D先用背景色(或紋理)填充渲染表面,Z緩存則統一設置成最大深度,即投影變換中后裁剪平面的距離,然后逐像素處理渲染表面:對於任意一個像素,Direct3D逐一測試所有與該像素重疊的三角形,如果三角形中像素對應點的Z坐標小於Z緩存中的數值,也就是說,此三角形離觀察者較近,則像素取該點的顏色,同時像素在Z緩存中的深度也設為該點的Z坐標,然后繼續測試下一個三角形... ...
坐標變換
渲染
在Direct3D中,一個設備對象至少包含兩個顯示緩存區:當前緩存區(Front Buffer)和后備緩存區(Back Buffer),前者可以看成Direct3D窗口的映射。當我們渲染圖形時,實際上並不是直接在窗口上輸出,而是在后備緩存區上繪圖。渲染完畢后,交換兩個緩存區,使原來的后備緩存區變成當前緩存區,從而實現窗口刷新。快速重復此過程,就會在屏幕上形成連續的動畫
有了上述的一些基本概念之后,我們就可以編寫出自己的d3d Demo:
詳情見demo
[D3D HACK]
通過上述了解,我們發現渲染中有個函數SetRenderState()可以設置渲染狀態,通過設置不同的參數既可以實現我們想要的功能:
透視:
SetRenderState(D3DRS_ZENABLE, FALSE)
去除煙霧:
SetRenderState(D3DRS_FOGENABLE, FALSE)
設置多邊形填充模式:
SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME) //線填充模式,D3D在多邊形的每個邊繪制一條線
......
[D3D HOOK]實現
分析發現在繪制圖形之前,通過頂點緩存,然后調用DrawIndexedPrimitive或者DrawPrimitive來繪制圖形。所以通過hook此函數就能感知繪圖操作,進而實現我們想要的功能。
可以知道Direct3D在內存中的布局為:
為了得到偏移,我們調試一下:
單步到call edx
此時打開pchunter,查看d3d9.dll所在地址
通過計算可以得到偏移為:+2B6B1(此偏移只針對win7,不保證其他平台下的偏移是否正確)
我們構造尋址函數
HANDLE handle = GetModuleHandle(L"d3d9.dll");
if (handle == INVALID_HANDLE_VALUE)
{
OutputDebugString(L"get d3d9.dll failed\n");
return NULL;
}
return (ULONG_PTR)handle + 0x2B6B1;
構造跳轉型型hook
if(VirtualProtect((LPVOID)g_uladdr,5,PAGE_EXECUTE_READWRITE,&dwoldprotect))
{
DWORD dwjmp = (DWORD)New_DrawIndexedPrimitive - g_uladdr - 5;
_asm
{
mov eax, g_uladdr
mov byte ptr[eax], 0xe9
add eax, 1
mov ebx, dwjmp
mov dword ptr[eax], ebx
}
}
VirtualProtect((LPVOID)g_uladdr,5,dwoldprotect,&dwoldprotect);
構造hook函數
查找DrawIndexedPrimitive定義:
HRESULT DrawIndexedPrimitive(
[in] D3DPRIMITIVETYPE Type,
[in] INT BaseVertexIndex,
[in] UINT MinIndex,
[in] UINT NumVertices,
[in] UINT StartIndex,
[in] UINT PrimitiveCount
);
官方給了6個參數,通過調試我們發現,需要將this傳進去
故構造函數如下
HRESULT WINAPI New_DrawIndexedPrimitive(
LPDIRECT3DDEVICE9 m_pDevice,
D3DPRIMITIVETYPE Type,
INT BaseVertexIndex,
UINT MinIndex,
UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount
)
{
//做你想要的功能
return Old_DrawIndexedPrimitive(m_pDevice, Type,BaseVertexIndex, MinIndex, NumVertices, StartIndex, PrimitiveCount);
}
構建跳轉
分析函數頭5個字節
構建跳轉
__declspec(naked) HRESULT WINAPI Old_DrawIndexedPrimitive(
LPDIRECT3DDEVICE9 m_pDevice,
D3DPRIMITIVETYPE Type,
INT BaseVertexIndex,
UINT MinIndex,
UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount
)
{
_asm
{
mov edi,edi
push ebp
mov ebp, esp
mov eax, g_dwjmpto
jmp eax
}
}
注入游戲:
實現透視
實現線框透明:
Direct3D的核心功能集中在IDirect3DDevice9的接口中,只要能hook其中的EndScence(), DrawPrimitive()或DrawIndexedPrimitive()就能感知繪圖操作,進而實現我們想要的功能!
jpg改rar