标 题: 【转载】在游戏中显示自己的文字和图形的方法2 u8 @* I/ t& u4 K8 e0 t) h
作 者: runjin" Z2 }. _4 h n# V/ ?
时 间: 2009-04-03,22:44:510 j' J. T" @% \( P$ a6 e! }
链 接: http://bbs.pediy.com/showthread.php?t=853683 T* e+ Q0 Z4 s& `% F# H
; I( A0 X. D% E. H, q8 _Hook Directx:在游戏中显示自己的文字和图形的方法7 E+ g# g- a X- M! n
$ F* ^# e4 V" q5 m% y这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象." b& C! c5 ~ [6 k! E+ B
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.- r3 ]8 g1 W/ d7 n1 L! q
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.* a4 h" q9 V2 m9 i) b( i
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.$ f+ Y |$ I6 v# }1 [, w0 `# Z2 o! U
pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址7 ]6 k& d5 ?9 W2 X v0 w4 I" k
DWORD oldpro=0;
% L [- |" h: a S- W2 g memcpy(d3dcen5bytes,pC,5);: ]5 f( Q# P& j! v/ t
VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);+ y! X+ y9 b; }1 g6 X
*(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
: p# R: {; l3 b; ]2 _8 ]& ` *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
7 j8 z" A) S5 i7 j. y7 f! i% K
" Z, s4 r% L* }. _# m# K* t, |0 O6 L! P' G1 |) C
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:- Y5 x4 j3 c0 b* h" }, ^6 |
HRESULT CreateDevice(5 j3 O( ]$ Y& X# D+ x
UINT Adapter,
, w V* `1 u9 l9 u* y, G+ b D3DDEVTYPE DeviceType,
* O$ [. w# d$ k, t- e# P HWND hFocusWindow,
& d8 p; i8 x* q8 w DWORD BehaviorFlags,6 ]! {4 h! D) t4 C5 [
D3DPRESENT_PARAMETERS * pPresentationParameters,
) d, U% y2 m" r2 p1 w% h' x IDirect3DDevice9 ** ppReturnedDeviceInterface
7 g6 E" O' g/ F8 j, Z' e$ s);& ]! u9 B( }& d& y1 P# [3 m
: H- D" @4 a7 e1 n
7 b4 J0 J& c/ C5 w. k而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:, ]) l' I* p9 M; l: q1 p( q/ C0 G( n
HRESULT _stdcall hookedCreateDevice(
9 o2 R# d8 P3 X v LPDIRECT3D9 pDx9,5 u% L% D& a& m) l. j* f5 V, ?
UINT Adapter,; ?- f. Y8 [8 \" @
D3DDEVTYPE DeviceType,
5 }$ f( Z7 g. h! [: ?- @/ ]: L HWND hFocusWindow,8 @) z0 I0 ~) ]3 K8 P
DWORD BehaviorFlags,% o w- } C# E" s
D3DPRESENT_PARAMETERS * pPresentationParameters,, f9 Q3 e5 L7 Z& G7 R
IDirect3DDevice9 ** ppReturnedDeviceInterface1 }; Z- v* u u* M0 X
0 j+ G. ^& P6 l$ G
);6 G- [$ L/ k. D3 I7 a) O
. d& Y/ |/ t' Y7 V3 d0 W其中的LPDIRECT3D9 pDx9就是this指针.
- V6 ^3 A& N9 o2 R1 m) m) ^4 E
" A) C9 Q: E6 B: ^5 _hookedDirect3DCreate9的关键代码如下:
. i9 l* t U6 N( X5 b, |0 s1 QpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针* ^5 y& T( b& v) z0 F; a
1 A! o! B2 T1 ~* @
DWORD oldpro=0;# v9 R$ s: r; o3 K
memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节2 ]9 P/ v5 C1 u4 j- N; R- N
VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
- s" t7 s) A3 Y" i4 |/ E3 g$ Y *(BYTE*)pCdev=0xe9;
- `/ b( F9 q" Z *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;9 C! {: ]& f' ~1 s' A0 B
3 r. k' {8 g9 w' U0 z9 S; {6 g
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
) T: a8 s4 O4 n& x8 mpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针- O3 P9 L/ T+ ]+ l
memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
+ L7 O8 ^1 Q2 c0 \ DWORD oldpro=0;
+ T6 A6 J& P# ?5 W VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);2 \' s4 f w7 x3 d
*(BYTE*)pPre=0xe9;
. p4 Z6 Q) K% @1 y+ V *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;( Z- d5 i4 U- n1 P1 m
9 y( l3 K- X2 F
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:) Y# i% c, h- Q* w2 w8 I
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
& h# V5 v. B$ c8 |' @% ] DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
; `, J; Z9 @- [. u+ w7 J //在这里写入您的其它绘图代码
1 V) i/ H5 U2 l0 q- W; h: B3 N& V# {0 g. R( E5 I, W6 l: `! D, b" Z
+ H4 i1 T! i0 ?0 `% h# U再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:6 c d' _5 O* ]/ e
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
8 `" B# Z7 I. l- I! Q{
4 @. @! s0 @! v0 _
% B7 M& Z9 k( h: p0 m' I0 W) M if(m_pD3D && pDxdevice){
: B4 d, O$ p8 V% G; t' r } P5 h8 {
( P( Z5 r ~/ ^' ^7 ^0 t RECT myrect;7 d% d" T/ K, J/ ~
myrect.top=150; //文本块的y坐标
# K) `8 q! X' O6 w myrect.left=0; //文本块的左坐标, a$ B+ K' h& J) Z* ~6 [0 F
myrect.right=500+myrect.left;
; \& t4 _4 g- G# H& A8 `4 b myrect.bottom=100+myrect.top;
8 \. ?6 d# |' R9 }. X; E0 ~ pDxdevice->BeginScene();//开始绘制
$ q; y2 O" n5 C2 B8 V$ ]. y; a( n
D3DXFONT_DESCA lf;' e7 f& |8 R2 A+ {9 a: C
ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
% `1 D8 [' n: { lf.Height = 24; //字体高度# \- m3 ~) S' F! d
lf.Width = 12; // 字体宽度, ^- I0 U3 C' E' u, A
lf.Weight = 100;
: m& M. H2 m# G" Q% m$ Q lf.Italic = false;+ ? U: p5 e4 J# a
lf.CharSet = DEFAULT_CHARSET;
* [9 e b7 {& |* H strcpy(lf.FaceName, "Times New Roman"); // 字型
3 G1 [. A7 }* _* Y' v8 c ID3DXFont* font=NULL;4 M: _0 J& [) l
if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
* s" P3 y; z O& A& O, q return false;2 Q! i8 C# ], \, r7 I4 ]) t# ]7 o
+ Q8 F" f! F# F! @# L
font->DrawText(
0 Q) J( o" y1 k- K p2 e( | NULL,# N6 B8 E' u$ c! d; ^# \" y
strText, // 要绘制的文本
7 O# s( Q `" U2 N$ S# G nbuf, 7 \6 b* P) ` l
&myrect,
V2 m3 S6 U7 {+ X5 q% E; Y+ T DT_TOP | DT_LEFT, // 字符居左显示+ ^# _, H5 e; B& Z
D3DCOLOR_ARGB(255,255,255,0)); , b8 C! t* j6 w! s, t' T
! w8 W% w: G+ r, A! } Q+ v
pDxdevice->EndScene();//结束绘制( F4 {6 ~0 M) Y' Q$ e9 N! d
font->Release();//释放对象" R$ p' y4 B) T3 W0 n. o1 W: m/ O
}
* [: S/ J3 r, H1 [ return true;
- j1 ]4 o3 J) r/ B$ u; X8 Y} |