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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法4 {$ L1 Z- j% t
作 者: runjin: W$ _$ i! r2 z' P4 Q8 u1 ^
时 间: 2009-04-03,22:44:514 S/ n: O) F. T  Y6 Z6 `
链 接: http://bbs.pediy.com/showthread.php?t=85368
- G5 ~6 O0 N$ E( e
6 l% y& I& w1 T% g7 dHook Directx:在游戏中显示自己的文字和图形的方法
3 t6 y/ y1 |: a( o$ j4 z: r4 E
+ F2 C* H4 L" [( D: X+ z. b这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.( |- B! M" {2 ]- m
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.1 H9 R( `7 O8 c; C% l# z2 R5 T
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.8 X$ A! |$ j/ X5 Y- [4 P
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.; t; S! p- o! M8 E) B' I
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
* L' j! c8 k/ e% @; |$ C+ h    DWORD oldpro=0;; J/ i* O+ q: Z4 J* a: y) C
    memcpy(d3dcen5bytes,pC,5);* X! T: L) `. t. k
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);3 I+ ~4 z* M& M0 U# T
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码& t" O+ G5 K. g) k1 C  `
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
/ n  g8 L- B' I* s) L( H$ v: B6 L5 W5 [7 T3 Q
! s8 A  o; C" k  q3 S# e- b+ E
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
- h, ?6 }% r' ?6 L! D( w! z: }HRESULT CreateDevice(
& u: s+ m; P# C% V0 Y+ D  UINT Adapter,% m+ ^  @- D! D
  D3DDEVTYPE DeviceType,6 d" D) ^1 s, v, w6 i
  HWND hFocusWindow," R, ]9 X' K4 R( x
  DWORD BehaviorFlags,2 x0 q0 g% D+ T7 p2 t2 B' C
  D3DPRESENT_PARAMETERS * pPresentationParameters,
% V8 |0 e. F- O: T! C1 U6 G  IDirect3DDevice9 ** ppReturnedDeviceInterface
3 t7 u; M) _  T1 u+ t& i- K4 F);/ C! l6 K5 h& x3 h& j& V
% Y9 j+ M: g) n; r4 u+ j- _
: ]# _2 W5 I  ?# @/ {, H
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
& Q5 T/ c1 k5 a" xHRESULT _stdcall hookedCreateDevice(
6 }. g4 V( J; W+ N2 B                                  LPDIRECT3D9 pDx9,
6 b. w4 W& N" R9 R2 h# Z. y2 i7 i                                  UINT Adapter,8 @' Q) r) G5 @! U. ~) X
                                  D3DDEVTYPE DeviceType,. W4 V4 p! u* o  J
                                  HWND hFocusWindow," @- ^% G8 o7 A: B/ W
                                  DWORD BehaviorFlags,' ]4 I2 c$ R% G4 a# J6 k5 N& F& V
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
. e5 j- w2 V6 r5 s# I% V                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
7 t. q4 o6 e' a, T% x% V0 ~! i
! @0 K7 I% ~3 w! c+ O4 l                                  );
) s2 C" _( c5 y/ |. k( @: d' o
) m9 U* M* y/ B* s  _! w其中的LPDIRECT3D9 pDx9就是this指针." h$ V+ V, M/ X# F1 M
: p* f9 c9 d, M' ?3 ^7 L) C, `
hookedDirect3DCreate9的关键代码如下:
) |/ S1 Y  M0 `/ H" t: a% VpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针0 H7 \7 ?4 V( F0 S' {9 }0 l: T# H

2 k4 N! M5 v  {; n6 z* e9 T        DWORD oldpro=0;
6 j2 R+ n8 ^8 o& \5 V        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
. Z4 W' J6 L* g2 ?        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
+ z, n9 j9 W. Q( s        *(BYTE*)pCdev=0xe9;
; M) ]* b: Q& |: O: K. _        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;$ F, M6 `6 @. {5 S2 ^* K

, |' H  @' A' ?; U) v3 e$ x" d2 H在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:: ]+ F2 z+ g) W9 P+ b
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
, L# j) k  S1 ^4 ~( A) Q$ e6 Q        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
6 \% n7 j/ p: @: Y: D( R/ @        DWORD oldpro=0;2 O! X) K+ m$ Z7 ]8 u5 f* d2 h
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
; j. x2 u3 n0 g        *(BYTE*)pPre=0xe9;5 G. \6 C* n; M4 e, f7 A
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;  U+ |1 p. v7 r* V* U+ h" {4 E' t

