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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法3 h# ^5 k) h7 W* B& m3 ?% p
作 者: runjin
& n) ^2 ^. ]2 h时 间: 2009-04-03,22:44:51% S4 g4 ?9 S" ]" p
链 接: http://bbs.pediy.com/showthread.php?t=85368
7 M- l, o3 i1 C9 ]- p2 j% I6 ~1 ?; V$ ?! Z7 B( X8 ?
Hook Directx:在游戏中显示自己的文字和图形的方法
! f; }, ?5 u, p$ t& A: O  G* L
3 h2 I3 n' P. a% |. ]这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.0 w4 O: g4 z0 t- _( P
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.( h4 b8 l* \( d+ C# N/ u
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
5 _* \$ {. @% K9 X& o. C首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.2 D# f' r' G$ d: L% q9 a
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
, M3 x8 S) }5 ?! e. E( U    DWORD oldpro=0;& D7 P! D. m: J+ Y, Y: N! I
    memcpy(d3dcen5bytes,pC,5);1 x7 C( k, }; f# l0 A
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
: [3 F- M, e. H' J    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
( R* u( f! r: M    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
! _. z* V+ |. u- Q# ?4 ]! |: j: S8 I* m6 i' ]

! ^* [; l8 J. f& d4 Y2 t这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
7 X( H% ]3 x, s+ s/ R" {HRESULT CreateDevice(/ m5 p: w5 ~: D6 w! f0 m
  UINT Adapter,
: R* c! X! C; C/ }" f' R% a  D3DDEVTYPE DeviceType,
" C1 x2 _1 y) D  r, V  Z  HWND hFocusWindow,
* B2 s  B/ r' z  DWORD BehaviorFlags,1 b# x  }4 w2 [, [# `" d# N
  D3DPRESENT_PARAMETERS * pPresentationParameters,! d' w* n8 t. f! c; g
  IDirect3DDevice9 ** ppReturnedDeviceInterface  K5 R& P) i" Q
);; z) z2 `$ Y+ T+ _. l* G

3 {- v' l' o# |! M" D* Z3 N0 s) \: ^4 o
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
2 F9 @: L2 n: ]) g+ N* nHRESULT _stdcall hookedCreateDevice(
, z' c4 w: {/ u2 a4 F& r- P' T                                  LPDIRECT3D9 pDx9,2 N. ?# j8 Y4 A5 I
                                  UINT Adapter,
' d' k1 e, l2 b1 R' ~$ {                                  D3DDEVTYPE DeviceType,, u3 \; c! U7 D* S
                                  HWND hFocusWindow,! q7 }8 @6 m5 W9 @1 u% o% }- Q
                                  DWORD BehaviorFlags,
) |- D# M' `3 K  U9 M                                  D3DPRESENT_PARAMETERS * pPresentationParameters,3 X9 J. R# Y/ I% U
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface; ?; M/ K) L3 W9 ]  D
) I5 [, s+ h6 r1 L
                                  );, Q  \% D; Q5 Q) x
