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

汉化教程 【转载】在游戏中显示自己的文字和图形的方法

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

[汉化教程] 【转载】在游戏中显示自己的文字和图形的方法

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

【转载】在游戏中显示自己的文字和图形的方法

标 题: 【转载】在游戏中显示自己的文字和图形的方法( M+ }% D8 E" i/ L( g. i: J% B
作 者: runjin+ A3 v3 Y0 g  v% L% O, }2 }8 B2 t
时 间: 2009-04-03,22:44:51
7 C2 W( K$ v/ L/ u) j  I7 F链 接: http://bbs.pediy.com/showthread.php?t=85368
* s, D& k# [  f+ h- M9 w
6 S! d+ Q7 `1 SHook Directx:在游戏中显示自己的文字和图形的方法  I' ?' L) _4 H$ h

4 J% U* K3 g+ Y" R这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
9 [. W7 V. ~0 N/ Q0 p" b其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.9 Q5 ?: U" `8 x( Z- u' E
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.9 Y/ P8 V: H7 C$ V- q5 H2 P, o
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.3 r7 D, C3 ~; x$ N) e! K
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址+ G+ f" I3 i  ~: N: {, p" M$ _! W7 t8 T
    DWORD oldpro=0;" J; O  x! }! U
    memcpy(d3dcen5bytes,pC,5);
