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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法
3 d0 Z: m0 t! k8 _# B作 者: runjin, K/ [, f5 D$ e" d) O
时 间: 2009-04-03,22:44:51/ L% A' p1 N7 X) ^( s* o: O( e
链 接: http://bbs.pediy.com/showthread.php?t=853687 Q5 S& ^- N( `7 m" ]) b1 h/ d" J
! U% L: U( K7 K" z5 Y+ |5 U
Hook Directx:在游戏中显示自己的文字和图形的方法/ q9 |) N1 b, I3 s% l; n9 G

! S- M' j( C5 P9 W- Z, a这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象./ [4 G4 A) S# i7 O6 T
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
. @) z. T7 ]2 `1 |1 v; H/ ~还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.! Q( W- K5 Q" c) W/ K& E
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
' r" `/ v6 f) M/ t. Z6 G    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址* ]$ ~# q6 I& P9 U
    DWORD oldpro=0;# U8 Q. n! d/ @. L6 j4 Y: `( t* T2 V* \
    memcpy(d3dcen5bytes,pC,5);
2 y5 b% U4 B, e4 D% w    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
; Y" G0 s% T1 `! j3 i    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码! j7 ^- F7 h. R
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-55 ]9 g7 Y1 J) U3 t1 u

! _+ K/ a" [0 ^0 F3 |- ?( K3 f
* Y* R5 N. n" \这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:4 ?1 y5 Z% |+ m; z  ^
HRESULT CreateDevice(
8 m) c* b1 n0 `% D* ~% O0 r  UINT Adapter,
# X" h; @7 l1 _% ?5 q( H) u" I  D3DDEVTYPE DeviceType,
' `1 `3 [4 B+ J1 v# Y  HWND hFocusWindow,) m0 l7 W% A" F
  DWORD BehaviorFlags,
9 [2 Y9 P# W* t( L, x  D3DPRESENT_PARAMETERS * pPresentationParameters,2 H1 _/ H+ f$ y- H2 W: [( r
  IDirect3DDevice9 ** ppReturnedDeviceInterface& D/ q6 _" O% }+ X
);
: k4 `% S: ]! r: b& U4 ^! o0 D) a9 s- T" U( c# K
1 i2 Q5 O: G! q
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
  A( ~$ d7 A: Z  X9 V1 ]HRESULT _stdcall hookedCreateDevice(+ f- l+ D* V, s; O* m
                                  LPDIRECT3D9 pDx9,5 d" B0 z2 x9 z! j! Z
                                  UINT Adapter,
" f3 I& u) l" ~2 x7 k                                  D3DDEVTYPE DeviceType,
7 K. Y" ]. h; U9 n7 W, @                                  HWND hFocusWindow,) X& L/ l6 n! S& J: M/ y  P% k
                                  DWORD BehaviorFlags,3 r3 r7 S: ]! Y' d, M. {
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
, V- b* F" J% A: Y$ _5 z8 ~) Q                                  IDirect3DDevice9 ** ppReturnedDeviceInterface  U/ t3 p! v* q