% t; W% y) n, R3 b
其中的LPDIRECT3D9 pDx9就是this指针.
0 ^; Y: v* h9 ~& O1 m! H7 A$ A& X: ]* \* H& n9 M
hookedDirect3DCreate9的关键代码如下:" {7 x) h0 ?# Q7 S6 z. X9 s
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
0 C5 \* q, `1 [! g' K( r% E9 Y3 }! U! o
        DWORD oldpro=0;8 G; }. B! t- G2 J  f
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
/ F) Z5 {+ K' D" t3 L9 m        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);9 ~+ q" G2 k- s0 _& v: [
        *(BYTE*)pCdev=0xe9;( ?; q1 ]; u7 `, j) _7 t% u/ K
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
( u2 t* B6 O, D' a1 e7 L4 C2 @' q+ m8 t
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
* i( _4 E# P. a- C/ LpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
# J, N0 J; |. I. }4 V8 T  r0 \3 M, ~        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
( s# u8 L% c5 C: c1 t/ S4 k$ W        DWORD oldpro=0;
6 b- K! O, x- W3 T        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);+ P  {/ j/ v( u$ Q9 s( i
        *(BYTE*)pPre=0xe9;6 u' ^& C2 ~* [$ Z( O1 c
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
) X0 n0 R( X: n4 T& N
2 g) C. E- N% Y! R( s实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:% S; I1 M( x" P
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
, j3 Y, |' O/ v- k  q8 r            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
+ X6 t0 c* i& D2 A            //在这里写入您的其它绘图代码
2 C" l7 I; d4 `3 `. a
$ \  n# K  @4 L; l8 [. q, t/ e& S/ O
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:1 D) g# \3 ~, ?& N2 h  g
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
# G, g# a3 S8 B, {4 r! @{, U) }+ S, [9 x! E/ r! u2 I4 I

$ j; `* i9 C7 r# s    if(m_pD3D && pDxdevice){# l" N3 \( y5 n) o8 M7 j# p

; g. J2 x+ o+ O7 Y3 p; z# j        RECT myrect;- a0 E" C* ]( J4 q& \
        myrect.top=150;  //文本块的y坐标4 s1 V8 N  s7 G7 h1 ?4 i
        myrect.left=0; //文本块的左坐标
$ D- h0 g0 W2 C  s; K& W        myrect.right=500+myrect.left;
% f$ |& J' Y! l, O0 P4 }$ }        myrect.bottom=100+myrect.top;
) k8 N+ U' D- n* S8 l5 ^        pDxdevice->BeginScene();//开始绘制1 Z2 J+ s5 ?: O0 s6 L

  J$ \2 c7 K9 G0 B2 X- H7 g        D3DXFONT_DESCA lf;
. l( A  \" C4 V% I1 X$ n) A0 d/ u: s        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
$ d# ^+ R- Z% e        lf.Height = 24; //字体高度. F7 @( r/ B: J6 x5 f
        lf.Width = 12; // 字体宽度
$ i+ \* p; \5 x' A2 Y        lf.Weight = 100;
/ M* u2 t4 U2 V! H) C7 s9 U4 S- p        lf.Italic = false;
2 ^; M- n# ~" J. Z" Y8 G        lf.CharSet = DEFAULT_CHARSET;
- ]5 j/ E( m3 \! c4 g; J        strcpy(lf.FaceName, "Times New Roman"); // 字型
5 _5 E/ W+ N0 s9 Q  R        ID3DXFont* font=NULL;4 Q5 }# A4 F+ ~$ t* t5 w. m3 k0 ?- ]* @
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
9 ^, S. ]9 d4 Q+ @            return false;& D6 `6 s+ n# V7 {" s+ l$ M  H

4 ^' i/ N6 P- l* u3 r        font->DrawText(3 O2 `' V# G7 m! E$ W
            NULL,
, y/ R( \: @1 b0 y5 i7 ]/ b, n            strText, // 要绘制的文本% v4 T: z2 d) M6 y( J
            nbuf, 1 H6 X: ]6 r/ q; Q1 U- y+ J; {
            &myrect,
7 Q( r9 R2 J  K( Y- s' m            DT_TOP | DT_LEFT, // 字符居左显示. L' r8 K: }5 ~8 p0 V2 \) k
            D3DCOLOR_ARGB(255,255,255,0));
. Y4 R7 L: h( @/ o: f: r  E# k1 T; c8 o! M7 U+ d
        pDxdevice->EndScene();//结束绘制$ J/ w$ z# Q+ }( P  N8 ^' S4 X
        font->Release();//释放对象
, H& y. K0 K' _7 H$ {1 O    }
- Q" g6 r* L! U2 B# H    return true;2 }3 }, j0 U5 V, U- d
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。: P% }: F+ j( \% m3 {3 ]$ K6 D. t
https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大8 L! A6 K3 s& |- c5 b  }" L3 f
首先不能方便的HOOK所有函数,需要到处打JMP补丁
; f2 o- _7 ^$ m4 g/ W6 H另外每HOOK一个函数都必须自己去查找D3D对象偏移6 V/ {+ s7 j7 U2 `+ e& u
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
" }9 D. [  a9 F1 {, t; H7 W比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下! c# W, J$ t) N& c4 x/ n
8 s- M0 x# g% s7 X7 A: H: e; a3 b) ~
还是007那个方法好,也是我一直在使用的方法
; h7 A1 K# ?9 Z. q& z直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
  [( f$ C: ]* H只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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