设为首页收藏本站官方微博

汉化资料 【代理DLL汉化研究】Hook Directx:在游戏中显示自己的文字和图形的方法

[复制链接]
查看: 3740|回复: 0
打印 上一主题 下一主题

[汉化资料] 【代理DLL汉化研究】Hook Directx:在游戏中显示自己的文字和图形的方法

跳转到指定楼层
楼主
发表于 2009-8-28 21:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

【代理DLL汉化研究】Hook Directx:在游戏中显示自己的文字和图形的方法

在看雪找来的文章,和代理DLL的原理基本是一样的。
+ a( G  A/ x  z2 j4 o# m% R5 F' r  d2 t8 z( K0 M  L8 k
原文
: u4 Y7 X/ R  C, w1 f* Bhttp://bbs.pediy.com/showthread.php?t=85368
7 F. C' r4 S3 m1 _* h6 s3 k( e  z, `- o2 o
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
1 s' r+ i  ~: B, K5 J其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
2 c+ O- ]. Y1 R4 m还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.: d$ T5 T) Z4 j. T
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.: H% p  Q% l! |+ x+ [+ M
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址% r- m$ ]+ f/ ?/ D! O
    DWORD oldpro=0;
' H" s' {4 d8 r2 d    memcpy(d3dcen5bytes,pC,5);
* f( s; P/ s; O2 e- f    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
1 r8 k) N7 n4 k3 E8 _! ?    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
! K$ m# ~: |( I+ i- _    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
0 c: K5 X* P6 r; l) a1 b( J/ r5 I! b2 ^& k4 d+ B
. j( Q7 z9 H1 y4 Z$ v. ^# q
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
, ?7 I0 B  i" H1 F+ ]/ GHRESULT CreateDevice(
' I$ [( k  P1 n& R  UINT Adapter,. L+ I, k1 ^5 Z2 r% E
  D3DDEVTYPE DeviceType,8 v1 D  I4 N% n, k# ~% E# m) R; u
  HWND hFocusWindow,
9 D! P  y( V1 m4 z5 v  DWORD BehaviorFlags,: I5 j/ w+ d. V: F/ I
  D3DPRESENT_PARAMETERS * pPresentationParameters,6 I, D3 R8 u# u8 m  `$ B' y
  IDirect3DDevice9 ** ppReturnedDeviceInterface
+ q: f- ?2 ?6 Y; N+ `- s. a/ O);
2 X; F( [6 y& S8 R$ t6 {! C! {- J2 L: Y; D) Q2 x1 R
# M/ y$ Q+ q4 {  {5 S: }; U* ?) }
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
( c* [& \6 V2 |: o6 C+ w! s: pHRESULT _stdcall hookedCreateDevice(1 P6 S9 c$ Y4 `8 _' @9 l
                                  LPDIRECT3D9 pDx9,
$ r5 [: }) `; }5 w) q5 @, Z                                  UINT Adapter,
; H) w; t0 m2 t) O$ n                                  D3DDEVTYPE DeviceType,
! q3 F  Z% D3 r7 h                                  HWND hFocusWindow,
  T. O) D, ~6 ~# P! }                                  DWORD BehaviorFlags,, u/ w. [2 t2 j; {% X0 z
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
$ q" g6 H1 C- H7 @$ C2 w                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
0 P: r4 T! j5 B; e
* s9 T" {# P% Y+ y                                  );
  S9 |2 c0 C2 l/ b6 _" a+ v* E! ]. O; S' r# G" N; U6 U: D5 q
其中的LPDIRECT3D9 pDx9就是this指针.
2 U3 m- T2 G1 t& w7 r& k
7 F; C! k6 R0 R- fhookedDirect3DCreate9的关键代码如下:1 L$ e: O) S, Z( B
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
" [% s! `4 R: o8 b
- ^; N  L1 C$ C  v) u9 J1 Y        DWORD oldpro=0;# V, M: _- }0 P) ^$ C
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节% N5 \/ k+ W3 |6 V0 H! a! I( w
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);4 A7 o3 u" W. w5 F- v
        *(BYTE*)pCdev=0xe9;! [/ F4 Y% Y6 w( O
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
5 D; n4 P  N0 r! P6 a4 J& \( G5 H5 Z/ b" f) _1 A* t* m* Z' [
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
5 r0 F% @# P) O8 a$ x% Q& npPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针0 D; ^. K: l9 W) d
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
. L) L  u) _/ F; b. S4 W3 A, B: m        DWORD oldpro=0;
* Q+ b# m5 W# g" }' E6 |        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
  g0 |* o) g" d( p2 Y        *(BYTE*)pPre=0xe9;
5 o* \7 e1 N( _* F, Y" Z        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;4 A, j' B: f) i9 Z/ }

' `1 \- b4 \5 r- H0 b2 O7 Y0 S实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:* C) v9 l0 ^* `3 T
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";2 _$ P: B2 r) [0 D5 H, u- `
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本+ M) r. O8 |. n* V$ q
            //在这里写入您的其它绘图代码
! k  I3 e6 T" a
; r( F: J( A  `4 \9 W7 R# q( x
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
4 f- T& |. U. e, CBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
6 q2 i. G$ S" o  D! L  l' B{
7 f" A% Y! \' s6 J! d6 \" W; \5 H. t3 Y& g5 c) J1 }9 C
    if(m_pD3D && pDxdevice){
1 A4 v9 v1 b  c$ O. t. X: C# x- I5 _0 a) G
        RECT myrect;
7 l5 B: ]4 t% q5 V        myrect.top=150;  //文本块的y坐标1 g6 R7 k3 }) L" e& A: q
        myrect.left=0; //文本块的左坐标
; K9 ?* g! s# p0 w1 k        myrect.right=500+myrect.left;5 z4 ~+ K( u# O5 Z# H/ b
        myrect.bottom=100+myrect.top;1 L& J) }" W- h* f* g, u9 t2 g; A
        pDxdevice->BeginScene();//开始绘制
) R5 t6 y0 d( H7 A; o8 \' I  r
8 h. z8 y6 c& u2 C7 ^        D3DXFONT_DESCA lf;
* {  I$ w& J/ v8 z- h        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
6 f  b& M6 u" D* y        lf.Height = 24; //字体高度
+ y5 J, o' G& ]9 C4 ~, E        lf.Width = 12; // 字体宽度
/ G/ t0 L% l) N0 R# Q1 @        lf.Weight = 100;
) Q) [0 t9 S3 d3 i" v+ r        lf.Italic = false;
5 \" F2 C$ L) j  ?$ `* I; s! J        lf.CharSet = DEFAULT_CHARSET;/ L5 S" g$ ^9 O2 g: M* d7 |& v7 w7 [8 O
        strcpy(lf.FaceName, "Times New Roman"); // 字型! P) G# ]  F4 X8 Z
        ID3DXFont* font=NULL;
/ G* J7 e+ B1 s4 J        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
, ?2 U: O" {, a1 ?            return false;7 j& d1 w4 R; V2 O3 x) ~

" z. a0 v3 I6 e        font->DrawText(
( H+ d' Y* E. h4 W6 u  M* k) _+ a            NULL,( P8 [5 S# s# j2 K  F0 l/ x
            strText, // 要绘制的文本
2 o" z2 c2 E$ j4 X            nbuf, 3 ~" Z$ o9 @: W
            &myrect,
5 U  E& Z% d6 {0 q6 k            DT_TOP | DT_LEFT, // 字符居左显示" F, W8 y  T( H# L- d
            D3DCOLOR_ARGB(255,255,255,0)); 3 f% H3 @1 i* L* R5 _3 r; N2 F4 w
. ?; N9 e5 T& N+ m* \6 f. k; c' ?
        pDxdevice->EndScene();//结束绘制2 f( ~4 H- p* z% s/ {
        font->Release();//释放对象0 h9 a+ u7 g. Y4 }' U& M8 k
    }
/ }9 ?) N! o& e    return true;
$ z5 |% \- I) ~5 p: J! I" N: c6 Y}
# b) ^( O* b( E/ q
2 _0 s/ j% n6 I- H7 Z; d7 o4 C源代码:
) I% f4 v2 ?, MHookDx.rar
$ v; @" f( m, l% L% t/ C5 s2 n* k3 [3 V( K& A% z6 o
后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的5个字节改成跳转指令这种hook方法并不是那么好,其实可以直接改掉虚函数表中的函数指针,这样更加的安全保险.
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

冒险解谜游戏中文网 ChinaAVG

官方微博官方微信号小黑屋 微信玩家群  

(C) ChinaAVG 2004 - 2019 All Right Reserved. Powered by Discuz! X3.2
辽ICP备11008827号 | 桂公网安备 45010702000051号

冒险,与你同在。 冒险解谜游戏中文网ChinaAVG诞生于2004年9月9日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

快速回复 返回顶部 返回列表