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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法  {$ `9 d  ?: t. N+ k" I
作 者: runjin1 m& T+ w( N6 J, o4 |
时 间: 2009-04-03,22:44:51
7 l) w; b  n( B2 A链 接: http://bbs.pediy.com/showthread.php?t=85368& u# d# c; ]4 E+ d. [

- J7 j9 |: U# O- y% N2 B  SHook Directx:在游戏中显示自己的文字和图形的方法! W/ m9 c$ `: r' u+ m$ t/ k/ j& q

& n. z9 N; E( v! n$ }这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
$ f* @  A9 ?* O) e其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
: [- g9 q; \) b/ J( B7 _还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.6 ?7 u# N- R, Q; u* L  {- ]8 Q  S  H
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.# x: B! T( ?8 l, K
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址) V  d- ]7 A* U' j. a
    DWORD oldpro=0;( \) @8 p9 U7 P/ C
    memcpy(d3dcen5bytes,pC,5);
* j2 I1 D' |' t; N6 ?% S! F    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);6 u3 A2 W8 \% k; W* U. t
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码7 h( u( M9 T& `3 r4 F+ g1 T2 F# a
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
8 Q: ]! S) A  ?5 i. p7 W( T% A2 P* m

& j0 k# R# K0 \8 m$ Q这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:& U! j" G9 r+ ~2 q/ W
HRESULT CreateDevice(8 ]! v1 ?/ |# l3 b
  UINT Adapter,
) |* ]& B0 z# g; l' C( T  D3DDEVTYPE DeviceType,
6 o$ Q" r3 D( |) J6 L" ^1 C4 u  HWND hFocusWindow,! e! n7 f+ O7 l+ _' \& B
  DWORD BehaviorFlags,
1 s. w' d, }* l; r0 I  D3DPRESENT_PARAMETERS * pPresentationParameters,9 V: o5 Y7 F( R
  IDirect3DDevice9 ** ppReturnedDeviceInterface# W7 M* A" f1 S$ B) O: T& a
);
+ n0 g  m+ `" n7 e8 L3 P( L! N7 a2 |8 W( w4 p* A* t

/ _# k$ X3 a( @而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
, b. D( m/ P5 `4 v" @6 [HRESULT _stdcall hookedCreateDevice(7 H0 B4 m/ |& z
                                  LPDIRECT3D9 pDx9,& R  C; O0 r2 W. I1 M/ C4 V6 G' J
                                  UINT Adapter,: Y5 r8 d9 N$ g4 F5 q- q
                                  D3DDEVTYPE DeviceType,
3 g( J4 x6 D% a' b  q- E. ~                                  HWND hFocusWindow,
0 _: L& i& f% E& a- X) N                                  DWORD BehaviorFlags,
( M5 c0 P2 z- t# P" e+ A                                  D3DPRESENT_PARAMETERS * pPresentationParameters,+ _4 W  V* i2 O& y! b( n3 `
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
- y: T, E. h/ c; N$ h0 W$ N* z2 k6 V/ L# O: R0 M9 d0 A
                                  );" |( T  ?& `; m/ U) \' Y) ?( `, A* I
- ~5 J. W/ S0 O1 W! i
其中的LPDIRECT3D9 pDx9就是this指针.' o1 S" x9 x7 C5 y& }: }5 C4 K% _
+ @- f; _2 \9 G/ x5 Z' c
hookedDirect3DCreate9的关键代码如下:3 i3 B  s- r8 L. Y7 Q2 `
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
; ~+ x6 v& O! D1 n4 d& Z) G+ V) W0 b( r; N/ r$ X/ m% |% Z
        DWORD oldpro=0;
" c- L6 C+ Y/ d* {0 v        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
3 k2 T' s( ~# q" }: L        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);$ O% k" x2 v- J  L/ t
        *(BYTE*)pCdev=0xe9;# k. x. J- l+ P) L! D+ g: k1 W% O' D, N
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;! L! p" W8 R) T/ J; c# J6 n; m# E
' O3 W! w! ?1 d" j: P
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
; F0 w- l% X! F  J: c7 @pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
" O2 ]" E) ^: [5 G        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节  [! H2 ^) `! n. q- v
        DWORD oldpro=0;$ x: @) K* M4 W2 L# k
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
( p& f( U+ M7 E+ l/ T        *(BYTE*)pPre=0xe9;9 h$ Y  o' T0 L. e% {
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
( \! z4 O; S+ I% B4 [" E. p+ ~, a- K! C2 N
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:- c& J4 N3 K1 `: R1 ^: A, V
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";- K0 j6 a' G" K6 {9 }% E1 W, H
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本6 W8 ^0 P+ z1 B) j) O8 L5 {5 q# I
            //在这里写入您的其它绘图代码9 {! W0 f+ j7 q; `9 A) h; |

, _9 k# f. A2 [) Q8 d' S0 u0 X  x; J, g7 h) S- j
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
, d, k( T3 `6 T" ^/ E, ABOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)3 K) ^$ f( U0 Q
{
% d0 e! j! ], _! w6 `* c, }# [! A! ?
    if(m_pD3D && pDxdevice){8 o1 O( g& T: ^' a8 ^% ?# g$ d
- X5 p) W& N) q
        RECT myrect;( x" `" r+ s0 h! V/ O$ Q
        myrect.top=150;  //文本块的y坐标
3 e& F) G( n1 Y" O5 d" E        myrect.left=0; //文本块的左坐标
8 Q' \5 F; a: g; ^6 y        myrect.right=500+myrect.left;" m8 C, @/ A% E5 z
        myrect.bottom=100+myrect.top;( S% x3 T: g7 G
        pDxdevice->BeginScene();//开始绘制1 I! ^4 H/ t% e" S/ u

. l3 s* e; L" S) z& g0 `# ]/ ~, a4 N        D3DXFONT_DESCA lf;
" O" J& J! u* Z5 [5 e9 y" s        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
7 g) P  B; [& ^5 r        lf.Height = 24; //字体高度
$ I$ O6 O5 b. G9 ]% k( x        lf.Width = 12; // 字体宽度
4 J6 b& q. H0 J* ~        lf.Weight = 100; / @" k* n( Y' q
        lf.Italic = false;
$ h* i  ?5 Q: y2 a' |  N! p7 `        lf.CharSet = DEFAULT_CHARSET;
: |: \- V- r2 F* S' G3 m# }        strcpy(lf.FaceName, "Times New Roman"); // 字型7 @; P7 X& x* o2 d& @: F! f
        ID3DXFont* font=NULL;
1 B9 d9 r2 U* K        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象" I9 @) @& A2 J
            return false;
# G* T3 N) U, _8 ^" H  y- |; _) S- o/ w& ^" n5 J* y0 u
        font->DrawText(5 X% x2 l* ^: ~; D3 R* e+ j
            NULL,# N5 L8 `0 c5 `. g6 s
            strText, // 要绘制的文本9 T' W. L4 X8 m7 O
            nbuf, + c4 Y( Q0 H- G  b, e
            &myrect, : j) s& z% |2 M6 e" M; W" w4 ~5 m
            DT_TOP | DT_LEFT, // 字符居左显示
2 @  s- _9 \# t# g7 W            D3DCOLOR_ARGB(255,255,255,0));
& o- U% D: ~9 j- p, k# I$ x
( u; [9 J7 b0 o0 Q+ S1 e        pDxdevice->EndScene();//结束绘制3 ?' r2 W4 V# w; P/ ]2 i0 I
        font->Release();//释放对象9 W: @/ ^. q6 m3 C7 R) e
    }
( Z  l* [( B+ c  Z) [. @4 s    return true;
; Q) Y2 O- U0 b- G, l$ Y1 @# U}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。9 X' V) Y2 b; J% B; Q
https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
* R: q4 E+ g% g# z$ Q3 T首先不能方便的HOOK所有函数,需要到处打JMP补丁9 N) D6 P! o) t+ s
另外每HOOK一个函数都必须自己去查找D3D对象偏移& k4 C! S  K6 D9 t
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
( @) N' A$ y" f7 p3 Y( O比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下9 T* W# J! Y% Z/ }3 r7 i

! G3 k) J7 u4 h$ E还是007那个方法好,也是我一直在使用的方法6 E; f5 I3 ]# f! L; W
直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下9 N* G2 r. D# _% y6 G" t! A
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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