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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法
/ Y/ ]9 T! q" \( u作 者: runjin7 e; P; q6 P( t
时 间: 2009-04-03,22:44:51
( N3 w" T7 G1 b( b链 接: http://bbs.pediy.com/showthread.php?t=85368( r) R7 G5 p6 n# }

! q! L5 u; a, d2 n0 D( vHook Directx:在游戏中显示自己的文字和图形的方法
. N8 F' s3 s, R- |: V, G" K
" @+ \1 O# [2 Z( x& u5 X- H; f这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.3 z6 H! L/ H- F  K6 ~: S% L
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
( s0 Z4 _$ [' U3 s6 o还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.. E9 e- y1 T3 P# M' G5 H
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.! ]# I  _  n- r) y: s# z
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
# r' d$ W. a0 h$ R    DWORD oldpro=0;
8 ?& P6 V& h) g% b    memcpy(d3dcen5bytes,pC,5);
, N/ |: o0 a: U( O! {& x    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);( q! x0 K" A5 `& h8 R# Q
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码) V& M/ ^; h; [1 m3 P
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-52 E9 O7 l% r7 V, t7 Z1 P
4 y; m1 t* m0 R7 x$ M

8 W! q) X- c! ]& N5 L" E这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
7 \# q) _. B4 b8 LHRESULT CreateDevice(6 y/ q; o9 w. `9 Q7 h6 K
  UINT Adapter,6 C5 C7 w9 {+ T4 w* z1 B) T3 E$ R
  D3DDEVTYPE DeviceType,
0 ^; w+ k  m3 G2 c4 h& ~  HWND hFocusWindow,1 y) D8 ]  M6 J# k
  DWORD BehaviorFlags,
% i4 R+ H/ K+ y  D3DPRESENT_PARAMETERS * pPresentationParameters,
! k! S" ^# v+ H# Q6 q. f! }  IDirect3DDevice9 ** ppReturnedDeviceInterface
/ o/ \( l7 d- Z0 O6 L. a);2 }& a- O. p5 Y- R) A
# l6 D; O0 @0 g
# ^0 i7 t2 z1 {; q, o4 V8 a4 P( U
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
# u& Y' _% \. `HRESULT _stdcall hookedCreateDevice(
( }& T0 W* o  I7 R2 ^                                  LPDIRECT3D9 pDx9,4 o2 a, S3 ^& I+ H, s7 e. y
                                  UINT Adapter,, D* g" I# F# [
                                  D3DDEVTYPE DeviceType,
' ^' @2 f- v9 K+ h% d5 t$ r                                  HWND hFocusWindow,
% G1 l- `' l/ E: e                                  DWORD BehaviorFlags,
9 O/ u8 b$ D( ?+ n! c                                  D3DPRESENT_PARAMETERS * pPresentationParameters,1 G" }) f8 I$ c2 _& S/ r
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface0 V3 X  p: ]( Q& N# x

. ?2 _  m; y: S% F- s! p- J                                  );" C, h1 K, n8 I5 z3 n" S0 T

6 q7 a: f, J' c/ q9 w- k其中的LPDIRECT3D9 pDx9就是this指针.$ w- p4 ~, ]; {6 O# o  q
: ?7 l* Z/ v* @% \' c3 B
hookedDirect3DCreate9的关键代码如下:) W# P9 m4 l, H3 g3 p
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针: p0 p6 ^# S8 T# E
0 ~! b" g' ?" `% @; d& {7 a
        DWORD oldpro=0;
/ s1 z8 u1 |& N0 a# h. ?        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
, F& C+ v  @& }3 L& `        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
3 }8 E8 n2 z4 ?+ G4 X& T/ W7 j6 {        *(BYTE*)pCdev=0xe9;
. a) Q% I" L2 j: a        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
9 e& D4 a) y0 r! V
3 C  P7 R, @0 z- p5 X2 v在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:" Z" H$ ?+ k1 R% }* p: R" x! ~# U. T
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
' l4 s3 z: X3 t! ?& A% L        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节7 A9 c% F  G$ c/ H( S6 }8 \
        DWORD oldpro=0;
+ e% B# @; b2 C, r/ ~" Y5 d& U        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
1 @9 A" R) ^$ x1 K$ H        *(BYTE*)pPre=0xe9;
0 y9 [4 q/ V! j7 n' i        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;2 }- P- k6 B  d, a6 R  _6 q! b
5 g8 E0 \5 B# b4 D8 F
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
0 T( z5 r4 c; N; kchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";$ q7 }8 o0 F3 D% o& p! ?
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
! E% c- m: }8 v            //在这里写入您的其它绘图代码3 T5 T6 v( `3 I5 l7 ^: ]& M3 t' O

