Lightweight Detours

On lunes, 27 de diciembre de 2010 0 comentarios

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:
        
Detourvoidfuncvoidhookuint bytes );
        ~
Detour();

        
void Unhook();
        
void Rehook();

        
// Call the original function ( without unhooking )
        
templatetypename 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 ediesiebpespebxedxecxeax;
            
// Get the arguments (as a struct) of a function with a stack frame
            
templatetypename T inline Targs() { 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_tctx );
        
typedef void (__cdecl *HookFn2)( context_tctx );

        
WiretapvoidplaceHookFn hookuint bytes );
        
WiretapvoidplaceHookFn2 hookuint 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:
  • Can hook any function and prevent it from running
  • Easily read and modify the stack arguments
Disadvantages of Detour:
  • 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:
  • Can hook in any function anywhere in its body
  • Easily read and modify the registers
  • Harder to detect (both will fail a checksum though)
Disadvantages of Wiretap:
  • 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 Magicint x )
    {
        return 
x;
    }
    
// This function will be hooked with Wiretap
    
__declspec(noinline)
    
int Magic2int aint bint c )
    {
        return 
5;
    }


    
int __cdecl Hooked_Magicint x )
    {
        return -
x;
    }
    
typedef int (__cdeclMagicFn)( int );

    
void Wiretap_Magic2Wiretap::context_tctx )
    {
        
struct args_t
        
{
            
int a;
            
int b;
            
int c;
        };
        
args_tpArgs ctx->args<args_t>();
        
pArgs->+= 1;
        
pArgs->+= 2;
        
pArgs->+= 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);
        
Wiretap hook2make_ptr<void*>( (void*)&Magic20x3 ), &Wiretap_Magic2);

        
// Call them
        
if ( Magic) != -) return false;
        if ( 
hook.Orig<MagicFn>()( ) != ) return false;

        if ( 
Magic211) != ) 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
Attached Files

0 comentarios:

Publicar un comentario