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

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

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

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

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

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

在看雪找来的文章,和代理DLL的原理基本是一样的。
5 M; |: r+ }, w* v9 R1 m7 x0 v0 p' B) x
原文7 n+ M& r0 \( C8 ^4 x; H+ T/ T
http://bbs.pediy.com/showthread.php?t=85368
6 f1 c9 C& Y# T- s# x. I& J7 C- s! n0 {6 C3 Y. ~" l& l
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
) m' W  k' p0 p8 E0 g) \1 r# t& Y其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
$ i3 Z9 C/ {& q. ]+ G+ C) }还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.5 q- M- e. F+ _6 q5 z8 T
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
; m5 ^  A8 t3 _$ C    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
% `/ g0 l6 n* Z& y    DWORD oldpro=0;$ }, z' Y- ?: Q% j; o  z' \
    memcpy(d3dcen5bytes,pC,5);
6 }7 P* F" y0 Z# ^- P! W    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
1 p' {  E! Q5 I( @+ {. j% C    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
. _& o, @! f) q" C! |" ^7 k! Q7 ]1 S+ H    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-54 F, T2 f$ h# f; {! T4 ?7 G

' X/ k) e8 W3 a4 w1 v0 q/ X1 Y* e# k' z* ^' D
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:% p  J; T7 P2 |2 V1 K) ]6 i
HRESULT CreateDevice(
1 y+ i" s5 j) ~6 J  UINT Adapter,6 Z& _9 d9 u8 w: k, L
  D3DDEVTYPE DeviceType,
" y5 t0 m9 d/ r3 w1 ~" J( u  HWND hFocusWindow,
; e( Z7 R2 x7 q& ~5 x0 B  DWORD BehaviorFlags,
& l. S# i6 z% p6 v) k" K  y  D3DPRESENT_PARAMETERS * pPresentationParameters,
9 x- q( s, C0 S: ~( |  IDirect3DDevice9 ** ppReturnedDeviceInterface
+ b3 D, t4 |5 J; f6 w  w);
: a; D* N3 g# f" s7 b  r  n
- j. x6 m  t& X/ c' X) H% Q+ P0 |, j  |2 S9 F) M+ b+ k
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:" ?) @2 ]( b, c+ V5 A. j
HRESULT _stdcall hookedCreateDevice(0 s# i; V0 E' q& |
                                  LPDIRECT3D9 pDx9,9 v* c9 ?; |' k$ f
                                  UINT Adapter,
# S( y5 O* p7 _7 ~% L: ^                                  D3DDEVTYPE DeviceType,/ \+ R% s$ r5 n1 q+ _! n' m0 h
                                  HWND hFocusWindow,+ A# [( \# S. v- o% h; S
                                  DWORD BehaviorFlags,3 }! u4 f- P  f/ k
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,+ M- u' K# z5 ~1 u5 ~& ~
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
/ r3 v$ f5 J- d' B& ~1 G& V
. C1 R7 m7 y, J/ l! G, ^; E7 G/ l                                  );
: z  _  M) ?6 N. o  y
6 q% L) G0 ~' [& E7 d" x$ _! U其中的LPDIRECT3D9 pDx9就是this指针." g3 ~4 F  Z2 k1 P9 a

4 ]( ^8 _; [) a# Y# M& o; @, |hookedDirect3DCreate9的关键代码如下:
9 [% q7 W2 [* l; XpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针7 G) W/ K; q& p, M1 O1 t$ P
# i( A% h  m# t# ]- g
        DWORD oldpro=0;! l; U$ @  y9 C$ d
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节8 r! p9 P2 b: Y
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);4 J6 j6 `" A+ x1 @) j; t5 [
        *(BYTE*)pCdev=0xe9;
9 b* ^/ _" W+ z% l- o4 B" R  t% g        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;, s9 z. P  |( z$ v) e2 N8 ]8 ~; ^2 E

