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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法, V, {/ i$ e/ h" Z1 D$ N
作 者: runjin
: j. @1 `( i- y8 W2 a% u; R; t& q- w时 间: 2009-04-03,22:44:51( ]2 h& t$ z0 ]- q7 F- `% B4 A
链 接: http://bbs.pediy.com/showthread.php?t=85368( K( s: M, S3 R; S

* ^9 S" e8 v! M; i0 F2 `2 THook Directx:在游戏中显示自己的文字和图形的方法; B. w* u, S. @& \) Z
% N5 @6 A8 z: S6 a( r
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.6 i: h0 s. C! x( {' \
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.5 Q. Y- P; ~) C
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
( C' n+ z0 F% y6 |# j首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令./ Z* z9 O  E+ b* X* n
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
) \/ P  u8 p9 c) y/ }8 I: l    DWORD oldpro=0;1 b0 m5 c  ]# [2 I: F# g
    memcpy(d3dcen5bytes,pC,5);
, J/ o' M8 c* ^! V8 J- X    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
* I" t, b& u0 i1 u, w$ n, u    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
8 R2 q) `9 E; y* M) x. t- Y    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5% S, m- ]$ }( n& b

1 ?; u1 J! R- w  M. M9 v; \9 L! p  ]2 p( R- N9 D& t/ I' ~' k" c
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:5 K( H. _, B6 G# M( Q4 m8 g! M& z
HRESULT CreateDevice(
7 q4 ?& R8 z4 @8 e# e  UINT Adapter,
2 ~4 c5 q1 j$ s+ p7 [  D3DDEVTYPE DeviceType," V! i1 U- X6 r, ~; j3 @+ M& D3 t  t
  HWND hFocusWindow,. o4 }3 B8 d: F7 z
  DWORD BehaviorFlags,
& V# L! T, Z5 @, }7 G& M- |  D3DPRESENT_PARAMETERS * pPresentationParameters,  V- u% b) O2 D' q2 s: H
  IDirect3DDevice9 ** ppReturnedDeviceInterface2 P+ @! v% Z% R1 V3 E" x% V; H
);' g, j0 H: o* h6 h8 O  U) T4 P

4 g8 W' u  |; A6 F0 d% p, s+ c' m- }% R: r6 \1 C$ T7 G$ t3 U: Z
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
9 B! G" Z( O* t  ~! Z  C3 i) c6 OHRESULT _stdcall hookedCreateDevice(, h2 O2 }0 Q# N) j
                                  LPDIRECT3D9 pDx9,
% P2 w- U: y1 N4 D: M/ U                                  UINT Adapter,3 U( I/ m$ [8 c: K+ o
                                  D3DDEVTYPE DeviceType,
  l4 R! u9 q# N! A. k3 w+ o                                  HWND hFocusWindow,
5 }8 q; f- U  M& I1 |" m6 R+ U' r                                  DWORD BehaviorFlags,9 u" A* o4 t% {/ m: R# N
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
+ p$ n: X# l# k: x! _' Y3 @8 b                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
) l+ I2 _1 t! J) N6 }4 z* K5 l( O6 Y) M- u: ?* Y& w
                                  );8 f1 t: {" B' e: t7 a1 b6 @

# x+ I4 d2 A* C  i其中的LPDIRECT3D9 pDx9就是this指针.# G) r! ]( M) p, X2 k7 H/ G

$ f9 y  d5 g8 T1 f9 @1 MhookedDirect3DCreate9的关键代码如下:  s8 S# A0 c8 c& a, r
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
5 G/ Z# G0 u1 J) D( ~
3 T5 y, N% g/ p* H) {" Q/ J! S        DWORD oldpro=0;
) {4 K& R; H2 s2 d% g7 J        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节3 I: q7 I$ U6 {
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);" ^$ I" l4 |# I8 X2 \0 {0 W! C
        *(BYTE*)pCdev=0xe9;7 b+ R7 J) R8 A! B  g* s4 Y
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
8 C# b; o% i* i$ x& X- y  q# q7 u( N0 X
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:& E5 f$ L8 }  z) O0 ^2 k' Q
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
+ L6 Y" P; Y  @  `  c        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
' Y4 [* Q( ^/ O1 C        DWORD oldpro=0;
& J/ k# k$ u8 w. |  _8 g        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
, |% j0 p2 h* x& k3 Q( a        *(BYTE*)pPre=0xe9;
: J* E! O% s+ T3 Y        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;9 y! N  }' k" y; \, z

