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

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

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

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

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

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

在看雪找来的文章,和代理DLL的原理基本是一样的。/ f. `' r& k! ^/ C" w0 Z

- U% G4 G+ `* k5 ~原文
, R  g: R" p, S5 a& a, H5 ~http://bbs.pediy.com/showthread.php?t=853689 z* c' X' k# I& I) R

: L0 H, M; d5 }3 {4 k- v/ c% \2 {这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.3 @) o" d, S+ C; J0 ]2 |
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
$ \' n$ Z+ n1 X4 h, G+ n0 Z还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
1 r! n4 B& y( h! o* e' T首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
! g# k* j- Y1 F$ t% f' E1 [    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
3 q' q0 j0 J* f  a7 m    DWORD oldpro=0;" y9 W8 F" e" Q' x0 Q
    memcpy(d3dcen5bytes,pC,5);# W/ r; u; o! `6 s% B/ L: g
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
# T/ i! \- B" e% Q: P' ]+ R    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
+ m% e! I* p2 G3 B* R  g3 I) I    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
3 d9 N0 i0 r6 T: T/ k8 @0 P$ D8 g9 h
/ \8 D9 ]3 x* q$ ~) H& b+ y# b
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:/ d9 A8 ]5 W  U% B/ z" t
HRESULT CreateDevice(
0 ~& K  p; Y5 U, {* C( L  UINT Adapter,) ?# }/ y7 I6 Y7 I# n
  D3DDEVTYPE DeviceType,$ y6 J$ q' ?" ?6 _8 R- V; Q3 V
  HWND hFocusWindow,
% B( C( b1 q; O2 u( P7 [6 i  DWORD BehaviorFlags,# c- D: x- F) P" k% k# q: _- j
  D3DPRESENT_PARAMETERS * pPresentationParameters,' {& h5 `" @, k& ~$ F. H* E
  IDirect3DDevice9 ** ppReturnedDeviceInterface2 r3 l: Q) b5 g: R( Z5 C
);4 v$ \. U5 d! D  m) J, m4 `
# E( [# V/ ?/ A' _6 _5 S

& O1 C0 o" d2 V, p* s$ s$ M3 r而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:% N  W0 e6 j2 \
HRESULT _stdcall hookedCreateDevice(
$ n7 s' V* E0 c                                  LPDIRECT3D9 pDx9,# y) B; \" T! L$ a& X" ]- o
                                  UINT Adapter,% s5 h. J: J% \" [" E
                                  D3DDEVTYPE DeviceType,
* q$ W) O, g" G( a+ v                                  HWND hFocusWindow,, `& ~+ }* L* V- S) }$ t7 t. p& C
                                  DWORD BehaviorFlags,, |# d, u+ m0 j
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,% e, Y. L. J  S, q4 U2 U
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
3 X! m  U1 w# z5 a
* Y9 G4 ]) v3 L4 p; ~8 F                                  );
8 F5 p3 B2 z3 V2 D# M; w" o2 b& {2 M
其中的LPDIRECT3D9 pDx9就是this指针.
5 q6 t3 s7 @+ D2 H2 a  [. \6 d' D& C2 W7 f$ D4 y$ ?9 ?7 q( Q1 M
hookedDirect3DCreate9的关键代码如下:& K5 i  h0 c3 T7 u# A
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针5 k! ~: s, N$ j, S

! M. }' S7 k/ |6 [1 h: ]; l2 t        DWORD oldpro=0;
; g9 A- d" T: K7 ?2 R& F        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节- s% c. z% J: o# U2 t
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
9 K; @- {1 G8 L( D( m. v7 q6 s        *(BYTE*)pCdev=0xe9;# L8 ^3 `$ U& |5 p$ K% R0 D
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
, C/ s9 c: |1 W: U2 m% O6 O2 p) p, s/ E! E+ G
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:0 q  r6 ?$ l: x/ U& W
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
' X5 n3 W( j- u5 i  ?# {        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
( _7 l5 W, H' U) }' w8 s1 _        DWORD oldpro=0;
/ h! B0 ?, p  I% g- U        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);3 }# j8 R" X/ f0 V. E1 ?
        *(BYTE*)pPre=0xe9;
3 v8 i. e. o! _- j% a" Q4 y' {4 E        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;8 u0 a+ t( h  o* |

( \6 ]8 b; V: ?# h, m实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:' m4 c& F$ B6 u5 M/ X
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
, Q# F! b! x$ Y6 \6 i1 i% j            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本. H/ I' K( R3 d
            //在这里写入您的其它绘图代码
0 x- B% W4 @  ]9 Q. V  Q. x, b
' }. }: e8 e% a$ D4 \
% v2 R2 F% M3 y2 u( Z& Z再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:+ d; |8 ~# y9 y
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)# K; W/ a7 u7 R. |; d4 S
{0 W4 T( M6 b0 W! Z: {0 D$ o3 x
+ I9 H' U3 B2 m9 n1 @# r
    if(m_pD3D && pDxdevice){
1 I0 ~1 L$ O" M6 ~
& K* F2 a  `& X  h: m        RECT myrect;
, L( R5 I: f! M* R2 c        myrect.top=150;  //文本块的y坐标( X" }1 q* @2 e1 y4 `, T7 v) G4 e
        myrect.left=0; //文本块的左坐标" K9 N3 s/ x# A
        myrect.right=500+myrect.left;9 Z$ N' C. L  z3 _
        myrect.bottom=100+myrect.top;
9 D; E5 v# H" q' ?/ e5 f7 n        pDxdevice->BeginScene();//开始绘制
! |0 P, Y+ g# W9 c0 w/ C
, s1 `, Q5 L+ b6 |+ v% n3 P0 }7 ?        D3DXFONT_DESCA lf;" i2 S) y, i8 w. W0 Z
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));- q4 q4 \. z7 j4 j9 U2 t
        lf.Height = 24; //字体高度
' {/ ?1 m& C* i2 m/ W! T        lf.Width = 12; // 字体宽度7 I4 z2 o& V4 _! A/ C8 M1 k
        lf.Weight = 100;
. |: Q& T! j* D        lf.Italic = false;
' n' ?% Z2 T+ F& ]0 p. T        lf.CharSet = DEFAULT_CHARSET;
+ u3 e9 q( D; k/ u3 w        strcpy(lf.FaceName, "Times New Roman"); // 字型
) J( C+ _3 o6 x/ V2 d1 ]" C        ID3DXFont* font=NULL;4 b3 j7 K* @6 S  l7 u; Y
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
$ L& B" x6 |; y            return false;7 B7 n3 g1 ~1 J- o  }) |
  c& w) G8 Y# a. X  X0 h) U5 X
        font->DrawText(
0 G8 B( N: P$ u9 I. Y            NULL,4 }# p+ E6 N$ M" @8 @8 [8 w
            strText, // 要绘制的文本1 `. ?' l3 A% U. J
            nbuf, 0 ~  \, _7 h' T  \% K. K% p: X2 x
            &myrect,
8 `- `* J- g% n8 t            DT_TOP | DT_LEFT, // 字符居左显示
7 k0 }( n, `! G5 N$ c$ x            D3DCOLOR_ARGB(255,255,255,0));
6 @7 m" J2 g% a5 c& K7 p/ {, \$ D, ]# s& g
        pDxdevice->EndScene();//结束绘制5 }* x" H4 w5 u3 R/ \
        font->Release();//释放对象
- G0 J, M5 d, E- p    }' o! V- \2 X: Z3 ~9 C5 f
    return true;' r8 X& o7 o% q" I, O! j/ P
}
  [7 L  y& O, H( F% l- ?
3 X, Q1 ~3 E4 S- k, s2 {6 ?源代码:
$ @. C; K4 S/ ?7 z, Q8 f& I; rHookDx.rar
9 \; C$ M# d9 r4 ]; U# V
: j5 A# h) \) m$ Y# Q* J, p8 D后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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