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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法
0 j/ T2 E  R9 u作 者: runjin: c; _. B. `5 a3 Q
时 间: 2009-04-03,22:44:51& Y$ p( ~7 R: w) f! |
链 接: http://bbs.pediy.com/showthread.php?t=85368; ~3 [$ K3 r( c) S' H& P( q2 ^
' U3 c" Y+ B4 s8 k
Hook Directx:在游戏中显示自己的文字和图形的方法
" q+ _9 t$ r4 L* X% h+ k$ r* W2 C. P; b* O
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
( M1 z, ]2 T  j4 {其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
' P7 k( ~9 Y+ r/ K4 W还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.; |% a/ b: W9 d2 ]+ |+ T" B
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.3 ?) a+ W* O: V5 ~
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址, B1 Q2 F9 F$ W
    DWORD oldpro=0;) Z: ], t  P3 q/ I: u- j: b
    memcpy(d3dcen5bytes,pC,5);) p" Z: m+ o% Q1 f
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);5 a; l# R! U" ^2 d2 S6 C
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
' w7 Z3 B7 [: W# X/ X2 ^6 |- B    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-50 e! Z  k, p# x5 b; {# l

7 ?( e7 ~' L/ Q  R, V8 F# y7 b- J4 n7 S( Z& q- ^- G8 I
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
5 u. i9 u1 E, d& [7 y! {3 oHRESULT CreateDevice(
4 f; O' M1 \* [9 c7 ?5 p  UINT Adapter,5 ^5 T, A" k' B% X, S
  D3DDEVTYPE DeviceType,/ e! v; G( `1 D. N6 m! T* c" K( F3 y
  HWND hFocusWindow,
