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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法% s% c9 F: ~( n0 Y
作 者: runjin
2 n; [( o1 k2 d1 Z" ~; \* w时 间: 2009-04-03,22:44:51$ g5 l) s9 c; y( F! ^# V2 m
链 接: http://bbs.pediy.com/showthread.php?t=85368
% q9 ?/ U! p2 ]3 {0 `6 Y4 X6 ^/ G) z0 s6 G5 A
Hook Directx:在游戏中显示自己的文字和图形的方法
: ?0 L# @/ a( L  P  C* A. M4 ~( J0 j5 b* M& G( W
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
  t. n8 Q+ g1 r4 v3 z$ ?" h8 S其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.& m2 ?& U# i! A- o1 {
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.% ^2 n4 f: X0 S+ @9 A3 r3 U
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
5 c% @1 }5 q8 ^( B' O* ^    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址% h% W- @9 |+ k* U/ {
    DWORD oldpro=0;& `1 q; X5 y8 A, d: d+ q4 W8 [( y- B
    memcpy(d3dcen5bytes,pC,5);0 ^6 h- U6 K% L! h  Y4 F- l
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);" p/ y+ N5 _1 k7 D3 s
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码# v4 I! v& k# [( X8 L
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5. q) C/ \) z2 Q# t% G% }

) X% x! k+ W. s, y6 I3 Z, M% X
; w7 l% @. m+ t) `! a( N这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:' t4 F/ O. R7 Z0 r" A" A
HRESULT CreateDevice(  D8 n! y7 t* _  `
  UINT Adapter,* }" R  _6 t! @5 g" ]8 t
  D3DDEVTYPE DeviceType,/ |. k2 ?$ V2 a/ @6 Q# v% B
  HWND hFocusWindow,
