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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法1 _9 o1 d' F7 r1 z$ e5 M: D& `2 q
作 者: runjin
. U3 ^4 T: G( L) h/ t时 间: 2009-04-03,22:44:51
0 A3 F2 f( U) s链 接: http://bbs.pediy.com/showthread.php?t=85368& L% I, G& O" ?7 P& w* I4 ?7 o

1 s0 R. z# k* }5 G* NHook Directx:在游戏中显示自己的文字和图形的方法
' U5 m5 K" v7 H# V- e3 s5 Z" n' G3 D9 l7 A9 ~
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
0 i. @. D! A6 b9 R; N其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.  M4 \2 c) ^! c- c6 _
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.) M  }' `3 e6 T: L2 Z" c
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.4 u; N# X2 ~$ p2 g* X/ ^1 _
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址. x# Q+ g4 t  E
    DWORD oldpro=0;5 N7 n" L/ f" D* ~+ [  c  h
    memcpy(d3dcen5bytes,pC,5);- E$ {7 O2 @& M3 z0 N
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
' c7 q( n* [' p' b* D  M    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码6 F% r0 x( j; j4 M8 x8 g* @. k8 W
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
9 Q! G9 T& d, b0 Q
+ {0 o1 M7 L  V. g5 r
. W: t. x& C  e; {这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
: i! Z6 g  ]8 H) N. u: [HRESULT CreateDevice(
4 h* H9 t; ~& X: Z8 k7 Y$ ?/ l  UINT Adapter,
# t9 c" t. |( M6 o  D3DDEVTYPE DeviceType,; g  D1 m% T! y% Z. U" V
  HWND hFocusWindow,
7 a8 L: U* C9 s2 `2 }& D  DWORD BehaviorFlags,
: a# f6 Z$ `! v/ d+ E/ i- v; Z  D3DPRESENT_PARAMETERS * pPresentationParameters,5 P5 B5 R9 P6 A8 ~, @* W4 r
  IDirect3DDevice9 ** ppReturnedDeviceInterface2 G6 V  I! b- D; F* ~
);
* C( B. u9 E; b7 D& n0 [; H0 n% ^* Q9 C  M' @  n# j. i

$ {# M- m1 ?" l' u) F而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
; e3 q+ Q9 j5 ]! @HRESULT _stdcall hookedCreateDevice(
! C; r4 W5 X4 V" O4 D                                  LPDIRECT3D9 pDx9,: `! p2 _/ |9 f) B6 w% N% j5 L
                                  UINT Adapter,
* j% J/ l0 F7 O7 z8 X/ B' i                                  D3DDEVTYPE DeviceType,$ y0 [  M" `, ?; A7 I
                                  HWND hFocusWindow,
* e6 E6 h/ c4 c. M/ |" M' Z" r                                  DWORD BehaviorFlags,' `; y( e6 u) t: v. Z' Z
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,5 k6 Q8 y! n7 ~  q+ p- O
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface" \2 V' s, P, J: k
& \9 E  x) q6 y- d, _3 _
                                  );
6 o1 D5 J$ g7 a% }8 T  r' d/ ?% s  \( [  W9 i' N$ W+ P
其中的LPDIRECT3D9 pDx9就是this指针.
# t0 A0 C1 Z9 e0 F! K
. X$ A6 @3 U0 vhookedDirect3DCreate9的关键代码如下:
& T$ z( n% q' qpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
- k" T% K0 W# M- U# {* W8 y9 E* w& }$ z) \8 b
        DWORD oldpro=0;
. G: I+ {7 h) t        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
8 k- X6 H( ]0 D8 }        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
) B0 q9 J7 |" V4 B        *(BYTE*)pCdev=0xe9;: F. d# m; G# w! Y8 c3 c1 D
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
" d$ ~% Y1 h$ }( K( D) ?
0 A; g. E' r: q8 Y在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
9 K- X$ F/ O3 p  J6 {2 z3 R5 T; cpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针9 x9 S! s/ ?3 f- z
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
2 ^' V0 Y+ ^, g9 \5 P        DWORD oldpro=0;
. |: X$ I1 D# I  J) q, U, d        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
2 v) g" T" e% G; n  o- B        *(BYTE*)pPre=0xe9;
7 e9 N$ F. I' b, m4 \        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;, e' I; J( C% S, }/ }) j

