冒险解谜游戏中文网 ChinaAVG

标题: 【汉化资料】中国人写的Hooking Direct3d [打印本页]

作者: shane007    时间: 2009-4-6 13:41
标题: 【汉化资料】中国人写的Hooking Direct3d
写得很不错的文章,国人用英语写的,还有代码。
0 l9 E- h3 U# T5 Z" sHooking Direct3d
0 B* `2 ^! D) |By Jijun Wang
+ i4 @( c% b4 j2 hIntroduction2 \! c1 ~1 n0 y8 p8 {1 S
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. * d* v4 F" R2 @5 l- I' q! E0 P
" p; X6 y7 w& j' O! A) o
In 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.  % g: m9 X7 ^) }$ _! b
Hooking Technologies
: ^0 Q9 j" a7 c; R2 a# l5 L& yThe 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
' s4 R* f- A: t6 O+ ^- k1) Register your DLL to the registry table.
: [1 Z! w3 A( r+ L/ P( @This 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.
! h- w- F- B8 f( z) u# ^6 r' @% f2) System-wide Windows Hooks5 A6 ?( F/ ^" `1 l, U' r6 i
This 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.
: {& {" @, V. Z& W  e+ z5 q4 B3) Create Remote Thread' N9 q# w" Y  D2 z! |5 z' Q
This 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. * w1 s+ S' t. X4 j' Q& f8 P
2 _9 ~2 {6 Z; N- [  j
To inject your code into another process’ code, we could use:. _* O: _6 Y- X/ i* o1 k
1) Proxy DLL5 Y9 E( k  u: c" V3 I
This 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.   ! u1 b* e! T+ a0 M4 j, A
2) Altering of the IAT (Import Address Table); u4 B0 R+ v0 I; L/ c# T
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.& }% I3 E8 m$ ?
3) Code Overwriting- T+ q, ~9 Y) K# d$ ^
The 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., t  F" |8 {% H% y" x' P
& F! m* W; R% b6 U8 \( X4 |
For 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.4 J9 w0 g( E) y7 ~' U1 U
Detours
# U+ d; k, {. @' i“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/1 Q* G* R. U& t; H

( @: G& J' Y6 A4 ^; v- ~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.
/ |$ @0 f% q7 {( a, ~; Z  w4 T4 `  C( s6 l; ~
DETOUR_TRAMPOLINE(
5 D5 m5 z5 ]) A2 z1 w$ w# NUINT WINAPI Real_SetTimer(HWND hWnd,     // handle of window for timer messages
$ O, m& b' Y- l8 y1 U                        UINT nIDEvent,         // timer identifier
8 o; V+ l+ @0 p6 T2 q, N7 q' e, w5 V                        UINT uElapse,            // time-out value
4 E; R& p0 s: `                        TIMERPROC lpTimerFunc), // address of timer procedure
: W# @/ f5 S5 V* v2 Z( q1 sSetTimer);$ t/ t0 U) ~2 b9 ~7 A- ]' L6 K
* f6 t& m. x: U: ^3 B2 G
Then we write our own SetTimer function.3 G: h. r9 A  L8 E
UINT WINAPI Mine_SetTimer(& j1 q( }- \. s- d; q
    HWND hWnd,           // handle of window for timer messages
- q) c/ y7 C8 Y9 E8 g; Z6 ?  i    UINT nIDEvent,        // timer identifier
- @! v5 t3 ^9 k    UINT uElapse,           // time-out value
  [" v8 J2 K9 S$ S/ @, \0 A    TIMERPROC lpTimerFunc   // address of timer procedure/ W  A' `/ D: ?3 F# y2 U3 C% J
    )
7 \$ o6 a: Y. v* P) J& O0 N{
9 u+ Z2 g6 L' e    uElapse=10* uElapse;
  |  z4 U2 V& x+ P2 r8 K7 q- B# [    return Real_SetTimer(hWnd,nIDEvent,uElapse,lpTimerFunc);
' P/ R' c' q$ H$ d+ h5 L8 A}  r% V$ _& {4 Q0 R( W

5 j7 c& Q6 s3 J- |' oThen write the functions to intercept the Real_SetTimer() function to your own Mine_SetTimer() function and the function that removes the interception.
8 j2 E$ J& ~* ?" x
( r( @# U0 c8 M  L3 yBOOL TrampolineWith(VOID), k# O/ A8 i8 v" R; w0 I3 {
{2 w) W! S6 `! X; o
    DetourFunctionWithTrampoline((PBYTE)Real_SetTimer,9 e0 i, C- M: A7 V1 d1 U. d9 T
                                        (PBYTE)Mine_SetTimer);: ~6 ]8 I: L: ~1 q" q4 G
    return TURE;3 Z8 N7 `! y( l; t6 q
}
) s' s! a8 @' n+ E. i2 U$ C2 r& _9 w* A* U, Z
BOOL TrampolineRemove(VOID)
3 r, E" k1 m1 B/ z{
$ m. C- a' \- O- S: L% R+ y0 N* ~    DetourRemove((PBYTE)Real_SetTimer,2 a$ v) `. [' @, Q% q1 U. F
               (PBYTE)Mine_SetTimer);# i6 S6 Y. p" l
    return TRUE;
- ]; {  y9 w$ J2 F5 k1 i}
! H" L/ W  T' r7 @, r0 p9 H
. Y0 J& T' Q9 {, }' a% RAt last, we write the DLLmain function.2 q  O6 E. I' X' F  J2 l
" L7 ?4 a" s! _" _: L
BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, PVOID lpReserved)
4 E+ j# b& U: u2 \$ {{
+ a) O2 J8 m! p# Q. p) d1 F, ^    switch (dwReason) {
) S; n+ g# S% {1 R      case DLL_PROCESS_ATTACH:
% h- c: g  H& m- O, W; v# e        return TrampolineWith();( W, S# T" U: N% q) x
      case DLL_PROCESS_DETACH:
