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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法. D+ p$ P3 t8 k
作 者: runjin
' \9 N( V  ]! Y8 e时 间: 2009-04-03,22:44:51$ p$ x# @" P0 K! J9 u
链 接: http://bbs.pediy.com/showthread.php?t=85368
( o+ L' {8 ^* O2 Y+ O9 o' x7 @3 e" L
Hook Directx:在游戏中显示自己的文字和图形的方法$ n& [1 o- F2 O, _* N" G* }
! B, Y& P, }0 Z
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.! ~# K0 @9 `* h  ^/ v2 `: `
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
$ ^5 Y5 Q8 Y: T6 D- h! u6 C还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明., X0 o" y7 R3 \3 ?# [8 x
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令., I% I5 Q& R0 S+ I8 r
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址4 }- ?! Q2 w6 g( p) g$ M
    DWORD oldpro=0;4 o3 j' W4 k% j
    memcpy(d3dcen5bytes,pC,5);% I  t( [3 {* q( {
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);$ b- \- w! u4 x) h* _! K7 s4 p1 ]- f
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码! `9 H8 z8 N. @3 P0 o( L* l' F; \
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-54 w# x: X$ b* ^6 O2 @

2 i3 v( C8 T# S, ]5 Z1 f8 T) g2 [/ G1 f4 a  ~. C
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
) S" [3 h2 z* P+ A1 W$ _HRESULT CreateDevice(
6 v: x+ x( C  }0 y: |7 S  UINT Adapter,+ W- a* w, Z" q1 w1 }7 l* P+ t" ~
  D3DDEVTYPE DeviceType,5 Q& r' ]* j' x4 x
  HWND hFocusWindow,! _+ {8 m' x* @5 K2 }$ U" Q. ]
  DWORD BehaviorFlags,! M$ O3 \: |- J9 t  E0 ]
  D3DPRESENT_PARAMETERS * pPresentationParameters,
; s$ s  d0 s& r  IDirect3DDevice9 ** ppReturnedDeviceInterface
0 E7 z* I; {! s);
4 _1 a. e, W& w; W# F; c$ y, y8 q" ^9 n: i$ X

6 z; ?5 m7 p# Y" Q6 u* c  ]而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
! L2 W* U2 _3 u( v- a0 |, zHRESULT _stdcall hookedCreateDevice(
! v  W/ g! x: J                                  LPDIRECT3D9 pDx9,& T' |+ {0 }7 ]3 S  w! b
                                  UINT Adapter,
3 k  N+ o% U6 W- y* D% n7 `' U                                  D3DDEVTYPE DeviceType,0 r% f9 t9 e* x" Z- @
                                  HWND hFocusWindow,$ F& m- Z1 S* ^! `7 d
                                  DWORD BehaviorFlags,8 I' P9 Y" W/ ]2 K* {6 J) p! A
                                  D3DPRESENT_PARAMETERS * pPresentationParameters," p0 W6 ~# c6 R
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface0 A' a1 {+ ?3 |2 |9 k' O% I8 Q2 Y7 t

" l" P3 v* a9 z! q7 T2 q                                  );
7 {6 K2 j, B0 Z0 w$ G0 Z( p# ?7 M
其中的LPDIRECT3D9 pDx9就是this指针.5 H5 E  Q- ]% q+ \7 x( A8 p
5 `3 Y3 i' J. i8 \: M
hookedDirect3DCreate9的关键代码如下:/ X+ C$ x% f- M7 U! H( r5 m
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
# y7 s" z4 S+ e% J/ `6 X  a- o+ q% }" L# W+ c
        DWORD oldpro=0;) r5 Q+ M7 ?; x% P6 n9 q
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
) z; d+ s( v) t8 P% Y; J' @& i  h        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
; \+ M  U9 q4 S6 \2 E        *(BYTE*)pCdev=0xe9;5 h7 h) u0 Q$ ]! |2 M2 B3 Y3 L
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
. U  x7 h! U6 ]" M' l, }5 l- L3 A, M7 R) V
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
$ n; t; {# B$ B- t+ n9 bpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针  ?2 C& ^5 Q6 j' L1 x
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
! J; D# r( ?1 a3 A! N        DWORD oldpro=0;
' q/ Q! m2 l0 `. A" }/ M/ o8 ?1 o        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
, l' C9 X+ k; G$ V5 H        *(BYTE*)pPre=0xe9;
, ?& W. G" \3 h! n$ U! A        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;, S8 e3 q- S* R8 O" _
/ m0 E5 Q$ |7 f- E- O$ W/ B
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
: o, _6 ?: y3 O$ t1 p/ Jchar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";: V2 D- K, r! w  D- F, N( X
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本' |2 o# \; `% W, e3 ^* }* N
            //在这里写入您的其它绘图代码
4 n0 @8 T0 A- w: ?$ }. ^1 K# y% M% S- q, Q4 r7 X% d4 N* O
  J" u: W9 J  P# N0 j; Y. `: W
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
: V# I( \' C$ Z4 yBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)  X, e7 ~: x; Z' \  y2 @
{
: a$ s5 N# Z) E1 k; `* B
9 }  q" Q; ~2 h( c5 @! M    if(m_pD3D && pDxdevice){- l! _$ R5 m  L6 d/ R8 B# d( P$ S
9 V+ |) q& a  _0 s5 e1 s
        RECT myrect;
