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

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

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

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

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

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

在看雪找来的文章,和代理DLL的原理基本是一样的。
: ?0 l* e+ s' r8 H; l' W$ i- f, R) g; K- C
原文6 Q% v" k" u5 i) b/ @; Z
http://bbs.pediy.com/showthread.php?t=85368
0 S7 Q& |! K$ Q3 ]2 r% \9 y4 v" Z+ v, C" i0 k7 U0 w% X
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.1 H- t# V, S% M$ v4 v: N. f3 O8 B# z  h
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
7 G9 e5 M4 [0 n0 a还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.5 u6 f0 e9 b4 G7 t+ |' f0 Z
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.# G/ O+ S; u9 @- D1 D
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
/ C4 A$ c+ E8 l  ?    DWORD oldpro=0;
0 c: d% j0 v  |, I/ x, Z    memcpy(d3dcen5bytes,pC,5);4 i$ K6 t7 y- r5 Q2 a$ M: h
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);+ A+ H9 g  ]2 }" M
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码: V: u" ^; C: Z/ G/ W$ ~8 ^
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5. M4 a! q$ y  L. M# m2 f% p: l

) m: d6 @/ L" W- K3 i& y2 _5 |- B+ U% y4 ^
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
, Y- }/ Z  c- f" x7 a) qHRESULT CreateDevice(
5 t$ }2 x! N* w  UINT Adapter,
6 i# o. G% g' q4 D  O  D3DDEVTYPE DeviceType,
/ V# q7 R3 Z2 n5 q- x! s% {  HWND hFocusWindow,- K( h. N* t/ r. ~3 ?
  DWORD BehaviorFlags,