* |) }1 F; A' k+ D        return TrampolineRemove();& X+ u1 e" [2 |6 E  x4 C
      case DLL_THREAD_ATTACH:( Y$ D- K9 \! Q) B4 v
        return TrampolineWith();1 S9 ?' [5 }  H; H7 j! U6 l; }9 J2 C" o
      case DLL_THREAD_DETACH:, f% o6 `' n- a$ [0 W
        return TrampolineRemove();5 p5 N, \( Y+ H  f. K/ Z# j0 Q2 d" ~
    }
7 L( p4 R( E6 Q; g. g    return TRUE;
8 O7 E5 r# x0 \+ Z2 }7 c4 }}
8 H4 n* u, v3 k9 f# W
* M: @8 Q/ G; x" d8 O" e0 ]By 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.
7 D9 T; O1 n' L1 z8 p( S; E
* S1 a" v9 Q& r2 d. v- }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.
* D; j. d# u: J
" f: P9 Q4 c/ S7 gTo 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.* _5 |6 {' o& n2 C2 E, M- c
Understanding Virtual Functions and Hooking Direct3d
3 H0 z: f1 U  `  ~, QVirtual 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”. 2 _( n. o" S9 G6 |

) }! i/ ~/ j2 b0 cBasically, 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:- S2 N: k* R; o% g4 R7 m/ Z3 b
1) Catch Direct3DCreate8() to get the IDirect3D8 object: h8 L$ f  Q4 \. o% z
2) Use the pointer of IDirect3D8 object, catch the CreateDevice() function to get the IDirect3DDevice8 object.
  l$ Q  m) Z3 K4 k3) Use the pointer of IDirect3DDevice8 object, catch Present() function.
& K8 C* L$ p5 \: ^$ u4) In the Present() function calculates the frame rate.
, T+ [* ?8 b7 P4 L
5 ]+ A4 A$ L" E  J0 t8 cHowever, 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.& f8 Q, F7 \0 ^. I. G

