标 题: 【转载】在游戏中显示自己的文字和图形的方法, V, {/ i$ e/ h" Z1 D$ N
作 者: runjin
: j. @1 `( i- y8 W2 a% u; R; t& q- w时 间: 2009-04-03,22:44:51( ]2 h& t$ z0 ]- q7 F- `% B4 A
链 接: http://bbs.pediy.com/showthread.php?t=85368( K( s: M, S3 R; S
* ^9 S" e8 v! M; i0 F2 `2 THook Directx:在游戏中显示自己的文字和图形的方法; B. w* u, S. @& \) Z
% N5 @6 A8 z: S6 a( r
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.6 i: h0 s. C! x( {' \
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.5 Q. Y- P; ~) C
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
( C' n+ z0 F% y6 |# j首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令./ Z* z9 O E+ b* X* n
pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
) \/ P u8 p9 c) y/ }8 I: l DWORD oldpro=0;1 b0 m5 c ]# [2 I: F# g
memcpy(d3dcen5bytes,pC,5);
, J/ o' M8 c* ^! V8 J- X VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
* I" t, b& u0 i1 u, w$ n, u *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
8 R2 q) `9 E; y* M) x. t- Y *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5% S, m- ]$ }( n& b
1 ?; u1 J! R- w M. M9 v; \9 L! p ]2 p( R- N9 D& t/ I' ~' k" c
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:5 K( H. _, B6 G# M( Q4 m8 g! M& z
HRESULT CreateDevice(
7 q4 ?& R8 z4 @8 e# e UINT Adapter,
2 ~4 c5 q1 j$ s+ p7 [ D3DDEVTYPE DeviceType," V! i1 U- X6 r, ~; j3 @+ M& D3 t t
HWND hFocusWindow,. o4 }3 B8 d: F7 z
DWORD BehaviorFlags,
& V# L! T, Z5 @, }7 G& M- | D3DPRESENT_PARAMETERS * pPresentationParameters, V- u% b) O2 D' q2 s: H
IDirect3DDevice9 ** ppReturnedDeviceInterface2 P+ @! v% Z% R1 V3 E" x% V; H
);' g, j0 H: o* h6 h8 O U) T4 P
4 g8 W' u |; A6 F0 d% p, s+ c' m- }% R: r6 \1 C$ T7 G$ t3 U: Z
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
9 B! G" Z( O* t ~! Z C3 i) c6 OHRESULT _stdcall hookedCreateDevice(, h2 O2 }0 Q# N) j
LPDIRECT3D9 pDx9,
% P2 w- U: y1 N4 D: M/ U UINT Adapter,3 U( I/ m$ [8 c: K+ o
D3DDEVTYPE DeviceType,
l4 R! u9 q# N! A. k3 w+ o HWND hFocusWindow,
5 }8 q; f- U M& I1 |" m6 R+ U' r DWORD BehaviorFlags,9 u" A* o4 t% {/ m: R# N
D3DPRESENT_PARAMETERS * pPresentationParameters,
+ p$ n: X# l# k: x! _' Y3 @8 b IDirect3DDevice9 ** ppReturnedDeviceInterface
) l+ I2 _1 t! J) N6 }4 z* K5 l( O6 Y) M- u: ?* Y& w
);8 f1 t: {" B' e: t7 a1 b6 @
# x+ I4 d2 A* C i其中的LPDIRECT3D9 pDx9就是this指针.# G) r! ]( M) p, X2 k7 H/ G
$ f9 y d5 g8 T1 f9 @1 MhookedDirect3DCreate9的关键代码如下: s8 S# A0 c8 c& a, r
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
5 G/ Z# G0 u1 J) D( ~
3 T5 y, N% g/ p* H) {" Q/ J! S DWORD oldpro=0;
) {4 K& R; H2 s2 d% g7 J memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节3 I: q7 I$ U6 {
VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);" ^$ I" l4 |# I8 X2 \0 {0 W! C
*(BYTE*)pCdev=0xe9;7 b+ R7 J) R8 A! B g* s4 Y
*(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
8 C# b; o% i* i$ x& X- y q# q7 u( N0 X
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:& E5 f$ L8 } z) O0 ^2 k' Q
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
+ L6 Y" P; Y @ ` c memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
' Y4 [* Q( ^/ O1 C DWORD oldpro=0;
& J/ k# k$ u8 w. | _8 g VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
, |% j0 p2 h* x& k3 Q( a *(BYTE*)pPre=0xe9;
: J* E! O% s+ T3 Y *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;9 y! N }' k" y; \, z
2 U% ]% V! b: ]. t: `) z* W+ f1 r实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
# H3 N. K, h& h, ]4 Qchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
7 Z9 k! b' `: b7 _, h1 Y DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本/ F- P v2 |, i" ^: ]; W' C' P
//在这里写入您的其它绘图代码. U! L5 Y+ n/ e
' P5 r* V: v) W( Q9 C H4 K
2 Y' c! e! v; x( r6 }2 c: H再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
4 |2 L9 { ~3 _. D6 VBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)7 M+ e$ t% q k* L6 |
{0 T$ ?! H& j) P9 X1 D% p
; Z/ U) g2 c0 z" y% K% r
if(m_pD3D && pDxdevice){7 E x1 l- T6 L+ k9 `
, ]& C# H0 z& e( i7 J6 Z6 O RECT myrect;4 I: O9 @. \! o8 x7 `( |2 E
myrect.top=150; //文本块的y坐标
, i# n6 c" a2 y- K2 Z myrect.left=0; //文本块的左坐标9 U& X; w2 s7 r1 u! {; o) W/ R
myrect.right=500+myrect.left;' { D0 I3 C* {; \! D
myrect.bottom=100+myrect.top;% E/ K9 Y# K$ w" O- s( O9 f9 }
pDxdevice->BeginScene();//开始绘制& A+ F6 M% D0 Z9 b3 q$ \
- K# E3 n; p: a* r0 ?2 c" Y D3DXFONT_DESCA lf;% ]) b: s; F# ?7 ]% G% Z, T
ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
/ P) U& i: t9 ^- y) G9 ^4 W lf.Height = 24; //字体高度: O! h7 y$ h# z B8 ^+ h
lf.Width = 12; // 字体宽度, M F' a! X& t0 j% Y& F- T2 k
lf.Weight = 100; / V/ H4 X( J+ [, c- V
lf.Italic = false;
6 n. Y1 g2 K. ]; F lf.CharSet = DEFAULT_CHARSET;
$ F3 \. w- P9 m- x strcpy(lf.FaceName, "Times New Roman"); // 字型9 v' s; m4 L2 |8 i1 G
ID3DXFont* font=NULL;
/ M7 w( Z5 k$ c if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
# E3 s, u5 @/ `+ O Z return false;
7 j5 P; b" a. k. |# p0 o. r, d, x4 S# e2 j6 W/ d: N
font->DrawText(' ?! ~5 y% S! H' m1 I5 I
NULL,& R; N7 K# j$ e$ ^) `
strText, // 要绘制的文本
$ g7 w& l- a, @) H9 O. f3 r- I nbuf, + x. o2 ^5 Q( x2 ]0 [. l, ]
&myrect, * L7 f8 K! Q/ | n* P
DT_TOP | DT_LEFT, // 字符居左显示9 L: f3 V$ [+ a8 l# U0 P/ o
D3DCOLOR_ARGB(255,255,255,0));
9 i( u$ z5 N/ T3 [6 E
7 a4 n% q5 K1 P+ s: w pDxdevice->EndScene();//结束绘制
1 z' {! W) I6 a. a3 a font->Release();//释放对象- [6 x* ~ Y; H% v: ?
}. S% X7 q% Z9 l7 ?& C. ~) B
return true;2 W O3 g4 r7 A+ ?- r; U% ^6 R* q
} |