aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.8.3/libsanitizer/interception/interception_win.cc
blob: 443bdce1859abe6ab65b8154e800fde00ffed8db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//===-- interception_linux.cc -----------------------------------*- C++ -*-===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Windows-specific interception methods.
//===----------------------------------------------------------------------===//

#ifdef _WIN32

#include "interception.h"
#include <windows.h>

namespace __interception {

bool GetRealFunctionAddress(const char *func_name, uptr *func_addr) {
  const char *DLLS[] = {
    "msvcr80.dll",
    "msvcr90.dll",
    "kernel32.dll",
    NULL
  };
  *func_addr = 0;
  for (size_t i = 0; *func_addr == 0 && DLLS[i]; ++i) {
    *func_addr = (uptr)GetProcAddress(GetModuleHandleA(DLLS[i]), func_name);
  }
  return (*func_addr != 0);
}

// FIXME: internal_str* and internal_mem* functions should be moved from the
// ASan sources into interception/.

static void _memset(void *p, int value, size_t sz) {
  for (size_t i = 0; i < sz; ++i)
    ((char*)p)[i] = (char)value;
}

static void _memcpy(void *dst, void *src, size_t sz) {
  char *dst_c = (char*)dst,
       *src_c = (char*)src;
  for (size_t i = 0; i < sz; ++i)
    dst_c[i] = src_c[i];
}

static void WriteJumpInstruction(char *jmp_from, char *to) {
  // jmp XXYYZZWW = E9 WW ZZ YY XX, where XXYYZZWW is an offset fromt jmp_from
  // to the next instruction to the destination.
  ptrdiff_t offset = to - jmp_from - 5;
  *jmp_from = '\xE9';
  *(ptrdiff_t*)(jmp_from + 1) = offset;
}

bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
#ifdef _WIN64
# error OverrideFunction was not tested on x64
#endif
  // Basic idea:
  // We write 5 bytes (jmp-to-new_func) at the beginning of the 'old_func'
  // to override it. We want to be able to execute the original 'old_func' from
  // the wrapper, so we need to keep the leading 5+ bytes ('head') of the
  // original instructions somewhere with a "jmp old_func+head".
  // We call these 'head'+5 bytes of instructions a "trampoline".

  // Trampolines are allocated from a common pool.
  const int POOL_SIZE = 1024;
  static char *pool = NULL;
  static size_t pool_used = 0;
  if (pool == NULL) {
    pool = (char*)VirtualAlloc(NULL, POOL_SIZE,
                               MEM_RESERVE | MEM_COMMIT,
                               PAGE_EXECUTE_READWRITE);
    // FIXME: set PAGE_EXECUTE_READ access after setting all interceptors?
    if (pool == NULL)
      return false;
    _memset(pool, 0xCC /* int 3 */, POOL_SIZE);
  }

  char* old_bytes = (char*)old_func;
  char* trampoline = pool + pool_used;

  // Find out the number of bytes of the instructions we need to copy to the
  // island and store it in 'head'.
  size_t head = 0;
  while (head < 5) {
    switch (old_bytes[head]) {
      case '\x55':  // push ebp
      case '\x56':  // push esi
      case '\x57':  // push edi
        head++;
        continue;
    }
    switch (*(unsigned short*)(old_bytes + head)) {  // NOLINT
      case 0xFF8B:  // 8B FF = mov edi, edi
      case 0xEC8B:  // 8B EC = mov ebp, esp
      case 0xC033:  // 33 C0 = xor eax, eax
        head += 2;
        continue;
      case 0xEC83:  // 83 EC XX = sub esp, XX
        head += 3;
        continue;
      case 0xC1F7:  // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX
        head += 6;
        continue;
    }
    switch (0x00FFFFFF & *(unsigned int*)(old_bytes + head)) {
      case 0x24448A:  // 8A 44 24 XX = mov eal, dword ptr [esp+XXh]
      case 0x244C8B:  // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh]
      case 0x24548B:  // 8B 54 24 XX = mov edx, dword ptr [esp+XXh]
      case 0x247C8B:  // 8B 7C 24 XX = mov edi, dword ptr [esp+XXh]
        head += 4;
        continue;
    }

    // Unknown instruction!
    return false;
  }

  if (pool_used + head + 5 > POOL_SIZE)
    return false;

  // Now put the "jump to trampoline" instruction into the original code.
  DWORD old_prot, unused_prot;
  if (!VirtualProtect((void*)old_func, head, PAGE_EXECUTE_READWRITE,
                      &old_prot))
    return false;

  // Put the needed instructions into the trampoline bytes.
  _memcpy(trampoline, old_bytes, head);
  WriteJumpInstruction(trampoline + head, old_bytes + head);
  *orig_old_func = (uptr)trampoline;
  pool_used += head + 5;

  // Intercept the 'old_func'.
  WriteJumpInstruction(old_bytes, (char*)new_func);
  _memset(old_bytes + 5, 0xCC /* int 3 */, head - 5);

  if (!VirtualProtect((void*)old_func, head, old_prot, &unused_prot))
    return false;  // not clear if this failure bothers us.

  return true;
}

}  // namespace __interception

#endif  // _WIN32