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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法' s# z1 x# U4 D7 [1 T/ R
作 者: runjin
* G) w9 o4 A5 M3 M  b& I  _" M时 间: 2009-04-03,22:44:51% o# k; V! T! D3 D/ d, I! R
链 接: http://bbs.pediy.com/showthread.php?t=85368
- b; w) l( ]- Y& E& p* C# z) z( T7 a5 a) L
Hook Directx:在游戏中显示自己的文字和图形的方法, O0 {9 l- ]4 p) L+ x
! z; {# X* u1 p$ ~
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.3 f) Z% ]- P- I1 D; u- @% f$ z
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
/ J- m& t7 b: j0 N) f' o% E还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
" e$ z2 D7 E7 j, c$ z+ e1 w- m首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
& Z  y9 a) j3 W1 b! E5 k; W    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
) U/ R/ ?0 s8 M3 r9 @" y    DWORD oldpro=0;
6 l5 O2 E! b" ]    memcpy(d3dcen5bytes,pC,5);' S/ h2 }, U6 [5 e- Q
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
- H, w7 L+ |$ `5 @) F7 q# _4 M9 t; |    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码8 W5 z2 m) j6 W' {" ^
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
  L, }! q4 q* [3 ~, _, A* `$ ]9 J  b2 I9 n& o. {. N
& H# L, [' L1 O' w$ h/ L
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:- a/ c5 G& f5 K. c- Y2 O0 R
HRESULT CreateDevice(
; y% e) ^) S6 Z  UINT Adapter,+ C, ]) U( J+ h: c; Q% c
  D3DDEVTYPE DeviceType,
. S: H1 [" [( a* c7 p& l, z2 B+ k  HWND hFocusWindow,( h0 j+ z# ?$ Z2 E
  DWORD BehaviorFlags,6 U& w$ a: A! h) N7 o# X" \
  D3DPRESENT_PARAMETERS * pPresentationParameters,+ h) h3 g' I2 [$ I2 C- Q: u
  IDirect3DDevice9 ** ppReturnedDeviceInterface3 i# @* P9 t* I  i1 m; ]
);$ v8 L2 p4 _: J1 e7 t
+ j, ?2 V. g6 T

