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

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

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

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

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

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

在看雪找来的文章,和代理DLL的原理基本是一样的。8 A2 v1 x% j. i- ?0 C+ R9 T7 g. Y

* _+ ^! _+ ~8 k; o9 `. I; T, x原文
( Z" @4 `- x) ?& P) o- U. a& bhttp://bbs.pediy.com/showthread.php?t=85368
' U$ v' |6 z9 ^- [$ y7 }6 S* l5 m& X; I& E
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.8 b1 F$ f$ ?* J% [6 A
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
; B& P+ t3 D* [8 Q8 O+ q% s还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.  ^' L8 _# }. X6 K
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.: [, t9 P7 U- C; M$ `" N- j' i
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址8 ]: _. m( M. k, P  I
    DWORD oldpro=0;
+ Q$ ^) D# v! K    memcpy(d3dcen5bytes,pC,5);! c4 s/ E% X* |  o+ O/ V3 W
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
" {+ c$ S- F8 x/ }1 s# A# i7 P    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码+ {4 z' _8 O2 B  i" t) x- w
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
5 `: r: N; G, r) U' Y/ V& p/ v! D* N4 C- N. ?* Y$ {
; R3 _$ j( N0 O9 G: Z
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
, J+ f1 q! Z. z0 IHRESULT CreateDevice(
* t: \& G" Z; V0 }. b  UINT Adapter,2 Z* D5 R5 W0 y+ [
  D3DDEVTYPE DeviceType,3 m* _2 \* z- G5 }  Z7 B
  HWND hFocusWindow,2 s2 I9 H! r( T. n* U! Q6 o
  DWORD BehaviorFlags,2 X9 R" Z- y0 g4 \# F% a" v! y
  D3DPRESENT_PARAMETERS * pPresentationParameters,4 Y$ p8 h) l* S$ [- ]
  IDirect3DDevice9 ** ppReturnedDeviceInterface: ~3 ]+ ?/ d8 ~( H4 ], q( l. |
);' A5 e- C3 J% c* D. d

. h. M& T% _/ Y- ~" b0 I
4 L8 o- k( p) |! m7 _2 [而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
4 Y' }% v% y7 r7 O) z- i% CHRESULT _stdcall hookedCreateDevice(1 l* ~7 W% C1 z
                                  LPDIRECT3D9 pDx9,
, o1 r, U4 ]9 \, I0 i3 s( ?; V                                  UINT Adapter,7 e7 F0 B  D, I6 K5 _
                                  D3DDEVTYPE DeviceType,
0 n4 H# N8 N$ ~                                  HWND hFocusWindow,
# |" u: b' \2 w- I; F                                  DWORD BehaviorFlags,9 ?4 ?' P7 H4 T4 f  ]
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
( I7 a* H3 Q" e                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
) Z+ J$ y0 }7 J
) _" C2 F! |0 b$ H6 l1 `  J                                  );
7 W* Q- j8 c3 ~1 Y7 u+ [: q$ @* F( q: O( Q
其中的LPDIRECT3D9 pDx9就是this指针.) b0 h& B* ]7 v& m' t

* }6 @: }* @9 U1 a" M$ fhookedDirect3DCreate9的关键代码如下:6 M" c$ H& _0 l7 w- }8 c( b
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针8 ?: H/ T! O! r6 z# i
4 `+ M* ~" t# }, V8 r5 g
        DWORD oldpro=0;$ J* V6 B2 m) V* J3 m' g
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
1 |6 C5 |, F( A: u$ \        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);3 l$ t# \0 Q) \* l' y' f) L
        *(BYTE*)pCdev=0xe9;
. m* B" w, Z" |$ E) Y0 i* a. X        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
1 G" H( j5 ^) H9 B+ {& J7 E1 z8 u- Z9 j
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
5 I& v  l2 G" b6 A9 NpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针: N1 \4 g2 D+ X- Z
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节( K/ v  i- Y/ @! _% u6 o
        DWORD oldpro=0;
% E0 B/ [) f6 |) O9 o7 b0 ^% a2 Q6 A        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
& k* r5 O. @7 V: x        *(BYTE*)pPre=0xe9;
! a! s0 s: d  y  z  K# Z7 K        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;3 g& e! Q  v+ j- W, Z- w# d$ O
7 I5 D9 y, s4 b* F. U7 j
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:& [9 J# Y  R- I! l- N3 C% Z0 y8 z# |
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";$ z; k5 m7 `8 b, U: q
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本2 D/ F8 j, |( e5 Z# E9 `
            //在这里写入您的其它绘图代码! b' ~# o3 u# ]
% g  T  P" E( Z; t7 C  I1 Y6 H) `% y
+ R# l' B4 `  ]/ X) f" Z
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
" q) z; `! l5 H% l0 X6 YBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)# w/ O9 H& ~2 `
{
" K* T4 O0 l" T( J0 V" k* \
# x3 i$ z2 Q% w1 H, P- U$ z    if(m_pD3D && pDxdevice){
! p+ s0 W0 U- t# Y' @, D6 E& b* q
) O* E9 _' A( a0 A& r& i% J        RECT myrect;
6 z0 B% \( K/ ]: J% ^        myrect.top=150;  //文本块的y坐标
) p+ b" `/ a1 d4 D" z: z) c, O  y$ M        myrect.left=0; //文本块的左坐标
( ?" S7 J  j) J6 e- A        myrect.right=500+myrect.left;
* m( ?* W+ ~5 Z& }/ Y& W8 Y        myrect.bottom=100+myrect.top;) e* n. V: e5 W& s$ P* d
        pDxdevice->BeginScene();//开始绘制. O  u# G5 V+ j7 u$ B9 `

" g- q0 t; Z) z        D3DXFONT_DESCA lf;4 h8 o3 x& k# _
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
$ z: g6 L: Z- Z3 b: R        lf.Height = 24; //字体高度
! Z, X  K7 U: w        lf.Width = 12; // 字体宽度" {8 `- `; r% u9 Y" d, ?
        lf.Weight = 100; 3 B7 G5 u# l: L
        lf.Italic = false;
" p" n0 M7 ]7 T$ h) t, w        lf.CharSet = DEFAULT_CHARSET;
$ a+ X' ]) |, ^. x# C/ d# `! p        strcpy(lf.FaceName, "Times New Roman"); // 字型
# W/ @2 i/ G, K5 K- J        ID3DXFont* font=NULL;1 I5 R' k% I( l
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象' m& ~, K* Q+ q" m' Z, F, h
            return false;/ b, g) h' y. ]' V5 s
