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

【汉化资料】中国人写的Hooking Direct3d

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

【汉化资料】中国人写的Hooking Direct3d

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

【汉化资料】中国人写的Hooking Direct3d

写得很不错的文章,国人用英语写的,还有代码。
. B' U' U7 J1 Y$ Q( V% U6 F" M6 x) dHooking Direct3d
0 e0 A4 j! P, F( }6 ?5 ABy Jijun Wang
: o9 F- o  F& y& `8 J5 a. GIntroduction* [7 r! A& d% C3 w5 }$ J
In the field of hooking technologies, hooking COM based applications is a big challenging because you can’t know their member functions’ real addresses previously. There are many papers on the Internet that talk about how to hook (somebody call it hijack) an application. However, seldom of them mention how to hook COM. Since I have seen some people asked questions about this topic and nobody answered their questions clearly, I decide to write this paper to share my experience with the guys who are interested in hooking COM. " l0 E0 b" M6 A' X. d

/ x9 Z5 j% b( D' \9 T. gIn this paper, I use direct3d, which is widely used in current games, as an example of COM. At first I will briefly introduce the hooking technologies and explain why I select Detours to hook direct3d. Then more details and examples of Detours will be presented and I will discus virtual functions and find the way to hook direct3d.  $ V: Q( x$ |$ [. }- c  n% U; t! _
Hooking Technologies
) {: d, u! o1 W2 _- G" [  [+ U9 GThe basic idea of hooking is injecting your code into a piece of code. When the target code executes your code will be invoked. To do this, you need to at first attach your code into the target process. And then inject your code into the target process’ code. The ways to attach your code into another thread or process include ( l* w% `4 \4 B# T9 b
1) Register your DLL to the registry table.
& J0 }2 |$ d% g3 LThis method registers your DLL to the key: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs and the dll will be loaded when user32.dll initializes itself. It is a safe method. But it only works for the applications that use user32.dll. And it’s not convenient to activate/deactivate your DLL.
2 A+ c. @: Z  |+ \7 C! H2 x2) System-wide Windows Hooks
8 `3 _) z, |+ x4 S, O& r- K+ MThis method use SetWindowsHookEx(),CallNextHookEx() and UnhookWindowsHookEx() winapis to monitor the system’s message traffic and attach your code into the processes that fit the hook filter. Since it is system-wide message monitor, the system’s performance will be significantly affected.
; c) g( p( }8 {( U3) Create Remote Thread
; x! k' n1 c% @* X* {6 x8 {$ TThis method use CreateRemoteThread()to inject your DLL into another thread (the to-be-hooked thread). It’s a very effective method. But there is one thing we need to consider. You must make sure you have enough privileges to access the to-be-hooked thread.
8 s& f+ G7 v" h
! f6 t$ ^! s5 r$ V/ VTo inject your code into another process’ code, we could use:' y( L7 g& n1 s* [+ k- S  n
1) Proxy DLL
% J( E+ K4 g" f$ UThis method writes a new DLL to replace the to-be-hooked DLL. The new DLL must have the same name, exported functions and variables as the old one. For example, the GLtrace is a replacement of the opengl32.dll. It is used to trace OpenGL. Although you could use function forward to reduce the time spent in rewriting functions, it’s a tedious work in some cases.   - l; y' N8 c/ ~, a0 {* e% e$ H
2) Altering of the IAT (Import Address Table)* s7 N' g& U3 Q$ C  N# t& p' {
This is a wildly used hooking method. Windows use IAT to find the functions’ addresses. Through changing the data of IAT, you could use your own function to replace the to-be-hooked function. The method is robust and easy to implement. However, it only works for statically linked API calls. And sometimes you may miss API calls since you must patch every module in the target application.
, }7 {7 n4 `, r4 B/ c# p3) Code Overwriting
: l5 F! V3 o' k/ p6 d, E0 A! AThe basic idea is to overwrite the API’s binary code in memory. We could save and replace the first several codes of a function call to a JUMP code to jump to your codes and then go back to the original position and restore the saved codes. It’s very hard to implement. However, once you have mastered it, it’s really a good hooking method.$ T5 U* r' z7 `

+ s: x6 Q. a  x9 m& yFor more details about hooking technologies you can read the wonderful paper “API hooking revealed” written by Ivo Ivanov and madshi’s discussion about hooking methods. There are some libraries or tools that wrap the hooking methods. "Detours" is a library developed by a Microsoft’s research term. It uses code overwriting technology. “madCodeHook” is a library built by madshi. It’s also based on code overwriting. “hookapi” is another tool can help you develop your hooking applications. Since Detours is free for research purpose and it’s stable and effective, I will use it to hook direct3d.
! x& M: V! N  `- X( vDetours; L5 M) B" s# n7 n; q
“Detours” is originally built for change a standalone system to distributed system. It can inject your code into another win32 function. Some utilities that attach your codes to another process are also provided in this library. The typical case you may need Detours is that you need to modify an application’s behavior without knowing its source code. For more details please visit Detours’ web site: http://www.research.microsoft.com/sn/detours/& f9 Q7 J& D7 k6 l