7 @' {/ `8 T' z% w: ^  DWORD BehaviorFlags,7 w" ?  a6 k' @; B
  D3DPRESENT_PARAMETERS * pPresentationParameters,
  `: x" G0 E9 G1 U1 e  IDirect3DDevice9 ** ppReturnedDeviceInterface
; {- s5 x' q  b( D  k);" H' [# Y  l1 c# N6 f
7 L5 g, u/ h& g* K9 A9 M" }
: s# z' `- v3 t: a; Z/ a6 ]
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
) Q) t: O$ P0 ~5 x5 ^6 ZHRESULT _stdcall hookedCreateDevice(
3 ]: M6 e* D$ B- s3 V                                  LPDIRECT3D9 pDx9,, |8 Q5 G' s& Z7 W' e- u" J
                                  UINT Adapter,/ y# M$ u5 M) i- H7 c  `
                                  D3DDEVTYPE DeviceType,# z* i6 G$ m: z- q
                                  HWND hFocusWindow,( e3 H' P, C1 P1 u8 R
                                  DWORD BehaviorFlags,& v* n2 P, n0 U" x8 F$ u
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,& S8 U; o! u' F& i0 i7 {+ b4 c
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
: _/ u: ^2 c% a4 W7 S% `
/ l4 K5 \6 _; A! p3 d                                  );! _' h$ @3 }; G  _- a7 D7 Y- \

, S0 ^' |2 J+ X# ?' I7 @其中的LPDIRECT3D9 pDx9就是this指针.. o& s+ S$ g, K8 F+ Q" }
+ [0 P* ~# f$ t* [( w  N% W% w
hookedDirect3DCreate9的关键代码如下:5 L8 l/ z0 [; W4 S0 ?
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
3 n+ V, V: t$ U. O9 Z4 K" t  Z
        DWORD oldpro=0;+ h7 }$ V8 O8 c1 x2 [0 u5 _) a# ]
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
9 D( H8 N0 G  P8 G1 Z0 M  Y        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
7 S2 j# G  b* C' B        *(BYTE*)pCdev=0xe9;* y  ~- a& L; R# N% T) j9 A
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;. k$ }0 N1 }6 y! O

# Q1 E; l0 D7 G在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
- c1 ]% d& `' d8 d, m/ EpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
5 K+ S( o! ^4 X0 v. S, t% b3 T7 `        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节+ e/ y3 R3 `: e: b$ w$ h, _* c
        DWORD oldpro=0;
" L' ~# r% G! I        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);5 T* G0 S7 R( H% {. P3 v
        *(BYTE*)pPre=0xe9;# h. Y& h" p$ Q4 p+ D: ]
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
" v( s. f+ t; J  i
) l/ K: g% q* J; }+ T* q实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:; q/ `, C* a, {7 x
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";' J/ Y5 ]! Y& p7 T0 W( B! B
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
: m( Y3 ]) ~$ P2 J# P. z/ O9 K( Z            //在这里写入您的其它绘图代码
0 g8 j2 ]% ^/ D" ]$ k  S. {, ]' I, C' ]: d* y* N

0 {6 s# {& ?: Y( _再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:5 V: q- ^/ g2 }+ _! i
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
4 N* M- u+ j- t1 D: i& x" s{- H& K; r" p! c( R9 w" |. z
3 x: [4 A$ |( m' q. r- x+ [. l
    if(m_pD3D && pDxdevice){' Y$ ~3 j  q# ?# H
/ \0 f& q  F' u
        RECT myrect;; f! A/ h( r6 L  X" |3 v# ~
        myrect.top=150;  //文本块的y坐标
$ _$ T' O. q* z* y7 y8 O7 s  a! J) X        myrect.left=0; //文本块的左坐标
4 }" u$ @9 G) ^. U; v  [- P        myrect.right=500+myrect.left;# Q8 L: f9 ]6 a9 k! M3 g5 u
        myrect.bottom=100+myrect.top;
" {$ B, z; `& t! F% s/ C( y        pDxdevice->BeginScene();//开始绘制, O' o, j( L. l6 g! ~" W# t

1 O( e, v  C2 h        D3DXFONT_DESCA lf;
' W+ w' o; j! z* \+ R( k        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
8 H4 y. g* g0 I& r- W+ K        lf.Height = 24; //字体高度
# B6 R4 t, V6 L7 w" D        lf.Width = 12; // 字体宽度! i( W6 P/ b- S7 ~8 h8 m* I
        lf.Weight = 100;
' f7 f; X5 x5 \% T        lf.Italic = false;
4 `* N$ {' p$ c. ^/ `% g        lf.CharSet = DEFAULT_CHARSET;2 s8 T0 Y/ q3 X
        strcpy(lf.FaceName, "Times New Roman"); // 字型
0 j$ }- ]* I/ ]) Q7 x        ID3DXFont* font=NULL;+ I' E& k( A0 P" {/ d5 S
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
% ]! E9 \/ \* A9 p            return false;
/ Y/ Z, ~( I; h: A7 U, o! s  T
3 N( @2 K9 l* h4 f        font->DrawText() N- A: y  ^& B# p& Y. ?  `
            NULL,
% @9 Y( J! v( @" x% I* H# x3 I            strText, // 要绘制的文本  h: M% P% w6 |
            nbuf,
" j1 r* G  K5 _            &myrect, 7 i$ k' N! A4 P6 f: e0 g3 @0 X1 \
            DT_TOP | DT_LEFT, // 字符居左显示
8 _1 Z: {( ^2 J/ W3 e+ U            D3DCOLOR_ARGB(255,255,255,0));
) P( h6 w" s, R' [3 J0 h
& G3 R3 M0 B$ G) ~2 ?. p$ E2 [( B7 P        pDxdevice->EndScene();//结束绘制9 Z8 T2 g5 h1 m& \  A% h
        font->Release();//释放对象% J/ p5 H! e* B2 B$ Z+ X; R4 g
    }; d( g3 m6 u8 x+ e/ U; Q& R) `
    return true;
( G9 e! ?7 o7 B: P}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大5 I4 K( \" _. ?' l7 ^- F$ h  h' E
首先不能方便的HOOK所有函数,需要到处打JMP补丁5 T" \. ^/ E/ h
另外每HOOK一个函数都必须自己去查找D3D对象偏移3 g. {! s/ ?: B7 Y9 l
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
9 \1 v# z$ C* Y比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
+ k( U9 u# e2 l+ ?8 v" O: u, ~* \* z% N. K
还是007那个方法好,也是我一直在使用的方法9 }- J  H  R: C/ X8 k9 |1 \* Y
直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
: ~3 s. ^; z8 A3 O只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。/ D" n: r/ L) K/ o
https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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