6 d( f" ^8 w4 D7 _, P* s7 W  D3DPRESENT_PARAMETERS * pPresentationParameters,
$ P) n* y( T6 ^- b  IDirect3DDevice9 ** ppReturnedDeviceInterface/ O. P4 j/ {# V! j) v
);
9 v" L5 v# I! I8 o8 y. L- d  @: {" ]( G3 _
8 m) e1 E  o' q3 s
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
% y. G9 c6 J3 R1 i1 WHRESULT _stdcall hookedCreateDevice(- |2 d! @4 [9 V$ E+ }7 U
                                  LPDIRECT3D9 pDx9,
7 o: C4 B6 v2 B* A( S2 y+ |8 t/ `' `                                  UINT Adapter,5 L; O, v, V9 M; `& q0 M# W; Z
                                  D3DDEVTYPE DeviceType,# d* c- w" a9 s7 B
                                  HWND hFocusWindow,
' G6 F2 m: M3 K9 \! u) w                                  DWORD BehaviorFlags,
. |" K3 R. K* s3 q  x8 G. G                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
/ S5 o5 M% C  F: T- J                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
' N  D- T3 S9 u' e, B+ u9 {8 M3 Z, w
                                  );7 M2 ~/ C$ k( D4 j7 b6 S

* T8 W9 E1 e4 s其中的LPDIRECT3D9 pDx9就是this指针.% P$ y+ n" i5 w1 }4 ^
- j( _2 ^  }" d  E* |  H
hookedDirect3DCreate9的关键代码如下:
: j' G8 R1 `, a* R3 c, TpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针3 k' M+ D- _* l; V$ R

1 R( Y& z4 a) |3 k: x        DWORD oldpro=0;* ]5 b7 L0 l8 z2 S1 f
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
$ L0 W5 c4 c* ~( u: \1 k        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
! {* p& F7 h2 Y' f! y  `        *(BYTE*)pCdev=0xe9;
* W1 ~6 d. f# l. `% g        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;+ t; F2 R2 e: p4 p/ c1 S, U
# T" b. _2 v: \8 U1 E
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
. F! d3 j" {6 M3 O! T& d  FpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
* \: W3 z/ ~7 J) S- X/ L3 a1 ~        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
" T0 x( z1 m. U( e! v        DWORD oldpro=0;0 e$ @( k( T2 Q" I1 i( @
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
  p" }9 m! u6 A$ v& N2 d2 E        *(BYTE*)pPre=0xe9;
6 K7 L. @% |# ]6 ]3 o( v% C7 N        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;& X; F! k5 y; M7 E1 }. G
* f. F2 {- z9 i' h  @. ?: T
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:3 Y9 S: h- U- P9 e
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";! x' Q- ~# Y* f0 W. K+ A+ a
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本9 U2 S$ w: Z; N3 g- s# s) s1 t; ?
            //在这里写入您的其它绘图代码
: e0 s1 s2 U) F9 k5 Y) `& @* ?- {7 l/ I# m2 k8 h
9 Z' n% Y: a* S3 S5 ~! O+ k1 m+ J! [
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:4 N# |) e7 M. Q# O
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)7 l8 l5 T% f, q! E
{) p' ^- J5 ~% m" `% l5 `
! L$ ]- V& J/ q: M
    if(m_pD3D && pDxdevice){& P) l8 Q# \. U5 A+ h- e' U  X

0 N0 \" m9 N7 @! b3 F2 H6 K+ \        RECT myrect;
  J* E5 [3 [( o" `( Y        myrect.top=150;  //文本块的y坐标1 m( G# b& o$ y+ |7 S% Q
        myrect.left=0; //文本块的左坐标
$ o. ^9 K& H8 ?        myrect.right=500+myrect.left;
' N* n2 e# R; M        myrect.bottom=100+myrect.top;
' P# P. T7 v& R& H        pDxdevice->BeginScene();//开始绘制/ g* G! k. @& Z; j! W* ?, I, a

1 I% N  _! M8 @5 V% ~( `! @        D3DXFONT_DESCA lf;
- f4 v2 r6 J3 F, A! F        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
& M6 O7 O( a" [        lf.Height = 24; //字体高度
+ `% k9 [5 v) S7 X' S        lf.Width = 12; // 字体宽度
( b, r8 c9 z2 y. L* o" ?0 \, s        lf.Weight = 100; 1 D! E# k# b" p  `
        lf.Italic = false;" u0 b1 W; t' G4 c, d0 i
        lf.CharSet = DEFAULT_CHARSET;
& g# Y1 q7 `$ i$ G, R        strcpy(lf.FaceName, "Times New Roman"); // 字型
) I$ V8 E& }4 c' A. ~# k# w! l        ID3DXFont* font=NULL;
8 Z( p, V9 g' f: @& V. Q        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
4 Y3 ^) `: O$ [8 \7 [1 O) ]            return false;. b' q8 ~3 T" W$ f( }
9 }( S, [4 ?  g1 {$ T
        font->DrawText(! H& F4 u+ ^9 ^) _, R" x, i7 R0 Z
            NULL,+ ^9 d/ Y( V, v, c3 l! y0 s* t
            strText, // 要绘制的文本4 H. G7 _5 n8 E! c2 {2 C/ R1 B
            nbuf,
& Z$ s9 e. o* B0 [7 D9 _4 H            &myrect,
2 z2 n& `! d4 Z" z            DT_TOP | DT_LEFT, // 字符居左显示) d+ K+ Z, U% L- t% C9 Y4 s) X3 ^
            D3DCOLOR_ARGB(255,255,255,0)); . G1 m! E8 t, G2 n3 p
6 Y( p" L- q8 F1 \
        pDxdevice->EndScene();//结束绘制
; K5 z4 n: D3 h& d        font->Release();//释放对象
+ s  a# h4 B0 b. J    }. S3 z) |3 A7 `; Z$ M: |8 O$ z2 @
    return true;! U7 c4 i1 O2 T5 a6 q* ^
}; s% `% \" ~$ w+ v, K
' ?# \) Z" V+ |& w& {
源代码:
* c' X$ t9 D7 f, ^HookDx.rar
# A1 d' d  J; }, Q' B, ^8 Y
! b3 _: F" J$ P' N后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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