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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法2 u8 @* I/ t& u4 K8 e0 t) h
作 者: runjin" Z2 }. _4 h  n# V/ ?
时 间: 2009-04-03,22:44:510 j' J. T" @% \( P$ a6 e! }
链 接: http://bbs.pediy.com/showthread.php?t=853683 T* e+ Q0 Z4 s& `% F# H

; I( A0 X. D% E. H, q8 _Hook Directx:在游戏中显示自己的文字和图形的方法7 E+ g# g- a  X- M! n

$ F* ^# e4 V" q5 m% y这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象." b& C! c5 ~  [6 k! E+ B
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.- r3 ]8 g1 W/ d7 n1 L! q
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.* a4 h" q9 V2 m9 i) b( i
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.$ f+ Y  |$ I6 v# }1 [, w0 `# Z2 o! U
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址7 ]6 k& d5 ?9 W2 X  v0 w4 I" k
    DWORD oldpro=0;
% L  [- |" h: a  S- W2 g    memcpy(d3dcen5bytes,pC,5);: ]5 f( Q# P& j! v/ t
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);+ y! X+ y9 b; }1 g6 X
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
: p# R: {; l3 b; ]2 _8 ]& `    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
7 j8 z" A) S5 i7 j. y7 f! i% K
" Z, s4 r% L* }. _# m# K* t, |0 O6 L! P' G1 |) C
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:- Y5 x4 j3 c0 b* h" }, ^6 |
HRESULT CreateDevice(5 j3 O( ]$ Y& X# D+ x
  UINT Adapter,
, w  V* `1 u9 l9 u* y, G+ b  D3DDEVTYPE DeviceType,
* O$ [. w# d$ k, t- e# P  HWND hFocusWindow,
& d8 p; i8 x* q8 w  DWORD BehaviorFlags,6 ]! {4 h! D) t4 C5 [
  D3DPRESENT_PARAMETERS * pPresentationParameters,
) d, U% y2 m" r2 p1 w% h' x  IDirect3DDevice9 ** ppReturnedDeviceInterface
7 g6 E" O' g/ F8 j, Z' e$ s);& ]! u9 B( }& d& y1 P# [3 m

: H- D" @4 a7 e1 n
7 b4 J0 J& c/ C5 w. k而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:, ]) l' I* p9 M; l: q1 p( q/ C0 G( n
HRESULT _stdcall hookedCreateDevice(
9 o2 R# d8 P3 X  v                                  LPDIRECT3D9 pDx9,5 u% L% D& a& m) l. j* f5 V, ?
                                  UINT Adapter,; ?- f. Y8 [8 \" @
                                  D3DDEVTYPE DeviceType,
5 }$ f( Z7 g. h! [: ?- @/ ]: L                                  HWND hFocusWindow,8 @) z0 I0 ~) ]3 K8 P
                                  DWORD BehaviorFlags,% o  w- }  C# E" s
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,, f9 Q3 e5 L7 Z& G7 R
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface1 }; Z- v* u  u* M0 X
0 j+ G. ^& P6 l$ G
                                  );6 G- [$ L/ k. D3 I7 a) O

. d& Y/ |/ t' Y7 V3 d0 W其中的LPDIRECT3D9 pDx9就是this指针.
- V6 ^3 A& N9 o2 R1 m) m) ^4 E
" A) C9 Q: E6 B: ^5 _hookedDirect3DCreate9的关键代码如下:
. i9 l* t  U6 N( X5 b, |0 s1 QpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针* ^5 y& T( b& v) z0 F; a
1 A! o! B2 T1 ~* @
        DWORD oldpro=0;# v9 R$ s: r; o3 K
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节2 ]9 P/ v5 C1 u4 j- N; R- N
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
- s" t7 s) A3 Y" i4 |/ E3 g$ Y        *(BYTE*)pCdev=0xe9;
- `/ b( F9 q" Z        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;9 C! {: ]& f' ~1 s' A0 B
3 r. k' {8 g9 w' U0 z9 S; {6 g
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
) T: a8 s4 O4 n& x8 mpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针- O3 P9 L/ T+ ]+ l
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
+ L7 O8 ^1 Q2 c0 \        DWORD oldpro=0;
+ T6 A6 J& P# ?5 W        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);2 \' s4 f  w7 x3 d
        *(BYTE*)pPre=0xe9;
. p4 Z6 Q) K% @1 y+ V        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;( Z- d5 i4 U- n1 P1 m
9 y( l3 K- X2 F
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:) Y# i% c, h- Q* w2 w8 I
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
& h# V5 v. B$ c8 |' @% ]            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
; `, J; Z9 @- [. u+ w7 J            //在这里写入您的其它绘图代码
1 V) i/ H5 U2 l0 q- W; h: B3 N& V# {0 g. R( E5 I, W6 l: `! D, b" Z

+ H4 i1 T! i0 ?0 `% h# U再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:6 c  d' _5 O* ]/ e
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
8 `" B# Z7 I. l- I! Q{
4 @. @! s0 @! v0 _
% B7 M& Z9 k( h: p0 m' I0 W) M    if(m_pD3D && pDxdevice){
: B4 d, O$ p8 V% G; t' r  }  P5 h8 {
( P( Z5 r  ~/ ^' ^7 ^0 t        RECT myrect;7 d% d" T/ K, J/ ~
        myrect.top=150;  //文本块的y坐标
# K) `8 q! X' O6 w        myrect.left=0; //文本块的左坐标, a$ B+ K' h& J) Z* ~6 [0 F
        myrect.right=500+myrect.left;
; \& t4 _4 g- G# H& A8 `4 b        myrect.bottom=100+myrect.top;
8 \. ?6 d# |' R9 }. X; E0 ~        pDxdevice->BeginScene();//开始绘制
$ q; y2 O" n5 C2 B8 V$ ]. y; a( n
        D3DXFONT_DESCA lf;' e7 f& |8 R2 A+ {9 a: C
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
% `1 D8 [' n: {        lf.Height = 24; //字体高度# \- m3 ~) S' F! d
        lf.Width = 12; // 字体宽度, ^- I0 U3 C' E' u, A
        lf.Weight = 100;
: m& M. H2 m# G" Q% m$ Q        lf.Italic = false;+ ?  U: p5 e4 J# a
        lf.CharSet = DEFAULT_CHARSET;
* [9 e  b7 {& |* H        strcpy(lf.FaceName, "Times New Roman"); // 字型
3 G1 [. A7 }* _* Y' v8 c        ID3DXFont* font=NULL;4 M: _0 J& [) l
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
* s" P3 y; z  O& A& O, q            return false;2 Q! i8 C# ], \, r7 I4 ]) t# ]7 o
+ Q8 F" f! F# F! @# L
        font->DrawText(
0 Q) J( o" y1 k- K  p2 e( |            NULL,# N6 B8 E' u$ c! d; ^# \" y
            strText, // 要绘制的文本
7 O# s( Q  `" U2 N$ S# G            nbuf, 7 \6 b* P) `  l
            &myrect,
  V2 m3 S6 U7 {+ X5 q% E; Y+ T            DT_TOP | DT_LEFT, // 字符居左显示+ ^# _, H5 e; B& Z
            D3DCOLOR_ARGB(255,255,255,0)); , b8 C! t* j6 w! s, t' T
! w8 W% w: G+ r, A! }  Q+ v
        pDxdevice->EndScene();//结束绘制( F4 {6 ~0 M) Y' Q$ e9 N! d
        font->Release();//释放对象" R$ p' y4 B) T3 W0 n. o1 W: m/ O
    }
* [: S/ J3 r, H1 [    return true;
- j1 ]4 o3 J) r/ B$ u; X8 Y}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
+ Z/ k( x% f3 Z% ghttps://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大% ?0 W( s5 l' E$ s( `5 }
首先不能方便的HOOK所有函数,需要到处打JMP补丁
& b1 L& v5 @1 t2 i. p8 J另外每HOOK一个函数都必须自己去查找D3D对象偏移
4 n* D/ d, S0 ^- M/ {* z/ ~pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针$ v. \" z# G( i! T
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
! O, E  {- k# ]
4 ]) u3 s( I0 p. n: J# k: f还是007那个方法好,也是我一直在使用的方法
! p' Z$ B3 g4 @6 A8 j0 R: D直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
4 y+ ^( W- v2 F8 a! [只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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