冒险解谜游戏中文网 ChinaAVG
标题:
【转载】在游戏中显示自己的文字和图形的方法
[打印本页]
作者:
jinxin8866
时间:
2010-4-4 21:42
标题:
【转载】在游戏中显示自己的文字和图形的方法
标 题: 【转载】在游戏中显示自己的文字和图形的方法
4 g/ l a8 d4 V1 S/ i
作 者: runjin
8 o9 L( V! e- q& ]
时 间: 2009-04-03,22:44:51
6 B) l% {' a; b
链 接:
http://bbs.pediy.com/showthread.php?t=85368
! G" h% h8 `/ a7 y
8 i. Y! c' i, P: X
Hook Directx:在游戏中显示自己的文字和图形的方法
S0 L/ R7 T f
; ^ g; P5 E; W, B% a0 S! @
这个方法出自我大概两年前的一个项目,现在经整理后贴出来和大家分享一下,利用该方法可以在一般的directx游戏里面绘制文本甚至图形对象.
1 y' F8 I6 j( |) Q: j' q
其实思路上非常简单,大致是这样的:要在directx中绘制文字和各种图形对象,只要获得一个类型为LPDIRECT3DDEVICE9的设备对象指针.怎样获得这个指针呢?我的方法是首先hook掉Direct3DCreate9以获得类型为LPDIRECT3D9的Direct3D对象的接口指针,这个Direct3D对象有一个成员函数为 IDirect3D9::CreateDevice,设备对象指针就是在这个函数里面创建的.所以,只要根据Direct3D对象接口指针找到Direct3D对象的虚函数表,再根据虚函数表确定IDirect3D9::CreateDevice的内存地址,就可以hook这个函数,从而获得类型为LPDIRECT3DDEVICE9的设备对象指针,然后就可以随意绘制文字或者图形了.
2 V+ J- l) b; y1 I
还有一个要hook的地方,即IDirect3DDevice9::Present,这个函数用于交换当前后备缓存区,刷新窗口.要使得我们自己绘制的东西一直显示在屏幕上,比较好的处理方法是hook掉IDirect3DDevice9::Present,在程序真正调用这个函数前插入我们自己的绘制代码.只要根据设备对象指针找到设备对象的虚函数表,根据虚函数表找到IDirect3DDevice9::Present在内存中的地址就可以hook了.下面是对hook directx的详细说明.
8 I3 e( N; p/ X
首先是找到Direct3DCreate9的内存地址,然后把入口的5个字节修改为跳转指令.
( K5 t* e8 Y% r, J- ~- b) Y% ~
pC=GetProcAddress(GetModuleHandle("d3d9.dll"),"Direct3DCreate9");//获得内存地址
6 X: N, w1 v/ e$ f9 K; Y A; {' ?7 _
DWORD oldpro=0;
& _1 [. Z0 c2 }% ]# I
memcpy(d3dcen5bytes,pC,5);
% L* W6 [ H3 o8 d3 R0 ~) C
VirtualProtect(pC,5,PAGE_EXECUTE_READWRITE,&oldpro);
4 I# G- S7 n8 L- t/ k( S$ V: s
*(BYTE*)pC=0xe9;//0xe9在汇编中是跳转指令操作码
x( X" h2 w- q" ~' O* @1 r
*(DWORD*)((BYTE*)pC+1)=(DWORD)hookedDirect3DCreate9-(DWORD)pC-5;//目标地址-原地址-5
% H% {$ E6 K! o, n( v$ Y
! x: H1 Q# g' }! b
' s: m( x7 X+ p( l, k
这样,在程序运行到Direct3DCreate9时就会跳转到hookedDirect3DCreate9,在这个函数中,首先是还原Direct3DCreate9入口的5个字节,然后调用真正的Direct3DCreate9,如果函数调用成功,就会返回类型为LPDIRECT3D9的Direct3D对象指针,这正是我们所需要的,得到这个指针后,就可以根据Direct3D对象的虚函数表确定IDirect3D9::CreateDevice的内存地址,然后就可以把这个函数入口的5个字节修改成为跳转指令,跳到我们自己的函数中去. 有个地方值得注意的是,在directx的虚函数中把this指针作为第一个形参入栈了.例如说sdk中IDirect3D9::CreateDevice的函数说明是这样:
, i/ f/ w/ M7 i; j# m: r' d7 H/ q* M
HRESULT CreateDevice(
& j7 M4 b$ C! }/ e( X
UINT Adapter,
4 w) j. I& Z L y% Z
D3DDEVTYPE DeviceType,
! e$ a* u8 U. ?) ^" E$ ]& `$ `" p
HWND hFocusWindow,
* g S% `1 S* V* b
DWORD BehaviorFlags,
+ P1 z" N, k5 \* o# a, i4 O# Y
D3DPRESENT_PARAMETERS * pPresentationParameters,
! }* \+ @& b! A% E& `
IDirect3DDevice9 ** ppReturnedDeviceInterface
4 ?! S- U% J9 I/ [4 }! l( k
);
; b) i4 f6 Q( ?7 T. S( a
2 u+ g6 D. b: Q9 Z' d! T9 ^
% J& |2 g. b6 z9 e* F# p* p, K
而为了程序跳转到我们的代码执行完后保持栈的平衡,hookedDirect3DCreat9函数声明应该是这样:
9 B6 v# ^' ]- n8 ^# l
HRESULT _stdcall hookedCreateDevice(
7 y* {4 _& `, U- n7 y9 f$ k
LPDIRECT3D9 pDx9,
$ J* |! \7 @1 s
UINT Adapter,
\' {6 Q, s) c7 m1 w/ B
D3DDEVTYPE DeviceType,
/ N. Q+ x! P1 ^/ R: h
HWND hFocusWindow,
6 \, E4 u }& O- D
DWORD BehaviorFlags,
9 t. I& t: H! @+ C- n/ |
D3DPRESENT_PARAMETERS * pPresentationParameters,
7 e. Q0 E+ J' a. v! J
IDirect3DDevice9 ** ppReturnedDeviceInterface
0 }5 ~& L2 m: t+ F" R% f; }
@8 w2 q) X: n3 E, ]- I
);
. Y1 n2 I5 }0 ?- F
6 ^% u4 S. o. @4 M4 l( M
其中的LPDIRECT3D9 pDx9就是this指针.
* C) r* J0 Q6 t% Q5 C
E) A5 r# R+ o
hookedDirect3DCreate9的关键代码如下:
* R' h1 x, i& Z' u- K* N8 Q w
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
( c' ?% N# y$ S
7 g4 T1 B/ n+ \
DWORD oldpro=0;
7 i' ]4 r8 F9 k
memcpy(devcen5bytes,pCdev,5);//保存IDirect3D9::CreateDevice入口5个字节
, e# C4 s# W, X, v' q
VirtualProtect(pCdev,5,PAGE_EXECUTE_READWRITE,&oldpro);
3 q/ Y+ ~( C A
*(BYTE*)pCdev=0xe9;
' L. H3 |( |3 r
*(DWORD*)((BYTE*)pCdev+1)=(DWORD)hookedCreateDevice-(DWORD)pCdev-5;
) O! t4 J5 I, }7 v6 E9 d: _
& a7 k7 m! f) |4 _+ ]- L& Z8 W% {$ i3 }
在hookedCreateDevice中,首先还原原来的CreateDevice函数入口的5个字节,然后调用原来的函数, IDirect3D9::CreateDevice的最后一个参数是一个二重指针,如果函数调用成功,这个二重指针所指向的指针就是我们所需要的设备对象指针,由此找到设备对象的虚函数表,然后确定IDirect3DDevice9::Present的内存地址,然后又可以改掉入口的5个字节为跳转指令. hookedCreateDevice的关键代码如下:
% X! ~' x1 h1 l5 U
pPre=(void*)*(DWORD*)(*(DWORD*)m_pDevice+0x44);//获得IDirect3DDevice9::Present的地址指针
0 V; G% A$ R7 T# K7 N$ ^" c
memcpy(pren5bytes,pPre,5);//保存IDirect3DDevice9::Present入口的5个字节
$ v$ E4 ~$ [1 c1 o& U7 @5 J
DWORD oldpro=0;
: L: ~# O8 }# Y9 y, C5 `4 F6 [! W- N
VirtualProtect(pPre,5,PAGE_EXECUTE_READWRITE,&oldpro);
# o8 T6 A# i2 ]7 |; n
*(BYTE*)pPre=0xe9;
( ~4 E% T& ~' R& a+ i0 G4 `, y
*(DWORD*)((BYTE*)pPre+1)=(DWORD)hookedPresent-(DWORD)pPre-5;
+ z2 p3 Z. [, X' }
8 f2 U- P! M+ r$ {! q7 K
实际上这几个函数的hook都是同样的道理,现在,当程序运行到IDirect3DDevice9::Present后又会跳转到hookedPresent,而我们自己的绘制代码就是放在hookedPresent里面.在执行完自己的绘制代码后再调用原来的IDirect3DDevice9::Present.我的hookedPresent的关键代码如下:
& ^/ h$ Q- C' y0 {$ ^1 W
char strdraw[]="The drawing in directx game\nAuthor:RunJin\nEmail:
[email protected]
";
& y! d' T+ ]0 w- h) b
DrawMyText(pDxdevice,strdraw,sizeof strdraw-1);//绘制文本
, ]7 m. I: m5 {. Z6 |3 S1 [
//在这里写入您的其它绘图代码
7 ~6 l" O5 g" X
4 p7 |& g# U3 L! g1 X
. |- {* O4 j4 a9 H1 ]
再来看看其中的DrawMyText,这个函数是我的绘制文本的函数:
- ~! [5 d& j9 f5 a% t
BOOL _stdcall DrawMyText(LPDIRECT3DDEVICE9 pDxdevice,TCHAR* strText ,int nbuf)
5 M' j2 ~& W$ a) F* |
{
; J0 C8 `; p: R! W% w
4 r2 a, G- o$ }& O
if(m_pD3D && pDxdevice){
( z$ O- n: u/ g4 n% U; ~8 E
6 F" I' J4 b1 t4 B7 O m# k
RECT myrect;
# P. w& w' S/ q3 h
myrect.top=150; //文本块的y坐标
( q% @. J$ n2 k
myrect.left=0; //文本块的左坐标
% v/ w4 P* k8 R9 H; \% a- q5 q
myrect.right=500+myrect.left;
9 T# J4 M" x6 w3 p3 @: X
myrect.bottom=100+myrect.top;
2 k1 f: d) O1 U# w1 P1 {* y5 V: A1 N
pDxdevice->BeginScene();//开始绘制
, u3 L4 O* ?# `: L9 r5 A7 E
+ S9 P7 I( ]' O/ {7 s! ^
D3DXFONT_DESCA lf;
1 X+ @" E, ?8 T, h0 }' n! A9 i) ^
ZeroMemory(&lf, sizeof(D3DXFONT_DESCA));
6 _* N e K2 v' Y. x
lf.Height = 24; //字体高度
& Y+ R# _" Q& J- s! }: s( p
lf.Width = 12; // 字体宽度
* G; ]0 A, V8 q
lf.Weight = 100;
- G" e ]6 M/ @, Y; o3 x
lf.Italic = false;
4 V, T, p5 u! X& L; y
lf.CharSet = DEFAULT_CHARSET;
4 F# h* j: Y$ ^8 Q5 x
strcpy(lf.FaceName, "Times New Roman"); // 字型
$ N" `- f/ w* N3 q% j+ ]
ID3DXFont* font=NULL;
4 ]+ o- Y) [, V! _8 X9 k+ o
if(D3D_OK!=D3DXCreateFontIndirect(pDxdevice, &lf, &font)) //创建字体对象
# {+ L& s. U( i+ t6 n8 S
return false;
8 a( f1 K( n+ R7 }: ]2 l& V' X, ]
" `! B: c4 B4 n A3 M) [
font->DrawText(
) i, `) f5 p$ S1 c
NULL,
* y8 F2 y# {! V5 Q! M* f/ p
strText, // 要绘制的文本
% K! Y+ b9 M" [8 b5 j
nbuf,
. M& V5 q8 J9 e. d: }) y
&myrect,
) O2 S1 G9 X. y! r
DT_TOP | DT_LEFT, // 字符居左显示
; r* k" C1 M3 n+ j) n% a9 @
D3DCOLOR_ARGB(255,255,255,0));
6 g1 G; u* H- ]( L C2 w
" b; p* ^& G# y0 c7 B% b8 j
pDxdevice->EndScene();//结束绘制
1 p/ u# b; A8 E5 x- M* j4 D {4 ^
font->Release();//释放对象
5 R1 s8 X; ~& ~- F* Z
}
* ^$ m8 k5 B) F7 X/ U
return true;
7 l9 g, y) f5 u- {9 E$ c; g
}
作者:
shane007
时间:
2010-4-4 21:49
多谢楼主,不过,其实我已经发过了。
2 R8 Q/ @) C' t
https://www.chinaavg.com/read.php?tid=18802
作者:
solidji
时间:
2010-4-6 16:02
他这个方法不好,局限性比较大
) F+ d# `* q8 N
首先不能方便的HOOK所有函数,需要到处打JMP补丁
4 l; S9 l1 {8 v
另外每HOOK一个函数都必须自己去查找D3D对象偏移
, w( Y* ^9 s0 t/ c
pCdev=(void*)*(DWORD*)(*(DWORD*)m_pD3D+0x40);//获得IDirect3D9::CreateDevice的地址指针
! \' u: c g7 x5 e& t
比如这里CreateDevice地址指针是m_pD3D+0x40,这个资料里一般是没有的要自己反汇编下
7 o! j0 y6 r# D& |# F/ R* J
$ J0 K* t3 ?$ d. | @: F( x
还是007那个方法好,也是我一直在使用的方法
6 S# _4 `4 n( }9 ?1 d& q
直接创建一个自己的D3D对象返回,针对Direct3DCreate9与CreateDevice特别处理一下
3 }! G# `0 `7 k
只需一个钩子,后面所有的D3D事件都得从这过了[s:109]
欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/)
Powered by Discuz! X3.2