' G& i0 ^7 p( @' U实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
" a+ q5 |/ M# W4 e- ?char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
; e: H; x% e$ ?; _            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
9 T5 G8 A& \/ k$ B# U            //在这里写入您的其它绘图代码7 g2 _9 c, k8 D1 g$ |
, @2 r7 K6 I) r( W: @% p# w5 A* j5 |

6 Q8 x5 B, Z! V  @/ f' ]再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
2 O! m3 d! i2 iBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
9 X8 L8 Y5 p+ d# H9 n+ p: p2 f{; \' _$ {2 f6 {* [

; Y$ e% I) d' Y) ]! u    if(m_pD3D && pDxdevice){
$ g% }- h5 i% V- [
9 f) h6 c( y% R        RECT myrect;( }( }7 J' Y, h: e5 S2 b
        myrect.top=150;  //文本块的y坐标, T: V' t/ T# }7 J
        myrect.left=0; //文本块的左坐标9 [; N0 H/ k4 j: L) Y8 I$ L6 n
        myrect.right=500+myrect.left;
- p% B4 E: p6 h% X, Z" A        myrect.bottom=100+myrect.top;
1 f7 E9 F  T5 L3 k+ }: {5 I/ j' M        pDxdevice->BeginScene();//开始绘制6 V0 H% p2 W+ n3 g% b
' ^- h- Q, x8 y7 J: X$ E
        D3DXFONT_DESCA lf;* I$ I: K3 M* v, s; T, C
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
( M" I1 {; C" O4 k/ s: k/ n        lf.Height = 24; //字体高度, x/ @$ ~' E9 g* C6 @
        lf.Width = 12; // 字体宽度
/ _- w! q# ]' u! @7 @        lf.Weight = 100;
8 f* }" n4 s* d! v! f# T* O        lf.Italic = false;- j/ c/ A$ a8 _6 N
        lf.CharSet = DEFAULT_CHARSET;4 G- P/ i& s( z& w, [; k. O4 F
        strcpy(lf.FaceName, "Times New Roman"); // 字型
$ V% k- \5 O# c# J        ID3DXFont* font=NULL;
. ]& e; O. D# y# q) u        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象& a; k% r$ k2 u8 O
            return false;
5 Z0 Q: [  b9 p- c& j5 n
3 r& N; M1 j6 o+ Y+ a2 g6 z( N        font->DrawText(
3 z$ ?: I" G  O( h/ ^: u            NULL,
/ ]$ E% y7 r) O  \4 p            strText, // 要绘制的文本
0 L7 N: V' `: @4 |; h) @            nbuf, ! F5 D: t2 ^; l7 k" ?7 p; H
            &myrect,
0 u4 D7 ?/ ~/ Z# T& l( h            DT_TOP | DT_LEFT, // 字符居左显示
" x% H2 G/ V( o$ l/ D: w            D3DCOLOR_ARGB(255,255,255,0));
4 X  t  Q; e+ P( w$ L; F! p6 t! i
# b  G2 Y8 D" u        pDxdevice->EndScene();//结束绘制
; l6 D( i- U+ |( d* n        font->Release();//释放对象
' z! V" o3 ?8 Q    }, D3 b/ R* f9 t
    return true;9 a0 h) g& v% F  L* E5 i
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
5 A* _7 A$ L* C2 p, H3 X- {https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
  _( y& O6 p2 W3 v! y首先不能方便的HOOK所有函数,需要到处打JMP补丁% Q: l$ A: Q, Y8 q9 z9 p
另外每HOOK一个函数都必须自己去查找D3D对象偏移: x- ]9 @& d! j+ I. ?; S! O
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针0 u& D" W: p5 {% @0 p
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
, W" {& N. m7 U: l9 U8 F( D; H. x0 I, _7 @3 \/ b
还是007那个方法好,也是我一直在使用的方法
+ j+ c, X  X* |1 }直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
3 v& v- X! L. w只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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