Dangerous Detours, Part 1: Introduction

Detours is a library that allows you to hook arbitrary functions by rewriting machine code. While a description of the exact implementation approach can be found in the corresponding paper as well as in numerous other sources, the basic idea is as follows:

  • In the to-be-hooked function, disassemble the first instructions until you have read at least 5 bytes. As instructions are variable length on x86, we may end up having to read more than 5 bytes to reach the next instruction boundary. Let the number of bytes read be n.
  • Allocate n+5 bytes of memory which will make up the trampoline.
  • Copy the n bytes from the to-be-hooked function to the trampoline, followed by a near jump to the to-be-hooked function + offset n (i.e. the first instruction after the instructions we copied)
  • Now overwrite the first 5 bytes of the to-be-hooked function with a near jump to the hook-function
  • The hook function may either return, so that the original function is never executed or instead jump to the trampoline

If we want to replace the to-be-hooked function, the execution flow is as follows:

  • Entering to-be-hooked function
  • Jump to hook function
  • Hook function does its work
  • Return to caller

The body of the to-be-hooked function is never executed.

        Caller function
       /          ^
      /           |
     v            |
  (via jmp        |
in to-be-hooked   |
  function)       |
     |            |
     v            | return
    Hook  --------+
  function

If instead the hook function, after having done its work, wants the original function to execute, the execution flow is as follows:

  • Entering to-be-hooked function
  • Jump to hook function
  • Hook function does its work
  • Jump to trampoline
  • Execute n bytes in trampoline
  • Jump to original function + offset n
  • Original function runs and finally returns

        Caller function
       /                ^
      /                  \
     v                    \
    

    (via jmp
    in to-be-hooked To-be-hooked function) function | ^ v | Hook ————-Trampoline function

Example: Replacing a function

As an example, we ‘replace’ OriginalFunction by AlternateFunction. The source code is as follows (error checking omitted):

    #include <stdio.h>
    #include <tchar.h>
    #include <windows.h>
    #include <detours.h>
    
    __declspec(noinline)
    static void OriginalFunction( PCWSTR Arg1, LONG Arg2 )
    {
      wprintf( L"OriginalFunction(%s, %d)\n", Arg1, Arg2 );
    }
    
    __declspec(noinline)
    static void AlternateFunction( PCWSTR Arg1, LONG Arg2 )
    {
      wprintf( L"AlternateFunction(%s, %d)\n", Arg1, Arg2 );
    }
    
    int wmain()
    {
      //
      // Install hook.
      //
      DetourTransactionBegin();
      DetourUpdateThread( GetCurrentThread() );
      
      PVOID Func = ( PVOID ) OriginalFunction;
      DetourAttach( 
          &Func, 
          AlternateFunction );
    
      DetourTransactionCommit();
      
      //
      // Call (hooked) function.
      //
      OriginalFunction( L"Hello", 42 );
    
      return 0;
    }
    

The code is straightforward – we instruct Detours to hook OriginalFunction and call AlternateFunction instead. We do not make use of the trampoline. The output is:

    AlternateFunction(Hello, 42)
    

Up to this point, everything works as advertised. And indeed there is little that can go wrong if we just want to ‘replace’ a function. If, however, we want the hook function eventually call the original function by making use of the trampoline, it gets more interesting, as we will see in Part 2.

« Back to home