. f* |8 T2 U7 a. v" N$ p& }  DWORD BehaviorFlags,
! ^8 N, w+ I3 u6 z% _6 M) p. w  D3DPRESENT_PARAMETERS * pPresentationParameters,
' {) k/ l! m( v  IDirect3DDevice9 ** ppReturnedDeviceInterface
- n& e( D2 J# T" g/ H4 N0 ]1 k);- r5 s" b5 e9 i: b! h( G
& q' O" j2 O' Z3 C* G/ S

4 U) }4 \/ V: v, u0 u而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:% n  t; Q! B- L4 e1 t" D8 v
HRESULT _stdcall hookedCreateDevice(
$ ~9 l( D1 _. r                                  LPDIRECT3D9 pDx9,* a2 E0 a6 e* J1 ?  Q
                                  UINT Adapter,
4 _* X( O* L2 a) L8 e( c2 Q                                  D3DDEVTYPE DeviceType,# N$ k  F! P3 l
                                  HWND hFocusWindow,
7 s7 x7 q) o3 r! ?' D- L                                  DWORD BehaviorFlags,8 e4 w) m/ |) u5 Y( n+ M% _+ j& l! @
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
! ~8 M0 X0 |8 t9 _0 z( g                                  IDirect3DDevice9 ** ppReturnedDeviceInterface- Y8 F# y' ~; E0 M3 n
/ v( D0 H' F, J7 D
                                  );$ Y( F/ E; u; Q! `/ a
; [) d% o$ A5 P9 q% X7 b
其中的LPDIRECT3D9 pDx9就是this指针.
9 F3 a7 w. m' F/ Q5 r
0 A8 ?8 ^  n# Z  j; GhookedDirect3DCreate9的关键代码如下:7 U6 M3 e3 s8 i) W! }) `( _. d- V1 ]
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
. a$ M2 s8 h$ I9 h; U2 P+ l; z2 R/ u3 y$ c
        DWORD oldpro=0;
0 F: ^7 g3 u2 K8 o; z        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
, K' \, j- p- _! g        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
* k# f" C# b. t9 g% d* f, n# s        *(BYTE*)pCdev=0xe9;8 s2 X; u$ G6 _' H2 S
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
# B" d7 y& z5 f3 I+ Y
: K: V0 Z7 o! c" a) S5 v在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
( R" N+ {- ^' L) y# k+ ZpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针& |# g! B1 g7 x4 t+ B
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
- [1 I% M# w4 |7 a7 i7 {        DWORD oldpro=0;, M. Y3 P4 l9 b. W' P
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);' w* e1 v2 [7 o
        *(BYTE*)pPre=0xe9;
3 d2 i: S" M' G7 }$ G        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;7 h. e8 ]5 ]- x# |3 w

) W" x0 Y- K2 L9 q6 J实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:" |7 ]6 ~! V3 i  @. h* t/ s/ l
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
) M' a& ~. z. {' g7 C            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
# D8 f2 I" q6 t# N( z* N& a            //在这里写入您的其它绘图代码0 J# @0 L0 N. e1 L# g% o' i

' N1 U; T$ M) H! X3 x  D' s& {1 S9 [
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:. l$ U- z+ y2 c# @2 G2 g
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
+ T; q+ v7 i6 z{
( y5 s7 `& K1 L  h& r. }2 l' @3 T1 N& w! _0 _# A
    if(m_pD3D && pDxdevice){
4 D+ j; x9 S& R$ G% d. b" Z6 h9 J: z0 o, ?" x; E
        RECT myrect;  O: ~3 C% x) r
        myrect.top=150;  //文本块的y坐标
2 I6 s/ A( r' g7 I  m' @  D        myrect.left=0; //文本块的左坐标' |$ |) C/ a% S& K" V
        myrect.right=500+myrect.left;+ |; q* g4 p% g3 c9 s
        myrect.bottom=100+myrect.top;
* G) }# w7 j: s8 b& s) N* }        pDxdevice->BeginScene();//开始绘制$ }2 b& C" s6 M7 N" ^3 ?1 l# q
+ ?! r- k* ]7 m" D
        D3DXFONT_DESCA lf;6 a7 y# t0 B0 g
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));, P; x! u! t! r. i, [4 Q+ N  q& |
        lf.Height = 24; //字体高度
' \6 E; W2 @/ U- H. h! K3 m, C9 H        lf.Width = 12; // 字体宽度
! x4 R1 g2 V4 z# `( m% O2 M        lf.Weight = 100;
6 J, r5 `: i$ c) `        lf.Italic = false;5 p2 }* T9 A( F9 ^* h/ }7 J" h
        lf.CharSet = DEFAULT_CHARSET;
) ^- q3 r4 N+ L: j$ M( p        strcpy(lf.FaceName, "Times New Roman"); // 字型9 G# [! M4 y0 C( }1 C0 Q
        ID3DXFont* font=NULL;
8 [% y9 P& @8 G" a        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象3 Z- E6 i4 i. _8 _* B3 m3 z2 x
            return false;
6 N' Q' J( y/ S8 U2 z4 L) K3 K5 ]3 f+ D9 V
        font->DrawText(
1 M1 v# d+ h) F9 H! u& Q            NULL,7 e# ~+ p+ {9 D& x0 |
            strText, // 要绘制的文本
) `; Q; Z9 l6 t! ]8 f/ G            nbuf, ! f8 s/ ?/ f3 q" Z0 Q4 a
            &myrect, 7 ^) D/ @) ?, \9 U! d. b
            DT_TOP | DT_LEFT, // 字符居左显示9 z" P6 ]$ [5 g) [' [/ N7 c8 E8 k
            D3DCOLOR_ARGB(255,255,255,0));
2 L$ _4 f6 A' [
0 K( }" I% C/ A, K        pDxdevice->EndScene();//结束绘制+ Q% H" w/ Q) G: x  n1 R
        font->Release();//释放对象8 j: [5 X& Q1 K7 W& [8 l- s5 T2 c
    }
. {9 ~& R  {2 j' L+ \6 j! Q; R    return true;
. t7 u* v6 F2 O8 a; e- P; j! G}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。2 X* E4 Y; P# s/ Y& m
https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大+ v$ m# Y$ h5 ^( c2 D
首先不能方便的HOOK所有函数,需要到处打JMP补丁
2 r. G0 ~: }- ^% S: Z( Q1 f另外每HOOK一个函数都必须自己去查找D3D对象偏移* u$ T# ^) J" g
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针5 I" |& E+ N$ a8 P/ ~! w: Y' }* ?
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
5 _$ E: l+ X/ r$ \6 t$ _% B/ s" `
还是007那个方法好,也是我一直在使用的方法' R0 z: e9 Q7 ?* f  m. s- R- X
直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
2 M# G, H" X' u只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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