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

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

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

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

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

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

在看雪找来的文章,和代理DLL的原理基本是一样的。
7 D/ a5 v2 t+ i) Q
+ K: O8 y5 x" f- f, p原文4 C$ N. @  U3 ]+ J2 O. H
http://bbs.pediy.com/showthread.php?t=85368
5 l' D% D2 a% C8 A6 h
, A2 h# @, ?8 ?9 ^- c这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
1 E1 ?) ^& `# _# r. F' _3 u' |- O其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
; n7 |* X2 d+ M- u1 D# C! S* N还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.+ k& M* D# i3 \9 t7 z
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
0 d3 A9 s! t' m; c1 w+ d+ j2 K: x! A  ^9 i    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
7 s, j2 x2 m3 g5 _: @    DWORD oldpro=0;1 Q( h; P2 S5 |
    memcpy(d3dcen5bytes,pC,5);
- H0 k- M6 A, ?' w    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);: D! ~3 G0 b/ I+ S8 U2 \
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
3 M/ H3 z# ^- T) y4 @) D    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5# S+ k' {- ?0 G7 v
/ Q- z/ x0 ?2 i* V& h( e/ G

8 `" Q1 _- E' d3 L这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:1 ]0 k0 R1 ]" r
HRESULT CreateDevice(
, d' F. B/ h3 }( Y! g* G  UINT Adapter,) B$ ?8 R# Q1 l) [. o
  D3DDEVTYPE DeviceType,
0 A0 j* V. F: O, F7 Q+ Z2 Z/ K  HWND hFocusWindow,
: r! K& _& ]( t; T4 P# h. t4 k  DWORD BehaviorFlags,
; s) L! m" u9 |  N  \7 R5 U7 I  D3DPRESENT_PARAMETERS * pPresentationParameters,& i8 W7 e4 n) e# u5 W' f2 |
  IDirect3DDevice9 ** ppReturnedDeviceInterface
1 k( O- L3 J8 X4 K& G2 {);- X4 ]9 h) {5 O) {
! h; \, ?0 Z! e* Z8 c
$ a! o# M5 X, y( X& \
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
; C$ F  {" y3 W* I- AHRESULT _stdcall hookedCreateDevice(6 r2 Z( \0 b+ m$ ^3 v: g3 z
                                  LPDIRECT3D9 pDx9,
+ H6 m/ o: ]7 t4 }+ {& k                                  UINT Adapter,
! P9 A: I& W$ L& n8 q/ f                                  D3DDEVTYPE DeviceType,
/ k. |6 G* K8 r$ e8 j                                  HWND hFocusWindow,: D9 V; V6 c: `* z# [9 y
                                  DWORD BehaviorFlags,
2 ], N( c, S1 `6 ^% h) s+ _                                  D3DPRESENT_PARAMETERS * pPresentationParameters,( K  @) ^+ f) g" G+ e: |% _- Q4 O
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface4 D5 V3 Q) e! e' d$ f2 }" T$ w4 G% v

- |) P& T- x' |8 H3 H& @; ?+ b3 l( t                                  );; K! v2 R6 @8 j  L. C) k) J1 s

# M2 B8 O" E# u1 T其中的LPDIRECT3D9 pDx9就是this指针.2 |: ?0 u3 P% b( E! e, H
) x& o* \0 K3 g* \. j
hookedDirect3DCreate9的关键代码如下:
5 T- |' g1 o7 BpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针) `4 J! n8 v3 ~5 H' J! _0 I
+ V$ n6 u! s( a) \
        DWORD oldpro=0;( B$ _8 v- x  j
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
$ @' O3 J- @, i0 Z9 z: r        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
1 C0 f' K, @6 N, g1 a        *(BYTE*)pCdev=0xe9;
- N1 p2 S: l+ l* o: D2 }3 |        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;/ o6 O) N" B) Y) ~- V! a2 e  A9 D