( A5 ?3 b% W- F$ v! O; G        myrect.top=150;  //文本块的y坐标1 ?3 a, s3 G' h2 I
        myrect.left=0; //文本块的左坐标$ H! O/ W5 ~8 ?1 y6 L2 y
        myrect.right=500+myrect.left;
* W; _3 Y) ]$ i' ^+ o+ H        myrect.bottom=100+myrect.top;) Z! W% @. |/ N* x+ I3 z, L
        pDxdevice->BeginScene();//开始绘制
+ A* L$ i, }8 v3 `6 c' s, d3 e4 R- [& \, a$ s+ \. s2 |, r
        D3DXFONT_DESCA lf;2 q7 b, B# M4 ~/ W5 l
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));) d" ^5 [1 h, M' z1 ~1 H
        lf.Height = 24; //字体高度) {3 g/ h+ T7 J( B9 ]' m8 c# L
        lf.Width = 12; // 字体宽度2 O/ b" b, {: X( V3 |0 q- P
        lf.Weight = 100;
' f) T& W. a8 N4 d        lf.Italic = false;
9 P0 E5 s; F& E! W; q6 d. L        lf.CharSet = DEFAULT_CHARSET;
4 A  u6 u8 b3 P; V        strcpy(lf.FaceName, "Times New Roman"); // 字型# z% T9 H, o& q3 I
        ID3DXFont* font=NULL;; y$ B  k0 c  F4 E3 R
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象- ~; h- `' N* d8 H
            return false;% E7 @7 ]3 m5 P# R

5 O- P: W( }, \' w" H4 @. h- J        font->DrawText(
- E, h8 ~( ]' o            NULL,7 V, ]6 |; ~# g/ j7 e, w9 d7 @
            strText, // 要绘制的文本4 n) d5 g  @! d% _+ {% g% s
            nbuf, # v4 x0 v: A2 D. g& T: J# {
            &myrect, , K9 w9 c1 y9 X2 A
            DT_TOP | DT_LEFT, // 字符居左显示3 e$ \. Z" W& y" K! p4 g8 Q
            D3DCOLOR_ARGB(255,255,255,0)); $ R! `, N2 N. A6 W* _3 n. }
8 ?; Q; X. u- T! u8 A: E' b
        pDxdevice->EndScene();//结束绘制3 V9 x$ }. C1 T8 E0 g8 G! f3 {$ L
        font->Release();//释放对象+ g9 Q6 p% W: s. p! ?
    }
+ ^9 h* N1 r) i3 c    return true;! N" k6 c! s6 f/ h9 N
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
$ v' {, l+ Y4 w7 u) o3 m首先不能方便的HOOK所有函数,需要到处打JMP补丁
# u' p' @; P6 w# K6 g2 M8 a6 K, j) `另外每HOOK一个函数都必须自己去查找D3D对象偏移3 ^' k+ d* M1 ]7 x+ b
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针4 C9 B5 R, P1 ?2 N: ?  a* y
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下6 Q+ U7 `7 c7 I. b5 W8 c
! {7 Q7 [: W4 B* K1 `
还是007那个方法好,也是我一直在使用的方法3 T, M4 g+ V0 H$ }' M7 X) p( y
直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
7 K6 X, ~" c& s- R8 J只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
' l% w- l3 [% K$ r) w7 d% `1 Fhttps://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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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