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

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

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

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

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

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

在看雪找来的文章,和代理DLL的原理基本是一样的。, ?* |( t% |2 d9 b6 s' }. D
" y: _- Y4 P5 i, N. @# A9 P
原文7 ]% Z$ t6 }0 Z% @: B
http://bbs.pediy.com/showthread.php?t=85368
8 K! W3 B# {5 Z4 D) {) w, w6 W2 M# O8 z  H4 _1 F6 y
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
- A, ?2 U5 B9 H  ~- Q0 H其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.9 C8 s6 o' `; Z8 H; _8 H
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
% U: X! u1 m6 F$ Q5 }- r首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
1 J7 I2 n# P3 @6 r- u5 O) R    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
: Z- Q8 t. d$ }8 E  e" O- ~' G    DWORD oldpro=0;; {7 u7 k- j6 g( g1 b9 g) L: b
    memcpy(d3dcen5bytes,pC,5);
5 H# ?+ C& m2 x; q6 D    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);* {5 K: U& \# r0 }8 p% y! y" I3 }
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码$ w* S! ]* j0 A/ c
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-54 [+ x2 V5 H1 r

! v. z, |: [1 f+ S* v% |6 e2 n& u; \. ^
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:3 G- j$ `4 c  j; e
HRESULT CreateDevice(; ~! i3 Q( ?. P
  UINT Adapter,+ j$ T0 q2 f0 V" u, M
  D3DDEVTYPE DeviceType,
& s4 j' W3 `- h$ S# q( @0 Z  HWND hFocusWindow,7 a$ B5 ], j3 ]( h/ Q5 _" s
  DWORD BehaviorFlags,
! p" G5 V% Y! g& s! p4 L  D3DPRESENT_PARAMETERS * pPresentationParameters,4 |" Z9 I& x( q. q- p, Q9 ?: V
  IDirect3DDevice9 ** ppReturnedDeviceInterface
% N' O, F% \' ?# X4 }% `+ z  q);) \! \2 W; v- O
& \( B- C1 s1 V2 |* m) k6 C2 p# `' {
' y. ?3 ~7 W* a) ?3 g4 i
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:# y1 K: F: @! `2 p+ v6 W* q5 ]
HRESULT _stdcall hookedCreateDevice(
0 P. `% g0 T1 K5 K                                  LPDIRECT3D9 pDx9,9 a$ L" O) X2 ~% D; A/ O
                                  UINT Adapter,, e  [% C- C& v1 d
                                  D3DDEVTYPE DeviceType,4 ^$ H$ ^7 ^9 a( t8 |' m
                                  HWND hFocusWindow,
3 w7 Z$ i$ Q1 K1 ]. Y                                  DWORD BehaviorFlags,
% A; O" Z8 Q* I6 s/ q' {                                  D3DPRESENT_PARAMETERS * pPresentationParameters,* _% t6 T7 l) E9 x3 {
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface8 u2 R3 |" v: _

- h0 b# W7 o5 o" D2 J$ H$ z8 x# E                                  );
  n! p% M8 |+ t& o' o  @1 z4 \# A* ~. @3 q0 I) g; e
其中的LPDIRECT3D9 pDx9就是this指针.
5 v( w% s" G( \7 o
* O) D4 B$ L: v( vhookedDirect3DCreate9的关键代码如下:+ O% C! @4 \4 Y
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针2 E- P# q# L1 @0 ~) s$ h0 `% I
7 R. Y  a7 V' ]
        DWORD oldpro=0;4 f' _' ^2 r3 i! z8 V% K* S( d
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
0 M8 C5 ~/ T; `0 E        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
$ I: S; }& j3 E6 R        *(BYTE*)pCdev=0xe9;
2 }4 J% ?3 W+ e% V! o6 a/ J        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
$ ^8 J+ O$ K, s! v2 r6 o
8 t# T5 N" l+ k# n" u在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:' y8 h) W0 }4 i$ v" F4 O* G  Y. @
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针  L0 q2 R/ ^6 c8 _* f$ l
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
4 m& h7 s5 T, g        DWORD oldpro=0;
$ {* _7 E8 o4 t* t. ~! D$ L        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);7 b+ {! x3 q& M, b$ q. Q
        *(BYTE*)pPre=0xe9;, P% ?* @) m& ^1 }5 s
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;# M+ Q# O: X1 W
' S0 v- p* i2 `# l3 b
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
/ d1 v& y  \+ E- Y! q6 Cchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";5 ~9 c/ @( r# m7 L
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
% ^, B6 ]+ p) y( m            //在这里写入您的其它绘图代码
! k& G; a* H4 o3 j8 _. @
. w; x  M/ l% b- k* ?& w6 M7 ]6 k9 X9 A3 {' {4 }
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:. Q3 P1 _  z2 ]9 C  i9 M6 ]) `5 f
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)! o- T; ?5 a; }- i
{. ]. a7 @/ O3 J$ t
  g' S' c7 b2 O/ j6 G
    if(m_pD3D && pDxdevice){2 U, S9 d3 Q7 H/ N

. z8 D9 L6 G3 |( @% F        RECT myrect;0 N0 _" [# c/ B0 ~
        myrect.top=150;  //文本块的y坐标
" }+ M; i+ V3 l0 {        myrect.left=0; //文本块的左坐标
6 o. T0 [7 ?. X  Z, t* v- [        myrect.right=500+myrect.left;
" d/ c2 ?  o# T0 o9 Z: {" D9 r/ z* w2 z        myrect.bottom=100+myrect.top;
3 f1 U# `6 q, h- v        pDxdevice->BeginScene();//开始绘制9 o+ M! R; s( F8 C

6 y+ R9 @! \. O* p. I8 m3 i        D3DXFONT_DESCA lf;. [0 ]& T2 ~2 v3 w# W0 S8 z
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
& O- V$ h* q2 ]# d8 r        lf.Height = 24; //字体高度
* O# G& _$ ?$ J# S( M! P5 j        lf.Width = 12; // 字体宽度& m& U) \/ Z$ J, ^' @8 {
        lf.Weight = 100;