5 s& [. o. o  O* r/ w# _实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:$ n7 p% j6 |0 l9 I5 g3 M  s3 E
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
* S# A! j5 l+ _: A- m            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本7 T& R- G) ~1 X5 a3 ]- f, s$ c$ X( {
            //在这里写入您的其它绘图代码4 O4 \& T; q2 }, _1 `
. u$ \5 w2 w  O) x& f5 U2 Q. m9 \1 m
: i% B- t+ z" R0 M
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
  c4 F+ b4 v5 D# Y4 EBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)- u, }. X# q* ^" @0 @& o" R
{: x+ r) O9 J$ H5 Q1 D  O5 T) N  O* s- y9 Q

/ r" j  w3 ^- i! c    if(m_pD3D && pDxdevice){
' a/ R0 I2 B$ m$ p' R7 P
! d/ O: |- F/ ?; |) `) o        RECT myrect;
; w" F7 ]( K. N- C/ u        myrect.top=150;  //文本块的y坐标0 d' A& F6 W: c% X4 U3 U: h/ u
        myrect.left=0; //文本块的左坐标
+ c. t/ j  U( }, |6 ?& R        myrect.right=500+myrect.left;' Y, ^3 @; g/ {6 y8 J' z8 R6 p
        myrect.bottom=100+myrect.top;
% F5 n5 o8 F% I8 G' u1 T$ A        pDxdevice->BeginScene();//开始绘制
9 I" {) y% |1 H% N" x0 u2 P' X2 w* ]2 t" k/ R
        D3DXFONT_DESCA lf;2 f% m% u1 p! ^1 [9 g8 a
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));5 ^2 v$ J( R- H5 Z8 T' P0 f
        lf.Height = 24; //字体高度
) @3 Y1 S: F2 E8 z        lf.Width = 12; // 字体宽度
% R# X# p" q& A( N        lf.Weight = 100;
9 c; e- [* P- U        lf.Italic = false;/ W7 o" s6 o2 A
        lf.CharSet = DEFAULT_CHARSET;
9 e, L: c8 o+ [% Z        strcpy(lf.FaceName, "Times New Roman"); // 字型* s" J! H; c* B- U9 y
        ID3DXFont* font=NULL;
. T; U) y. r. x0 a. S$ {6 `        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
6 s! E' U! K& U2 I3 g( F  D' c  h            return false;( T" L) y5 Z5 ~  ~" D

$ Q3 M( X- u4 L5 I        font->DrawText(# h1 u; L0 \: }2 H# {) y
            NULL,
' e% y8 ^, f& F0 L  D            strText, // 要绘制的文本
5 x2 t9 ~, z  ^& D$ j            nbuf,
2 m) _0 M8 ^9 D& T; _  r1 u            &myrect, . {+ b# P6 u  d$ w8 k8 D- K# ?
            DT_TOP | DT_LEFT, // 字符居左显示
) o+ }; K) @1 m. W- |- Q            D3DCOLOR_ARGB(255,255,255,0)); & ?& S" w5 j7 o4 |) t, Z

% _/ A% J9 i9 V        pDxdevice->EndScene();//结束绘制
; B/ h& O0 t: ]7 k* F' e4 v        font->Release();//释放对象& x3 x" ^& j* O( Q. ~/ z
    }
7 r" T5 B6 s! N: `6 n    return true;
; K$ K8 O8 Q* U; h}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
8 N2 [3 W  t7 I0 x! j/ V' ^https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大8 K" a  j, K+ b/ t' }" S' v
首先不能方便的HOOK所有函数,需要到处打JMP补丁8 h3 ^( ]/ L* S# Q. D2 P/ X# k6 |
另外每HOOK一个函数都必须自己去查找D3D对象偏移
* O4 |0 T. b) y% U3 |5 j/ D! F; s  TpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针) s( V: c# ?) q; t) {+ N
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
8 n! g& f( b3 C/ H- @7 u
1 t' j  z8 R( Q3 K还是007那个方法好,也是我一直在使用的方法
$ i# E+ x2 M! _' o- d8 x直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
- t5 M! {. A5 k1 L+ [9 u! D只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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