Lightweight Detours
While working on my toolkit library I did some work on detours and I think I made something interesting.
I made 2 'code patching' hook classes. One is a good old detour to hook functions. The other is far more interesting, it basically allows you to 'wiretap' any function any where in its body.
A quick review:
PHP Code:
#ifndef _TOOLKIT_DETOURS_HGUARD
#define _TOOLKIT_DETOURS_HGUARD
// ----------------------------------------------------------------
// Toolkit: Detours
// ----------------------------------------------------------------
// Classes to allow easy modifying of the target code in order to hook functions.
namespace toolkit {
// ------------------------------------------------
// Class: Detour
// ------------------------------------------------
// Detours a function using the standard [jmp xxxx] method.
class Detour
{
public:
Detour( void* func, void* hook, uint bytes );
~Detour();
void Unhook();
void Rehook();
// Call the original function ( without unhooking )
template< typename Fn >
inline Fn Orig() { return reinterpret_cast<Fn>( _stub ); }
private:
void* _func;
void* _hook;
void* _stub;
uint _bytes;
bool _hooked;
};
// ----------------------------------------------------------------
// Class: Wiretap
// ----------------------------------------------------------------
// Run your hook code in the middle of a function.
// It'll continue execution after your hook returns.
//
// The function will get redirected to a small stub which does this:
//
class Wiretap
{
public:
// Stores the result of a pushad instruction
struct context32_t
{
types::dword edi, esi, ebp, esp, ebx, edx, ecx, eax;
// Get the arguments (as a struct) of a function with a stack frame
template< typename T > inline T* args() { return reinterpret_cast<T*>( ebp + 0x8 ); }
// Get the return address
inline void*& retaddr() { return *reinterpret_cast<void**>( ebp + 0x4 ); }
};
typedef context32_t context_t;
// Access the function arguments manually trough esp or ebp.
// Modifying this context will modify the ACTUAL registers when the function continues.
typedef void (__stdcall *HookFn)( context_t* ctx );
typedef void (__cdecl *HookFn2)( context_t* ctx );
Wiretap( void* place, HookFn hook, uint bytes );
Wiretap( void* place, HookFn2 hook, uint bytes );
~Wiretap();
void Unhook();
void Rehook();
private:
void* _place;
void* _stub;
uint _bytes;
bool _hooked;
};
};
#endif // !_TOOLKIT_DETOURS_HGUARD
The Wiretap stub looks like this, the context32_t struct basically mimics the structure of stack after a pushad
Code:stub: pushad push esp call hook ;add esp, 0x4 ; When constructor is called where the hook has a __cdecl calling convention popad [ Overwritten bytes go here ] jmp continue
Comparison of my implementation of Detour and Wiretap:
Advantages of Detour:Disadvantages of Detour:
- Can hook any function and prevent it from running
- Easily read and modify the stack arguments
- Cannot hook in the middle of a function
- Easy to detect if a function has been hooked (just check the first few bytes for a jmp or other known codes)
- Can be annoying to read arguments passed via registers
Advantages of Wiretap:Disadvantages of Wiretap:
- Can hook in any function anywhere in its body
- Easily read and modify the registers
- Harder to detect (both will fail a checksum though)
- Cannot prevent the original function from running
- Reading and modifying arguments passed via the stack is more cumbersome, especially if the function doesn't have a stack frame (using ebp)
Example usage:
PHP Code:
// ----------------------------------------------------------------
// Testing Detours
// ----------------------------------------------------------------
// This function will be hooked with Detour
__declspec(noinline)
int Magic( int x )
{
return x;
}
// This function will be hooked with Wiretap
__declspec(noinline)
int Magic2( int a, int b, int c )
{
return a + 5;
}
int __cdecl Hooked_Magic( int x )
{
return -x;
}
typedef int (__cdecl* MagicFn)( int );
void Wiretap_Magic2( Wiretap::context_t* ctx )
{
struct args_t
{
int a;
int b;
int c;
};
args_t* pArgs = ctx->args<args_t>();
pArgs->a += 1;
pArgs->b += 2;
pArgs->c += 3;
}
bool TestDetours()
{ #ifdef NDEBUG
// Does not work in Debug mode because the compiler adds an indirection to function calls. (Possible to allow edit&continue).
// The operator& doesn't get the correct pointer.
Detour hook( &Magic, &Hooked_Magic, 6 );
Wiretap hook2( make_ptr<void*>( (void*)&Magic2, 0x3 ), &Wiretap_Magic2, 6 );
// Call them
if ( Magic( 1 ) != -1 ) return false;
if ( hook.Orig<MagicFn>()( 1 ) != 1 ) return false;
if ( Magic2( 1, 1, 1 ) != 7 ) return false; #endif // NDEBUG
return true;
}
I apologize for the dependencies, It's just how I've organized my toolkit library.
The memory.h is something I quickly slapped together for pooling read/write/exec memory. I also experimented with generating stub functions using templates (but dropped it in favor of VirtualAlloc).
Atm it's pretty much Windows x86 only (because that's all I need atm). Get me the opcodes for x64 and I'll make it work for x64 (although I can't test it due to running Vista x86).
http://www.ziddu.com/download/13158196/detours.zip.html
0 comentarios:
Publicar un comentario