OLD | NEW |
1 /* Copyright (c) 2007, Google Inc. | 1 /* Copyright (c) 2007, Google Inc. |
2 * All rights reserved. | 2 * All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 11 matching lines...) Expand all Loading... |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 * | 29 * |
30 * --- | 30 * --- |
31 * Author: Joi Sigurdsson | 31 * Author: Joi Sigurdsson |
| 32 * Author: Scott Francis |
32 * | 33 * |
33 * Implementation of PreamblePatcher | 34 * Implementation of PreamblePatcher |
34 */ | 35 */ |
35 | 36 |
36 #include "preamble_patcher.h" | 37 #include "preamble_patcher.h" |
37 | 38 |
38 #include "mini_disassembler.h" | 39 #include "mini_disassembler.h" |
39 | 40 |
40 // compatibility shims | 41 // compatibility shims |
41 #include "base/logging.h" | 42 #include "base/logging.h" |
42 | 43 |
43 // Definitions of assembly statements we need | 44 // Definitions of assembly statements we need |
44 #define ASM_JMP32REL 0xE9 | 45 #define ASM_JMP32REL 0xE9 |
45 #define ASM_INT3 0xCC | 46 #define ASM_INT3 0xCC |
46 #define ASM_JMP32ABS_0 0xFF | 47 #define ASM_JMP32ABS_0 0xFF |
47 #define ASM_JMP32ABS_1 0x25 | 48 #define ASM_JMP32ABS_1 0x25 |
48 #define ASM_JMP8REL 0xEB | 49 #define ASM_JMP8REL 0xEB |
| 50 #define ASM_JCC32REL_0 0x0F |
| 51 #define ASM_JCC32REL_1_MASK 0x80 |
| 52 #define ASM_NOP 0x90 |
| 53 // X64 opcodes |
| 54 #define ASM_REXW 0x48 |
| 55 #define ASM_MOVRAX_IMM 0xB8 |
| 56 #define ASM_JMP 0xFF |
| 57 #define ASM_JMP_RAX 0xE0 |
49 | 58 |
50 namespace sidestep { | 59 namespace sidestep { |
51 | 60 |
| 61 PreamblePatcher::PreamblePage* PreamblePatcher::preamble_pages_ = NULL; |
| 62 long PreamblePatcher::granularity_ = 0; |
| 63 long PreamblePatcher::pagesize_ = 0; |
| 64 bool PreamblePatcher::initialized_ = false; |
| 65 |
| 66 static const unsigned int kPreamblePageMagic = 0x4347414D; // "MAGC" |
| 67 |
52 // Handle a special case that we see with functions that point into an | 68 // Handle a special case that we see with functions that point into an |
53 // IAT table (including functions linked statically into the | 69 // IAT table (including functions linked statically into the |
54 // application): these function already starts with ASM_JMP32*. For | 70 // application): these function already starts with ASM_JMP32*. For |
55 // instance, malloc() might be implemented as a JMP to __malloc(). | 71 // instance, malloc() might be implemented as a JMP to __malloc(). |
56 // This function follows the initial JMPs for us, until we get to the | 72 // This function follows the initial JMPs for us, until we get to the |
57 // place where the actual code is defined. If we get to STOP_BEFORE, | 73 // place where the actual code is defined. If we get to STOP_BEFORE, |
58 // we return the address before stop_before. | 74 // we return the address before stop_before. The stop_before_trampoline |
| 75 // flag is used in 64-bit mode. If true, we will return the address |
| 76 // before a trampoline is detected. Trampolines are defined as: |
| 77 // |
| 78 // nop |
| 79 // mov rax, <replacement_function> |
| 80 // jmp rax |
| 81 // |
| 82 // See PreamblePatcher::RawPatchWithStub for more information. |
59 void* PreamblePatcher::ResolveTargetImpl(unsigned char* target, | 83 void* PreamblePatcher::ResolveTargetImpl(unsigned char* target, |
60 unsigned char* stop_before) { | 84 unsigned char* stop_before, |
| 85 bool stop_before_trampoline) { |
61 if (target == NULL) | 86 if (target == NULL) |
62 return NULL; | 87 return NULL; |
63 while (1) { | 88 while (1) { |
64 unsigned char* new_target; | 89 unsigned char* new_target; |
65 if (target[0] == ASM_JMP32REL) { | 90 if (target[0] == ASM_JMP32REL) { |
66 // target[1-4] holds the place the jmp goes to, but it's | 91 // target[1-4] holds the place the jmp goes to, but it's |
67 // relative to the next instruction. | 92 // relative to the next instruction. |
68 int relative_offset; // Windows guarantees int is 4 bytes | 93 int relative_offset; // Windows guarantees int is 4 bytes |
69 SIDESTEP_ASSERT(sizeof(relative_offset) == 4); | 94 SIDESTEP_ASSERT(sizeof(relative_offset) == 4); |
70 memcpy(reinterpret_cast<void*>(&relative_offset), | 95 memcpy(reinterpret_cast<void*>(&relative_offset), |
71 reinterpret_cast<void*>(target + 1), 4); | 96 reinterpret_cast<void*>(target + 1), 4); |
72 new_target = target + 5 + relative_offset; | 97 new_target = target + 5 + relative_offset; |
73 } else if (target[0] == ASM_JMP8REL) { | 98 } else if (target[0] == ASM_JMP8REL) { |
74 // Visual Studio 7.1 implements new[] as an 8 bit jump to new | 99 // Visual Studio 7.1 implements new[] as an 8 bit jump to new |
75 signed char relative_offset; | 100 signed char relative_offset; |
76 memcpy(reinterpret_cast<void*>(&relative_offset), | 101 memcpy(reinterpret_cast<void*>(&relative_offset), |
77 reinterpret_cast<void*>(target + 1), 1); | 102 reinterpret_cast<void*>(target + 1), 1); |
78 new_target = target + 2 + relative_offset; | 103 new_target = target + 2 + relative_offset; |
79 } else if (target[0] == ASM_JMP32ABS_0 && | 104 } else if (target[0] == ASM_JMP32ABS_0 && |
80 target[1] == ASM_JMP32ABS_1) { | 105 target[1] == ASM_JMP32ABS_1) { |
81 // Visual studio seems to sometimes do it this way instead of the | 106 // Visual studio seems to sometimes do it this way instead of the |
82 // previous way. Not sure what the rules are, but it was happening | 107 // previous way. Not sure what the rules are, but it was happening |
83 // with operator new in some binaries. | 108 // with operator new in some binaries. |
84 void **new_target_v; | 109 void** new_target_v; |
85 SIDESTEP_ASSERT(sizeof(new_target) == 4); | 110 if (kIs64BitBinary) { |
86 memcpy(&new_target_v, reinterpret_cast<void*>(target + 2), 4); | 111 // In 64-bit mode JMPs are RIP-relative, not absolute |
| 112 int target_offset; |
| 113 memcpy(reinterpret_cast<void*>(&target_offset), |
| 114 reinterpret_cast<void*>(target + 2), 4); |
| 115 new_target_v = reinterpret_cast<void**>(target + target_offset + 6); |
| 116 } else { |
| 117 SIDESTEP_ASSERT(sizeof(new_target) == 4); |
| 118 memcpy(&new_target_v, reinterpret_cast<void*>(target + 2), 4); |
| 119 } |
87 new_target = reinterpret_cast<unsigned char*>(*new_target_v); | 120 new_target = reinterpret_cast<unsigned char*>(*new_target_v); |
88 } else { | 121 } else { |
89 break; | 122 break; |
90 } | 123 } |
91 if (new_target == stop_before) | 124 if (new_target == stop_before) |
92 break; | 125 break; |
| 126 if (stop_before_trampoline && *new_target == ASM_NOP |
| 127 && new_target[1] == ASM_REXW && new_target[2] == ASM_MOVRAX_IMM) |
| 128 break; |
93 target = new_target; | 129 target = new_target; |
94 } | 130 } |
95 return target; | 131 return target; |
96 } | 132 } |
97 | 133 |
98 // Special case scoped_ptr to avoid dependency on scoped_ptr below. | 134 // Special case scoped_ptr to avoid dependency on scoped_ptr below. |
99 class DeleteUnsignedCharArray { | 135 class DeleteUnsignedCharArray { |
100 public: | 136 public: |
101 DeleteUnsignedCharArray(unsigned char* array) : array_(array) { | 137 DeleteUnsignedCharArray(unsigned char* array) : array_(array) { |
102 } | 138 } |
103 | 139 |
104 ~DeleteUnsignedCharArray() { | 140 ~DeleteUnsignedCharArray() { |
105 if (array_) { | 141 if (array_) { |
106 delete [] array_; | 142 PreamblePatcher::FreePreambleBlock(array_); |
107 } | 143 } |
108 } | 144 } |
109 | 145 |
110 unsigned char* Release() { | 146 unsigned char* Release() { |
111 unsigned char* temp = array_; | 147 unsigned char* temp = array_; |
112 array_ = NULL; | 148 array_ = NULL; |
113 return temp; | 149 return temp; |
114 } | 150 } |
115 | 151 |
116 private: | 152 private: |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 | 220 |
185 SideStepError PreamblePatcher::RawPatch(void* target_function, | 221 SideStepError PreamblePatcher::RawPatch(void* target_function, |
186 void* replacement_function, | 222 void* replacement_function, |
187 void** original_function_stub) { | 223 void** original_function_stub) { |
188 if (!target_function || !replacement_function || !original_function_stub || | 224 if (!target_function || !replacement_function || !original_function_stub || |
189 (*original_function_stub) || target_function == replacement_function) { | 225 (*original_function_stub) || target_function == replacement_function) { |
190 SIDESTEP_ASSERT(false && "Preconditions not met"); | 226 SIDESTEP_ASSERT(false && "Preconditions not met"); |
191 return SIDESTEP_INVALID_PARAMETER; | 227 return SIDESTEP_INVALID_PARAMETER; |
192 } | 228 } |
193 | 229 |
194 // @see MAX_PREAMBLE_STUB_SIZE for an explanation of how we arrives at | 230 BOOL succeeded = FALSE; |
195 // this size | 231 |
196 unsigned char* preamble_stub = new unsigned char[MAX_PREAMBLE_STUB_SIZE]; | 232 // First, deal with a special case that we see with functions that |
| 233 // point into an IAT table (including functions linked statically |
| 234 // into the application): these function already starts with |
| 235 // ASM_JMP32REL. For instance, malloc() might be implemented as a |
| 236 // JMP to __malloc(). In that case, we replace the destination of |
| 237 // the JMP (__malloc), rather than the JMP itself (malloc). This |
| 238 // way we get the correct behavior no matter how malloc gets called. |
| 239 void* new_target = ResolveTarget(target_function); |
| 240 if (new_target != target_function) { |
| 241 target_function = new_target; |
| 242 } |
| 243 |
| 244 // In 64-bit mode, preamble_stub must be within 2GB of target function |
| 245 // so that if target contains a jump, we can translate it. |
| 246 unsigned char* preamble_stub = AllocPreambleBlockNear(target_function); |
197 if (!preamble_stub) { | 247 if (!preamble_stub) { |
198 SIDESTEP_ASSERT(false && "Unable to allocate preamble-stub."); | 248 SIDESTEP_ASSERT(false && "Unable to allocate preamble-stub."); |
199 return SIDESTEP_INSUFFICIENT_BUFFER; | 249 return SIDESTEP_INSUFFICIENT_BUFFER; |
200 } | 250 } |
201 | 251 |
202 // Frees the array at end of scope. | 252 // Frees the array at end of scope. |
203 DeleteUnsignedCharArray guard_preamble_stub(preamble_stub); | 253 DeleteUnsignedCharArray guard_preamble_stub(preamble_stub); |
204 | 254 |
205 // Change the protection of the newly allocated preamble stub to | |
206 // PAGE_EXECUTE_READWRITE. This is required to work with DEP (Data | |
207 // Execution Prevention) which will cause an exception if code is executed | |
208 // from a page on which you do not have read access. | |
209 DWORD old_stub_protect = 0; | |
210 BOOL succeeded = ::VirtualProtect(preamble_stub, MAX_PREAMBLE_STUB_SIZE, | |
211 PAGE_EXECUTE_READWRITE, &old_stub_protect); | |
212 if (!succeeded) { | |
213 SIDESTEP_ASSERT(false && | |
214 "Failed to make page preamble stub read-write-execute."); | |
215 return SIDESTEP_ACCESS_DENIED; | |
216 } | |
217 | |
218 SideStepError error_code = RawPatchWithStubAndProtections( | 255 SideStepError error_code = RawPatchWithStubAndProtections( |
219 target_function, replacement_function, preamble_stub, | 256 target_function, replacement_function, preamble_stub, |
220 MAX_PREAMBLE_STUB_SIZE, NULL); | 257 MAX_PREAMBLE_STUB_SIZE, NULL); |
221 | 258 |
222 if (SIDESTEP_SUCCESS != error_code) { | 259 if (SIDESTEP_SUCCESS != error_code) { |
223 SIDESTEP_ASSERT(false); | 260 SIDESTEP_ASSERT(false); |
224 return error_code; | 261 return error_code; |
225 } | 262 } |
226 | 263 |
227 // Flush the instruction cache to make sure the processor doesn't execute the | 264 // Flush the instruction cache to make sure the processor doesn't execute the |
(...skipping 25 matching lines...) Expand all Loading... |
253 SideStepError PreamblePatcher::Unpatch(void* target_function, | 290 SideStepError PreamblePatcher::Unpatch(void* target_function, |
254 void* replacement_function, | 291 void* replacement_function, |
255 void* original_function_stub) { | 292 void* original_function_stub) { |
256 SIDESTEP_ASSERT(target_function && replacement_function && | 293 SIDESTEP_ASSERT(target_function && replacement_function && |
257 original_function_stub); | 294 original_function_stub); |
258 if (!target_function || !replacement_function || | 295 if (!target_function || !replacement_function || |
259 !original_function_stub) { | 296 !original_function_stub) { |
260 return SIDESTEP_INVALID_PARAMETER; | 297 return SIDESTEP_INVALID_PARAMETER; |
261 } | 298 } |
262 | 299 |
263 // We disassemble the preamble of the _stub_ to see how many bytes we | |
264 // originally copied to the stub. | |
265 MiniDisassembler disassembler; | |
266 unsigned int preamble_bytes = 0; | |
267 while (preamble_bytes < 5) { | |
268 InstructionType instruction_type = | |
269 disassembler.Disassemble( | |
270 reinterpret_cast<unsigned char*>(original_function_stub) + | |
271 preamble_bytes, | |
272 preamble_bytes); | |
273 if (IT_GENERIC != instruction_type) { | |
274 SIDESTEP_ASSERT(false && | |
275 "Should only have generic instructions in stub!!"); | |
276 return SIDESTEP_UNSUPPORTED_INSTRUCTION; | |
277 } | |
278 } | |
279 | |
280 // Before unpatching, target_function should be a JMP to | 300 // Before unpatching, target_function should be a JMP to |
281 // replacement_function. If it's not, then either it's an error, or | 301 // replacement_function. If it's not, then either it's an error, or |
282 // we're falling into the case where the original instruction was a | 302 // we're falling into the case where the original instruction was a |
283 // JMP, and we patched the jumped_to address rather than the JMP | 303 // JMP, and we patched the jumped_to address rather than the JMP |
284 // itself. (For instance, if malloc() is just a JMP to __malloc(), | 304 // itself. (For instance, if malloc() is just a JMP to __malloc(), |
285 // we patched __malloc() and not malloc().) | 305 // we patched __malloc() and not malloc().) |
286 unsigned char* target = reinterpret_cast<unsigned char*>(target_function); | 306 unsigned char* target = reinterpret_cast<unsigned char*>(target_function); |
287 target = reinterpret_cast<unsigned char*>( | 307 target = reinterpret_cast<unsigned char*>( |
288 ResolveTargetImpl( | 308 ResolveTargetImpl( |
289 target, reinterpret_cast<unsigned char*>(replacement_function))); | 309 target, reinterpret_cast<unsigned char*>(replacement_function), |
| 310 true)); |
290 // We should end at the function we patched. When we patch, we insert | 311 // We should end at the function we patched. When we patch, we insert |
291 // a ASM_JMP32REL instruction, so look for that as a sanity check. | 312 // a ASM_JMP32REL instruction, so look for that as a sanity check. |
292 if (target[0] != ASM_JMP32REL) { | 313 if (target[0] != ASM_JMP32REL) { |
293 SIDESTEP_ASSERT(false && | 314 SIDESTEP_ASSERT(false && |
294 "target_function does not look like it was patched."); | 315 "target_function does not look like it was patched."); |
295 return SIDESTEP_INVALID_PARAMETER; | 316 return SIDESTEP_INVALID_PARAMETER; |
296 } | 317 } |
297 | 318 |
| 319 const unsigned int kRequiredTargetPatchBytes = 5; |
| 320 |
298 // We need to be able to write to a process-local copy of the first | 321 // We need to be able to write to a process-local copy of the first |
299 // MAX_PREAMBLE_STUB_SIZE bytes of target_function | 322 // kRequiredTargetPatchBytes bytes of target_function |
300 DWORD old_target_function_protect = 0; | 323 DWORD old_target_function_protect = 0; |
301 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function), | 324 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), |
302 MAX_PREAMBLE_STUB_SIZE, | 325 kRequiredTargetPatchBytes, |
303 PAGE_EXECUTE_READWRITE, | 326 PAGE_EXECUTE_READWRITE, |
304 &old_target_function_protect); | 327 &old_target_function_protect); |
305 if (!succeeded) { | 328 if (!succeeded) { |
306 SIDESTEP_ASSERT(false && "Failed to make page containing target function " | 329 SIDESTEP_ASSERT(false && "Failed to make page containing target function " |
307 "copy-on-write."); | 330 "copy-on-write."); |
308 return SIDESTEP_ACCESS_DENIED; | 331 return SIDESTEP_ACCESS_DENIED; |
309 } | 332 } |
310 | 333 |
311 // Replace the first few bytes of the original function with the bytes we | 334 unsigned char* preamble_stub = reinterpret_cast<unsigned char*>( |
312 // previously moved to the preamble stub. | 335 original_function_stub); |
313 memcpy(reinterpret_cast<void*>(target), | |
314 original_function_stub, preamble_bytes); | |
315 | 336 |
316 // Stub is now useless so delete it. | 337 // Disassemble the preamble of stub and copy the bytes back to target. |
317 // [csilvers: Commented out for perftools because it causes big problems | 338 // If we've done any conditional jumps in the preamble we need to convert |
318 // when we're unpatching malloc. We just let this live on as a leak.] | 339 // them back to the orignal REL8 jumps in the target. |
319 //delete [] reinterpret_cast<unsigned char*>(original_function_stub); | 340 MiniDisassembler disassembler; |
| 341 unsigned int preamble_bytes = 0; |
| 342 unsigned int target_bytes = 0; |
| 343 while (target_bytes < kRequiredTargetPatchBytes) { |
| 344 unsigned int cur_bytes = 0; |
| 345 InstructionType instruction_type = |
| 346 disassembler.Disassemble(preamble_stub + preamble_bytes, cur_bytes); |
| 347 if (IT_JUMP == instruction_type) { |
| 348 unsigned int jump_bytes = 0; |
| 349 SideStepError jump_ret = SIDESTEP_JUMP_INSTRUCTION; |
| 350 if (IsNearConditionalJump(preamble_stub + preamble_bytes, cur_bytes) || |
| 351 IsNearRelativeJump(preamble_stub + preamble_bytes, cur_bytes) || |
| 352 IsNearAbsoluteCall(preamble_stub + preamble_bytes, cur_bytes) || |
| 353 IsNearRelativeCall(preamble_stub + preamble_bytes, cur_bytes)) { |
| 354 jump_ret = PatchNearJumpOrCall(preamble_stub + preamble_bytes, |
| 355 cur_bytes, target + target_bytes, |
| 356 &jump_bytes, MAX_PREAMBLE_STUB_SIZE); |
| 357 } |
| 358 if (jump_ret == SIDESTEP_JUMP_INSTRUCTION) { |
| 359 SIDESTEP_ASSERT(false && |
| 360 "Found unsupported jump instruction in stub!!"); |
| 361 return SIDESTEP_UNSUPPORTED_INSTRUCTION; |
| 362 } |
| 363 target_bytes += jump_bytes; |
| 364 } else if (IT_GENERIC == instruction_type) { |
| 365 if (IsMovWithDisplacement(preamble_stub + preamble_bytes, cur_bytes)) { |
| 366 unsigned int mov_bytes = 0; |
| 367 if (PatchMovWithDisplacement(preamble_stub + preamble_bytes, cur_bytes, |
| 368 target + target_bytes, &mov_bytes, |
| 369 MAX_PREAMBLE_STUB_SIZE) |
| 370 != SIDESTEP_SUCCESS) { |
| 371 SIDESTEP_ASSERT(false && |
| 372 "Found unsupported generic instruction in stub!!"); |
| 373 return SIDESTEP_UNSUPPORTED_INSTRUCTION; |
| 374 } |
| 375 } else { |
| 376 memcpy(reinterpret_cast<void*>(target + target_bytes), |
| 377 reinterpret_cast<void*>(reinterpret_cast<unsigned char*>( |
| 378 original_function_stub) + preamble_bytes), cur_bytes); |
| 379 target_bytes += cur_bytes; |
| 380 } |
| 381 } else { |
| 382 SIDESTEP_ASSERT(false && |
| 383 "Found unsupported instruction in stub!!"); |
| 384 return SIDESTEP_UNSUPPORTED_INSTRUCTION; |
| 385 } |
| 386 preamble_bytes += cur_bytes; |
| 387 } |
320 | 388 |
321 // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of | 389 FreePreambleBlock(reinterpret_cast<unsigned char*>(original_function_stub)); |
| 390 |
| 391 // Restore the protection of the first kRequiredTargetPatchBytes bytes of |
322 // target to what they were before we started goofing around. | 392 // target to what they were before we started goofing around. |
323 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), | 393 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), |
324 MAX_PREAMBLE_STUB_SIZE, | 394 kRequiredTargetPatchBytes, |
325 old_target_function_protect, | 395 old_target_function_protect, |
326 &old_target_function_protect); | 396 &old_target_function_protect); |
327 | 397 |
328 // Flush the instruction cache to make sure the processor doesn't execute the | 398 // Flush the instruction cache to make sure the processor doesn't execute the |
329 // old version of the instructions (before our patch). | 399 // old version of the instructions (before our patch). |
330 // | 400 // |
331 // See comment on FlushInstructionCache elsewhere in this file. | 401 // See comment on FlushInstructionCache elsewhere in this file. |
332 succeeded = ::FlushInstructionCache(::GetCurrentProcess(), | 402 succeeded = ::FlushInstructionCache(::GetCurrentProcess(), |
333 target, | 403 target, |
334 MAX_PREAMBLE_STUB_SIZE); | 404 MAX_PREAMBLE_STUB_SIZE); |
335 if (!succeeded) { | 405 if (!succeeded) { |
336 SIDESTEP_ASSERT(false && "Failed to flush instruction cache."); | 406 SIDESTEP_ASSERT(false && "Failed to flush instruction cache."); |
337 return SIDESTEP_UNEXPECTED; | 407 return SIDESTEP_UNEXPECTED; |
338 } | 408 } |
339 | 409 |
340 SIDESTEP_LOG("PreamblePatcher::Unpatch successfully unpatched."); | 410 SIDESTEP_LOG("PreamblePatcher::Unpatch successfully unpatched."); |
341 return SIDESTEP_SUCCESS; | 411 return SIDESTEP_SUCCESS; |
342 } | 412 } |
343 | 413 |
| 414 void PreamblePatcher::Initialize() { |
| 415 if (!initialized_) { |
| 416 SYSTEM_INFO si = { 0 }; |
| 417 ::GetSystemInfo(&si); |
| 418 granularity_ = si.dwAllocationGranularity; |
| 419 pagesize_ = si.dwPageSize; |
| 420 initialized_ = true; |
| 421 } |
| 422 } |
| 423 |
| 424 unsigned char* PreamblePatcher::AllocPreambleBlockNear(void* target) { |
| 425 PreamblePage* preamble_page = preamble_pages_; |
| 426 while (preamble_page != NULL) { |
| 427 if (preamble_page->free_ != NULL) { |
| 428 __int64 val = reinterpret_cast<__int64>(preamble_page) - |
| 429 reinterpret_cast<__int64>(target); |
| 430 if ((val > 0 && val + pagesize_ <= INT_MAX) || |
| 431 (val < 0 && val >= INT_MIN)) { |
| 432 break; |
| 433 } |
| 434 } |
| 435 preamble_page = preamble_page->next_; |
| 436 } |
| 437 |
| 438 // The free_ member of the page is used to store the next available block |
| 439 // of memory to use or NULL if there are no chunks available, in which case |
| 440 // we'll allocate a new page. |
| 441 if (preamble_page == NULL || preamble_page->free_ == NULL) { |
| 442 // Create a new preamble page and initialize the free list |
| 443 preamble_page = reinterpret_cast<PreamblePage*>(AllocPageNear(target)); |
| 444 SIDESTEP_ASSERT(preamble_page != NULL && "Could not allocate page!"); |
| 445 void** pp = &preamble_page->free_; |
| 446 unsigned char* ptr = reinterpret_cast<unsigned char*>(preamble_page) + |
| 447 MAX_PREAMBLE_STUB_SIZE; |
| 448 unsigned char* limit = reinterpret_cast<unsigned char*>(preamble_page) + |
| 449 pagesize_; |
| 450 while (ptr < limit) { |
| 451 *pp = ptr; |
| 452 pp = reinterpret_cast<void**>(ptr); |
| 453 ptr += MAX_PREAMBLE_STUB_SIZE; |
| 454 } |
| 455 *pp = NULL; |
| 456 // Insert the new page into the list |
| 457 preamble_page->magic_ = kPreamblePageMagic; |
| 458 preamble_page->next_ = preamble_pages_; |
| 459 preamble_pages_ = preamble_page; |
| 460 } |
| 461 unsigned char* ret = reinterpret_cast<unsigned char*>(preamble_page->free_); |
| 462 preamble_page->free_ = *(reinterpret_cast<void**>(preamble_page->free_)); |
| 463 return ret; |
| 464 } |
| 465 |
| 466 void PreamblePatcher::FreePreambleBlock(unsigned char* block) { |
| 467 SIDESTEP_ASSERT(block != NULL); |
| 468 SIDESTEP_ASSERT(granularity_ != 0); |
| 469 uintptr_t ptr = reinterpret_cast<uintptr_t>(block); |
| 470 ptr -= ptr & (granularity_ - 1); |
| 471 PreamblePage* preamble_page = reinterpret_cast<PreamblePage*>(ptr); |
| 472 SIDESTEP_ASSERT(preamble_page->magic_ == kPreamblePageMagic); |
| 473 *(reinterpret_cast<void**>(block)) = preamble_page->free_; |
| 474 preamble_page->free_ = block; |
| 475 } |
| 476 |
| 477 void* PreamblePatcher::AllocPageNear(void* target) { |
| 478 MEMORY_BASIC_INFORMATION mbi = { 0 }; |
| 479 if (!::VirtualQuery(target, &mbi, sizeof(mbi))) { |
| 480 SIDESTEP_ASSERT(false && "VirtualQuery failed on target address"); |
| 481 return 0; |
| 482 } |
| 483 if (initialized_ == false) { |
| 484 PreamblePatcher::Initialize(); |
| 485 SIDESTEP_ASSERT(initialized_); |
| 486 } |
| 487 void* pv = NULL; |
| 488 unsigned char* allocation_base = reinterpret_cast<unsigned char*>( |
| 489 mbi.AllocationBase); |
| 490 __int64 i = 1; |
| 491 bool high_target = reinterpret_cast<__int64>(target) > UINT_MAX; |
| 492 while (pv == NULL) { |
| 493 __int64 val = reinterpret_cast<__int64>(allocation_base) - |
| 494 (i * granularity_); |
| 495 if (high_target && |
| 496 reinterpret_cast<__int64>(target) - val > INT_MAX) { |
| 497 // We're further than 2GB from the target |
| 498 break; |
| 499 } else if (val <= NULL) { |
| 500 // Less than 0 |
| 501 break; |
| 502 } |
| 503 pv = ::VirtualAlloc(reinterpret_cast<void*>(allocation_base - |
| 504 (i++ * granularity_)), |
| 505 pagesize_, MEM_COMMIT | MEM_RESERVE, |
| 506 PAGE_EXECUTE_READWRITE); |
| 507 } |
| 508 |
| 509 // We couldn't allocate low, try to allocate high |
| 510 if (pv == NULL) { |
| 511 i = 1; |
| 512 // Round up to the next multiple of page granularity |
| 513 allocation_base = reinterpret_cast<unsigned char*>( |
| 514 (reinterpret_cast<__int64>(target) & |
| 515 (~(granularity_ - 1))) + granularity_); |
| 516 while (pv == NULL) { |
| 517 __int64 val = reinterpret_cast<__int64>(allocation_base) + |
| 518 (i * granularity_) - reinterpret_cast<__int64>(target); |
| 519 if (val > INT_MAX || val < 0) { |
| 520 // We're too far or we overflowed |
| 521 break; |
| 522 } |
| 523 pv = ::VirtualAlloc(reinterpret_cast<void*>(allocation_base + |
| 524 (i++ * granularity_)), |
| 525 pagesize_, MEM_COMMIT | MEM_RESERVE, |
| 526 PAGE_EXECUTE_READWRITE); |
| 527 } |
| 528 } |
| 529 return pv; |
| 530 } |
| 531 |
| 532 bool PreamblePatcher::IsShortConditionalJump( |
| 533 unsigned char* target, |
| 534 unsigned int instruction_size) { |
| 535 return (*(target) & 0x70) == 0x70 && instruction_size == 2; |
| 536 } |
| 537 |
| 538 bool PreamblePatcher::IsNearConditionalJump( |
| 539 unsigned char* target, |
| 540 unsigned int instruction_size) { |
| 541 return *(target) == 0xf && (*(target + 1) & 0x80) == 0x80 && |
| 542 instruction_size == 6; |
| 543 } |
| 544 |
| 545 bool PreamblePatcher::IsNearRelativeJump( |
| 546 unsigned char* target, |
| 547 unsigned int instruction_size) { |
| 548 return *(target) == 0xe9 && instruction_size == 5; |
| 549 } |
| 550 |
| 551 bool PreamblePatcher::IsNearAbsoluteCall( |
| 552 unsigned char* target, |
| 553 unsigned int instruction_size) { |
| 554 return *(target) == 0xff && (*(target + 1) & 0x10) == 0x10 && |
| 555 instruction_size == 6; |
| 556 } |
| 557 |
| 558 bool PreamblePatcher::IsNearRelativeCall( |
| 559 unsigned char* target, |
| 560 unsigned int instruction_size) { |
| 561 return *(target) == 0xe8 && instruction_size == 5; |
| 562 } |
| 563 |
| 564 bool PreamblePatcher::IsMovWithDisplacement( |
| 565 unsigned char* target, |
| 566 unsigned int instruction_size) { |
| 567 // In this case, the ModRM byte's mod field will be 0 and r/m will be 101b (5) |
| 568 return instruction_size == 7 && *target == 0x48 && *(target + 1) == 0x8b && |
| 569 (*(target + 2) >> 6) == 0 && (*(target + 2) & 0x7) == 5; |
| 570 } |
| 571 |
| 572 SideStepError PreamblePatcher::PatchShortConditionalJump( |
| 573 unsigned char* source, |
| 574 unsigned int instruction_size, |
| 575 unsigned char* target, |
| 576 unsigned int* target_bytes, |
| 577 unsigned int target_size) { |
| 578 unsigned char* original_jump_dest = (source + 2) + source[1]; |
| 579 unsigned char* stub_jump_from = target + 6; |
| 580 __int64 fixup_jump_offset = original_jump_dest - stub_jump_from; |
| 581 if (fixup_jump_offset > INT_MAX || fixup_jump_offset < INT_MIN) { |
| 582 SIDESTEP_ASSERT(false && |
| 583 "Unable to fix up short jump because target" |
| 584 " is too far away."); |
| 585 return SIDESTEP_JUMP_INSTRUCTION; |
| 586 } |
| 587 |
| 588 *target_bytes = 6; |
| 589 if (target_size > *target_bytes) { |
| 590 // Convert the short jump to a near jump. |
| 591 // |
| 592 // 0f 8x xx xx xx xx = Jcc rel32off |
| 593 unsigned short jmpcode = ((0x80 | (source[0] & 0xf)) << 8) | 0x0f; |
| 594 memcpy(reinterpret_cast<void*>(target), |
| 595 reinterpret_cast<void*>(&jmpcode), 2); |
| 596 memcpy(reinterpret_cast<void*>(target + 2), |
| 597 reinterpret_cast<void*>(&fixup_jump_offset), 4); |
| 598 } |
| 599 |
| 600 return SIDESTEP_SUCCESS; |
| 601 } |
| 602 |
| 603 SideStepError PreamblePatcher::PatchNearJumpOrCall( |
| 604 unsigned char* source, |
| 605 unsigned int instruction_size, |
| 606 unsigned char* target, |
| 607 unsigned int* target_bytes, |
| 608 unsigned int target_size) { |
| 609 SIDESTEP_ASSERT(instruction_size == 5 || instruction_size == 6); |
| 610 unsigned int jmp_offset_in_instruction = instruction_size == 5 ? 1 : 2; |
| 611 unsigned char* original_jump_dest = reinterpret_cast<unsigned char *>( |
| 612 reinterpret_cast<__int64>(source + instruction_size) + |
| 613 *(reinterpret_cast<int*>(source + jmp_offset_in_instruction))); |
| 614 unsigned char* stub_jump_from = target + instruction_size; |
| 615 __int64 fixup_jump_offset = original_jump_dest - stub_jump_from; |
| 616 if (fixup_jump_offset > INT_MAX || fixup_jump_offset < INT_MIN) { |
| 617 SIDESTEP_ASSERT(false && |
| 618 "Unable to fix up near jump because target" |
| 619 " is too far away."); |
| 620 return SIDESTEP_JUMP_INSTRUCTION; |
| 621 } |
| 622 |
| 623 if ((fixup_jump_offset < SCHAR_MAX && fixup_jump_offset > SCHAR_MIN)) { |
| 624 *target_bytes = 2; |
| 625 if (target_size > *target_bytes) { |
| 626 // If the new offset is in range, use a short jump instead of a near jump. |
| 627 if (source[0] == ASM_JCC32REL_0 && |
| 628 (source[1] & ASM_JCC32REL_1_MASK) == ASM_JCC32REL_1_MASK) { |
| 629 unsigned short jmpcode = (static_cast<unsigned char>( |
| 630 fixup_jump_offset) << 8) | (0x70 | (source[1] & 0xf)); |
| 631 memcpy(reinterpret_cast<void*>(target), |
| 632 reinterpret_cast<void*>(&jmpcode), |
| 633 2); |
| 634 } else { |
| 635 target[0] = ASM_JMP8REL; |
| 636 target[1] = static_cast<unsigned char>(fixup_jump_offset); |
| 637 } |
| 638 } |
| 639 } else { |
| 640 *target_bytes = instruction_size; |
| 641 if (target_size > *target_bytes) { |
| 642 memcpy(reinterpret_cast<void*>(target), |
| 643 reinterpret_cast<void*>(source), |
| 644 jmp_offset_in_instruction); |
| 645 memcpy(reinterpret_cast<void*>(target + jmp_offset_in_instruction), |
| 646 reinterpret_cast<void*>(&fixup_jump_offset), |
| 647 4); |
| 648 } |
| 649 } |
| 650 |
| 651 return SIDESTEP_SUCCESS; |
| 652 } |
| 653 |
| 654 SideStepError PreamblePatcher::PatchMovWithDisplacement( |
| 655 unsigned char* source, |
| 656 unsigned int instruction_size, |
| 657 unsigned char* target, |
| 658 unsigned int* target_bytes, |
| 659 unsigned int target_size) { |
| 660 SIDESTEP_ASSERT(instruction_size == 7); |
| 661 const int mov_offset_in_instruction = 3; // 0x48 0x8b 0x0d <offset> |
| 662 unsigned char* original_mov_dest = reinterpret_cast<unsigned char*>( |
| 663 reinterpret_cast<__int64>(source + instruction_size) + |
| 664 *(reinterpret_cast<int*>(source + mov_offset_in_instruction))); |
| 665 unsigned char* stub_mov_from = target + instruction_size; |
| 666 __int64 fixup_mov_offset = original_mov_dest - stub_mov_from; |
| 667 if (fixup_mov_offset > INT_MAX || fixup_mov_offset < INT_MIN) { |
| 668 SIDESTEP_ASSERT(false && |
| 669 "Unable to fix up near MOV because target is too far away."); |
| 670 return SIDESTEP_UNEXPECTED; |
| 671 } |
| 672 *target_bytes = instruction_size; |
| 673 if (target_size > *target_bytes) { |
| 674 memcpy(reinterpret_cast<void*>(target), |
| 675 reinterpret_cast<void*>(source), |
| 676 mov_offset_in_instruction); |
| 677 memcpy(reinterpret_cast<void*>(target + mov_offset_in_instruction), |
| 678 reinterpret_cast<void*>(&fixup_mov_offset), |
| 679 4); |
| 680 } |
| 681 return SIDESTEP_SUCCESS; |
| 682 } |
| 683 |
344 }; // namespace sidestep | 684 }; // namespace sidestep |
OLD | NEW |