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

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

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

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

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

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

标 题: 【转载】在游戏中显示自己的文字和图形的方法) H! p6 Q& U6 l5 s6 d
作 者: runjin# p6 {- s6 b5 G' d+ L( `
时 间: 2009-04-03,22:44:51
# z, r2 d) O) Q* n5 k% ]% u链 接: http://bbs.pediy.com/showthread.php?t=85368
0 y6 w  s+ f+ E( H# F/ ^0 `8 G, w2 Y/ X3 P
Hook Directx:在游戏中显示自己的文字和图形的方法: @$ u, q6 t$ n1 r$ q1 S
. C% y; q" j( \
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.( d* {# Y* u/ N
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.$ C3 B7 L% q+ H: I, }
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
- I9 K  t7 l# x$ `. O( ]" a# ~首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.# G4 E1 A$ Z' s% j
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
# a7 h9 T7 K% K' m7 a. v6 I/ \7 I6 O    DWORD oldpro=0;! y. w1 y* F; i# f6 c
    memcpy(d3dcen5bytes,pC,5);) b# b- e7 z  t/ k9 C
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);! Q/ h6 \% o& n% d, i
    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
, P. B5 A  c. b    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
) @  M/ ~' \; u  l% x4 p; p1 o/ F! e/ [! ?8 k( U
' j; W* R8 ?4 D( O, l) t, r! Q& V
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
% L% W% G2 E# J: k, FHRESULT CreateDevice(
+ j: }- F, j- s( A  UINT Adapter,8 y- s/ ?) g. a8 j1 l% ^
  D3DDEVTYPE DeviceType,0 N. t% w+ L$ Q& l
  HWND hFocusWindow,
' _8 z3 R1 G$ E% V  DWORD BehaviorFlags,
! [' _) A1 v/ n" n  D3DPRESENT_PARAMETERS * pPresentationParameters,0 Y* b* [+ `" e1 x# [( }# b1 |  }
  IDirect3DDevice9 ** ppReturnedDeviceInterface& J, u* K8 d; s( t: e
);9 t7 r" j% O$ ^2 D+ N( H' l. X
1 H; a' F% g) N- r$ U$ `

  j9 Y, e7 r) ^+ \5 S4 E! `+ a& \而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
. a- W; D" c+ FHRESULT _stdcall hookedCreateDevice(' m$ U: z6 @! W1 F( G- b
                                  LPDIRECT3D9 pDx9,: \0 I. I# \! |
                                  UINT Adapter,
; H% T( `0 d4 I' T6 ?' m" R7 X7 ]( L$ W                                  D3DDEVTYPE DeviceType,
7 Q- x! b) w) s# m# f+ w                                  HWND hFocusWindow,* k$ p: t4 s) |$ K" i
                                  DWORD BehaviorFlags,# r5 _, \8 D/ n7 l# N, h
                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
+ Y# ~6 B8 ]0 g6 C8 ^                                  IDirect3DDevice9 ** ppReturnedDeviceInterface: Y7 l7 `5 i$ ]9 x5 l3 o

4 {- O0 H6 P# U) d                                  );1 a! C  |7 G9 K/ g: J" n

% u, j' H. F4 O" Q9 S$ ~* N其中的LPDIRECT3D9 pDx9就是this指针., G+ h5 b4 r, n3 d

9 H% |6 ?. S/ w& A5 e7 [' MhookedDirect3DCreate9的关键代码如下:3 h+ A6 c7 i+ O# p8 s
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
( e- |* N, C3 Z4 T4 N
# i; T0 v0 b4 q0 K2 K( A6 I9 F        DWORD oldpro=0;
! ]: h4 l$ F: H6 t0 N, i' X& ]        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
1 _2 ?6 g' ~* y& O2 D$ k        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);, K9 d. Y( ?  Q: I
        *(BYTE*)pCdev=0xe9;
) ]7 B# Y5 [/ Y% L' }" R        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
# _2 B( v- Y% E0 K
2 [1 N* v  x3 J在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
! S! R! j, }, Q3 }) [& C! ~5 tpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针$ a  N% m3 p- m7 }4 M+ T$ x+ y
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节8 D8 L. F4 P* F- Y& O/ \# U
        DWORD oldpro=0;
