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

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

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

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

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

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

在看雪找来的文章,和代理DLL的原理基本是一样的。
3 Z2 I* _& G# j% d! f, y+ ^6 q9 }: f+ C( J$ [! D+ B. N$ f
原文3 \! o8 O6 K" ]# ^2 }* J/ h( j2 u
http://bbs.pediy.com/showthread.php?t=85368) C1 e- [6 b" |! r) q" W/ H

' D) I" Z0 G3 X这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.9 @# o8 |: w3 q& O2 k+ t' |# u; y
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
: E7 i2 F& d9 J9 _- O3 _. v还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明./ G$ B) z# z, c
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.8 V& w- E+ h! g% O/ a& \- S
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
' G/ T. z# k& [- ]7 a" e3 X" u    DWORD oldpro=0;
& S3 O( w5 g2 b/ h    memcpy(d3dcen5bytes,pC,5);
% K: K/ g, m1 I    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);" s3 X$ X6 f; B+ j( n* Q
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码9 d' q: j' e2 j- c: f
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
2 g+ G2 z4 f6 O2 T9 r4 \
' W, c# a7 h% t5 E; Z6 Z- k& w" \0 h3 a/ G7 Z# Z
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:% L- d$ f+ S8 A7 o. v
HRESULT CreateDevice(
- q  t* M/ k) {( k1 t$ ]" ~  [- i% N  @  UINT Adapter,
0 M' q( @! C, s, x3 {  D3DDEVTYPE DeviceType,
) [3 v/ a8 b2 g; t% L; h  HWND hFocusWindow,2 k' O4 o* M8 Q3 t1 a" E
  DWORD BehaviorFlags,4 Q9 g: f- f: |. E) `6 i2 Y
  D3DPRESENT_PARAMETERS * pPresentationParameters,2 C( b' L! b2 Y0 m0 p
  IDirect3DDevice9 ** ppReturnedDeviceInterface7 [% o+ ~0 ]7 I4 B
);
- H3 m/ D( \7 e* ~9 Q8 Y0 y" Q3 H4 X9 z5 E! E5 ^% L# s' a" c1 {
) J- \! H1 D& s; \5 x
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
2 H) Y: k" L8 g  G# m2 u; rHRESULT _stdcall hookedCreateDevice(
1 y+ H  M3 y, k% E# Y4 g                                  LPDIRECT3D9 pDx9,
# h. F3 F3 C" g  E                                  UINT Adapter,
  Y/ J/ q5 r' x& V/ q                                  D3DDEVTYPE DeviceType,
& b' f- T% D7 O" g, k5 A                                  HWND hFocusWindow,
1 |5 K7 f( o2 f+ b1 j0 L3 W                                  DWORD BehaviorFlags,
0 X7 i+ K6 I& N# s: j/ p6 g  B                                  D3DPRESENT_PARAMETERS * pPresentationParameters,) e8 Z- W) ]2 M* e+ c: h
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface! s6 b9 u1 A+ |. c- G5 M# A

' z  ~6 I* V6 N3 w3 \$ D  h                                  );8 }( T, V( w5 t' ]) o

: }. d- C1 u0 I: c+ S其中的LPDIRECT3D9 pDx9就是this指针.* U; ?* `$ J6 i# W* s( d

( F& f0 e# J+ P) r6 ^. _+ ihookedDirect3DCreate9的关键代码如下:
7 Z; n! ~0 |' u; vpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针/ S% j' a: f+ i$ R
: S- y% \: c+ d7 n& |6 \) E% e$ y
        DWORD oldpro=0;
