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

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

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

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

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

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

在看雪找来的文章,和代理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方法并不是那么好,其实可以直接改掉虚函数表中的函数指针,这样更加的安全保险.
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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