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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法  F3 M! M" k2 g- T1 q( C2 \/ B
作 者: runjin; {; @5 n" }. @9 l
时 间: 2009-04-03,22:44:51
. d7 h9 a; w" F3 r! A: c链 接: http://bbs.pediy.com/showthread.php?t=85368
/ a/ f% T. n2 U0 ~2 j; B/ z
& T( d8 T' a8 i4 k2 Y. n& s7 L; THook Directx:在游戏中显示自己的文字和图形的方法
4 T: r& F4 t6 h6 U/ @
: |3 b5 Z3 V5 l& G  k+ y0 h这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.1 D8 X# o) [. F9 o! g  j# |; @% R* n4 y
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
+ N6 [  W* w$ I& B5 W还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.$ `; W; p# i# F8 e2 {. e/ D/ Y9 m% C! ]
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.( i& ^6 Z. ]- d& q7 P3 ~
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址2 L0 e2 {0 o% v, D# ~/ y7 t# k' q. J) ^0 W
    DWORD oldpro=0;
( S" J' R4 _* ^5 {' i; a6 u! y    memcpy(d3dcen5bytes,pC,5);
5 G7 M: F% ]: W- o" E    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);$ c# P: V* x% ]1 r+ i/ I
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码+ Q9 W& T8 S8 F0 |& W$ B
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5% Z' E+ E& ]  Y& Q! M
5 [! B& L! }) P. w& k: x
$ s; G* L4 |0 Q
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
9 k$ G! i8 q) xHRESULT CreateDevice(6 p/ a' ]8 L8 o, D. U( m* g
  UINT Adapter,
$ p0 ?" ]+ b$ J' D6 _# Q  D3DDEVTYPE DeviceType,4 }% [9 s+ \" [" i8 X' x! a3 T: m
  HWND hFocusWindow,& y. V6 ]4 a+ H7 g/ z
  DWORD BehaviorFlags,# _( N  G- p$ N
  D3DPRESENT_PARAMETERS * pPresentationParameters,, Y+ F0 a* c& v4 v2 y0 f- d
  IDirect3DDevice9 ** ppReturnedDeviceInterface
1 P9 A2 O7 a! ~- p- r! A" Q);
& R; ]/ h2 B3 J9 a7 k: t( y  j3 ^. I
- ?% u; X) V! ^" L
& d5 `3 U) ^% W5 h+ A1 G* ^- T& A而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
7 n- e  F; Y2 @' h7 D7 yHRESULT _stdcall hookedCreateDevice(9 h% y' }$ {" c
                                  LPDIRECT3D9 pDx9,5 a3 ^$ i; t' y, P. A
                                  UINT Adapter,6 c1 M5 _& ^  U' K* W9 f( T
                                  D3DDEVTYPE DeviceType,
! {( B' T5 L' r1 Z2 ^- J6 a2 d! ]                                  HWND hFocusWindow,4 \$ L1 D( o8 _+ l9 E' s" t
                                  DWORD BehaviorFlags,$ W9 X3 t2 P' @' {% b' G
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,5 ]$ K* \" |" m+ G
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface: v5 `+ U' S! H; L) y

0 F- [; N& f3 i. l                                  );
6 N8 j+ X' E% u
5 M! i2 z% _* K其中的LPDIRECT3D9 pDx9就是this指针.
7 Q+ C2 Q7 T3 _% N' u% [/ s: A3 o. h4 Z
hookedDirect3DCreate9的关键代码如下:
6 E8 M4 I' |4 ]- r: n2 |pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
5 u: X* I8 X1 m& x: Y4 d- N% P* |7 D3 b4 n8 p% ?. S' k( V
        DWORD oldpro=0;2 E6 U  s; _+ ]/ j7 H5 C
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
! k+ ^" K+ t+ L0 [8 O, C( s+ a        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);! N. x9 R' N( A0 K& b- S1 D
        *(BYTE*)pCdev=0xe9;2 k3 u( X( X; W) w. t# H0 t
        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;  N1 n# l% c' a
) R2 }% \' P7 K5 K
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:* r/ G9 n! U! g$ Y8 M& \; }
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
- E* I4 s, Y+ t4 ]2 m; @: j        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
; x: f8 F5 Q4 e+ ]; S& d8 M# X        DWORD oldpro=0;) q7 D' x- T) e! x' k/ _
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);! @; _/ j+ a# M4 n3 s
        *(BYTE*)pPre=0xe9;
2 t5 ]; `* i8 S- f( y        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
1 ~, l; m5 `( Y9 B4 H" ^% O. H3 I* Z5 P) S+ H3 o6 j
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:; h! w* }+ y: W; }
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";4 \( r- t# y6 F) l) v1 o
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
: m1 T2 p/ T3 u& r2 i" t$ x            //在这里写入您的其它绘图代码0 l6 H' b* t1 p! P/ @
& \. A5 I: d/ ]* u- c: V. M! q
& @5 H/ n4 u- a( @  z: r6 J! V
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:* w' s" ?$ Y6 ~- o1 [
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
8 o; y1 K4 E/ c2 s{& J  Z& E; m- y8 G. o% U
' ]: Z# B% E4 K$ p: ~/ N
    if(m_pD3D && pDxdevice){
8 n5 M  C4 b! `7 U& x7 Q
8 q; v2 k/ B7 X- }7 ]* P" H        RECT myrect;( i) J: f5 S2 ~+ z0 [
        myrect.top=150;  //文本块的y坐标; I7 U, z1 n7 f
        myrect.left=0; //文本块的左坐标( e# G+ k- V7 @  J$ j: U1 L
        myrect.right=500+myrect.left;3 v4 e8 Y; a& a6 o0 a; _6 Z: W
        myrect.bottom=100+myrect.top;
# w+ G8 F  [( f! z        pDxdevice->BeginScene();//开始绘制
  f$ ?! r. p  P# X2 _  K* ]( R' Y
; }2 H% g5 O& I        D3DXFONT_DESCA lf;. E% p) |- D& i$ \5 _* Y- E
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
8 t" E& Y9 E# J8 x: p0 X        lf.Height = 24; //字体高度& y$ l$ V% [/ }
        lf.Width = 12; // 字体宽度. x  v2 p8 ^4 o6 G! S8 A; J( `
        lf.Weight = 100; & {7 F, U3 d& _; e7 o7 Y
        lf.Italic = false;& i  Z8 |% m6 [
        lf.CharSet = DEFAULT_CHARSET;3 a6 J& I* `2 j8 z$ b* s) g) T
        strcpy(lf.FaceName, "Times New Roman"); // 字型3 j4 ^, J/ [$ }3 x
        ID3DXFont* font=NULL;
7 x. t2 a1 H( O) Z% ]# w        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
5 r' n1 Q9 V7 D; [            return false;7 z# h: }; w) \4 x7 R8 R/ v
4 v3 H# ?3 U) W6 a& ?
        font->DrawText(
7 F2 L6 F1 V" P            NULL,! q+ A5 t- U2 j3 g; q
            strText, // 要绘制的文本
7 K% }$ S8 Y1 N7 E            nbuf, % y* d0 T. Y3 R+ |
            &myrect,
) H) H4 {0 D4 Q. b8 X; L" M            DT_TOP | DT_LEFT, // 字符居左显示
( @5 y! [( T, s  @6 {& j            D3DCOLOR_ARGB(255,255,255,0));
, q$ Q" @) H! M( h0 }7 ~3 A3 E! Y2 r- M
        pDxdevice->EndScene();//结束绘制
7 ^4 F0 r4 o. k  [4 T% c& V        font->Release();//释放对象% ?6 ]- x  y* F
    }6 \5 `6 S+ c5 x+ H; r7 t6 @
    return true;- q( x4 U* W6 l0 L& e8 ]' p
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。! ?% A$ b! y4 t3 ]' G! K9 w9 [" V/ ^
https://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
- R4 T6 O7 S$ }2 F0 ~. P首先不能方便的HOOK所有函数,需要到处打JMP补丁
# q6 Q$ D( g# }! {" W9 ]另外每HOOK一个函数都必须自己去查找D3D对象偏移
  e- \0 Z- J5 m  |2 W" LpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针) K( C  v* f5 i4 e' b/ D2 o$ j
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下! R$ J3 `8 @! D6 Y. g- x/ N, J

( L' r) [: s9 S' H还是007那个方法好,也是我一直在使用的方法
  x  h  Z8 g; _4 ]  q' k6 c# p6 g直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下$ d; c, D# D, R4 \! [) |- e- i
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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