Home | History | Annotate | Download | only in interception
      1 //===-- interception_linux.cc -----------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is a part of AddressSanitizer, an address sanity checker.
     11 //
     12 // Windows-specific interception methods.
     13 //===----------------------------------------------------------------------===//
     14 
     15 #ifdef _WIN32
     16 
     17 #include "interception.h"
     18 #include <windows.h>
     19 
     20 namespace __interception {
     21 
     22 bool GetRealFunctionAddress(const char *func_name, uptr *func_addr) {
     23   const char *DLLS[] = {
     24     "msvcr80.dll",
     25     "msvcr90.dll",
     26     "kernel32.dll",
     27     NULL
     28   };
     29   *func_addr = 0;
     30   for (size_t i = 0; *func_addr == 0 && DLLS[i]; ++i) {
     31     *func_addr = (uptr)GetProcAddress(GetModuleHandleA(DLLS[i]), func_name);
     32   }
     33   return (*func_addr != 0);
     34 }
     35 
     36 // FIXME: internal_str* and internal_mem* functions should be moved from the
     37 // ASan sources into interception/.
     38 
     39 static void _memset(void *p, int value, size_t sz) {
     40   for (size_t i = 0; i < sz; ++i)
     41     ((char*)p)[i] = (char)value;
     42 }
     43 
     44 static void _memcpy(void *dst, void *src, size_t sz) {
     45   char *dst_c = (char*)dst,
     46        *src_c = (char*)src;
     47   for (size_t i = 0; i < sz; ++i)
     48     dst_c[i] = src_c[i];
     49 }
     50 
     51 static void WriteJumpInstruction(char *jmp_from, char *to) {
     52   // jmp XXYYZZWW = E9 WW ZZ YY XX, where XXYYZZWW is an offset fromt jmp_from
     53   // to the next instruction to the destination.
     54   ptrdiff_t offset = to - jmp_from - 5;
     55   *jmp_from = '\xE9';
     56   *(ptrdiff_t*)(jmp_from + 1) = offset;
     57 }
     58 
     59 bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
     60 #ifdef _WIN64
     61 # error OverrideFunction was not tested on x64
     62 #endif
     63   // Basic idea:
     64   // We write 5 bytes (jmp-to-new_func) at the beginning of the 'old_func'
     65   // to override it. We want to be able to execute the original 'old_func' from
     66   // the wrapper, so we need to keep the leading 5+ bytes ('head') of the
     67   // original instructions somewhere with a "jmp old_func+head".
     68   // We call these 'head'+5 bytes of instructions a "trampoline".
     69 
     70   // Trampolines are allocated from a common pool.
     71   const int POOL_SIZE = 1024;
     72   static char *pool = NULL;
     73   static size_t pool_used = 0;
     74   if (pool == NULL) {
     75     pool = (char*)VirtualAlloc(NULL, POOL_SIZE,
     76                                MEM_RESERVE | MEM_COMMIT,
     77                                PAGE_EXECUTE_READWRITE);
     78     // FIXME: set PAGE_EXECUTE_READ access after setting all interceptors?
     79     if (pool == NULL)
     80       return false;
     81     _memset(pool, 0xCC /* int 3 */, POOL_SIZE);
     82   }
     83 
     84   char* old_bytes = (char*)old_func;
     85   char* trampoline = pool + pool_used;
     86 
     87   // Find out the number of bytes of the instructions we need to copy to the
     88   // island and store it in 'head'.
     89   size_t head = 0;
     90   while (head < 5) {
     91     switch (old_bytes[head]) {
     92       case '\x55':  // push ebp
     93       case '\x56':  // push esi
     94       case '\x57':  // push edi
     95         head++;
     96         continue;
     97     }
     98     switch (*(unsigned short*)(old_bytes + head)) {  // NOLINT
     99       case 0xFF8B:  // 8B FF = mov edi, edi
    100       case 0xEC8B:  // 8B EC = mov ebp, esp
    101       case 0xC033:  // 33 C0 = xor eax, eax
    102         head += 2;
    103         continue;
    104       case 0xEC83:  // 83 EC XX = sub esp, XX
    105         head += 3;
    106         continue;
    107       case 0xC1F7:  // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX
    108         head += 6;
    109         continue;
    110     }
    111     switch (0x00FFFFFF & *(unsigned int*)(old_bytes + head)) {
    112       case 0x24448A:  // 8A 44 24 XX = mov eal, dword ptr [esp+XXh]
    113       case 0x244C8B:  // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh]
    114       case 0x24548B:  // 8B 54 24 XX = mov edx, dword ptr [esp+XXh]
    115       case 0x247C8B:  // 8B 7C 24 XX = mov edi, dword ptr [esp+XXh]
    116         head += 4;
    117         continue;
    118     }
    119 
    120     // Unknown instruction!
    121     return false;
    122   }
    123 
    124   if (pool_used + head + 5 > POOL_SIZE)
    125     return false;
    126 
    127   // Now put the "jump to trampoline" instruction into the original code.
    128   DWORD old_prot, unused_prot;
    129   if (!VirtualProtect((void*)old_func, head, PAGE_EXECUTE_READWRITE,
    130                       &old_prot))
    131     return false;
    132 
    133   // Put the needed instructions into the trampoline bytes.
    134   _memcpy(trampoline, old_bytes, head);
    135   WriteJumpInstruction(trampoline + head, old_bytes + head);
    136   *orig_old_func = (uptr)trampoline;
    137   pool_used += head + 5;
    138 
    139   // Intercept the 'old_func'.
    140   WriteJumpInstruction(old_bytes, (char*)new_func);
    141   _memset(old_bytes + 5, 0xCC /* int 3 */, head - 5);
    142 
    143   if (!VirtualProtect((void*)old_func, head, old_prot, &unused_prot))
    144     return false;  // not clear if this failure bothers us.
    145 
    146   return true;
    147 }
    148 
    149 }  // namespace __interception
    150 
    151 #endif  // _WIN32
    152