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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法% L* H+ j( Z) w( I8 u/ N) p, ?
作 者: runjin
6 ^0 X% ~! h6 o9 _时 间: 2009-04-03,22:44:51- H/ i+ W# X0 a
链 接: http://bbs.pediy.com/showthread.php?t=85368
+ w1 S  {: R/ g2 n' ?( t+ z: r4 J$ T
Hook Directx:在游戏中显示自己的文字和图形的方法7 g% r5 V* J! B' F( P5 c. m# `

0 G& [0 _* M, H$ m7 M这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.( c* G. m9 ~1 {
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
8 p3 ?8 X- ^& r: P: @0 {, t还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.) W/ j% i% e# ^0 f! K0 s
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
& O0 B. d: L7 Q    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
  b; Q/ V2 l( m3 f( q/ v2 b    DWORD oldpro=0;
5 \# {. C1 q( R8 U- I5 I& O    memcpy(d3dcen5bytes,pC,5);3 J, e( N( O% L$ t1 H$ ~" n
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);4 l& z2 J7 N$ y3 U
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
3 a5 ~4 z5 H8 Q$ `# k, c    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
4 [# Q" {$ K, k+ E2 P6 F3 i; X, O7 D2 L% ]8 U  H2 ?  ~1 @

1 _; e" [  B  B7 r1 q* j这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:" y. r7 J; r# S$ r; I
HRESULT CreateDevice(
4 V: j6 i8 I: i* O  UINT Adapter,
+ u' ?+ B" r% w7 |. g  {. k  D3DDEVTYPE DeviceType,; [% o* u, u( \4 A& q' h: q. Y
  HWND hFocusWindow,. G) K3 l) x# F1 N0 B( w. G
  DWORD BehaviorFlags,
) Q& L' l$ J7 h& |6 k( s  I" ^  D3DPRESENT_PARAMETERS * pPresentationParameters,
* Y6 t9 C4 ?# e/ T8 K/ u  IDirect3DDevice9 ** ppReturnedDeviceInterface
/ }/ c% k+ j* h& e5 Y' @  O);. Q5 v% m& ?' n$ v  W
2 ^. B9 o" Y- Y
  G2 L( t! z6 u+ O! t% ]" E9 c) y
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
. {7 l2 ?- B+ z. ZHRESULT _stdcall hookedCreateDevice(2 B, W4 N6 J6 a+ V0 L, f
                                  LPDIRECT3D9 pDx9,, C9 d2 I2 ?" R' U, @% ~
                                  UINT Adapter,
# ?& A. Y  d5 ^! T5 H0 ]                                  D3DDEVTYPE DeviceType,; X0 J' T* M7 q; t
                                  HWND hFocusWindow,
- S1 ]3 Z6 s3 ]  f: ^                                  DWORD BehaviorFlags,
4 ?. o3 G: c9 i# M. q, u- o: _                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
/ ^2 L7 h) S& K" Q6 A( t# V/ i0 A                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
( t+ [( o1 s2 p; q+ m. {1 {( o% q! K
                                  );+ B# Q5 Q- ]1 p7 E7 Q" R
9 k4 u. b4 n$ d0 [0 Y. F
其中的LPDIRECT3D9 pDx9就是this指针.
+ i9 b5 S" {4 y. c5 Y, B1 X0 j7 W) a1 H) l! i0 T2 A6 ~! E
hookedDirect3DCreate9的关键代码如下:9 v9 \: M3 F$ w, [
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
+ p) J/ w0 s$ v3 W
" F+ z$ m. T/ J, q/ N# H        DWORD oldpro=0;5 ~$ p" k$ a5 P2 j6 Y
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
3 J4 w8 c. |8 W2 v+ h( E3 q( N        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);- F( n- a6 ~0 y% i. w+ t) W3 W4 d
        *(BYTE*)pCdev=0xe9;+ u! m& }8 F2 k+ r( t
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
: |: C# @; G. X$ w: W5 j9 ~
" }4 U3 X2 L$ h在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
6 m8 h) N! m. w( kpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
/ {& [/ e4 Y# t# y: E& J        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
' w: T8 j" q3 o  I        DWORD oldpro=0;. h/ j6 s/ t) U' Z! L. `0 K5 x
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);6 }/ V' ?7 t5 u3 `
        *(BYTE*)pPre=0xe9;
( ?) X4 p8 E- w: ?3 y4 s. Q        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
4 f6 T3 t8 A1 i: L+ t4 R; S& x  k4 o' k  t
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
# B( \) M+ V* M% I3 C8 tchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";2 L3 d  }3 d1 q; N. B
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
* L8 K$ m0 z1 a& }+ |+ o" Y  c            //在这里写入您的其它绘图代码
6 Q6 B; n& G* i' g: V# ~  F6 n# B" O# l7 z+ \* u
% o+ G9 o  x7 ~/ T6 B2 f: V1 F) w
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
* j6 Z; Y5 u, x: _BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf): o% `/ h6 C4 t# J; w, P
{
# Q7 o/ {3 K; ~  C* I7 ~$ n, Y0 D% \* Y; S. [3 ]5 n: w
    if(m_pD3D && pDxdevice){. x% Q5 I1 v, O4 ?6 p9 w" h8 H9 V+ U

% O, i' R5 M- k9 v5 ^4 w        RECT myrect;
" l- x5 E- l& W+ z5 f  q        myrect.top=150;  //文本块的y坐标, G( m7 P; h1 @) B8 m4 w
        myrect.left=0; //文本块的左坐标
( ]6 t; q' c: j4 K4 [  M. A" W! _        myrect.right=500+myrect.left;% ?8 f: n) I' |" |% u! S
        myrect.bottom=100+myrect.top;1 j" y+ A8 t( ?$ t& y  l7 S
        pDxdevice->BeginScene();//开始绘制" R$ D2 c0 @" F: B- d
8 u6 H( `; |- q7 Z
        D3DXFONT_DESCA lf;9 Y5 D+ B( H5 H! }- D
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
" s$ G; w8 Q- i* L, \        lf.Height = 24; //字体高度
0 M- F/ a& ^+ m* b# h        lf.Width = 12; // 字体宽度/ G: x2 ?3 Q( s* m
        lf.Weight = 100; ; g" |' ~! d" x5 \5 a  r
        lf.Italic = false;; ?! p, b1 ~9 _6 I/ ^; A  A; I
        lf.CharSet = DEFAULT_CHARSET;! t3 i0 ?: Y( D. H
        strcpy(lf.FaceName, "Times New Roman"); // 字型# Q# ~. g8 ~3 b$ T- s' a  i  @8 S
        ID3DXFont* font=NULL;7 K" Q# p% l, z( E; M
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象. Z7 l" K  \- v% N
            return false;
& J: }; x/ v9 K6 O/ F3 U5 e, @: i' p" H- ~, R: V  k3 L/ [
        font->DrawText(
! y! M* k9 o$ a7 w" a2 h& p7 C            NULL,
7 G7 J0 V4 x* u            strText, // 要绘制的文本# i& p: ]9 v8 E$ G  ^- c
            nbuf, & ^8 k4 q+ B& d. A, C! o0 w. {
            &myrect, 2 A; q- V7 u& [) F7 n% o
            DT_TOP | DT_LEFT, // 字符居左显示
% a& h5 Q& a0 E! _$ Y/ k            D3DCOLOR_ARGB(255,255,255,0));
/ x& P+ [9 ?  u( F2 d5 o% p% l" i) v# ~
        pDxdevice->EndScene();//结束绘制- t# N* m/ O4 F7 @
        font->Release();//释放对象
( ]- ?+ M' w" T7 Q9 Y4 c    }2 Y, j- e, X: K" J8 l2 K  v7 Y
    return true;
1 \& L& ]: `! j4 m1 N. h$ `; K}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大, S& F! u! ^0 Z! B: ]: B
首先不能方便的HOOK所有函数,需要到处打JMP补丁# T+ S* v) D5 y- V  v+ u/ m
另外每HOOK一个函数都必须自己去查找D3D对象偏移
7 \8 l5 z4 E  p/ s7 }  hpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针, g6 b! M7 k0 Y: |' s
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下6 U. `6 _- a0 A: H! D

0 u) y' f9 }: V7 I1 p+ P7 a还是007那个方法好,也是我一直在使用的方法
8 |  ~' @% Z$ j直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
& M7 J/ F% |( ]# E# l只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
# t* T3 j6 L; V; k* Nhttps://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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