" [+ ?1 Y  J$ P* S& w5 @As an example of how to use Detours, we will try to catch the function “SetTimer()”. Then change the elapse time to a big number. So for the applications use SetTimer(), you can steal time by hooking this function. The first step is to create TRAMPOLINE. It changes SetTimer()’s binary code and lets it jump to the Real_SetTimer() function.7 U& e8 x! U/ Q: N" B

* I1 c2 A% ]9 Z. BDETOUR_TRAMPOLINE(' h' J3 ?- h- P: W3 J3 B4 M4 j
UINT WINAPI Real_SetTimer(HWND hWnd,     // handle of window for timer messages
& g% C$ J6 J6 [  J& j$ q: Z- I% u2 S                        UINT nIDEvent,         // timer identifier5 t$ ~/ T- b& N3 G& \2 E  q2 c
                        UINT uElapse,            // time-out value' ~/ N0 P8 e, B- F/ g5 G
                        TIMERPROC lpTimerFunc), // address of timer procedure' z( a9 @/ S+ L/ E2 a
SetTimer);9 U) d* f& d( a. [& ~: G% ~( P! Z
+ a4 L" O$ S% C# v/ ^& o/ h0 M7 Y
Then we write our own SetTimer function.
) C2 c9 A$ _& x2 V1 YUINT WINAPI Mine_SetTimer(
/ D0 a1 w5 T$ n( Q- f    HWND hWnd,           // handle of window for timer messages7 o/ G. H1 b+ Y5 G
    UINT nIDEvent,        // timer identifier1 C! R& o1 e! e$ K9 ?
    UINT uElapse,           // time-out value
$ U3 D5 ^9 J# {, ?9 F    TIMERPROC lpTimerFunc   // address of timer procedure
/ Y  }5 A9 j4 G: r: R/ N    )
: r7 {. ~0 L0 n% o- V{/ q2 {: V4 J2 k0 S' X" ]1 T
    uElapse=10* uElapse;: \: K6 M; J( n( Q- y5 ~
    return Real_SetTimer(hWnd,nIDEvent,uElapse,lpTimerFunc);& j$ Z( t0 X; N. _" q) [  R0 C: z
}3 l1 H" E' t1 F+ d2 |
/ m0 J  M$ ]& ?) _" G  A* T, ~9 [
Then write the functions to intercept the Real_SetTimer() function to your own Mine_SetTimer() function and the function that removes the interception.
: g- i0 A5 f4 x, Q6 {# p
: k1 p% U! Y$ E+ p) F0 n9 f. hBOOL TrampolineWith(VOID)* X+ y! f$ @& S/ X
{
# p& B# D& f3 V: z* ^  ?    DetourFunctionWithTrampoline((PBYTE)Real_SetTimer,# {2 X9 x. }& G
                                        (PBYTE)Mine_SetTimer);
2 B# Z6 s( ]7 a9 n' u) g    return TURE;; H& }! J& J1 g) O* ^; U
}4 o+ @' e/ J7 p7 S9 b
6 n* q, e* Z) U2 ?  T* Q% j
BOOL TrampolineRemove(VOID). B) m) j4 b' A/ D9 p4 ~
{, m  z; Q) e4 g6 p% V4 f
    DetourRemove((PBYTE)Real_SetTimer,- [! _* X& C# C+ Z% j, |8 Z, F6 {
               (PBYTE)Mine_SetTimer);
+ Q- y( h4 K0 H* k9 W    return TRUE;
2 L$ u( [$ i+ U/ s. ^8 I}1 C4 N4 g6 ^" a( i

: P7 ^9 O& \; {& Y. m6 mAt last, we write the DLLmain function.
- V- ~, B* U7 `  M& M9 P3 ?/ R
1 \& Q, d' f0 X3 a3 [BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, PVOID lpReserved)
  O6 K' p0 h- I7 u; b% F{
  b7 p+ I( _1 \$ N& k1 e$ w    switch (dwReason) {
) n8 ^, T4 H3 Q; N/ A$ L" ^4 [      case DLL_PROCESS_ATTACH:0 a# e- t0 Q6 w& i7 X
        return TrampolineWith();: X5 w% e- l5 D+ b4 d
      case DLL_PROCESS_DETACH:* g  b% }7 i/ p
        return TrampolineRemove();
0 ]% }$ j! [; \3 [* X. G& s      case DLL_THREAD_ATTACH:# L# {: \2 z( ^) a0 f
        return TrampolineWith();
5 o: h* {2 Y6 R; ~      case DLL_THREAD_DETACH:
! m4 V+ P% V  O5 B4 q        return TrampolineRemove();
+ o4 S; X9 q: G- F* c# h5 Z    }) v1 {, s$ G' ^0 N. x: L$ Y
    return TRUE;
8 b5 E+ {# r* b9 U0 z+ J4 D}
7 C& \3 w  G0 ?4 q  N  Z
9 }9 Y% Y2 f; pBy including the “detours.h” file and linking the “detours.lib”, you can build this DLL (let’s say it is myTest.dll). Then you can write your own application to attach the DLL into another application, or use the “withdll.exe”, which is included in Detours 1.5’s samples, to attach it. If you place the DLL file, withdll.exe and winmine.exe in the same directory, then play the Mine game use command “withdll.exe –d:myTest.dll winmine.exe”, you will get a very high score.
- \4 m4 v0 i/ @2 i/ @" S# G7 s& @. B+ ?* l
This is the simplest case of using Detours. If you want to hook a member function of a class, you need to write your own class and your member function. You can find an example of hooking member function in Detours 1.5’s samples. ) V! J* M/ t2 W5 Y
9 u  q2 G# D5 d3 }! s3 k1 ^$ c
To hook direct3d, you may create a class, write your own member functions, for example CreateDevice(), Present(), and then detour direct3d’s member function to your own functions. Unfortunately this doesn’t work. The reason is that Directx is COM based, it uses virtual functions. When you detour the member functions, you will use the virtual function’s address not the real implement function’s address. So the key of hooking direct3d is finding out the real function address. To do this we need to know how virtual function works.
" C) G+ y" h$ f8 uUnderstanding Virtual Functions and Hooking Direct3d9 t# L1 Q( Z% U. T5 j! K
Virtual function is used for reusable and polymorphic purposes. It is runtime bound to the real implement function. For more details about how virtual functions work, please read Shivesh V.’s paper “Virtual Functions and their implementation in C”. $ f0 g! |& \; l) g+ k9 K
: h2 I9 n3 E8 S3 V5 ^
Basically, after you call the Direct3DCreate8() function (let’s assume we use directx8), you will get a  pointer to the created IDirect3D8 object. In the pointer is the address of the vtable, a structure that contains all the member functions’ addresses. The address in the vtable is the real implement function’s address. So if we want to build a benchmark application to test the frame update rate, we could do it in the following steps:
8 [- s7 i! C! }  `$ A! R1) Catch Direct3DCreate8() to get the IDirect3D8 object
7 `8 c3 ~0 I' p3 ]/ h, h2) Use the pointer of IDirect3D8 object, catch the CreateDevice() function to get the IDirect3DDevice8 object.
& s) _  o; v& b( j, X9 R3) Use the pointer of IDirect3DDevice8 object, catch Present() function.
% Q+ W! D2 u# _: V6 u; m4) In the Present() function calculates the frame rate.
9 T# P, d) e- u' P  y& j+ h& S! r& q% v
However, to catch the member function, there is a very important parameter we need to know. It’s the address data in the vtable that contain the to-be-hooked function’s address. There is no general way to know the offset in the vtable. For direct3d, you could use the d3d8.h file to figure out the offset. Or you can debug a direct3d application and get the offset by read the disassembly code. The following is a piece code of the directx 8.1 SDK’s Text3D sample code. The comments of the disassembly code are added by the author.
# a: S! V5 J( K# V- e2 W8 p
$ z( R% Y+ X+ x0 n+ ~: ]873:      // Create the device
4 Y$ @% G% z% I3 _6 x# ~874:      hr = m_pD3D->CreateDevice( m_dwAdapter, pDeviceInfo->DeviceType," v0 V7 h7 m% h8 ?% M; X
875:                                 m_hWndFocus, pModeInfo->dwBehavior, &m_d3dpp,+ o/ H# [' N4 t
876:                                 &m_pd3dDevice );" T; a7 O- b  j/ p4 \
00403114   mov         eax,dword ptr [this]
: b/ ]$ C; S5 Y; S00403117   add         eax,2A4A8h
+ U7 z9 |6 W; \2 W# s0040311C   push        eax            // push the sixth parameter &m_pd3dDevice
7 R" r7 I$ }; j( R6 y& L# T- `0040311D   mov         ecx,dword ptr [this]
# C# @2 d/ i" [: b- X/ @. Q( u00403120   add         ecx,2A464h
/ K; Y1 N+ \7 f3 F& w( e00403126   push        ecx            //push the fifth parameter &m_d3dp
' V" H' P! u$ e% d) u! `, G00403127   mov         edx,dword ptr [pModeInfo]
. K0 Q2 Q) ]' W4 t% e$ Q0040312A   mov         eax,dword ptr [edx+0Ch]
( z, j5 F7 M0 P5 ?& Q& B! v& a* L0040312D   push        eax    //push the fourth parameter pModeInfo->dwBehavior
& ]( k6 ?- x0 B$ a7 v0040312E   mov         ecx,dword ptr [this]
! v; I3 _# t, B* A! b/ l, T; U00403131   mov         edx,dword ptr [ecx+2A49Ch]
2 ^8 U2 G+ k! Z: r, k  t& E0 H00403137   push        edx    //push the third parameter m_hWndFocus$ I' @3 o# N# J5 L) P: L( L1 k
00403138   mov         eax,dword ptr [pDeviceInfo]) L$ n% g! a2 y5 g; z/ c
0040313B   mov         ecx,dword ptr [eax]
" i3 |, r# P" }+ P7 q0040313D   push        ecx    //push the second parameter pDeviceInfo->DeviceType
2 f" Z6 c! s6 d! x1 S0040313E   mov         edx,dword ptr [this]
2 v- n6 {' d- v0 P" |. Q6 d2 Z0 T00403141   mov         eax,dword ptr [edx+2A448h]8 \  e- X9 h. z  \! d1 u; W4 B
00403147   push        eax    //push the first parameter m_dwAdapter
, ~9 w6 p/ H: C. H00403148   mov         ecx,dword ptr [this]5 A# L" H, f7 D. p
0040314B   mov         edx,dword ptr [ecx+2A4A4h]    //calculate the return address
  w& }4 j& T5 q8 ^8 s00403151   mov         eax,dword ptr [this]
- Z, B3 h. F7 J" y& q0 y00403154   mov         ecx,dword ptr [eax+2A4A4h]
: p) S# J6 R0 T$ j" c' q" l. b0040315A   mov         eax,dword ptr [ecx]    //calculate the vtable address. It’s the value
: n9 y# e  J! k7 i                        //stored in ecx register.
: c  j8 a' r: @- |7 o7 ]0040315C   push        edx    //push the return address
9 F6 h, K$ G, u7 B- S  n0040315D   call        dword ptr [eax+3Ch]    //call the CreateDevice() function
8 `$ r6 ?' W- u& S00403160   mov         dword ptr
,eax
" o# O; C" b* b( w" g
+ u# Z$ B6 S* b1 r7 z* vFrom the assembler code, it’s obvious that the offset is 0x3ch. You can do the same thing for the Present() function. And the offset happen is also 0x3ch.
! D$ P- y9 ^, k& A1 Y% T* T. j) [9 |2 n# H0 L
The sample code can be found at http://usl.sis.pitt.edu/wjj/UTClient/direct3d8.zip. To compile it you need Directx 8.1 SDK. Please note that the method of display frame count is not every effective. If you remove the displaying code, you will find Detours has almost no effect to the frame rate.
' r9 j! `$ P$ p( fReference:
. s& N* b. |! }9 y# m7 n% A[1] “API hooking revealed”, Ivo Ivanov9 f: x5 L! N9 k
[2] “madCodeHook”, madshi
' T5 t) K3 V" {8 d4 A[3] "Detours", Galen Hunt and Doug Brubacher! r6 K1 N5 O- r. k
[4] “Virtual Functions and their implementation in C”, Shivesh V.
: U* e6 b! }8 C9 o% P2 G  C8 Y  \[5] “Pointers to C++ Member Functions”, Michael D. Crawford

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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