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

汉化资料 【代理DLL汉化研究】Hook Directx:在游戏中显示自己的文字和图形的方法

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

[汉化资料] 【代理DLL汉化研究】Hook Directx:在游戏中显示自己的文字和图形的方法

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

【代理DLL汉化研究】Hook Directx:在游戏中显示自己的文字和图形的方法

在看雪找来的文章,和代理DLL的原理基本是一样的。9 Y. h' S9 v2 ^+ \1 l

7 R: z& u; }+ i0 D原文
2 C; U- x4 r& D) k5 jhttp://bbs.pediy.com/showthread.php?t=85368: Q- s3 U8 o( K- Y# S

& b9 P* @$ R; n这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
, T  W; }1 \, [  v5 X+ c/ W$ ~其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了., `( _* y# C3 S* p
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
" n+ L5 `  B# \& x3 W' K! g首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.4 _* _. M9 w: y5 N, |! ?* n2 ]) ^
    pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址: v( Z+ t2 c7 W9 n2 q
    DWORD oldpro=0;
; ~7 U6 a8 E2 i5 L# ^    memcpy(d3dcen5bytes,pC,5);7 D" j, S" \' ~6 z; N7 ?/ o4 ~
    VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
) ~& d( t0 C1 B( y    *(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
4 X* u, t; R2 f  K" ^% t/ L2 ]) {    *(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
6 O- o4 C: m, B# E/ y3 l
8 t' y. e5 z: O4 d1 Q/ t  b- e- i$ k$ D& ~) e
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
! ^6 @, x- l5 i9 FHRESULT CreateDevice(& h7 }# [4 u+ y" A  Z3 i) I
  UINT Adapter,5 E( X$ [/ ^4 L4 b- W) n% d
  D3DDEVTYPE DeviceType,
% q7 W' e2 |8 U6 }2 t. m9 S  HWND hFocusWindow,
6 X0 _& M4 c. [- {. b7 B2 }! x; K* Q  DWORD BehaviorFlags,4 N0 I. H* O/ a( k  ^" S
  D3DPRESENT_PARAMETERS * pPresentationParameters,/ q9 |" D4 ~! {/ h& ?" @; ], l. `
  IDirect3DDevice9 ** ppReturnedDeviceInterface
+ F) J5 p6 \0 [3 c+ H$ ~* h);% X- f. A: Q; v

& S2 [" d+ p& f5 R8 q/ b6 e) B# d4 Y9 {
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
7 x& `4 [& b0 ]6 p8 P* uHRESULT _stdcall hookedCreateDevice(9 m) q# L2 }% `+ V( v
                                  LPDIRECT3D9 pDx9,- y8 e. Z) g) s6 L3 g' Q0 F2 ~
                                  UINT Adapter,0 o3 C0 R! y( v7 i- o/ b1 o
                                  D3DDEVTYPE DeviceType," F' `; @6 f, q( f3 @
                                  HWND hFocusWindow,
9 {7 \1 H$ Z! Q5 ^                                  DWORD BehaviorFlags,
  g# M$ k  m' l3 k' ^# \' f- b' ~) d                                  D3DPRESENT_PARAMETERS * pPresentationParameters,
, {) N, A8 C  V+ H1 s! _                                  IDirect3DDevice9 ** ppReturnedDeviceInterface+ h7 ]3 O8 Y8 v6 T

; O6 A+ ~4 c/ e4 d( d                                  );
8 t' u5 q; l7 ~
9 S7 i- ?7 m: r, F* t其中的LPDIRECT3D9 pDx9就是this指针.5 o% F5 |( R! u; `8 n! z+ V
6 f* H, X% @; p% T$ h/ @
hookedDirect3DCreate9的关键代码如下:6 a( p. J$ L5 G2 r, {6 w; G
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
' f5 ?. }. ?% q6 T! H2 x0 i, Z( T: t
9 W+ @2 L& H8 a4 `5 [" ~- J        DWORD oldpro=0;2 s3 G7 W+ d. F3 {5 w
        memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
. Q( b$ x7 C3 {& e/ X        VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);2 T& }1 {6 O$ @. X5 G" L; ^+ x
        *(BYTE*)pCdev=0xe9;
) H: }! ]8 {* j7 w/ k4 [5 l        *(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
$ c2 R0 a  v% [# ]3 p/ f4 f# H
' H2 Z" @. I6 |# }9 D在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
# {* F7 ]% o) p/ g, [' m) b' ?* N! T9 I$ cpPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针9 O. ?! R/ v, l0 _0 }4 _4 z
        memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
" M6 b  L4 d( I4 K# [        DWORD oldpro=0;
7 t  t; j9 d+ s+ V. k        VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
- Y6 _( D5 k* i* r) {        *(BYTE*)pPre=0xe9;
4 n; g! \* W* g9 a        *(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;  C+ ^9 U1 f. r) a/ f7 }4 i9 x% [
% l# `6 j  T$ u$ t$ c2 d9 j( c
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
1 o+ s1 I( Z% W3 O- Q/ s# \char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:[email protected]";; S& w0 R) k* f& _2 R# x. N3 }
            DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
& z  Z* Q' Q! N& z            //在这里写入您的其它绘图代码
# b& y7 ^9 g. c% ^  p! e4 V( q
3 m  E% g" P+ J* F5 Y$ S  Z* N- f) b, \# C/ v3 N; E/ B
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:) w  ~' }5 a+ ?; \
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
/ j0 u# v. s7 Y' |( p{
8 d8 m% q3 c7 q/ V, {. T$ Z* J; z( Y# Y( R( |: p
    if(m_pD3D && pDxdevice){: d. @5 B# K+ o: J' k+ n

4 m5 u- n& G. J2 Z        RECT myrect;9 j+ {3 K. Z9 X( d) E
        myrect.top=150;  //文本块的y坐标
  c4 G9 H9 [6 t7 W7 K2 l7 S7 e" p        myrect.left=0; //文本块的左坐标- _$ [4 `7 H0 j7 t- p" j
        myrect.right=500+myrect.left;( {) I) U- {' r* \: R
        myrect.bottom=100+myrect.top;& S0 X9 @' p! U+ E. \+ `; T
        pDxdevice->BeginScene();//开始绘制
9 d2 b& ~- ^% k* C
* P% ?; |" ~; L% h        D3DXFONT_DESCA lf;
. K& q% h- G0 v4 q: ?        ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
8 ~; F2 E. F) p" {/ t; Y2 k        lf.Height = 24; //字体高度
; x/ ^$ u. ^1 s* T& @        lf.Width = 12; // 字体宽度' d- R0 E3 L" q& M' ]) Q$ e
        lf.Weight = 100; ( ?& X* e. E6 }3 N
        lf.Italic = false;
  v3 {$ K) d% i/ Z+ c! X+ L: l        lf.CharSet = DEFAULT_CHARSET;
3 _$ l3 Y/ }6 b. I        strcpy(lf.FaceName, "Times New Roman"); // 字型
4 C! a+ Y! F1 E        ID3DXFont* font=NULL;! d# y- q7 ^9 u2 ]4 _
        if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
" I# }' s0 ?7 U; x1 M3 h5 o            return false;
" x( r* w! Q3 M8 U" ]& U$ E3 M" @2 c' ?; o6 z
        font->DrawText(6 Q6 n2 R8 a+ l0 U9 B( w2 I( R" P
            NULL,
0 T  j; I) ^4 J! y- Y2 \8 A- z            strText, // 要绘制的文本" L* M( n% m' {! h6 i4 t& u1 F
            nbuf,
9 V; s3 w) c0 V) n% `5 y( K" Q            &myrect,
! @5 o- g  e. n% t3 _2 V7 }2 g            DT_TOP | DT_LEFT, // 字符居左显示/ j5 [( i/ X) g: D; f+ r
            D3DCOLOR_ARGB(255,255,255,0)); 8 ^" u& {- ^3 @
/ S5 S2 D- U9 x2 M/ ^0 O. j6 A5 `
        pDxdevice->EndScene();//结束绘制
) ]* q& y3 v& |$ Q5 Y        font->Release();//释放对象
( K9 n6 v) ]1 z( Y5 `    }
* K/ a1 \6 C; q0 S    return true;% a6 Z0 d4 q# Y  L0 ~5 ^" L
}
5 `6 E6 _2 T0 N+ d: q' c4 G5 S! ]* Q, E- f$ O7 j& R! Y6 C1 h5 c) a
源代码:
8 i" J1 D: e5 c6 bHookDx.rar
/ l6 t3 c" y% t- }- i- P: g7 E- V! l9 y/ ?' i9 F
后记: 代码是以前写的,因此文章就按照着代码来讲.然而现在看来,把函数入口点的5个字节改成跳转指令这种hook方法并不是那么好,其实可以直接改掉虚函数表中的函数指针,这样更加的安全保险.
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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