: }/ [% X: [% m873:      // Create the device# Z8 `% C2 t7 v5 t. K8 c2 b
874:      hr = m_pD3D->CreateDevice( m_dwAdapter, pDeviceInfo->DeviceType,) p8 E1 @5 P0 R# b3 h5 Q; J. E
875:                                 m_hWndFocus, pModeInfo->dwBehavior, &m_d3dpp,$ e$ e# }7 n3 J% {! |
876:                                 &m_pd3dDevice );
4 }. Z0 u- o. T+ Q00403114   mov         eax,dword ptr [this]" N; D( W/ j* ^/ N5 l
00403117   add         eax,2A4A8h9 l& d4 B+ c7 K7 z+ L1 z
0040311C   push        eax            // push the sixth parameter &m_pd3dDevice0 f- Y( |* S* T1 ]  Y  d$ O; x/ K4 y# p
0040311D   mov         ecx,dword ptr [this]
  q: f0 E2 A0 L/ P. P, y4 C) o00403120   add         ecx,2A464h( |6 J& s8 W. n, f6 k  x8 Y
00403126   push        ecx            //push the fifth parameter &m_d3dp6 ?4 ~/ ]. B& y) ?$ G+ y7 v3 N" V
00403127   mov         edx,dword ptr [pModeInfo]
1 ]$ H$ U9 e  f/ H/ B; V' g0040312A   mov         eax,dword ptr [edx+0Ch]  h: j0 N5 R& }' g# L
0040312D   push        eax    //push the fourth parameter pModeInfo->dwBehavior
7 c/ H5 W6 h2 l6 f) a2 J0040312E   mov         ecx,dword ptr [this]. j1 S% ~) d7 q. \* u
00403131   mov         edx,dword ptr [ecx+2A49Ch]
7 m# W! A+ }$ M/ Q' O' X( H. s4 t8 ?" |00403137   push        edx    //push the third parameter m_hWndFocus
7 _( V; k: ^2 T00403138   mov         eax,dword ptr [pDeviceInfo]
/ g6 J" Q: U. ^4 g  f# N  e1 p) E0040313B   mov         ecx,dword ptr [eax]  d0 \. {4 e  [' ~3 x$ E
0040313D   push        ecx    //push the second parameter pDeviceInfo->DeviceType
$ `3 ?( K* H1 c7 K5 ?3 e$ e! D$ A# Q- w, m0040313E   mov         edx,dword ptr [this]: ^( W9 T$ y5 X) a- h8 c
00403141   mov         eax,dword ptr [edx+2A448h]
: L: x( Q. t- u& [00403147   push        eax    //push the first parameter m_dwAdapter( K2 K% |' U# |$ _2 y2 t
00403148   mov         ecx,dword ptr [this]1 e4 m/ g* Y# o$ p& X( ]# O  z
0040314B   mov         edx,dword ptr [ecx+2A4A4h]    //calculate the return address! ^% x' [/ v7 A! O$ s* e
00403151   mov         eax,dword ptr [this]$ D' f) ^" L2 ^, k. x
00403154   mov         ecx,dword ptr [eax+2A4A4h]
6 F- V4 o% T, d$ \* q/ m; o0040315A   mov         eax,dword ptr [ecx]    //calculate the vtable address. It’s the value
2 S5 C. d' X* O                        //stored in ecx register.
  w) k! n# g% \# J7 C9 \" h4 ]0040315C   push        edx    //push the return address
( ^7 h) G( ^2 Q: j* D# S0040315D   call        dword ptr [eax+3Ch]    //call the CreateDevice() function
# g$ H' D  k! Z' a$ E+ X" e7 J00403160   mov         dword ptr
,eax" C$ j: f( _' J$ q# ~- u
1 x8 M- H. |, x. [# }; B
From 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.( [( h. f6 Y4 W% A% K; M( b! U
/ H0 G, S7 K1 j" e  k& W. T
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.
; @  ~* Y, j( K3 @% I6 sReference:
4 T* t% ]; Z. Z/ A6 ^. G[1] “API hooking revealed”, Ivo Ivanov% \7 K+ }4 ?6 I  d. q9 A
[2] “madCodeHook”, madshi) J% n5 I. l8 v1 J
[3] "Detours", Galen Hunt and Doug Brubacher) `  ?0 v. D  V3 @$ Y: T
[4] “Virtual Functions and their implementation in C”, Shivesh V.6 h( U+ J4 J( V! v5 [! N' W
[5] “Pointers to C++ Member Functions”, Michael D. Crawford




欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/) Powered by Discuz! X3.2