3 @( r( K+ d6 R2 h# K( d" K' [% K0 k1 K
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
0 l( s. d+ Z) R5 S! w) c6 G0 mBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
$ A5 L: i! B. |4 Q; h  h{. s1 K2 w$ T; Z! t; x3 i

+ J2 d1 J( g7 P9 D* E4 y    if(m_pD3D && pDxdevice){
/ ^* E$ g0 B8 Q+ ~
2 F& s' A! O3 ~! i$ @/ t1 Y        RECT myrect;
5 Q' f0 o! a( G/ \. j        myrect.top=150;  //文本块的y坐标( q5 `4 W- m% O* ~
        myrect.left=0; //文本块的左坐标
' i; }( T# W; `" c        myrect.right=500+myrect.left;( p9 R4 }+ s( ?( i# ]  y
        myrect.bottom=100+myrect.top;$ b8 T* a1 d; w# m+ x( r
        pDxdevice->BeginScene();//开始绘制( C1 h( T; ~: P2 x0 u

) ?& E; \# b9 e* W( _3 Q$ O  a& h/ {" Y        D3DXFONT_DESCA lf;+ m' E$ ~5 g* q- U# D; ?6 k  y
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));3 ~5 J# h9 `+ M: }
        lf.Height = 24; //字体高度# f' L7 n5 q" t+ Y; x4 G& x7 [
        lf.Width = 12; // 字体宽度: w( A3 @' A$ s4 N, i
        lf.Weight = 100; * x* A% Q6 U  r8 e5 x  b
        lf.Italic = false;
' I- ?/ E$ @2 R! j        lf.CharSet = DEFAULT_CHARSET;! O: }4 q. d, L3 h' x
        strcpy(lf.FaceName, "Times New Roman"); // 字型
5 `8 l0 v4 V! x, t        ID3DXFont* font=NULL;
" P: v5 Q% y1 b4 G3 M        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象. g7 j6 ^" q! P3 D- M  ]0 \
            return false;& _; X4 o: Q9 _8 e! U$ S$ L' ^: F

3 t' h* L2 X, R) [3 t        font->DrawText(
; D! R. ~- d: }& Z            NULL,+ ^  b$ T. l+ [4 d1 \7 W% \
            strText, // 要绘制的文本) `3 d) Q2 z& L! K9 Y, P
            nbuf, + X$ W/ I% H/ N1 p2 g+ D
            &myrect, 3 ]3 M9 U; O) F) e- `) N& G2 B
            DT_TOP | DT_LEFT, // 字符居左显示8 ?) e9 |! V- x/ V) H+ \9 o
            D3DCOLOR_ARGB(255,255,255,0));
, v# F) O* O6 @% p0 v( ^$ Z( m9 f. R: x
        pDxdevice->EndScene();//结束绘制& H. O& v+ I; h( M4 y5 L8 P0 E
        font->Release();//释放对象3 S2 z6 u2 [" W5 z+ b3 A
    }
/ y7 i; g- K! z- y& C6 }! H8 `+ }1 S    return true;+ _& J8 I# U8 a' d5 t9 w9 e. r5 M
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
, t+ \0 Z% a. F$ \6 Vhttps://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
" B- X. _: q* r0 X# @! h首先不能方便的HOOK所有函数,需要到处打JMP补丁
7 t3 ^' v1 x3 \另外每HOOK一个函数都必须自己去查找D3D对象偏移, q& _# }; u) J; ]8 p
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针# P. K2 ?) q; t' k2 K+ l) }
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
! [2 {; B& E9 V" O0 j; @$ |$ `* ?+ D8 q" ^
还是007那个方法好,也是我一直在使用的方法
1 d' F$ R3 [. |& R" I, C4 U直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下% [/ J7 |9 z2 k$ `4 C2 T
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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