, g1 K) u! w" ~2 y9 h. |% [: c
        font->DrawText(/ B0 y; ]( f  u8 ~2 {; i+ X
            NULL,
% Y' }+ Z" H8 c5 S* b5 T: L            strText, // 要绘制的文本
( b8 n, N. }+ W            nbuf, 8 E1 ]# o7 j" S5 B) Q! ^- A
            &myrect, $ a4 T0 d7 u6 u+ M
            DT_TOP | DT_LEFT, // 字符居左显示! {) {! V3 y/ @5 Z
            D3DCOLOR_ARGB(255,255,255,0));
! J: L, A. C  g/ {. ?) V
- u7 ]5 M) ?1 U8 R" y# f7 t        pDxdevice->EndScene();//结束绘制# T' b) M3 l6 H$ @- n0 F% q
        font->Release();//释放对象/ S4 K* P3 M* r+ L( M( A
    }, P( A& r" b5 ~8 u
    return true;0 x. u8 W+ P" Y+ w+ s& b( }7 o
}
& p8 ~6 X+ `( ~. }" D' F. r2 g9 }8 }6 {8 w& N9 [
源代码:
' o7 z- w$ a6 i( x7 R0 SHookDx.rar
" R7 o6 y! K1 ^! M
, B9 E4 {) a4 P8 z- }后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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