冒险解谜游戏中文网 ChinaAVG
标题:
【代理DLL汉化研究】Hook Directx:在游戏中显示自己的文字和图形的方法
[打印本页]
作者:
shane007
时间:
2009-8-28 21:13
标题:
【代理DLL汉化研究】Hook Directx:在游戏中显示自己的文字和图形的方法
在看雪找来的文章,和代理DLL的原理基本是一样的。
: o* [2 J4 u% n+ v
, X, B- @8 t4 y
原文
( C2 x7 o# @$ Y8 P( z; {/ d7 c
http://bbs.pediy.com/showthread.php?t=85368
& `9 m' {( G: m: U: T! C
& e' A: y9 A2 [* [
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
$ U4 t. O3 y# u0 V. G
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
6 y$ }( {' s! }. D! Y
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
# t# U: @( O2 G l
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
) a8 n q0 S0 h' g" i' u
pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
/ m2 z; I' M" \8 I) Y
DWORD oldpro=0;
- W/ |, K- j' ^$ K( R1 b" p9 D* f
memcpy(d3dcen5bytes,pC,5);
6 Q( ?/ ?0 L8 h' S2 {/ D
VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
6 i+ T) Z' R/ y& R8 @& d3 Z4 b
*(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
8 u( V! M* k$ L2 U6 `2 B) b; S
*(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
( t- i; y: c8 z0 G' l' { E
: ]# e. G% @7 g( _- n3 V, B8 Q
/ t$ e7 V7 O/ Y( ]' E& j
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
( h7 ], K4 v, S: @) }9 g' }
HRESULT CreateDevice(
) v2 v7 H/ E7 D- g# d
UINT Adapter,
- [+ l: @ u$ O" `8 s, z. M5 `
D3DDEVTYPE DeviceType,
0 ~. Q8 n# {8 p: h# @, m6 |
HWND hFocusWindow,
# c' s0 H1 F4 N! @; D! J
DWORD BehaviorFlags,
8 w7 Z5 b3 A5 e4 g7 x! o
D3DPRESENT_PARAMETERS * pPresentationParameters,
; j' W9 `2 O% Q
IDirect3DDevice9 ** ppReturnedDeviceInterface
% q; k$ |9 _# C" J
);
% _, M8 f0 i l. c
/ l1 B- s0 O2 l/ ?; Y& P# w
, J/ _, ]* Z$ T+ {( V" L; M
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
- @) `, y+ h$ m
HRESULT _stdcall hookedCreateDevice(
4 I7 T, \# B3 H& c1 l, Y
LPDIRECT3D9 pDx9,
P# W2 j1 i3 b1 W7 Q
UINT Adapter,
# Z7 q! N: Z) M
D3DDEVTYPE DeviceType,
9 ^" f Q8 J( T+ a$ O7 [
HWND hFocusWindow,
$ {+ w- H7 ~7 ?, n/ ^' Q" V
DWORD BehaviorFlags,
. L2 ~* F o* U8 R6 b4 M- {
D3DPRESENT_PARAMETERS * pPresentationParameters,
% `& m O2 }& `& K
IDirect3DDevice9 ** ppReturnedDeviceInterface
" z# S4 v+ [2 L% T' D: O* c5 l
" ]: r" ~4 ?. m& Y
);
/ u4 l# B0 j4 g4 }4 ?; Y
T2 r( Y* {; X8 c& K1 W
其中的LPDIRECT3D9 pDx9就是this指针.
: S0 O7 f) e7 z* e, A
: D* A; b) d- m6 n3 N' K$ |3 C
hookedDirect3DCreate9的关键代码如下:
9 x5 C- E" n1 K' w* G, \
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
: }0 i9 @5 T4 z1 c1 H
9 O% t' x. ~0 [9 q' V3 D- X
DWORD oldpro=0;
; W1 k5 C/ G m' A
memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
: ]( D6 l! T+ V, m+ ]
VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
" L% e8 L: n* j1 v
*(BYTE*)pCdev=0xe9;
; W/ `* y6 }' A& g
*(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
: K& i1 R7 X( p+ h
' Y x+ e" P- Q% M' r( Y5 K
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
" Q3 d9 A7 |% L" E% S
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
* b m* v+ F; |8 U
memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
4 G& \9 }. C* m" C( @- X
DWORD oldpro=0;
, P* [) R( S2 ^* A) F
VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
; \) d: H. l, G0 X E
*(BYTE*)pPre=0xe9;
% _( R' L3 h8 P& B, e- c
*(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
' b+ l, X( |9 k1 M. t2 y
. e2 d4 C' n, b; o( e/ M/ H/ H
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
8 C8 I% [1 o0 X X( e2 {( t% D; C
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:
[email protected]
";
% @2 b" x4 T4 B/ g! @7 v$ k
DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
. w: P* q+ R* e
//在这里写入您的其它绘图代码
1 H$ U$ C2 ^6 a: E1 z8 n2 b
( e5 r, h) c( G; O4 q5 A0 _
6 ^0 k7 n3 a: M' `) Y
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
% {+ J' H: {5 w* f
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
1 N" O7 D7 b& }0 |6 z
{
. ?3 A( x: h# g! q
+ ?7 U: u8 Y! J
if(m_pD3D && pDxdevice){
5 k' U% P" v0 q6 c$ A A8 ]5 p
" a D" X: _# f* B, A2 i! I
RECT myrect;
3 ]4 P& `4 |- P' L! w/ w
myrect.top=150; //文本块的y坐标
]* M7 I: h {- j
myrect.left=0; //文本块的左坐标
+ `! N& x1 q! ^& }
myrect.right=500+myrect.left;
: J; R8 ]% U4 n
myrect.bottom=100+myrect.top;
3 v! H6 v7 C" A% a: K. K- k( N( w
pDxdevice->BeginScene();//开始绘制
0 u$ R. W) t. C
! Q y) Q+ B" N
D3DXFONT_DESCA lf;
' |2 p2 _9 R1 x% ?9 N- A' E
ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
4 g$ c: P& i' h7 @5 A c% Z
lf.Height = 24; //字体高度
. z/ O2 V) o) H6 I
lf.Width = 12; // 字体宽度
7 g$ F$ v& f$ u# d1 V# w* A
lf.Weight = 100;
; D6 u D3 W: z( I1 u! u; e
lf.Italic = false;
5 D- \- t+ u; I' m2 {' }
lf.CharSet = DEFAULT_CHARSET;
, ~ _" i8 c5 \( ?2 f
strcpy(lf.FaceName, "Times New Roman"); // 字型
/ Q$ T' t+ Q4 O& _/ h- R( S
ID3DXFont* font=NULL;
* I9 {6 V* R9 ~* A6 O* Q: z
if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
# @3 N' I" F; y# E9 u
return false;
4 y p6 o5 Z! |" M8 {
! g, j" z' h5 }+ T: u r
font->DrawText(
7 I& C# T u5 w3 x6 e
NULL,
+ m! O; W3 a, n0 S
strText, // 要绘制的文本
$ j% r6 v" m/ v5 T
nbuf,
/ G% ^0 Z7 T6 B* E P
&myrect,
2 a' B- h! X' A
DT_TOP | DT_LEFT, // 字符居左显示
7 n7 u) F+ p5 D& M9 {: T
D3DCOLOR_ARGB(255,255,255,0));
) X. Z+ v) E* V" H2 G
5 h1 }# \! `" D/ |+ V- {
pDxdevice->EndScene();//结束绘制
2 j; D( v1 _; o; z, {: a# H' Y
font->Release();//释放对象
5 J" ]- y0 ~1 i% d$ u" e( E
}
$ h$ k1 A- [. b) o% h. D- M
return true;
8 [6 r' ?$ k. c( ^$ X) C) {
}
: Q8 w- L |% r1 \5 d- P
: L# ` d8 Q: h; N/ C3 P! f
源代码:
/ k0 T% F+ X0 s# ?$ `, F4 t
HookDx.rar
+ V7 t$ T- f9 l6 ]
- v6 ^7 o6 w) H! s- e) [& q: E/ v
后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的5个字节改成跳转指令这种hook方法并不是那么好,其实可以直接改掉虚函数表中的函数指针,这样更加的安全保险.
欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://www.chinaavg.com/)
Powered by Discuz! X3.2