]>
Commit | Line | Data |
---|---|---|
549e2197 | 1 | //===-- interception_linux.cc -----------------------------------*- C++ -*-===// |
2 | // | |
3 | // This file is distributed under the University of Illinois Open Source | |
4 | // License. See LICENSE.TXT for details. | |
5 | // | |
6 | //===----------------------------------------------------------------------===// | |
7 | // | |
8 | // This file is a part of AddressSanitizer, an address sanity checker. | |
9 | // | |
10 | // Windows-specific interception methods. | |
11 | //===----------------------------------------------------------------------===// | |
12 | ||
13 | #ifdef _WIN32 | |
14 | ||
15 | #include "interception.h" | |
16 | #include <windows.h> | |
17 | ||
18 | namespace __interception { | |
19 | ||
549e2197 | 20 | // FIXME: internal_str* and internal_mem* functions should be moved from the |
21 | // ASan sources into interception/. | |
22 | ||
23 | static void _memset(void *p, int value, size_t sz) { | |
24 | for (size_t i = 0; i < sz; ++i) | |
25 | ((char*)p)[i] = (char)value; | |
26 | } | |
27 | ||
28 | static void _memcpy(void *dst, void *src, size_t sz) { | |
29 | char *dst_c = (char*)dst, | |
30 | *src_c = (char*)src; | |
31 | for (size_t i = 0; i < sz; ++i) | |
32 | dst_c[i] = src_c[i]; | |
33 | } | |
34 | ||
35 | static void WriteJumpInstruction(char *jmp_from, char *to) { | |
36 | // jmp XXYYZZWW = E9 WW ZZ YY XX, where XXYYZZWW is an offset fromt jmp_from | |
37 | // to the next instruction to the destination. | |
38 | ptrdiff_t offset = to - jmp_from - 5; | |
39 | *jmp_from = '\xE9'; | |
40 | *(ptrdiff_t*)(jmp_from + 1) = offset; | |
41 | } | |
42 | ||
7d752f28 | 43 | static char *GetMemoryForTrampoline(size_t size) { |
549e2197 | 44 | // Trampolines are allocated from a common pool. |
45 | const int POOL_SIZE = 1024; | |
46 | static char *pool = NULL; | |
47 | static size_t pool_used = 0; | |
7d752f28 | 48 | if (!pool) { |
49 | pool = (char *)VirtualAlloc(NULL, POOL_SIZE, MEM_RESERVE | MEM_COMMIT, | |
50 | PAGE_EXECUTE_READWRITE); | |
51 | // FIXME: Might want to apply PAGE_EXECUTE_READ access after all the | |
52 | // interceptors are in place. | |
53 | if (!pool) | |
54 | return NULL; | |
549e2197 | 55 | _memset(pool, 0xCC /* int 3 */, POOL_SIZE); |
56 | } | |
57 | ||
7d752f28 | 58 | if (pool_used + size > POOL_SIZE) |
59 | return NULL; | |
549e2197 | 60 | |
7d752f28 | 61 | char *ret = pool + pool_used; |
62 | pool_used += size; | |
63 | return ret; | |
64 | } | |
65 | ||
66 | // Returns 0 on error. | |
67 | static size_t RoundUpToInstrBoundary(size_t size, char *code) { | |
68 | size_t cursor = 0; | |
69 | while (cursor < size) { | |
70 | switch (code[cursor]) { | |
71 | case '\x51': // push ecx | |
72 | case '\x52': // push edx | |
73 | case '\x53': // push ebx | |
74 | case '\x54': // push esp | |
549e2197 | 75 | case '\x55': // push ebp |
76 | case '\x56': // push esi | |
77 | case '\x57': // push edi | |
7d752f28 | 78 | case '\x5D': // pop ebp |
79 | cursor++; | |
80 | continue; | |
81 | case '\x6A': // 6A XX = push XX | |
82 | cursor += 2; | |
83 | continue; | |
84 | case '\xE9': // E9 XX YY ZZ WW = jmp WWZZYYXX | |
85 | cursor += 5; | |
549e2197 | 86 | continue; |
87 | } | |
7d752f28 | 88 | switch (*(unsigned short*)(code + cursor)) { // NOLINT |
549e2197 | 89 | case 0xFF8B: // 8B FF = mov edi, edi |
90 | case 0xEC8B: // 8B EC = mov ebp, esp | |
91 | case 0xC033: // 33 C0 = xor eax, eax | |
7d752f28 | 92 | cursor += 2; |
549e2197 | 93 | continue; |
7d752f28 | 94 | case 0x458B: // 8B 45 XX = mov eax, dword ptr [ebp+XXh] |
95 | case 0x5D8B: // 8B 5D XX = mov ebx, dword ptr [ebp+XXh] | |
549e2197 | 96 | case 0xEC83: // 83 EC XX = sub esp, XX |
a9586c9c | 97 | case 0x75FF: // FF 75 XX = push dword ptr [ebp+XXh] |
7d752f28 | 98 | cursor += 3; |
549e2197 | 99 | continue; |
100 | case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX | |
a9586c9c | 101 | case 0x25FF: // FF 25 XX YY ZZ WW = jmp dword ptr ds:[WWZZYYXX] |
7d752f28 | 102 | cursor += 6; |
103 | continue; | |
104 | case 0x3D83: // 83 3D XX YY ZZ WW TT = cmp TT, WWZZYYXX | |
105 | cursor += 7; | |
549e2197 | 106 | continue; |
107 | } | |
7d752f28 | 108 | switch (0x00FFFFFF & *(unsigned int*)(code + cursor)) { |
549e2197 | 109 | case 0x24448A: // 8A 44 24 XX = mov eal, dword ptr [esp+XXh] |
a9586c9c | 110 | case 0x24448B: // 8B 44 24 XX = mov eax, dword ptr [esp+XXh] |
549e2197 | 111 | case 0x244C8B: // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh] |
112 | case 0x24548B: // 8B 54 24 XX = mov edx, dword ptr [esp+XXh] | |
7d752f28 | 113 | case 0x24748B: // 8B 74 24 XX = mov esi, dword ptr [esp+XXh] |
549e2197 | 114 | case 0x247C8B: // 8B 7C 24 XX = mov edi, dword ptr [esp+XXh] |
7d752f28 | 115 | cursor += 4; |
549e2197 | 116 | continue; |
117 | } | |
118 | ||
119 | // Unknown instruction! | |
7d752f28 | 120 | // FIXME: Unknown instruction failures might happen when we add a new |
121 | // interceptor or a new compiler version. In either case, they should result | |
122 | // in visible and readable error messages. However, merely calling abort() | |
a9586c9c | 123 | // leads to an infinite recursion in CheckFailed. |
7d752f28 | 124 | // Do we have a good way to abort with an error message here? |
a9586c9c | 125 | __debugbreak(); |
7d752f28 | 126 | return 0; |
549e2197 | 127 | } |
128 | ||
7d752f28 | 129 | return cursor; |
130 | } | |
131 | ||
132 | bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) { | |
133 | #ifdef _WIN64 | |
134 | #error OverrideFunction is not yet supported on x64 | |
135 | #endif | |
136 | // Function overriding works basically like this: | |
137 | // We write "jmp <new_func>" (5 bytes) at the beginning of the 'old_func' | |
138 | // to override it. | |
139 | // We might want to be able to execute the original 'old_func' from the | |
140 | // wrapper, in this case we need to keep the leading 5+ bytes ('head') | |
141 | // of the original code somewhere with a "jmp <old_func+head>". | |
142 | // We call these 'head'+5 bytes of instructions a "trampoline". | |
143 | char *old_bytes = (char *)old_func; | |
144 | ||
145 | // We'll need at least 5 bytes for a 'jmp'. | |
146 | size_t head = 5; | |
147 | if (orig_old_func) { | |
148 | // Find out the number of bytes of the instructions we need to copy | |
149 | // to the trampoline and store it in 'head'. | |
150 | head = RoundUpToInstrBoundary(head, old_bytes); | |
151 | if (!head) | |
152 | return false; | |
153 | ||
154 | // Put the needed instructions into the trampoline bytes. | |
155 | char *trampoline = GetMemoryForTrampoline(head + 5); | |
156 | if (!trampoline) | |
157 | return false; | |
158 | _memcpy(trampoline, old_bytes, head); | |
159 | WriteJumpInstruction(trampoline + head, old_bytes + head); | |
160 | *orig_old_func = (uptr)trampoline; | |
161 | } | |
549e2197 | 162 | |
7d752f28 | 163 | // Now put the "jmp <new_func>" instruction at the original code location. |
164 | // We should preserve the EXECUTE flag as some of our own code might be | |
165 | // located in the same page (sic!). FIXME: might consider putting the | |
166 | // __interception code into a separate section or something? | |
549e2197 | 167 | DWORD old_prot, unused_prot; |
7d752f28 | 168 | if (!VirtualProtect((void *)old_bytes, head, PAGE_EXECUTE_READWRITE, |
549e2197 | 169 | &old_prot)) |
170 | return false; | |
171 | ||
7d752f28 | 172 | WriteJumpInstruction(old_bytes, (char *)new_func); |
549e2197 | 173 | _memset(old_bytes + 5, 0xCC /* int 3 */, head - 5); |
174 | ||
7d752f28 | 175 | // Restore the original permissions. |
176 | if (!VirtualProtect((void *)old_bytes, head, old_prot, &unused_prot)) | |
549e2197 | 177 | return false; // not clear if this failure bothers us. |
178 | ||
179 | return true; | |
180 | } | |
181 | ||
a9586c9c | 182 | static const void **InterestingDLLsAvailable() { |
0328398d | 183 | const char *InterestingDLLs[] = {"kernel32.dll", |
184 | "msvcr110.dll", // VS2012 | |
185 | "msvcr120.dll", // VS2013 | |
186 | NULL}; | |
a9586c9c | 187 | static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 }; |
188 | if (!result[0]) { | |
189 | for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) { | |
190 | if (HMODULE h = GetModuleHandleA(InterestingDLLs[i])) | |
191 | result[j++] = (void *)h; | |
192 | } | |
193 | } | |
194 | return (const void **)&result[0]; | |
195 | } | |
196 | ||
197 | static bool GetFunctionAddressInDLLs(const char *func_name, uptr *func_addr) { | |
198 | *func_addr = 0; | |
199 | const void **DLLs = InterestingDLLsAvailable(); | |
200 | for (size_t i = 0; *func_addr == 0 && DLLs[i]; ++i) | |
201 | *func_addr = (uptr)GetProcAddress((HMODULE)DLLs[i], func_name); | |
202 | return (*func_addr != 0); | |
203 | } | |
204 | ||
205 | bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func) { | |
206 | uptr orig_func; | |
207 | if (!GetFunctionAddressInDLLs(name, &orig_func)) | |
208 | return false; | |
209 | return OverrideFunction(orig_func, new_func, orig_old_func); | |
210 | } | |
211 | ||
549e2197 | 212 | } // namespace __interception |
213 | ||
214 | #endif // _WIN32 |