8 p* x; r- t. n8 n, c' p在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
1 ]9 m( N/ w# npPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针4 D! v. N8 k: o0 F
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
+ P. t( |7 d) C) n        DWORD oldpro=0;9 |8 L# c7 T  E5 f7 J/ p$ {/ }
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
4 R7 |8 L5 v3 L+ H        *(BYTE*)pPre=0xe9;5 X/ X& z1 y+ M; E+ \, Y  f+ p9 r
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
4 d0 y5 R7 ]. o9 U4 s
+ N! \: I7 V9 P3 t# |+ O实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
3 V+ b; ^! k! V8 f% kchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
4 H! m$ T+ X2 b0 t6 S; f" t% }            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
0 z; a' u) V: F" L# b5 [            //在这里写入您的其它绘图代码+ }4 e3 {- q' n! g! U3 ~, B
$ @8 K4 }1 w8 n0 f% A
; W' M, S# t  f8 m
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:* Z$ r$ `- |0 B  Q
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
# o2 ]; K( J7 ?! S, ?{
3 W9 A  A  _* X; ^& R
4 T/ y+ p% Z# u$ O5 I- P1 F; i    if(m_pD3D && pDxdevice){% Y& V! \7 J# ]' w  J

4 [, S2 x1 t4 B9 q$ N, R. o; K6 F( f0 d        RECT myrect;
) Q9 Q( W8 x/ `. P* \( Y' @        myrect.top=150;  //文本块的y坐标8 F; Y* n( z  R% ]
        myrect.left=0; //文本块的左坐标
( R! m, P% T3 N$ [" r' a6 L        myrect.right=500+myrect.left;+ k" `5 c4 O( K" A3 _- S5 j8 m+ U* E
        myrect.bottom=100+myrect.top;
, ~% M. {2 f9 `8 E. [" B, i/ V        pDxdevice->BeginScene();//开始绘制
, v. z$ `! s4 Y. U/ F0 n& R; m; g9 A
        D3DXFONT_DESCA lf;. i6 N  H& \0 `- v1 T  K9 X! ]
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));- |6 h4 N; {, u1 z! n
        lf.Height = 24; //字体高度8 m- G9 S- {7 L, y. x9 W2 w
        lf.Width = 12; // 字体宽度
  P: f8 g# q1 p) A) C        lf.Weight = 100;
" g) g1 x$ I7 `7 {        lf.Italic = false;0 M' ]; K, u" ^+ x! j
        lf.CharSet = DEFAULT_CHARSET;
8 ?% b7 w+ Z! j# h1 F        strcpy(lf.FaceName, "Times New Roman"); // 字型5 \* Y8 B* f! K* @& k. F
        ID3DXFont* font=NULL;
5 a$ V# a0 \: a7 D% K. `        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象1 X% l; m3 s# c% [1 O' V, t
            return false;
( s2 U6 @; G/ p* w9 Q0 l; Y- F- B- H8 k
        font->DrawText(0 ~7 y" ?2 A( C$ y
            NULL,6 p6 {1 ^$ C+ x7 v: `) D( h" o
            strText, // 要绘制的文本; P! Y5 b* l  C/ O) i! _. l* W
            nbuf,
0 I8 I- o, r! B( T0 o/ h            &myrect,
: W# z1 `* c2 T* z/ F            DT_TOP | DT_LEFT, // 字符居左显示
9 O2 G4 A. i9 B, S            D3DCOLOR_ARGB(255,255,255,0)); 8 w! p/ J1 }( U; K0 v* {

% c0 u2 g: r! `) f/ B. h/ H        pDxdevice->EndScene();//结束绘制
+ R: l( S, y; ^" W& a        font->Release();//释放对象9 |8 n- U6 n; W+ s% U8 i
    }
3 C$ v# t0 H' ^' U. e1 \* U7 i- v    return true;
5 x+ n; H; M: h( G  ?8 e- q; M}
; N7 y5 u/ q9 y3 H/ z0 h1 X- L4 u3 }! j$ i. w! K* Z# Y
源代码:- @6 d6 d8 R% P( M
HookDx.rar
* L3 s* }& x8 ^- J
# A. j$ J8 N5 {3 N( D  a% C后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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