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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法
5 g: q- R0 y, ?3 J7 o作 者: runjin& W+ S& F3 x5 l5 V
时 间: 2009-04-03,22:44:51) j, p# E/ p9 m- Q% @! f0 k9 c
链 接: http://bbs.pediy.com/showthread.php?t=85368# P! ]6 d' }4 v0 x
/ K1 E$ W, e9 U
Hook Directx:在游戏中显示自己的文字和图形的方法
. F/ C0 v# i; J9 v
6 S- P1 [2 k7 g8 {' b这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
) [8 m: m- @& j: D1 t其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.6 q9 p5 n: g- X9 L& B& U# }& w
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.# P( d& f% F1 U& ]/ P( d# e
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.. J! A# w( y3 y! B. O$ ]- u
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址1 j) Z+ P3 C$ V4 w  d) {8 M
    DWORD oldpro=0;
, v3 u- h( K" E    memcpy(d3dcen5bytes,pC,5);% Q: Q- Z1 h3 o! n$ S
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);6 }0 s# |6 k8 R7 z2 E; c+ a7 u& ~
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码) U' Q! ~2 q% z. V& L  @/ L8 g* X$ R! U
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
4 Y7 _3 c( R: b1 \4 d! w6 v& }& ~) j& U, ?' k
, ~9 M# w2 Q2 Z7 C2 ?0 L
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
- l4 I" m' `+ ~: a: M; Q# Z" FHRESULT CreateDevice() r% g! D) Q6 [) N) `% @
  UINT Adapter,
3 I) O# Y' G* U  D3DDEVTYPE DeviceType,9 p& n; P9 ]0 P; `, M7 }
  HWND hFocusWindow,1 B$ C6 Z, G& c* \) ]% n
  DWORD BehaviorFlags,2 u$ A3 j2 X( U# Q
  D3DPRESENT_PARAMETERS * pPresentationParameters,  I3 N$ O, [6 @4 K2 F1 F/ @( D2 J
  IDirect3DDevice9 ** ppReturnedDeviceInterface
% H4 E) u, b  f+ x7 g);
9 e1 g* k& }- T/ y; N) S3 ^9 Z
6 [6 z7 e7 t& r8 Y! P% L" W) ?) p! Z8 t+ n+ y
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
7 P/ E( D% k4 I, [  `" c6 VHRESULT _stdcall hookedCreateDevice() ~: J/ }. H" V8 a6 e; C
                                  LPDIRECT3D9 pDx9,9 ~$ {/ J6 E. }, i+ a+ C" z
                                  UINT Adapter,
1 m- F, X% ?! u% c                                  D3DDEVTYPE DeviceType,) }  r8 P$ O9 Y/ T, u8 R8 I- ^3 J; Z
                                  HWND hFocusWindow,' H5 B1 Z2 _! F7 |! u$ |, ]; F9 K, w
                                  DWORD BehaviorFlags,
8 u, m2 X0 D# R, M                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
" @& p+ K4 `7 T) y1 x0 s                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
- p8 c' A$ a' x* d3 d! f; j4 }) m, y( Q' T
                                  );
) U$ m9 H- }4 k& d3 g1 B. e
- j$ ]) E3 L8 t& m5 _; k% U其中的LPDIRECT3D9 pDx9就是this指针.0 ?+ \4 y6 A1 O
0 L  T" f: V8 y. l! T- Z" Z$ z
hookedDirect3DCreate9的关键代码如下:3 a* r: z$ b2 Q
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针/ Q* ~; z- u8 E$ S

