标 题: 【转载】在游戏中显示自己的文字和图形的方法( M+ }% D8 E" i/ L( g. i: J% B
作 者: runjin+ A3 v3 Y0 g v% L% O, }2 }8 B2 t
时 间: 2009-04-03,22:44:51
7 C2 W( K$ v/ L/ u) j I7 F链 接: http://bbs.pediy.com/showthread.php?t=85368
* s, D& k# [ f+ h- M9 w
6 S! d+ Q7 `1 SHook Directx:在游戏中显示自己的文字和图形的方法 I' ?' L) _4 H$ h
4 J% U* K3 g+ Y" R这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
9 [. W7 V. ~0 N/ Q0 p" b其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.9 Q5 ?: U" `8 x( Z- u' E
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.9 Y/ P8 V: H7 C$ V- q5 H2 P, o
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.3 r7 D, C3 ~; x$ N) e! K
pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址+ G+ f" I3 i ~: N: {, p" M$ _! W7 t8 T
DWORD oldpro=0;" J; O x! }! U
memcpy(d3dcen5bytes,pC,5);
. n5 m1 y! s2 f$ I0 A% x; t VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
" e: _4 {2 t6 F. A$ M: _% [, B5 D& B *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
. {* j6 P$ W! d% p *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
9 u. U) [- c+ A, ]. Z% S: Y; o w& N8 y5 @$ H" J
4 M9 L8 x* v8 ?* B3 T
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:) k0 v8 [1 Q5 P( W% h
HRESULT CreateDevice(
; R: b1 g- x! S" ^ UINT Adapter,
/ r @" \, ]; I8 J# q' T$ }" v+ M! A D3DDEVTYPE DeviceType,
) N; p3 @6 m) m$ `3 O HWND hFocusWindow,
2 L& I* {' v, N6 ^' T8 n DWORD BehaviorFlags,, l) x' I' b4 ]- Z {3 H5 ^: w
D3DPRESENT_PARAMETERS * pPresentationParameters,& d% S: C8 P* V
IDirect3DDevice9 ** ppReturnedDeviceInterface& M6 {2 @0 F' b" a% Z4 B n6 F4 g
);5 R5 c) N6 ?# q
\$ y r, B% m1 i& a, E4 \2 P
: c1 f+ J& |3 v而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样: L" U m8 e) ?0 `
HRESULT _stdcall hookedCreateDevice(% g) p+ a6 ?- K. e! v. I
LPDIRECT3D9 pDx9,
5 f; H7 U2 x; p3 k' J UINT Adapter,9 Z% o+ z' J! Z, o7 C1 c; d
D3DDEVTYPE DeviceType,7 `; X8 D1 K, \# Y$ c. I: C
HWND hFocusWindow,
& l* J c0 E) P! s+ p$ L/ A: { DWORD BehaviorFlags,7 @" R8 S z# g+ p: z7 d
D3DPRESENT_PARAMETERS * pPresentationParameters, ^- d+ V: }- N5 p+ |) C
IDirect3DDevice9 ** ppReturnedDeviceInterface
: d: i* g9 V3 L# j( N. M) O0 G. [
);
i+ w8 j( g! W0 X5 W9 ?) }9 y. b
+ u1 ?- m5 A/ u" D2 f其中的LPDIRECT3D9 pDx9就是this指针.$ Q( n; X& e2 R0 W. [% n
9 v7 x" d% ^% g: S# U0 }hookedDirect3DCreate9的关键代码如下:6 G K Q1 t/ c5 D: q& I
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针! C0 Y5 h: t* r3 O* V8 s( ^
4 K6 J9 i* H. x! W( \& E3 F
DWORD oldpro=0;& L7 N6 j; h5 ?5 a9 O0 @* E! m, O
memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节3 o1 B- d2 x; s, S: X
VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
3 y# ]2 u) A& U a1 g *(BYTE*)pCdev=0xe9;
9 m& O8 W7 p# F7 j C" @7 L* E *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;, n. m9 O! c/ b# X# r& r. `: \
1 z3 R L7 @; \- v) U$ N W* j
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
: c9 [$ F* P! VpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
: v) f) G4 K( T5 S: e$ n5 p& w memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
# ~& q/ k: h: w, L7 m( B9 [1 r9 x DWORD oldpro=0;
+ B" t' e0 b; a VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
. Y# g2 O: f( ~, B% ?7 l4 z( ^ *(BYTE*)pPre=0xe9;
b" C+ Y7 y2 `; Z *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;% Y! n; _. n5 ~4 T3 R' s+ L$ s
- \# k. A) Y: i6 ^; O实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
7 v& ^: D6 ^1 H6 G- schar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";, |5 ]1 Q4 ~+ E( n1 L' {' H8 r9 M
DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本7 D6 O2 c1 H! P
//在这里写入您的其它绘图代码: M( R. S9 p& F0 {9 z! L6 s# e: T
8 c+ T# k C* n( H6 V' K ~) s1 ], U' ~' W g
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:* s; l: E& S! D3 C
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)/ y! c m( M3 d+ z9 ]
{. P: u! g$ G0 ^8 L( W
6 R# s" ?' c) \
if(m_pD3D && pDxdevice){% M& v0 `$ B$ h: b1 Y
5 \4 N- E& b# |7 n+ E% ~
RECT myrect;1 u5 o$ u n4 u: q- l' H9 ?& ]
myrect.top=150; //文本块的y坐标! Y7 H) q1 e8 F3 i
myrect.left=0; //文本块的左坐标- }. f8 N3 `% t9 W* ` A0 k: [. L
myrect.right=500+myrect.left;
/ I/ G/ z2 R, t, d4 p( Z0 d myrect.bottom=100+myrect.top;
; K7 X. j8 }' E& ^2 U. F4 Q% D pDxdevice->BeginScene();//开始绘制
4 B/ p" }1 p! p! [7 r8 i# M r2 E8 U$ T, w) b; o6 [
D3DXFONT_DESCA lf;
* j" Z. [) z: d% d ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));$ f; N& M) b$ E/ [2 R
lf.Height = 24; //字体高度4 I' C9 ?0 I* n3 d( t& z5 |
lf.Width = 12; // 字体宽度
z+ e6 R' E' q) w5 h) T, b& i lf.Weight = 100; 4 z" |3 d" E7 q6 a5 m l
lf.Italic = false;( l7 z, m3 r8 d0 p2 X. z% a7 p
lf.CharSet = DEFAULT_CHARSET;. }9 c" R, x* i
strcpy(lf.FaceName, "Times New Roman"); // 字型- `1 K1 y) F( Q5 i) g
ID3DXFont* font=NULL;" }% A9 x" A+ }, |) x' h
if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象4 A4 d' ^$ |0 A# K
return false;3 j8 d/ j9 |- w, z5 `8 l
* m% O/ D0 ~0 s2 ?. ^0 J% L font->DrawText(
( }8 N: r$ d8 o NULL,0 o: e1 v( U: @! ?: G8 {
strText, // 要绘制的文本
* e0 j ?* S+ }; a3 P nbuf,
* _$ y3 e0 `; q3 k" z9 I/ O &myrect,
: ~4 N) w; D6 y1 C" \) z+ N& ]3 j DT_TOP | DT_LEFT, // 字符居左显示
9 k% c* L) J1 Y& s$ p9 _& j D3DCOLOR_ARGB(255,255,255,0)); + c, y9 d7 D. {0 ~2 g2 I( ~
+ K* h' D+ d) j4 ?6 U, A
pDxdevice->EndScene();//结束绘制, Q/ i3 F" [" B7 E+ b
font->Release();//释放对象& P; R3 k- P2 I6 K. f7 T0 J
}
9 U& T5 I, d" t8 I return true;0 U' g0 l9 B s) `
} |