7 p% w/ X: r$ Q# \8 U0 C# U        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节, y3 e: a+ n7 F1 k
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);7 m5 Y7 K/ ]4 c" r
        *(BYTE*)pCdev=0xe9;0 D7 M1 l6 X2 p) |3 X( G3 D- K- j
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
8 O5 c2 l! |& c% u+ W! d5 [6 a5 Y- |) p' u0 I' c; z1 r
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
% v; E: e, n: f" K! G" OpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针: p5 f# V3 g* J
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节" ~9 I* i# |" i1 o2 w5 z2 L
        DWORD oldpro=0;: i! w7 u  i# j* _! k: _
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
1 F+ T/ F$ G# U" \        *(BYTE*)pPre=0xe9;
: t) e+ R5 b8 }" |. l$ ~        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
& F6 b( I; @' O; V) E3 o" |+ L6 x( M* s! W+ r# T$ u0 d& B
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
% c, d# R) C( I, Z* X; Fchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
; G/ c% x" q3 q" w' o            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本* v8 }9 `1 t0 `. m/ H
            //在这里写入您的其它绘图代码
1 c7 ]# S9 ~4 ]  t1 |6 J7 w# r& J) E  d- {! H! l9 L4 O
# t+ _1 n3 a  b6 _6 G7 R
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:' R5 ~: h! E/ r  ~
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
( v: O. Z- d* N* j{' ^7 a! ~1 P& t# W
% p, h+ s# w1 T0 f( [1 ?  u
    if(m_pD3D && pDxdevice){7 F3 Q. F6 ?  i
8 k1 X# |( S% E
        RECT myrect;' C- K8 c. t% ]& N+ b
        myrect.top=150;  //文本块的y坐标) ^# i5 M, e8 B! W+ e" Q2 R
        myrect.left=0; //文本块的左坐标
; }. H# n! ]7 K3 u+ p  y8 V% f        myrect.right=500+myrect.left;
; F$ K& Z0 K. ^$ ~        myrect.bottom=100+myrect.top;, X6 g. g3 [2 k/ P( ^9 S9 V! j
        pDxdevice->BeginScene();//开始绘制
' W9 Q; z1 }: ~6 q, K. c8 J9 F, h. V7 {7 {, v
        D3DXFONT_DESCA lf;
+ P# l; D/ n7 ~( C6 @/ `* k        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
3 Q3 g$ V2 A6 B        lf.Height = 24; //字体高度4 {/ U- I" Q0 Q6 Q3 L! t
        lf.Width = 12; // 字体宽度/ N# T( o4 A# }8 o& t( k
        lf.Weight = 100;
1 O0 F/ a% l: t( ^! v' K        lf.Italic = false;, Q( E. ~4 d" R& g* W% w' ^4 Q
        lf.CharSet = DEFAULT_CHARSET;
. T! t. `  J; n5 L* h. O+ h7 }, k        strcpy(lf.FaceName, "Times New Roman"); // 字型
8 d- V/ W! ]5 _. c& D( Q        ID3DXFont* font=NULL;
: n6 L: p1 {- R1 z3 Y, ]  [  |        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象) M9 v" R4 C) n  ~. `
            return false;# m4 e8 i' ^7 E. B' @& J$ v7 r: C

, a( ~; l" b$ B! R        font->DrawText(- ]2 [3 G" c' C/ {/ ~3 h+ t
            NULL,8 Y- b/ q# ?7 I) B! T
            strText, // 要绘制的文本
. b; ?* Y+ _9 l' R/ y            nbuf,   }0 p: I7 m4 M1 Y. w( }' |
            &myrect,
, A# ^" \* y1 N) e  Y: G! x            DT_TOP | DT_LEFT, // 字符居左显示0 V# {$ a1 i9 P! P9 F
            D3DCOLOR_ARGB(255,255,255,0)); " e1 t/ I5 ^" D' h" P- v; W9 A
- R9 N+ s8 P3 a' }  C- z5 P9 ^
        pDxdevice->EndScene();//结束绘制3 c1 S0 A/ C9 Z" J0 J- `
        font->Release();//释放对象
# u9 K( H! [+ A: B' v) [    }! e5 e, c. n' f/ @9 S4 |
    return true;: q. |2 ~( D: i) _0 Q
}; M8 U7 ^7 \6 |9 X* X6 f) U
" `: D1 K. f9 z0 Q' z0 p
源代码:; {5 d3 U5 `. r- z# Q5 X  D
HookDx.rar
+ l6 W: ~% I/ j; `/ ^) o& W( {3 ]7 J4 E# ?
后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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