! M8 y* l/ j1 x' B; {在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:3 o8 E5 d5 i6 f4 H
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
9 A, \/ X# D3 T3 {  f! {+ v- t        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
- R) g4 M$ Y$ G4 ~        DWORD oldpro=0;% u7 g1 T& p  w% R
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);$ k- @8 k6 L/ V# L- N, u
        *(BYTE*)pPre=0xe9;
1 m. @! ~2 W! k, i' [$ \# l        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;8 C/ T) e4 b5 j" f  d! a
7 p8 z/ [+ G# _
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
7 n: H3 t" h( J* T4 Xchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";+ a% n; v7 y$ s3 O1 d
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
* G9 W: E( _( P  g, q& p& b9 w            //在这里写入您的其它绘图代码3 m4 R2 [$ G3 y5 G1 |- R

; ?, ?( S3 E2 G% s9 u
3 E7 I$ |. `$ i4 _7 C( i1 B$ Y5 @再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
% m  g$ v: n* |) Y/ Q* BBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)2 r  |8 v& j7 C& X$ R
{
8 N2 R. o3 I7 `& a2 t
  r4 Z) t& M: S, p    if(m_pD3D && pDxdevice){
! ]  R" m0 {+ \0 }/ G
9 Y' z6 u" M: v; w        RECT myrect;
* e* m8 X2 n) _# o/ v3 t        myrect.top=150;  //文本块的y坐标
! W+ p' A, e# B1 x7 a        myrect.left=0; //文本块的左坐标3 ^: {) R. T" x) R8 J+ E1 W" [
        myrect.right=500+myrect.left;
$ n& \& l- u7 f8 K        myrect.bottom=100+myrect.top;7 B0 y2 ~6 e' e+ z1 |
        pDxdevice->BeginScene();//开始绘制
. q) X7 u2 o8 U) b
6 ?* k0 M1 m. H# v        D3DXFONT_DESCA lf;( p) f9 s0 n2 f  `* q/ a; Y
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));/ U/ @+ c7 q2 t. i/ n& M' d
        lf.Height = 24; //字体高度
( V! A7 |3 P2 o: y) H        lf.Width = 12; // 字体宽度& T6 g& V( i4 w/ w6 F4 Q
        lf.Weight = 100;
$ @5 b0 q( ~4 V/ `+ ^        lf.Italic = false;/ N% T6 A+ K4 F0 `2 X( H
        lf.CharSet = DEFAULT_CHARSET;
8 w# [7 n1 Z0 _1 R, H        strcpy(lf.FaceName, "Times New Roman"); // 字型
# d1 Y9 T$ E1 B/ ~5 O) F        ID3DXFont* font=NULL;5 O- ?3 r, e1 P& J+ a+ v9 `
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象) g  u% w5 u, M0 A; q
            return false;
  G1 |. C. I3 R8 F! q: q
2 M1 S/ J* |- w" f; L        font->DrawText(4 @2 U5 U  M& ]  F
            NULL,
& j& B) W) z7 n9 y0 ?  b& s            strText, // 要绘制的文本
2 _$ o9 M" |# t" [            nbuf,
7 I3 D/ e1 c. p$ M. f            &myrect,
4 K0 G9 D2 Z) O$ ^2 {            DT_TOP | DT_LEFT, // 字符居左显示
7 A( j7 u% q% ]% R8 r            D3DCOLOR_ARGB(255,255,255,0)); 8 ^/ s" p! P% O) i+ _
- c: O# W  H* c3 n
        pDxdevice->EndScene();//结束绘制$ V0 x  f6 \; C' d: `
        font->Release();//释放对象
/ z# i- J! b) b    }; r. H/ X; d# q) M; V: o
    return true;( _! D, c/ T4 v7 T/ ^/ _4 |0 U
}+ X7 \! Q; D$ v* L  p' `% [/ a

1 j- s- D# l7 Q3 n0 ?. O源代码:  i7 A0 X+ H+ A
HookDx.rar0 P# ?, N8 P7 E0 O' e8 ?

9 S2 ~0 Z/ }! F2 b3 N, M' `/ B8 |9 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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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