- z) R$ N1 M4 f0 n* Z' \        DWORD oldpro=0;; \8 z  r3 n  X& P5 q1 F8 u  l, A7 E
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
6 P7 P8 O; N; U: F5 K3 S, i$ E7 V" f        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
# E6 S8 t3 S. n, R; E: G        *(BYTE*)pCdev=0xe9;) V2 {  A$ ]8 V4 e/ p2 A
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;& p( o* |$ J& }
; q3 S- d7 F! Y8 G/ I1 R# h0 |+ q# z
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
5 u1 g9 R; ?' Q2 l0 g* tpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
2 v0 {  C) G! B, M4 [1 o6 h        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节' }. J+ b2 k4 m" i8 h; H1 n
        DWORD oldpro=0;
( `# a" H- \6 d$ n4 @! b7 R/ i        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
7 b9 q$ j3 v7 J9 e" p0 n) y        *(BYTE*)pPre=0xe9;2 l8 l# I4 f7 T' L, t
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;7 s0 J9 F8 s" a
" ]3 ~& h) l: F% }& n8 K( }' d$ ?! Z
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:5 I+ ~2 @& x8 A" ~
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";2 z, T/ J/ z" Z7 d2 _
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
. [: T; L$ L5 g            //在这里写入您的其它绘图代码  e- q4 X0 l2 p/ E3 q. C

/ z% N1 `" @" ], d
% `+ H9 e+ [1 `1 P再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
9 B4 g0 Y  h8 V* i* B3 ?BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
2 Q5 {; }5 ?" Z2 x, f- ]* L{
- c* n! k- e* B$ w. T
% Z( A0 q6 x6 w' p* i! L6 K    if(m_pD3D && pDxdevice){
0 e  C! P& }; H+ a  Y6 t9 i. n6 {' E7 ?+ [
        RECT myrect;8 L5 i! n1 E% v; H4 _
        myrect.top=150;  //文本块的y坐标& r" x+ U& f3 ?0 q. _
        myrect.left=0; //文本块的左坐标( ~( @7 F1 V& `" U4 ~" t
        myrect.right=500+myrect.left;
9 \& }/ W* @" r+ G5 ], Y        myrect.bottom=100+myrect.top;3 I# i/ _/ s& Q6 I+ t! f
        pDxdevice->BeginScene();//开始绘制/ ~6 [) C4 J9 t$ ?# b' x

9 v" q! T$ T8 \- t1 h$ V        D3DXFONT_DESCA lf;
, `' D$ _4 [' {+ E  T        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
9 m( q; K% D: ^. D  }        lf.Height = 24; //字体高度/ ^! b9 d8 x2 {' x3 }8 `2 M
        lf.Width = 12; // 字体宽度4 E: g) r& X2 p0 B2 n6 ]+ g* a% z
        lf.Weight = 100;
1 H8 U' h( o" C" d3 R& ^  U& |: U8 t1 _        lf.Italic = false;
# n3 J( w( b, X( e        lf.CharSet = DEFAULT_CHARSET;
1 p: A& @$ J2 K  w6 a        strcpy(lf.FaceName, "Times New Roman"); // 字型
2 B7 e1 e1 c' G, H7 n+ Z        ID3DXFont* font=NULL;- P  r( N4 R; _* U
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象( y* U: H$ U; l% C
            return false;
, T2 `2 x! W/ o4 ]7 F3 R; ]; m% z6 I
  ^; C) R5 x$ c/ F: \" K+ z        font->DrawText(
% v% G8 p" e8 k, v1 _* B            NULL,' i& _. o- x  ]$ B- l$ a0 @' {
            strText, // 要绘制的文本
5 ?! V* W+ A$ ~# k; o9 o            nbuf, . j- Q. A; `+ l% @
            &myrect, & v0 U) Q4 b& Z
            DT_TOP | DT_LEFT, // 字符居左显示
8 ?6 c. N2 ?2 @8 F, Y            D3DCOLOR_ARGB(255,255,255,0));
: K8 D: \+ F" k4 o* S! }
3 w  T0 y4 b7 i4 j! F' A& `        pDxdevice->EndScene();//结束绘制
) h% _7 b. c. A- O9 a4 S$ n        font->Release();//释放对象* L- f, {2 {3 L4 n4 W
    }% ~" \. \+ R6 \- i) ]
    return true;
. |* k1 P! e* V! n1 H0 c* C+ y' P& K}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。! v% l# j2 U- {8 c
https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
/ B7 a/ H* ]6 p3 [" A首先不能方便的HOOK所有函数,需要到处打JMP补丁( i7 k5 D* \0 y+ ?
另外每HOOK一个函数都必须自己去查找D3D对象偏移5 g$ D$ C) s. b2 _  @* o0 V. Y
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
; P: ^& o- m9 V3 l比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下; B5 M6 v1 U+ i0 L% t! J
/ H* s# w' I8 N% }1 W
还是007那个方法好,也是我一直在使用的方法  W5 Q/ O& [4 z4 {, b5 y; |3 o
直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下& \( ~' n$ Y( u: u) P% g  F
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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