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

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

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

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

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

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

在看雪找来的文章,和代理DLL的原理基本是一样的。
  J. y# m2 ?: I" m1 r1 v( S0 M3 `4 }0 R
原文
( M, h4 o3 m% Z0 X- ohttp://bbs.pediy.com/showthread.php?t=85368
" S7 S3 M' o8 H$ c. H# A5 V
6 y( Y! V5 l$ J+ G, ?这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.! W; W# g+ ^$ O2 m6 |
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
& |! {, d  l0 S  j还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.! N& j! |& w3 @/ d3 A
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
' A% P4 B  d% V7 @4 A2 \8 o9 B    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
' w6 M5 B4 S  i    DWORD oldpro=0;' w' Z5 @- Z3 i- r7 m4 Q
    memcpy(d3dcen5bytes,pC,5);
3 k0 d7 X' T- g5 p5 }* E! U9 r    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
1 m' p& N5 `# z' a. W5 v; J# y    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码. x; O- M, p8 V' O; i0 \
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
8 T- P) y' _; y2 l' n- c( k' ]9 o+ ]% _  v% u8 [
( n6 @, ^/ E. H+ Z
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
. }. Y8 |; Z7 uHRESULT CreateDevice(
3 y' E+ c* C2 i# p5 G  UINT Adapter,
7 u% \2 J; }7 A" H* N; ^  D3DDEVTYPE DeviceType,& C9 e# o: ?5 R) d' Y0 O
  HWND hFocusWindow,4 z5 r3 J  A" S6 C8 A
  DWORD BehaviorFlags,* F6 U/ ~, I' @# w0 R
  D3DPRESENT_PARAMETERS * pPresentationParameters,' K4 f4 J: \( C8 G' z: i0 _1 e5 `
  IDirect3DDevice9 ** ppReturnedDeviceInterface
# }4 w+ K$ A1 V% \' G& p: D" N);+ V1 E6 Q0 M: J. f/ p7 ?

  T) B- S3 Y9 G0 j! x
2 M4 T2 i8 U% h. n( ?' d5 A, L而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
. b9 \5 e& E) }6 n+ L- k) IHRESULT _stdcall hookedCreateDevice(% s% Q2 V$ w( P
                                  LPDIRECT3D9 pDx9,
( h1 y( _5 S& i& v+ \" e7 ~+ H                                  UINT Adapter,
' u, D( J) j. \# k* r8 l6 G8 @+ b                                  D3DDEVTYPE DeviceType,
# @8 m/ u% R$ r1 A                                  HWND hFocusWindow,
+ \; ?% J1 o* V: j& ~1 J+ h1 ?                                  DWORD BehaviorFlags,$ {3 m* v* ^& i7 A0 z& X
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,& ^. @- B" U$ h8 `
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface+ B5 t  _  S( a  \3 n* N
& R  N: i/ W. K7 t
                                  );
2 d: a& t. H) P* S
' r8 |: j: J) R其中的LPDIRECT3D9 pDx9就是this指针.9 w8 |( J& c" x. q. ]. H8 |

; g6 w) j. B0 o  ~; A% K7 G. O% ThookedDirect3DCreate9的关键代码如下:% J( b8 p5 K. N# m( @
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针1 {# h3 ^7 I0 r' a

. m5 E2 m) j' J0 [: S        DWORD oldpro=0;4 ]5 x1 K5 s+ ?' T$ {8 y
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节: ?& \0 N; L* {/ ]/ y
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);9 O( w+ }) l* J6 y
        *(BYTE*)pCdev=0xe9;
+ q& ^4 u8 ]( {4 F0 ?: K& q. R$ o        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;* [3 _8 U8 }/ I& B+ p, K
7 @0 u; a* W; V5 D( L/ }& b, C
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:8 c( `6 e: ~8 Z5 u' e
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针6 L  V+ }0 d6 x  a
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节9 O" }" ~6 w' e& W
        DWORD oldpro=0;
8 N  ^: ~! d+ }! w' D2 K3 s        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);# ?9 D& v% ^' w
        *(BYTE*)pPre=0xe9;2 ^$ n" B; P7 U  C2 i
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;5 ?4 x# G5 g$ _! K$ K
0 q7 V+ A  U0 T
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:  Y4 }! a  @* V: G6 f: J$ k
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";" b, k) |5 B, ~& F; z6 O. F
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
$ _4 m0 N/ _; b# L5 [5 E9 {            //在这里写入您的其它绘图代码
* X8 t4 y2 s8 f, e9 d& n0 M$ s- d7 j4 F- [6 H0 O- a( k

3 p" h5 @# N, h+ z4 U) n再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:3 k! N3 Z) _- T6 k4 U
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf); R, `1 Y& r% V9 F( s
{# `. k$ ?9 V$ ?" q9 L

0 b" b1 I% e  I: n, q    if(m_pD3D && pDxdevice){
6 h; {4 ~9 J$ Y5 d. @& q  F
& g. `; H- E& x3 V/ o' ?9 ]" a# B        RECT myrect;0 A6 k0 C' g' |0 y0 L9 u
        myrect.top=150;  //文本块的y坐标$ |4 d+ J1 q! ?" z" ~1 V, s$ _
        myrect.left=0; //文本块的左坐标
: X" R2 U7 J. S& s. r. i        myrect.right=500+myrect.left;; K5 I4 T% j4 @+ H+ H' b/ Q  ~) W- z
        myrect.bottom=100+myrect.top;
* L- ^( Y! J. c0 Y% l: J8 }0 ^        pDxdevice->BeginScene();//开始绘制* t6 m7 P7 F$ O& H

% N, ?; b3 M6 R& @! F        D3DXFONT_DESCA lf;
9 n1 f, y# X8 ]2 D2 B8 Z1 D- z1 \        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));0 D. ?8 S+ I# U
        lf.Height = 24; //字体高度8 D5 v8 s& B. o# `
        lf.Width = 12; // 字体宽度
6 D$ A7 p/ T0 Y/ ^1 H        lf.Weight = 100; 3 N- Y; x" k( b9 u6 |3 w
        lf.Italic = false;, w" z+ @5 e' i# I0 }
        lf.CharSet = DEFAULT_CHARSET;7 K0 w$ l1 C3 ~  C2 f
        strcpy(lf.FaceName, "Times New Roman"); // 字型
0 F: |( e  l1 @3 T4 f: O        ID3DXFont* font=NULL;
% |4 u! O5 n0 }- N4 E) Q        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象9 X8 I8 B- N  k. T' m8 r
            return false;
8 |- G- ^4 V  `
1 a* y& `# T7 T+ ~* K* E        font->DrawText(" @: c! }8 n0 V0 x" F# G
            NULL,
2 C' A1 p$ ?" x6 H            strText, // 要绘制的文本: {7 B; K. e! z5 J6 ]0 Q. x
            nbuf, - x( q/ P" y1 R9 h0 m* V
            &myrect, / a$ |9 o( p0 X2 y. M
            DT_TOP | DT_LEFT, // 字符居左显示) u& f% ?$ X: T/ V, p" U( Y4 V; f
            D3DCOLOR_ARGB(255,255,255,0));
7 V& h9 P4 j& m" C8 T
  k/ u; B: |! |        pDxdevice->EndScene();//结束绘制
. e9 \; _2 x. Y+ E; ^        font->Release();//释放对象5 W) a6 L$ c; M1 @7 ?
    }7 @+ S3 ^7 K3 C; Z- o
    return true;, H: G9 J* F! R  J% G' J3 }2 V
}' b) q$ P' D: @/ @

3 v9 U- n" j1 k7 Y$ N源代码:
) X% t4 b5 u+ f! [- VHookDx.rar
  `. T2 e  x, p; s
' v* X1 r& T' e5 R后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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