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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法
+ @2 B, t" S( v+ b作 者: runjin
3 u( I) `8 i6 N; L5 P7 ]. s时 间: 2009-04-03,22:44:51- c- s1 K9 e' o& L6 F, O! _
链 接: http://bbs.pediy.com/showthread.php?t=853682 c5 @- D  H/ _& x5 V3 _+ Y& C

) B0 K. b" i0 ~7 D, vHook Directx:在游戏中显示自己的文字和图形的方法; H6 S, z! U1 f8 D$ i' L8 h. X

- l' V4 N, k) m0 ?$ E这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
+ r, a! o  j* i7 E: u- M& r: N6 t其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.# [: D; _3 A7 g9 f2 Z" W5 v/ S
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.8 `' r/ v& K1 \. g/ _# h% X
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.: L# c2 H& J. g# ?# k  R8 q
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
9 S5 {" N4 o6 e; }" `3 |( ~( u. ^    DWORD oldpro=0;
% G- x7 k& ]6 C    memcpy(d3dcen5bytes,pC,5);$ s7 P  @$ P! T5 G6 R( l
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
" L1 T( G$ K) n    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码- I% f/ F8 U* O
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
7 X4 Q- G" A; P) E. h+ W) ?  u
9 p4 n) o2 e5 v0 X: J( U" [+ P, a
* A3 K9 d  O/ n& y3 `' }, I这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:. s, }( E4 e; ]) Y- V5 j$ g
HRESULT CreateDevice(5 Y+ F, u1 {8 w: V8 ?/ W
  UINT Adapter,1 M* [" N  p! a- d: t7 P6 X
  D3DDEVTYPE DeviceType,
2 G( x9 l* }3 N  [( p  HWND hFocusWindow,4 K$ \5 Y9 F* @0 u9 j
  DWORD BehaviorFlags,
, R# ~3 ?- c; p& ~8 I  D3DPRESENT_PARAMETERS * pPresentationParameters,
. N0 t  r2 C9 U) E9 V7 ^* {  IDirect3DDevice9 ** ppReturnedDeviceInterface
6 _/ X1 |' O6 z9 ?);) z0 W9 t: ~6 o
( X; W* u5 |& m

# B. ?3 G; ?, h3 g% D而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
; C0 x0 M3 n; u7 zHRESULT _stdcall hookedCreateDevice(/ j( ^: N1 o/ [0 ^+ Q
                                  LPDIRECT3D9 pDx9,# K! ], C8 E$ V: Q
                                  UINT Adapter,
: ]# ?1 Z/ S8 ^                                  D3DDEVTYPE DeviceType,! z" A7 k+ }& r. |$ Q  O
                                  HWND hFocusWindow,
* z0 J8 c' P( ~9 {7 C! d                                  DWORD BehaviorFlags,; z/ k4 N/ H' ^
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
& C9 q# l) [. U8 C. Q                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
* Y3 ^4 Z% X( K( U, T0 i9 D9 m- b
/ e6 p0 _2 |, n6 R                                  );
9 K7 Y. j0 j1 h( d+ f4 s" E0 h+ M1 _" W2 s' R, r: Q0 m
其中的LPDIRECT3D9 pDx9就是this指针.9 \' L2 ?# o; r" C
6 z3 e5 t! N$ |" h+ L0 z
hookedDirect3DCreate9的关键代码如下:9 w( g% |/ g' l
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针/ W/ k% y- A- m# S
  W9 H, D: o; U( H
        DWORD oldpro=0;
: ]$ x& u' P5 l7 T; c1 D        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
4 e* j& v# ]; h" {& f" P        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);7 p. r0 p3 e* ?% k! A
        *(BYTE*)pCdev=0xe9;
/ a" `5 U/ k$ V( k, o8 q        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
' A+ ~+ T* R3 j3 N. M3 b# v# p) n4 d7 h0 U4 ~- i% x( s5 c
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
: N" m0 `# O# m* x- X, JpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针$ v# l- y$ `: a% h5 x
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节$ {# c; O* W+ |- |
        DWORD oldpro=0;" T9 o5 B& q2 W+ z
        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);4 H$ S( b# P/ H) f! J7 o2 E% H6 A# ]
        *(BYTE*)pPre=0xe9;% q; E1 @; c+ B: b1 |$ S
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;8 \5 x; W  V, k* k! u; F

  D) C9 j# r6 \# a$ X( z实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
$ ~6 T0 V) g3 s( l. ichar strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";/ u) Q& E) ^, `" h
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本2 ^- w: {9 P0 q2 ^" ?
            //在这里写入您的其它绘图代码& R  T: Y& x; w
7 h+ `# `4 s) ~2 o+ S. M
2 o; C5 _+ F! g+ [) t
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:+ s3 n" @) {4 H3 E6 E( d' c7 {/ T7 w
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
- H9 b% ?4 n8 v5 w/ h+ C{3 {( U2 J- q# K  V# J$ H4 D* f

1 G3 ~- W' x, E    if(m_pD3D && pDxdevice){
5 ~# X- }; L; |1 [  g& L; \3 U  ?7 [/ b. w4 k
        RECT myrect;
, R. ~5 N; M7 b: F0 W/ U        myrect.top=150;  //文本块的y坐标
7 N1 _3 j( J' T" m. ^        myrect.left=0; //文本块的左坐标
% _3 H8 D5 Z; v! S1 `5 x        myrect.right=500+myrect.left;( c5 q- a* |; u$ P
        myrect.bottom=100+myrect.top;
% w1 {& ~+ g% l8 F# o4 i        pDxdevice->BeginScene();//开始绘制2 s' w+ g# D5 ~3 _) m  ^

9 S9 |( ~& C4 I$ U/ X        D3DXFONT_DESCA lf;
3 Y6 `2 p- y8 z        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
  w) o- Q9 r4 A0 n7 T( i* D        lf.Height = 24; //字体高度+ x/ |" D' s8 a+ C- X
        lf.Width = 12; // 字体宽度
& e# e$ C8 u  ]) }        lf.Weight = 100;
8 w7 Z( A' U8 i% a3 Q" |" s+ i        lf.Italic = false;
6 ]3 A/ P& ?4 Q* N6 D& k% C3 \        lf.CharSet = DEFAULT_CHARSET;! k3 D2 V5 I5 J
        strcpy(lf.FaceName, "Times New Roman"); // 字型
7 K8 W2 [! |& C. c9 H% A8 s        ID3DXFont* font=NULL;, D# ~- [1 r! }' N/ I- z- y4 L' [
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象* l' H/ i) `9 r+ O" j$ V
            return false;
& @" \& O2 P$ q+ i" R% N( S  t: K3 c  C
        font->DrawText(" S) }) H) o! G- i. M* |: S
            NULL,! _+ V/ p2 p+ X0 j+ p) u  n* ?4 w
            strText, // 要绘制的文本* h( ?" h- s- {0 R
            nbuf, & u' R: r5 s3 T+ m9 `7 X( B
            &myrect, + O- C) G6 `5 G1 C% n- M3 g* w
            DT_TOP | DT_LEFT, // 字符居左显示" ~, c- w- C! `, x# I  a
            D3DCOLOR_ARGB(255,255,255,0)); . T5 K/ @0 Q0 T! g8 N

" T$ b9 ?  j4 i8 z2 |        pDxdevice->EndScene();//结束绘制6 ^4 ?5 ~  E  \' j& M- R
        font->Release();//释放对象
) ]2 E5 p( K+ x  S    }
! {& i2 s, h1 d' ~9 g" V    return true;  O" L) R# ^$ Q1 A
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
. F/ K8 I" D# o4 l9 \# I  V( m! zhttps://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大% a8 {, |: M2 F
首先不能方便的HOOK所有函数,需要到处打JMP补丁
* X" t3 t0 N$ k6 u: p另外每HOOK一个函数都必须自己去查找D3D对象偏移+ ^# ^# C$ ?" w# X6 b% d
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
+ V: L% w" O$ h$ l比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
5 R. m7 s; f- s6 g3 c6 U* [0 C% Z
6 p$ z2 L# n0 ~1 [: j还是007那个方法好,也是我一直在使用的方法
6 _/ `: G% h, j" D" x直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
2 O* I# h, L- _4 Z- |# e只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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