标 题: 【转载】在游戏中显示自己的文字和图形的方法 F3 M! M" k2 g- T1 q( C2 \/ B
作 者: runjin; {; @5 n" }. @9 l
时 间: 2009-04-03,22:44:51
. d7 h9 a; w" F3 r! A: c链 接: http://bbs.pediy.com/showthread.php?t=85368
/ a/ f% T. n2 U0 ~2 j; B/ z
& T( d8 T' a8 i4 k2 Y. n& s7 L; THook Directx:在游戏中显示自己的文字和图形的方法
4 T: r& F4 t6 h6 U/ @
: |3 b5 Z3 V5 l& G k+ y0 h这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.1 D8 X# o) [. F9 o! g j# |; @% R* n4 y
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
+ N6 [ W* w$ I& B5 W还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.$ `; W; p# i# F8 e2 {. e/ D/ Y9 m% C! ]
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.( i& ^6 Z. ]- d& q7 P3 ~
pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址2 L0 e2 {0 o% v, D# ~/ y7 t# k' q. J) ^0 W
DWORD oldpro=0;
( S" J' R4 _* ^5 {' i; a6 u! y memcpy(d3dcen5bytes,pC,5);
5 G7 M: F% ]: W- o" E VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);$ c# P: V* x% ]1 r+ i/ I
*(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码+ Q9 W& T8 S8 F0 |& W$ B
*(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5% Z' E+ E& ] Y& Q! M
5 [! B& L! }) P. w& k: x
$ s; G* L4 |0 Q
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
9 k$ G! i8 q) xHRESULT CreateDevice(6 p/ a' ]8 L8 o, D. U( m* g
UINT Adapter,
$ p0 ?" ]+ b$ J' D6 _# Q D3DDEVTYPE DeviceType,4 }% [9 s+ \" [" i8 X' x! a3 T: m
HWND hFocusWindow,& y. V6 ]4 a+ H7 g/ z
DWORD BehaviorFlags,# _( N G- p$ N
D3DPRESENT_PARAMETERS * pPresentationParameters,, Y+ F0 a* c& v4 v2 y0 f- d
IDirect3DDevice9 ** ppReturnedDeviceInterface
1 P9 A2 O7 a! ~- p- r! A" Q);
& R; ]/ h2 B3 J9 a7 k: t( y j3 ^. I
- ?% u; X) V! ^" L
& d5 `3 U) ^% W5 h+ A1 G* ^- T& A而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
7 n- e F; Y2 @' h7 D7 yHRESULT _stdcall hookedCreateDevice(9 h% y' }$ {" c
LPDIRECT3D9 pDx9,5 a3 ^$ i; t' y, P. A
UINT Adapter,6 c1 M5 _& ^ U' K* W9 f( T
D3DDEVTYPE DeviceType,
! {( B' T5 L' r1 Z2 ^- J6 a2 d! ] HWND hFocusWindow,4 \$ L1 D( o8 _+ l9 E' s" t
DWORD BehaviorFlags,$ W9 X3 t2 P' @' {% b' G
D3DPRESENT_PARAMETERS * pPresentationParameters,5 ]$ K* \" |" m+ G
IDirect3DDevice9 ** ppReturnedDeviceInterface: v5 `+ U' S! H; L) y
0 F- [; N& f3 i. l );
6 N8 j+ X' E% u
5 M! i2 z% _* K其中的LPDIRECT3D9 pDx9就是this指针.
7 Q+ C2 Q7 T3 _% N' u% [/ s: A3 o. h4 Z
hookedDirect3DCreate9的关键代码如下:
6 E8 M4 I' |4 ]- r: n2 |pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
5 u: X* I8 X1 m& x: Y4 d- N% P* |7 D3 b4 n8 p% ?. S' k( V
DWORD oldpro=0;2 E6 U s; _+ ]/ j7 H5 C
memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
! k+ ^" K+ t+ L0 [8 O, C( s+ a VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);! N. x9 R' N( A0 K& b- S1 D
*(BYTE*)pCdev=0xe9;2 k3 u( X( X; W) w. t# H0 t
*(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5; N1 n# l% c' a
) R2 }% \' P7 K5 K
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:* r/ G9 n! U! g$ Y8 M& \; }
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
- E* I4 s, Y+ t4 ]2 m; @: j memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
; x: f8 F5 Q4 e+ ]; S& d8 M# X DWORD oldpro=0;) q7 D' x- T) e! x' k/ _
VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);! @; _/ j+ a# M4 n3 s
*(BYTE*)pPre=0xe9;
2 t5 ]; `* i8 S- f( y *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
1 ~, l; m5 `( Y9 B4 H" ^% O. H3 I* Z5 P) S+ H3 o6 j
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:; h! w* }+ y: W; }
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";4 \( r- t# y6 F) l) v1 o
DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
: m1 T2 p/ T3 u& r2 i" t$ x //在这里写入您的其它绘图代码0 l6 H' b* t1 p! P/ @
& \. A5 I: d/ ]* u- c: V. M! q
& @5 H/ n4 u- a( @ z: r6 J! V
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:* w' s" ?$ Y6 ~- o1 [
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
8 o; y1 K4 E/ c2 s{& J Z& E; m- y8 G. o% U
' ]: Z# B% E4 K$ p: ~/ N
if(m_pD3D && pDxdevice){
8 n5 M C4 b! `7 U& x7 Q
8 q; v2 k/ B7 X- }7 ]* P" H RECT myrect;( i) J: f5 S2 ~+ z0 [
myrect.top=150; //文本块的y坐标; I7 U, z1 n7 f
myrect.left=0; //文本块的左坐标( e# G+ k- V7 @ J$ j: U1 L
myrect.right=500+myrect.left;3 v4 e8 Y; a& a6 o0 a; _6 Z: W
myrect.bottom=100+myrect.top;
# w+ G8 F [( f! z pDxdevice->BeginScene();//开始绘制
f$ ?! r. p P# X2 _ K* ]( R' Y
; }2 H% g5 O& I D3DXFONT_DESCA lf;. E% p) |- D& i$ \5 _* Y- E
ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
8 t" E& Y9 E# J8 x: p0 X lf.Height = 24; //字体高度& y$ l$ V% [/ }
lf.Width = 12; // 字体宽度. x v2 p8 ^4 o6 G! S8 A; J( `
lf.Weight = 100; & {7 F, U3 d& _; e7 o7 Y
lf.Italic = false;& i Z8 |% m6 [
lf.CharSet = DEFAULT_CHARSET;3 a6 J& I* `2 j8 z$ b* s) g) T
strcpy(lf.FaceName, "Times New Roman"); // 字型3 j4 ^, J/ [$ }3 x
ID3DXFont* font=NULL;
7 x. t2 a1 H( O) Z% ]# w if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
5 r' n1 Q9 V7 D; [ return false;7 z# h: }; w) \4 x7 R8 R/ v
4 v3 H# ?3 U) W6 a& ?
font->DrawText(
7 F2 L6 F1 V" P NULL,! q+ A5 t- U2 j3 g; q
strText, // 要绘制的文本
7 K% }$ S8 Y1 N7 E nbuf, % y* d0 T. Y3 R+ |
&myrect,
) H) H4 {0 D4 Q. b8 X; L" M DT_TOP | DT_LEFT, // 字符居左显示
( @5 y! [( T, s @6 {& j D3DCOLOR_ARGB(255,255,255,0));
, q$ Q" @) H! M( h0 }7 ~3 A3 E! Y2 r- M
pDxdevice->EndScene();//结束绘制
7 ^4 F0 r4 o. k [4 T% c& V font->Release();//释放对象% ?6 ]- x y* F
}6 \5 `6 S+ c5 x+ H; r7 t6 @
return true;- q( x4 U* W6 l0 L& e8 ]' p
} |