& N& @8 {( w" F" _% [( d        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);5 f7 c) `& ~, M
        *(BYTE*)pPre=0xe9;+ ^; `8 {8 _4 Z( r3 m0 e" \
        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
" D8 F$ ]" N- C  b; \/ _8 G* t
7 k3 _# q6 I; V- i实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:4 N: b% e3 H4 q
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";3 u" o4 m( k2 W/ E# m! I: F
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
) d3 n$ U! M8 ?# J0 g7 L8 p- |            //在这里写入您的其它绘图代码
  J1 {8 T$ i" _9 N& x: ^5 ^' f
6 m' W( g! Z7 p7 w% d; `: M/ {
9 [/ T2 x8 ^! r: {& c. O: _再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:/ F% Z9 m7 g3 _0 L( h. _7 s
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
6 m& U3 D# U, R{8 t0 C6 ^, N- X# F8 z) K
% L- m2 w! U7 F. d8 j2 T) }
    if(m_pD3D && pDxdevice){- t; F1 k/ T+ j" y' x1 }* O
/ U2 l  v$ R' H! K3 C5 m6 a8 a
        RECT myrect;, R/ e/ F( k1 h1 z0 m
        myrect.top=150;  //文本块的y坐标3 v/ Y1 i: \" g  e
        myrect.left=0; //文本块的左坐标
2 L  O0 M  R6 D# F        myrect.right=500+myrect.left;& ]' J+ G  h0 I8 g$ T
        myrect.bottom=100+myrect.top;" b2 \+ ~$ A- Z" ^
        pDxdevice->BeginScene();//开始绘制1 ?4 t3 q! {; n9 b( }" `% S
* {+ |+ U% o6 o8 L% i9 |0 T
        D3DXFONT_DESCA lf;: c8 |! @! E4 K; _+ e
        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
. {/ Y, }, K- ]+ V+ w0 y! l        lf.Height = 24; //字体高度
  `, u; j# x9 \$ d0 V0 {: p( Y        lf.Width = 12; // 字体宽度
" t3 G3 ]. h( p: c        lf.Weight = 100; 2 D" R: k8 u- |  A8 r) x/ U
        lf.Italic = false;
; k- t( a2 s# l1 e6 G        lf.CharSet = DEFAULT_CHARSET;
8 c0 }) t* x2 `2 f( Z8 I        strcpy(lf.FaceName, "Times New Roman"); // 字型
( S3 X7 e- D/ Y; a7 k0 L        ID3DXFont* font=NULL;8 G, i, v) ^& ?/ l4 G) w& H
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
- L/ d9 X1 T. Y* ^+ ^9 ^: s            return false;0 e6 e+ y: G$ d. p
! B- C9 i% W6 u# R' z
        font->DrawText(; E) `4 K" H- e  h9 a  c, s
            NULL,
$ ?/ [" z" G0 A- F, j0 M# w            strText, // 要绘制的文本
. k& _$ g0 `! A+ n            nbuf,
* z5 H0 q0 o& }" H5 D# K! y            &myrect, 9 M0 q, l! E% S: L/ l; ?- n2 F- F
            DT_TOP | DT_LEFT, // 字符居左显示; G$ ^" m8 H4 t9 X. v& t( U6 ]
            D3DCOLOR_ARGB(255,255,255,0)); 4 Y& J6 \5 U+ O' R2 a* i1 t- m

4 k; F2 p. T/ i# n2 a7 w        pDxdevice->EndScene();//结束绘制
# Q3 m) j- H, l8 s- K        font->Release();//释放对象' X$ V! |: _% U8 ^' N; |% E
    }
+ M4 y' l( j+ |  f2 v; R    return true;
; k8 S- |  Z! h, e}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2010-4-4 21:49 | 只看该作者
多谢楼主,不过,其实我已经发过了。
# Z; U5 ?: i& N0 Xhttps://www.chinaavg.com/read.php?tid=18802
回复 支持 反对

使用道具 举报

板凳
发表于 2010-4-6 16:02 | 只看该作者
他这个方法不好,局限性比较大
0 B8 Q: Q+ H) t; J首先不能方便的HOOK所有函数,需要到处打JMP补丁
7 F* \4 F/ Y, T+ o/ \另外每HOOK一个函数都必须自己去查找D3D对象偏移
8 `5 |8 t' _4 c. h" YpCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针3 K& C% ^$ E. B$ ?+ S( K
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下* b+ ^. {) [9 O
, h4 f6 u7 G2 C* u  c$ A
还是007那个方法好,也是我一直在使用的方法
* b. J4 t& C+ ]9 F; @直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下3 s- {; s% H: ^5 x% A+ W
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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