在看雪找来的文章,和代理DLL的原理基本是一样的。. h! l) M! X) ^% l3 R! N
) {4 t1 x' I c y( ^" x8 r" j原文0 i0 Z4 w' e8 o: A2 o$ f# [
http://bbs.pediy.com/showthread.php?t=85368
3 r* `& A2 h5 F) ~3 x' C- s7 }# V0 r4 P( G
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
& f# U, [& c6 P% Q) w其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.+ U5 p6 n, u/ c
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
# E) n* F) m% |7 n首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.* E5 p1 F) q. C* ^1 _" a! H
pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址' i/ `3 u3 g. n6 b3 I; @
DWORD oldpro=0;, G a' \) L# \, c, [
memcpy(d3dcen5bytes,pC,5);. y% T9 E$ W$ k- C
VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);: p. s, u" b( g: x
*(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
+ k) l! L4 g1 M$ c *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
+ e( ?2 U+ a" K# Y& L! |# N8 \8 W* _+ P4 [) b% o
- A t: W9 A6 r4 R这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:' }+ A% t5 C h) y( K' T
HRESULT CreateDevice(7 O8 s3 ^8 M0 x% X0 l' ^
UINT Adapter,
9 S/ w0 u( j" H) U) C D3DDEVTYPE DeviceType,
: n' C$ \# r6 _8 L. X HWND hFocusWindow, a1 ?. K7 B8 x/ q+ h) c0 O
DWORD BehaviorFlags,
* F. }+ W8 d o( G, {! @ D3DPRESENT_PARAMETERS * pPresentationParameters,
+ @# J$ o1 z7 z2 l7 @/ T IDirect3DDevice9 ** ppReturnedDeviceInterface
+ ^) H: X- x% F4 @: n* {: v);
# d6 R0 O/ {9 B2 L
_4 X) P& O' W# A6 q7 J( y1 I% z8 x+ Y
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
C9 h! C/ \* M# J4 G; THRESULT _stdcall hookedCreateDevice(6 o# u0 m9 Z; m4 d8 [: w% s) B
LPDIRECT3D9 pDx9,
0 {0 O; y' U0 M- h: K$ U UINT Adapter,, o0 d' S8 b9 y a. m# [
D3DDEVTYPE DeviceType,3 r. \9 S( S3 Q' a& A' E
HWND hFocusWindow,
% V: t# l% K$ f8 s( l8 @ DWORD BehaviorFlags,
( E$ h; b0 |' c! }- z; L D3DPRESENT_PARAMETERS * pPresentationParameters,
* L4 ^# \7 `6 V9 o7 D* Q IDirect3DDevice9 ** ppReturnedDeviceInterface$ w+ l7 d# G/ c; z9 ]
+ p/ w b+ j& j3 W- B ] );* f" _; S6 T# p o4 }/ F
2 t8 y+ o% c8 s/ A2 a2 f
其中的LPDIRECT3D9 pDx9就是this指针.7 v- ~$ {1 Y6 O# |! g, |
8 R% s Y0 C" N- } y7 a/ b* Z
hookedDirect3DCreate9的关键代码如下:
* K2 A$ F6 ~% f, G: {- Q6 UpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针9 u0 {' W B3 \$ `) G
4 ?& F. K' D* G( S' J! E' o) Q DWORD oldpro=0;
( B$ L4 `3 e! x7 s& f9 y: J& L memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节 Z4 ~! Z9 T/ f+ z4 {- e
VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);) |4 f- q! u3 D% \( a& ?% N
*(BYTE*)pCdev=0xe9;- _- A' G- X( i( O4 m$ b+ |
*(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
& e q" n/ P( ^- F9 r
5 `, C0 m, d9 g1 i3 l在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:/ v8 x! X- u6 S# u! F6 m5 r; M
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针+ G! F( {# U6 l) x0 Y Y- ~+ P
memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
1 U5 F0 w$ G( Y DWORD oldpro=0;
* y* Z L# @: J- E3 n) ? VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
6 V D% r+ ^# _+ p0 w n *(BYTE*)pPre=0xe9;
( U1 n0 s, f/ y& \ *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;# y1 V! O" q" j+ A4 k- u ?& J u
# q, a+ S u8 H! t3 L1 i. u实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
4 U$ ?( K* l5 L) w; e2 Jchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
8 M" I0 ~2 |: p& I7 A DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本" n5 P7 Y+ N7 t, C
//在这里写入您的其它绘图代码
6 M' d' U2 D, C5 x6 C# ^7 M1 j. U7 P% q! @$ {
! u# E$ l% h% l6 F6 p# P1 K0 g# |
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
: {0 k- K) K8 SBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)# G- B' {0 c% b* |6 f: s* ?
{
, x2 b0 w- G; b+ X& h R+ q1 e3 o H, T/ g
if(m_pD3D && pDxdevice){
' M3 t x- _; r& K
& M2 p, J' U/ j6 g( f RECT myrect;
/ a& s" h, r3 x& h4 F6 i myrect.top=150; //文本块的y坐标0 E6 T, B/ B: V( Y8 r8 ^2 ~
myrect.left=0; //文本块的左坐标
& J1 o$ o. J0 X1 `1 Q$ }$ x myrect.right=500+myrect.left;' \( h2 c0 I+ _1 H
myrect.bottom=100+myrect.top;
3 V' A3 m& d8 {: i3 F pDxdevice->BeginScene();//开始绘制
: _& W' A; ~+ k) q+ A L8 W: M
- c- S+ @8 Z- i& o1 l$ K3 t D3DXFONT_DESCA lf;
, c. B6 g7 k3 C( {0 w8 i) x ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));( C$ z$ p' ^" k C. T4 U6 q9 p4 k, K
lf.Height = 24; //字体高度
& K' N: v$ y- }+ s8 a6 p' X lf.Width = 12; // 字体宽度
+ R" Y, q7 y( @+ t# ] lf.Weight = 100;
& V( b4 _' A) S3 U0 W lf.Italic = false;
n9 C1 j% [ R: w1 V lf.CharSet = DEFAULT_CHARSET;; I" O0 h0 b9 g6 T" K
strcpy(lf.FaceName, "Times New Roman"); // 字型. h! T! _$ ^3 C7 ? r, c( ~( f
ID3DXFont* font=NULL;* m: j2 j* c7 A" @
if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象2 j9 ~4 Y) A, T1 n
return false;
) m( }& m" x0 t% N% |; b1 T8 e- [
font->DrawText(
3 v& w( w9 c7 | NULL,
% [9 g, a0 P. S) a0 V strText, // 要绘制的文本" u$ d4 y0 v! r: c' {9 u% [
nbuf, 0 g" E6 F( b% r0 X' A
&myrect, 0 P& y; A; N O! E& Q
DT_TOP | DT_LEFT, // 字符居左显示
! p$ |* h# m& i T. s1 [ D3DCOLOR_ARGB(255,255,255,0)); ~, \7 l, i, K& a
& _1 ?) A; r9 v pDxdevice->EndScene();//结束绘制9 a8 K- E. ]3 A! O0 y! d2 s+ i3 c
font->Release();//释放对象& k9 v D6 Z* w8 z0 D
}* }% S5 v) l+ G G+ s4 O4 A" F* J! M
return true;
; \, A' d2 |( ]8 j5 R* {}
, O# i, A! m4 d, N! J3 m
' |) N! i6 a1 B& m4 I9 r- X% p源代码:
8 W* n# F6 w# IHookDx.rar
8 o" [1 D+ G4 j: ~; ^; k& K6 c( U
+ G& K5 l( t; f后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的5个字节改成跳转指令这种hook方法并不是那么好,其实可以直接改掉虚函数表中的函数指针,这样更加的安全保险. |