. n5 m1 y! s2 f$ I0 A% x; t    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
" e: _4 {2 t6 F. A$ M: _% [, B5 D& B    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
. {* j6 P$ W! d% p    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
9 u. U) [- c+ A, ]. Z% S: Y; o  w& N8 y5 @$ H" J
4 M9 L8 x* v8 ?* B3 T
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:) k0 v8 [1 Q5 P( W% h
HRESULT CreateDevice(
; R: b1 g- x! S" ^  UINT Adapter,
/ r  @" \, ]; I8 J# q' T$ }" v+ M! A  D3DDEVTYPE DeviceType,
) N; p3 @6 m) m$ `3 O  HWND hFocusWindow,
2 L& I* {' v, N6 ^' T8 n  DWORD BehaviorFlags,, l) x' I' b4 ]- Z  {3 H5 ^: w
  D3DPRESENT_PARAMETERS * pPresentationParameters,& d% S: C8 P* V
  IDirect3DDevice9 ** ppReturnedDeviceInterface& M6 {2 @0 F' b" a% Z4 B  n6 F4 g
);5 R5 c) N6 ?# q
  \$ y  r, B% m1 i& a, E4 \2 P

: c1 f+ J& |3 v而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:  L" U  m8 e) ?0 `
HRESULT _stdcall hookedCreateDevice(% g) p+ a6 ?- K. e! v. I
                                  LPDIRECT3D9 pDx9,
5 f; H7 U2 x; p3 k' J                                  UINT Adapter,9 Z% o+ z' J! Z, o7 C1 c; d
                                  D3DDEVTYPE DeviceType,7 `; X8 D1 K, \# Y$ c. I: C
                                  HWND hFocusWindow,
& l* J  c0 E) P! s+ p$ L/ A: {                                  DWORD BehaviorFlags,7 @" R8 S  z# g+ p: z7 d
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,  ^- d+ V: }- N5 p+ |) C
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
: d: i* g9 V3 L# j( N. M) O0 G. [
                                  );
  i+ w8 j( g! W0 X5 W9 ?) }9 y. b
+ u1 ?- m5 A/ u" D2 f其中的LPDIRECT3D9 pDx9就是this指针.$ Q( n; X& e2 R0 W. [% n

9 v7 x" d% ^% g: S# U0 }hookedDirect3DCreate9的关键代码如下:6 G  K  Q1 t/ c5 D: q& I
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针! C0 Y5 h: t* r3 O* V8 s( ^
4 K6 J9 i* H. x! W( \& E3 F
        DWORD oldpro=0;& L7 N6 j; h5 ?5 a9 O0 @* E! m, O
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节3 o1 B- d2 x; s, S: X
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
3 y# ]2 u) A& U  a1 g        *(BYTE*)pCdev=0xe9;
9 m& O8 W7 p# F7 j  C" @7 L* E        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;, n. m9 O! c/ b# X# r& r. `: \
1 z3 R  L7 @; \- v) U$ N  W* j
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
: c9 [$ F* P! VpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
: v) f) G4 K( T5 S: e$ n5 p& w        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
# ~& q/ k: h: w, L7 m( B9 [1 r9 x        DWORD oldpro=0;
+ B" t' e0 b; a        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
. Y# g2 O: f( ~, B% ?7 l4 z( ^        *(BYTE*)pPre=0xe9;
  b" C+ Y7 y2 `; Z        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;% Y! n; _. n5 ~4 T3 R' s+ L$ s

- \# k. A) Y: i6 ^; O实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
7 v& ^: D6 ^1 H6 G- schar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";, |5 ]1 Q4 ~+ E( n1 L' {' H8 r9 M
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本7 D6 O2 c1 H! P
            //在这里写入您的其它绘图代码: M( R. S9 p& F0 {9 z! L6 s# e: T

8 c+ T# k  C* n( H6 V' K  ~) s1 ], U' ~' W  g
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:* s; l: E& S! D3 C
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)/ y! c  m( M3 d+ z9 ]
{. P: u! g$ G0 ^8 L( W
6 R# s" ?' c) \
    if(m_pD3D && pDxdevice){% M& v0 `$ B$ h: b1 Y
5 \4 N- E& b# |7 n+ E% ~
        RECT myrect;1 u5 o$ u  n4 u: q- l' H9 ?& ]
        myrect.top=150;  //文本块的y坐标! Y7 H) q1 e8 F3 i
        myrect.left=0; //文本块的左坐标- }. f8 N3 `% t9 W* `  A0 k: [. L
        myrect.right=500+myrect.left;
/ I/ G/ z2 R, t, d4 p( Z0 d        myrect.bottom=100+myrect.top;
; K7 X. j8 }' E& ^2 U. F4 Q% D        pDxdevice->BeginScene();//开始绘制
4 B/ p" }1 p! p! [7 r8 i# M  r2 E8 U$ T, w) b; o6 [
        D3DXFONT_DESCA lf;
* j" Z. [) z: d% d        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));$ f; N& M) b$ E/ [2 R
        lf.Height = 24; //字体高度4 I' C9 ?0 I* n3 d( t& z5 |
        lf.Width = 12; // 字体宽度
  z+ e6 R' E' q) w5 h) T, b& i        lf.Weight = 100; 4 z" |3 d" E7 q6 a5 m  l
        lf.Italic = false;( l7 z, m3 r8 d0 p2 X. z% a7 p
        lf.CharSet = DEFAULT_CHARSET;. }9 c" R, x* i
        strcpy(lf.FaceName, "Times New Roman"); // 字型- `1 K1 y) F( Q5 i) g
        ID3DXFont* font=NULL;" }% A9 x" A+ }, |) x' h
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象4 A4 d' ^$ |0 A# K
            return false;3 j8 d/ j9 |- w, z5 `8 l

* m% O/ D0 ~0 s2 ?. ^0 J% L        font->DrawText(
( }8 N: r$ d8 o            NULL,0 o: e1 v( U: @! ?: G8 {
            strText, // 要绘制的文本
* e0 j  ?* S+ }; a3 P            nbuf,
* _$ y3 e0 `; q3 k" z9 I/ O            &myrect,
: ~4 N) w; D6 y1 C" \) z+ N& ]3 j            DT_TOP | DT_LEFT, // 字符居左显示
9 k% c* L) J1 Y& s$ p9 _& j            D3DCOLOR_ARGB(255,255,255,0)); + c, y9 d7 D. {0 ~2 g2 I( ~
+ K* h' D+ d) j4 ?6 U, A
        pDxdevice->EndScene();//结束绘制, Q/ i3 F" [" B7 E+ b
        font->Release();//释放对象& P; R3 k- P2 I6 K. f7 T0 J
    }
9 U& T5 I, d" t8 I    return true;0 U' g0 l9 B  s) `
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。# V4 p, x! i2 W4 ]- U
https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
. s3 M/ Y; K  K  F首先不能方便的HOOK所有函数,需要到处打JMP补丁" h2 h* H4 l6 I6 ]1 r) S( `
另外每HOOK一个函数都必须自己去查找D3D对象偏移
8 f5 g( c2 H0 f; V; |# r& |pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针6 Z7 `  j. j/ b
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下* i1 K, m9 f1 ^9 G; K9 L
& p# S& S$ h' q5 V1 r) u! O! \
还是007那个方法好,也是我一直在使用的方法
' b7 m8 z3 @, ^直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下; X3 K: C* r& G5 d0 X
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

冒险解谜游戏中文网 ChinaAVG

官方微博官方微信号小黑屋 微信玩家群  

(C) ChinaAVG 2004 - 2019 All Right Reserved. Powered by Discuz! X3.2
辽ICP备11008827号 | 桂公网安备 45010702000051号

冒险,与你同在。 冒险解谜游戏中文网ChinaAVG诞生于2004年9月9日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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