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

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

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

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

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

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

在看雪找来的文章,和代理DLL的原理基本是一样的。  g+ Q! n8 _1 `& b

  O! c3 Y; ]6 R- }4 ~: o原文
! V3 M6 u* N0 |8 G( }8 t! X2 o, Ihttp://bbs.pediy.com/showthread.php?t=85368
6 `$ q3 s7 h8 @) g: a+ g) [( N# u* a
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
; t( p* X6 N$ q% z9 c* q0 m其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
7 d! B  E" q; \% u还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
( h" Z2 S0 ~- [# P3 c首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.0 }$ C. A' y1 O5 E; ?
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
: r  g9 J# z! s; x" n2 B! ]/ j    DWORD oldpro=0;2 H: L4 B8 G3 W# S( P/ s8 m- D
    memcpy(d3dcen5bytes,pC,5);9 Y) Y5 o8 N8 S. n' k
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
9 k/ N2 I4 ~! P; M, B/ I6 x    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
- s6 R3 H) j4 Q  a    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-52 }/ I3 {6 N; Q" U4 F/ {
' L+ Z, C! G9 G0 {6 w$ @
. k4 j/ A& `' e. O+ L2 h
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
( u* G* v# c6 M5 \3 g- pHRESULT CreateDevice(, U& y. t% p: ?/ y- O1 v" p
  UINT Adapter,3 t+ Q8 K9 c4 `/ `% s& k6 m
  D3DDEVTYPE DeviceType,; n7 g' v1 [( q/ O! S  W. f- A
  HWND hFocusWindow,
9 @9 |( E1 C% ]# c$ ~4 N; ^) _  DWORD BehaviorFlags,0 W! x3 A) V% K9 l) `  U
  D3DPRESENT_PARAMETERS * pPresentationParameters,: g" Q! V+ c" w8 T
  IDirect3DDevice9 ** ppReturnedDeviceInterface7 D8 p* ?8 o0 |7 n# {* E
);4 K# B6 G* D9 p  q" X
: p: U2 ?( u- H' m% j& ]& s) ]

! s& k7 q8 ~/ x而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:* D& a; u3 g" y9 m, a+ S
HRESULT _stdcall hookedCreateDevice(
& {$ \2 `5 r) e* }& s" x: E! r                                  LPDIRECT3D9 pDx9,
* N7 h" f" I% [% u                                  UINT Adapter,
. M, c$ j7 {3 v. `                                  D3DDEVTYPE DeviceType,7 K" B8 T% ~+ _2 @% E
                                  HWND hFocusWindow,2 u" S8 y; ]# Q# ^
                                  DWORD BehaviorFlags,
$ G/ Y3 Z9 t5 [2 l: C8 N- Q$ a                                  D3DPRESENT_PARAMETERS * pPresentationParameters,1 r6 _) b8 l* ]
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface( {7 F) T) a" X9 k6 l  l5 a2 u

+ H5 s, R/ l) S8 s$ w6 L+ u                                  );
5 f" `/ k8 w4 M. W9 G/ M! b* c
4 j1 s  M% ^0 E2 M. B" |4 G4 `0 u& ?* T其中的LPDIRECT3D9 pDx9就是this指针.
2 Q2 a  u! k& T8 J0 J' M% j" ^8 Z) ~2 j
hookedDirect3DCreate9的关键代码如下:3 o+ A$ P9 u4 C& `- t% z
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
- x: ]/ Y6 y( m: o' t; X. ]: m) [% Y+ K
        DWORD oldpro=0;# |# {9 l7 e$ G  Z  V
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
" t9 H0 W( r6 F7 q        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);! }- _* P  H6 d
        *(BYTE*)pCdev=0xe9;
' Z- R/ {% @4 s2 \* R  x        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
: r0 M1 x/ d9 M( z* x- O  d
8 w1 Q5 C7 [& q; ^8 N( ^在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:2 R) l1 k8 L, c* t' y3 @
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
8 c/ T/ U, h6 M$ S        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
' n% s7 ~! E! j+ Q5 C1 H. [- F        DWORD oldpro=0;
4 Z  b, D& b% r% G. T        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
6 J% I+ k  v$ I        *(BYTE*)pPre=0xe9;6 Z/ q( \& M! \" F' K& [9 p
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;, M$ E% O$ Y* a8 E. x

- T1 W) z  ^( W; l7 |- I& i实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:% Q4 x; L- U$ y, K2 O8 ^! ^; z
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";7 Z% J$ w7 c# P4 a' r
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
' A$ ]8 W! x. v$ W1 Z7 V            //在这里写入您的其它绘图代码
/ ?8 K+ ]! P& X: g7 d) P- ?
$ f. T* l* x2 h' N" b1 q( H1 J/ C5 l" g, y  v
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
' R/ v1 F  |1 Q7 I  oBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
, {, T# _  y3 F  J' X8 y( C{
5 `5 b+ Z4 F" V) N; w- Z+ E' S
  D9 s, ~# R3 Z' X8 s7 I    if(m_pD3D && pDxdevice){' P! Y& [" l1 a. Q' R
; K# e  N( r6 t
        RECT myrect;& `4 `! q5 `/ ]
        myrect.top=150;  //文本块的y坐标. V' q/ B# X6 u. H0 U
        myrect.left=0; //文本块的左坐标
+ T4 p7 s3 K  ~* g        myrect.right=500+myrect.left;
1 m, I$ Z( p/ P" ~: f  D        myrect.bottom=100+myrect.top;
5 z4 m8 R! t8 D# S9 F        pDxdevice->BeginScene();//开始绘制
! N& }4 v9 G* z  n0 D" H" v3 s  Q8 W8 N, {" k# [( h
        D3DXFONT_DESCA lf;- l# y+ y  |3 @6 ]! X( y' q
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
, D* }0 |& f. u        lf.Height = 24; //字体高度
8 G( h" D7 [* ^1 U; b1 R        lf.Width = 12; // 字体宽度
: X: W) D2 f0 }        lf.Weight = 100;
3 w, A3 S/ e$ {! S6 N3 ?' f        lf.Italic = false;. Z1 h: H' X+ r* y( N# Y
        lf.CharSet = DEFAULT_CHARSET;
) X0 P& w. ~+ {( @) J7 g        strcpy(lf.FaceName, "Times New Roman"); // 字型+ w3 V% m" G! Q7 s
        ID3DXFont* font=NULL;' W9 t6 S' Q/ a7 S
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
: H/ y( X" m$ u9 C# ~            return false;
5 b5 C1 S9 t; n$ m1 T
' j# o; ^. x/ f" G1 X/ S, x        font->DrawText(; W, `# ^* z+ d3 a) Y
            NULL,
' y# h+ T7 Z9 U, m9 h5 K# O$ }* F            strText, // 要绘制的文本
* _  H1 L- a9 ~  b: s            nbuf,
" Y1 n: v9 F" u) A  o            &myrect, - b8 }: a7 ^, Y. U1 ?8 Q
            DT_TOP | DT_LEFT, // 字符居左显示
$ J( i* _+ B  F( S  c            D3DCOLOR_ARGB(255,255,255,0));
' S9 a1 f$ T" ~" _6 b- z' o, [" ~: Q+ g
        pDxdevice->EndScene();//结束绘制# D$ Z* R; |* W! `' O
        font->Release();//释放对象- y0 h! z6 o2 _/ E! I
    }5 ~1 J9 l- M6 g- U# Y- g  _
    return true;" W9 l* \9 R: N% |5 J8 t
}
8 k2 o( u2 ~9 C6 T2 w. s
, q/ X/ r3 @7 p# T5 n源代码:9 l2 Z8 V" K& W
HookDx.rar
& R. P4 P4 N/ r7 t) W. e" Y
" `4 u% K) w- w; a0 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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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