2 U% ]% V! b: ]. t: `) z* W+ f1 r实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
# H3 N. K, h& h, ]4 Qchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
7 Z9 k! b' `: b7 _, h1 Y            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本/ F- P  v2 |, i" ^: ]; W' C' P
            //在这里写入您的其它绘图代码. U! L5 Y+ n/ e

' P5 r* V: v) W( Q9 C  H4 K
2 Y' c! e! v; x( r6 }2 c: H再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
4 |2 L9 {  ~3 _. D6 VBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)7 M+ e$ t% q  k* L6 |
{0 T$ ?! H& j) P9 X1 D% p
; Z/ U) g2 c0 z" y% K% r
    if(m_pD3D && pDxdevice){7 E  x1 l- T6 L+ k9 `

, ]& C# H0 z& e( i7 J6 Z6 O        RECT myrect;4 I: O9 @. \! o8 x7 `( |2 E
        myrect.top=150;  //文本块的y坐标
, i# n6 c" a2 y- K2 Z        myrect.left=0; //文本块的左坐标9 U& X; w2 s7 r1 u! {; o) W/ R
        myrect.right=500+myrect.left;' {  D0 I3 C* {; \! D
        myrect.bottom=100+myrect.top;% E/ K9 Y# K$ w" O- s( O9 f9 }
        pDxdevice->BeginScene();//开始绘制& A+ F6 M% D0 Z9 b3 q$ \

- K# E3 n; p: a* r0 ?2 c" Y        D3DXFONT_DESCA lf;% ]) b: s; F# ?7 ]% G% Z, T
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
/ P) U& i: t9 ^- y) G9 ^4 W        lf.Height = 24; //字体高度: O! h7 y$ h# z  B8 ^+ h
        lf.Width = 12; // 字体宽度, M  F' a! X& t0 j% Y& F- T2 k
        lf.Weight = 100; / V/ H4 X( J+ [, c- V
        lf.Italic = false;
6 n. Y1 g2 K. ]; F        lf.CharSet = DEFAULT_CHARSET;
$ F3 \. w- P9 m- x        strcpy(lf.FaceName, "Times New Roman"); // 字型9 v' s; m4 L2 |8 i1 G
        ID3DXFont* font=NULL;
/ M7 w( Z5 k$ c        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
# E3 s, u5 @/ `+ O  Z            return false;
7 j5 P; b" a. k. |# p0 o. r, d, x4 S# e2 j6 W/ d: N
        font->DrawText(' ?! ~5 y% S! H' m1 I5 I
            NULL,& R; N7 K# j$ e$ ^) `
            strText, // 要绘制的文本
$ g7 w& l- a, @) H9 O. f3 r- I            nbuf, + x. o2 ^5 Q( x2 ]0 [. l, ]
            &myrect, * L7 f8 K! Q/ |  n* P
            DT_TOP | DT_LEFT, // 字符居左显示9 L: f3 V$ [+ a8 l# U0 P/ o
            D3DCOLOR_ARGB(255,255,255,0));
9 i( u$ z5 N/ T3 [6 E
7 a4 n% q5 K1 P+ s: w        pDxdevice->EndScene();//结束绘制
1 z' {! W) I6 a. a3 a        font->Release();//释放对象- [6 x* ~  Y; H% v: ?
    }. S% X7 q% Z9 l7 ?& C. ~) B
    return true;2 W  O3 g4 r7 A+ ?- r; U% ^6 R* q
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
, }- A) X6 c- X! Thttps://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
; q6 I3 ^- m: z首先不能方便的HOOK所有函数,需要到处打JMP补丁; g: O' |& }; ^- R/ P+ E
另外每HOOK一个函数都必须自己去查找D3D对象偏移! U8 _* U- l" ]2 w
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针: n; }/ K6 H* ^5 F
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下& ^4 T1 a1 R9 X+ g4 _+ }
; A$ v/ o3 X/ ^: F6 y: j6 `
还是007那个方法好,也是我一直在使用的方法
, G3 T' n6 Z, N( e- }/ |直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下  h% O: V+ }+ t2 J6 f
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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