3 a6 z9 Q' n3 m9 l3 W4 ~! w. I而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:/ r0 s: ^+ q1 g  W, m
HRESULT _stdcall hookedCreateDevice(
  V5 e$ X" i6 h9 `& j, m$ s8 f                                  LPDIRECT3D9 pDx9,
, Y' w/ D. \, q! n  k; r% w                                  UINT Adapter,
9 J6 F% Q! A" v. d# P, _" K5 `                                  D3DDEVTYPE DeviceType,
8 u# W$ u  _: R1 ?/ M                                  HWND hFocusWindow," Y, j( R; w# `2 W8 Y3 {
                                  DWORD BehaviorFlags,' Z) w) t: C& g& h* E& c. N
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
- f# C: e$ L# V/ A6 e5 u                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
" O6 l+ K; |: U4 D: \( ~/ u
; i& S4 x6 L7 M- W1 r                                  );
& D+ O" f8 w8 ]1 z# \5 J$ O5 d: A; H  _) |# J- }$ z8 J; ~3 n' O0 @
其中的LPDIRECT3D9 pDx9就是this指针.1 a1 B$ {" U  I3 m
9 D# v5 \" L/ I& O0 M; {4 y
hookedDirect3DCreate9的关键代码如下:& h, y1 v" I# p7 U2 ~$ ~
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
" O& z3 G5 K. c; e  f; g3 p
; M4 }  t2 r0 g9 t# A        DWORD oldpro=0;
  y# O" c5 V" [& p# {8 ]        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
2 [/ i2 f$ d) h, X! ]' @+ f        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);( l$ d' r1 t/ B* Q, M
        *(BYTE*)pCdev=0xe9;' K' O6 s; V! o3 o  J7 ~
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
: P! Y' R* G1 f+ i/ t, a2 u* O3 j! i2 k& x0 Q% |
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
! Y" w( c/ c2 _/ D7 R) z9 g$ HpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
9 `4 Z7 ?, w3 ^0 c+ F( |        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
* S8 m, S! H' ^; l        DWORD oldpro=0;) W" @% @- c1 x: A* v
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
4 c7 F5 L! C! X# S4 e4 v        *(BYTE*)pPre=0xe9;( a& j- @" k! m( U) g
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
% }/ x, Z! k- W% X4 ^& }- A
) W" w7 W3 `- R; I  C! ]实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:( W! }- l( O! k+ o5 ?3 Q
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";7 \8 d& D2 u/ d5 E2 K4 C* @
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
; M( _* T! F) l; N( Y            //在这里写入您的其它绘图代码
# W" Y) k! X; K0 j: R- K. P' O
8 X, r% P/ l* B$ t5 p7 H: L" S9 V9 _2 r+ D3 C
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:& v1 ^0 e* A  ], y! [
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
9 I0 F2 x/ ], F9 T0 I! w{
# s& D3 R5 D- g/ y7 W, X
' S$ C' |6 `  r    if(m_pD3D && pDxdevice){
2 ]# k0 v  w" V6 _, p3 q$ Z" i: L( \$ o/ v+ H+ }- `3 V3 P- e
        RECT myrect;8 J+ X" j; h& e
        myrect.top=150;  //文本块的y坐标8 w4 U# N: V7 J/ u, f$ x
        myrect.left=0; //文本块的左坐标+ P7 \' I6 \% L% A2 d% B2 Z) u
        myrect.right=500+myrect.left;
; T0 m: I8 t% _1 j        myrect.bottom=100+myrect.top;, M$ j2 O: _8 R
        pDxdevice->BeginScene();//开始绘制
9 ?4 G! W' Z0 h0 B4 _9 Y
8 F# w- M7 G/ r        D3DXFONT_DESCA lf;
/ k. r2 }2 u8 g$ v0 {$ b        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
% M0 g0 K# Z  T& @0 C( N2 C        lf.Height = 24; //字体高度
& w9 O, Q( k+ }7 |        lf.Width = 12; // 字体宽度
$ A- X5 b  y% D5 |; V        lf.Weight = 100; . ~/ p+ \, W, Z8 M  J
        lf.Italic = false;
! @( j$ C1 J: ?! P% ~* U        lf.CharSet = DEFAULT_CHARSET;
+ c1 z* l) V' K: O        strcpy(lf.FaceName, "Times New Roman"); // 字型3 ?2 @& w  f! M! ^+ I
        ID3DXFont* font=NULL;7 u; k* n2 \# X1 k# d
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
- e/ g2 P$ B" c. C  a            return false;/ ~; m4 x9 F# ^& H' W7 ~

6 r: a. B1 h# ~+ q        font->DrawText(
  m9 x* x/ Y0 \8 P  L7 h            NULL,4 k; R2 z8 A& R, j' x
            strText, // 要绘制的文本6 @4 R8 z+ K0 ^. c( z
            nbuf,
  m' p( U4 c5 S            &myrect,   n  z' Q; Z+ D+ r! ~( [9 R
            DT_TOP | DT_LEFT, // 字符居左显示
, ]/ L8 D9 i; r5 I( g            D3DCOLOR_ARGB(255,255,255,0)); . L1 M6 J# D4 N$ {
8 e9 o8 M1 G; ^% a" [
        pDxdevice->EndScene();//结束绘制+ y$ u6 A, p* T1 \
        font->Release();//释放对象
6 l  G3 e. m( v/ T$ [- I    }" o* ]$ d, ]+ D$ w* J. T, a
    return true;
% v& f1 g) ~1 ]+ ^}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。- {- N* u3 ^$ S# S! e
https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
0 g1 f" R0 _/ K首先不能方便的HOOK所有函数,需要到处打JMP补丁; a5 j" d8 \2 }, ~8 X) }( i; B: j% }& X
另外每HOOK一个函数都必须自己去查找D3D对象偏移  @6 \4 r- P+ Y  H' R- I0 _" j
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针# g/ X9 A1 d0 y
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下/ q: |7 K5 W, b. N/ c$ P% v
% v% k7 `7 r# s! g# K
还是007那个方法好,也是我一直在使用的方法1 t# x# \) I( Q: c8 b. ]
直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下6 Q7 A* X  |& S" A: b
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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