+ z! ^1 I/ G( ]: _                                  );
4 y! ^9 ^8 F  K- C
; b7 V  j2 M+ }6 Z& c4 |$ i其中的LPDIRECT3D9 pDx9就是this指针.
7 t  h+ V9 h8 a2 E0 `) Z  n9 i% Q; K$ e. Q2 ^( A" \
hookedDirect3DCreate9的关键代码如下:1 Y: U+ ?3 |  u. m8 |
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
$ |3 g$ L8 j/ f+ F" A/ Q/ ^$ G: a4 v0 [& f
; S/ U/ X$ P. o  B/ V        DWORD oldpro=0;
; E6 m1 @: p! O- A        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节+ ?: i& U% {4 Q1 ^5 [
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);. B# G9 X$ q' E9 V3 S( N" {  t# U  Q
        *(BYTE*)pCdev=0xe9;
6 ~$ y2 o3 g) G: G' S( |' ^. @! _: N  _        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
& x, |: U7 r$ C+ c  S; D7 m9 U6 K0 z% o) W( f) {3 r9 j
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:! x: R' x% B* F: Y7 Y! j9 }- O
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针2 D) h% p& U5 K. S7 {
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节: s  ]' ]0 a. P  a( L
        DWORD oldpro=0;
8 Y/ u( I) g/ B) j, {% `        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
8 t5 p4 }& T+ m0 T8 y# K/ }, C1 _        *(BYTE*)pPre=0xe9;
# `! H# v( ?! @! v6 ^        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;4 C, J1 w" ]$ i
' g' p  X( Q% Q$ F6 o9 `- a
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
+ `  I$ h( p! s7 k$ A/ `char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";* K- Q0 h5 N$ z' h7 R$ f
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
+ g+ m  u- R/ A' {# c- r' z            //在这里写入您的其它绘图代码$ O/ R- T1 l; H) C, ~
( M" g' G  \, ^% }7 A2 }
" F' T+ h) G0 i/ Z- Q4 Y/ L& @
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:6 ^1 N- C/ g. J8 y/ y+ D
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)1 `7 r2 D4 {8 K6 c. }
{
( i: E8 _# L3 k/ s& M9 K) @* `2 W/ F& T: _; v
    if(m_pD3D && pDxdevice){$ V) T" f' G0 h7 R1 x$ f

/ Y# `/ d# I( i2 V        RECT myrect;
" j; K2 r0 Z' _* l        myrect.top=150;  //文本块的y坐标: \- P8 N1 A3 Y3 K9 ?
        myrect.left=0; //文本块的左坐标1 H/ X  ]5 V6 i3 ]) H4 O
        myrect.right=500+myrect.left;6 L7 S, v' P* I
        myrect.bottom=100+myrect.top;
+ A3 @+ P( S6 r( d* v, s3 w        pDxdevice->BeginScene();//开始绘制! P2 H% Z2 q- g9 i. Q0 {" k! S
0 J; J4 F* v! \3 h1 d/ d1 P
        D3DXFONT_DESCA lf;
" n4 Q. ~" U3 G1 L8 z( o        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));5 `  l/ Z1 X/ e* r* Y
        lf.Height = 24; //字体高度9 c: S2 v9 Q" i
        lf.Width = 12; // 字体宽度3 o. z: x1 L# O) [( b$ S" t5 g# N- V
        lf.Weight = 100;
3 e$ U- ~, U0 x! m( j6 T; A6 O        lf.Italic = false;& n: C% B9 j1 t0 Q3 d4 n. d. `: h
        lf.CharSet = DEFAULT_CHARSET;* e6 v6 L9 g& _- E
        strcpy(lf.FaceName, "Times New Roman"); // 字型
7 v2 o6 X" H! x" C( j% X        ID3DXFont* font=NULL;
1 r1 e5 f/ x# I. o, N$ p( K; x        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象# M- K. V; N4 `& }  a
            return false;. `( y, e$ L) I
, }7 N9 N" }1 N+ i4 l( u
        font->DrawText(
& J& r" b" m/ y1 e" A1 ?0 }/ O6 @            NULL,* y4 x5 _; h& S) `
            strText, // 要绘制的文本
# V! H' ]' z: _" E7 E2 L: m- @" d            nbuf,
# l! `! O' N1 c) v/ `6 n            &myrect, " \7 [1 F  Z# t. ?. o6 m+ |
            DT_TOP | DT_LEFT, // 字符居左显示
: X% J! {& V6 d3 }* `            D3DCOLOR_ARGB(255,255,255,0)); / r" ~, t: s3 H4 P

6 d, y% e- T: X        pDxdevice->EndScene();//结束绘制
3 `: y( n7 E9 M' E  }7 X  j* x        font->Release();//释放对象1 B5 o' n% u9 i  h8 [( y" Y
    }4 H* J; B; W4 h- f
    return true;
# W9 n' M* O# Z' Q2 n}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
+ F' e9 K6 `2 z( H+ ]( Fhttps://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
3 f' C& O# [* @- Y3 C: d, T首先不能方便的HOOK所有函数,需要到处打JMP补丁3 z  z/ i# K: X
另外每HOOK一个函数都必须自己去查找D3D对象偏移/ d% M8 A- d# G  l) w0 N
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针/ P+ x- s$ d' K0 I3 f  q# W$ m
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
/ Q: j$ S6 w2 \
, X9 j' I2 C9 O4 n, X$ ~还是007那个方法好,也是我一直在使用的方法9 g  R/ e" m/ E" q: C3 l
直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
: R; g5 c5 L5 ]2 |9 {2 R只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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