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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法
! T0 h: s6 v2 U7 `8 g, F作 者: runjin
9 {# ]& ^- |, I8 k# D7 ~1 E, |6 e时 间: 2009-04-03,22:44:51
9 d4 o  Z, g6 [3 u# m* @链 接: http://bbs.pediy.com/showthread.php?t=85368; `0 W. D: v" R" c  t
, E+ C* R- I) l7 g
Hook Directx:在游戏中显示自己的文字和图形的方法
5 v: s* b2 X" ?. S+ I6 P5 E' N6 Y7 x% D2 ?. w3 t) R
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
9 F: D7 A0 Y9 t- w& x( A! f, w- y其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.6 N2 V* l* a$ U* I5 _
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.- o6 `3 z3 i' |
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令." `! y- |. p3 N* s0 ?
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址0 c0 C4 J! I5 f! H4 n) j; [
    DWORD oldpro=0;) i1 M5 w8 e% ?& g# x
    memcpy(d3dcen5bytes,pC,5);; j3 i$ z& b2 O
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);; U" C$ z0 z0 j3 u$ m8 E) O6 C
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码( {7 j) g  g/ b4 g
    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5# N7 Y; z; r  K4 ^' }7 j
- ?5 K8 n# Y( V" h( l1 R$ ^
1 F9 e; Z2 }5 g  k* O& D
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:% r$ ~$ i) F; M( T- N: J( q& E( k
HRESULT CreateDevice(" J/ n! ?8 }" g9 r8 i, S9 z& k
  UINT Adapter,
2 L( G, e; U3 U+ Z, B  D3DDEVTYPE DeviceType,, T, q& a2 v- ?/ D- T5 T
  HWND hFocusWindow,0 V: X2 a' S" p7 K+ y  W
  DWORD BehaviorFlags,( o* s' v( }, @( D/ j
  D3DPRESENT_PARAMETERS * pPresentationParameters,
; D+ g$ n) d4 e) C  IDirect3DDevice9 ** ppReturnedDeviceInterface
: |7 K3 W+ E, ~. l" F" y);- v# {5 m( ~# E9 t% `! v5 v

9 f- ~( Q0 _6 f7 {5 u
9 V) ?; Z7 o- l# W; J而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
; p% c+ s$ d1 g: T; z. h5 ^! eHRESULT _stdcall hookedCreateDevice(
. O9 i, S: s; r2 t) v                                  LPDIRECT3D9 pDx9,7 U0 k: {) o0 S2 |1 A
                                  UINT Adapter,0 w& u& F0 B9 a' I+ [
                                  D3DDEVTYPE DeviceType,) _, R, O7 Q0 Q+ D/ o
                                  HWND hFocusWindow,
( `/ ^9 i+ C. A: Q" }+ a                                  DWORD BehaviorFlags,/ W) S' X8 }" v; @8 b0 q  w
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,. S  w: f6 C# J
                                  IDirect3DDevice9 ** ppReturnedDeviceInterface
$ |& K" n6 x" H6 Q+ p! ^! l
8 s! f& A. Z& q! c                                  );7 L0 ]% y- L$ \8 p) @' _! I) \

- p3 W  z6 W- o2 h4 R# i其中的LPDIRECT3D9 pDx9就是this指针.7 v* H: q) u7 \! Z
6 L6 d: M7 ]$ Q3 J6 {$ ~# l
hookedDirect3DCreate9的关键代码如下:6 G, P, r! q# U0 B$ c+ F
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
- m. C$ \: R  C( Q8 j
  @, p) o+ Y- R2 ~0 T% b& T% C        DWORD oldpro=0;
1 o/ C1 v1 {$ s5 d2 a9 j6 q        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节  v0 S7 D8 H: A; X/ y- y$ h7 I
        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
! J) j9 t& d2 G8 |6 ^8 s        *(BYTE*)pCdev=0xe9;
* D' W$ w0 i, j& |, t        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
$ a8 U$ T" |+ \6 B. G* e: Z2 Z8 V# ^6 [* W' L) K0 i
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
4 k, a5 n, {+ |/ Z+ C3 R' w- jpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针" h1 z0 O: i! H2 C  \% A
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节7 Z% U* V1 S- b' K- s) i
        DWORD oldpro=0;
  S0 @  D5 U! n+ N        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
7 y  U' A+ H: {, U$ |2 Z$ p        *(BYTE*)pPre=0xe9;
2 M5 C2 f6 a# e  E- D4 o- ^        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;) ~6 f0 a* ]% l! B

' h0 G1 T- w; C: K实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:/ a5 _. ?% ~5 D3 j* h
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";
/ Z; R6 N6 _0 l" I7 r            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
( p5 i5 |* N, X7 Z4 l            //在这里写入您的其它绘图代码
2 X0 |5 I5 x5 r: Q
& Z& Q7 i5 k# T/ _! ~8 u- w' g
5 w1 L, k" E+ O4 T  e/ n再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
$ V. F$ P1 q( M0 ?+ P3 @( p% K4 KBOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)1 N1 r3 w1 @3 `- a- U
{
$ q3 t- ?1 I% A7 L# y) {2 C7 Y0 z6 T7 B0 @0 l+ C6 ]
    if(m_pD3D && pDxdevice){
0 O) l9 \$ {  _( g0 h, d1 `0 `0 l3 z& W5 p7 ?8 z9 Y. D
        RECT myrect;
& u6 U* p' R3 H2 f4 D( q6 `4 ^        myrect.top=150;  //文本块的y坐标7 y. H- g- v* U. {$ C3 u. E
        myrect.left=0; //文本块的左坐标! }6 I0 G9 u# j% v% c* H
        myrect.right=500+myrect.left;
2 Q% H. \9 I/ b/ ]5 N* G        myrect.bottom=100+myrect.top;% c4 v0 T: z3 A- [# U
        pDxdevice->BeginScene();//开始绘制
" N7 @5 X) o$ w; w2 X" W/ e/ i2 X* y. P7 I+ z1 ?
        D3DXFONT_DESCA lf;
4 G" N: C  o) q: P3 U/ Z        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));. E0 o" o: q6 Q, z
        lf.Height = 24; //字体高度. c/ f0 p- N7 O& ~6 i2 F2 E4 c2 o
        lf.Width = 12; // 字体宽度5 @7 A8 M( b% N4 P* \, [6 W+ V' d
        lf.Weight = 100; 1 {! j, F6 p& l/ p
        lf.Italic = false;
/ k4 z( {! w: b# K0 }        lf.CharSet = DEFAULT_CHARSET;
0 a: \0 v; B& v        strcpy(lf.FaceName, "Times New Roman"); // 字型9 ~" F: U5 P3 f( ?- P5 C( y; l$ J, M7 B
        ID3DXFont* font=NULL;
6 j) a4 y2 M" G: Y; J% A        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象& D4 k& h# x6 `' x) p% v
            return false;$ A* e7 m" L0 X+ Q/ c
- G" M8 M' i- t7 j  @( f
        font->DrawText(
4 K, ?6 R4 J" T+ _" v            NULL,
% U2 B: N1 l# w3 M) O4 |6 z            strText, // 要绘制的文本
/ @$ ~) S" j4 B: M( `1 R            nbuf,
6 a9 z1 K( ]3 k. \6 L1 q            &myrect, 4 d( Z/ l8 M/ s1 i; |4 B; T) h1 g
            DT_TOP | DT_LEFT, // 字符居左显示( d$ b4 H* Y5 a( Q4 ?: g* X, L
            D3DCOLOR_ARGB(255,255,255,0));
) i# a0 a, H6 J! N, [6 @& ]& _1 u. o
        pDxdevice->EndScene();//结束绘制
6 L8 i: Z* b7 r6 O        font->Release();//释放对象. D( c* t/ U8 H- e7 r. C: R: a
    }
  ^' m6 R/ s' W9 F( \3 O+ O2 l4 s    return true;
) D4 B0 n% O" h7 F8 f9 w6 |}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
2 L2 ^: B2 o- c$ Ghttps://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
9 M9 o" R$ z. K# L" c8 B首先不能方便的HOOK所有函数,需要到处打JMP补丁
9 U% q$ f$ E+ x$ q另外每HOOK一个函数都必须自己去查找D3D对象偏移
/ V# Q: m0 R( b3 l- v5 YpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针) P) g! o. d' i/ g* n' V6 @; F
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
- v5 O" V8 y5 m5 t
: C+ U6 n+ L1 J5 m: D* d2 @还是007那个方法好,也是我一直在使用的方法8 o1 l! h/ @$ j& Y+ @) c
直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下% v8 ~2 X- `2 G( z! q6 x2 I
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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