0 V5 j7 X2 t9 `" J        lf.Italic = false;0 H" Y. [# o0 ^% q- r
        lf.CharSet = DEFAULT_CHARSET;4 F" B- Q4 |- x, j9 h
        strcpy(lf.FaceName, "Times New Roman"); // 字型% F5 |  v  f1 A9 C! L4 {7 R
        ID3DXFont* font=NULL;
& F, \9 k/ W" H9 ~% I4 Q9 o# j$ t2 y  Y1 h0 q        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
$ H( L7 Y; L  k* w' T4 L' N2 K            return false;) G/ l  M- y& b
/ O8 [- L- b$ }9 P2 N9 G0 y$ ^, S. g/ @
        font->DrawText(
/ N4 f0 z6 Y1 Z* F# A3 g9 r# X5 Y: n            NULL,. C' b- ?6 K6 h: |# U& x/ z6 f& l8 n
            strText, // 要绘制的文本
( ~) m) `1 U0 S: m            nbuf, ' S7 O! Z: R! X' l
            &myrect, ; B3 f- B' U+ W8 P' a% W
            DT_TOP | DT_LEFT, // 字符居左显示
3 m9 K1 S6 `5 x, I4 s            D3DCOLOR_ARGB(255,255,255,0)); ) r, Y: E: H5 _  Q. s
. E4 h. o% V$ ~4 y$ S5 A
        pDxdevice->EndScene();//结束绘制
" r7 v8 p. |5 |8 K* z/ _        font->Release();//释放对象
& Q" B) {0 W4 q2 e! `- v    }" }) J6 I& A' p5 Q9 d
    return true;3 k# ]; p, L& i9 N8 m+ D4 A( a
}6 k  w8 \3 W- o
3 X8 K. P$ R0 `$ r- h8 y8 {
源代码:" x; G$ _% i4 p+ s8 D) R% b. @
HookDx.rar& d$ b) i, ^' B" ^7 L% `; d2 P

, k4 c! ?2 L* k2 T后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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