preamble_patcher.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1/* Copyright (c) 2007, Google Inc. 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 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. 29 * 30 * --- 31 * Author: Joi Sigurdsson 32 * Author: Scott Francis 33 * 34 * Implementation of PreamblePatcher 35 */ 36 37#include "preamble_patcher.h" 38 39#include "mini_disassembler.h" 40 41// compatibility shims 42#include "base/logging.h" 43 44// Definitions of assembly statements we need 45#define ASM_JMP32REL 0xE9 46#define ASM_INT3 0xCC 47#define ASM_JMP32ABS_0 0xFF 48#define ASM_JMP32ABS_1 0x25 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 58 59namespace sidestep { 60 61PreamblePatcher::PreamblePage* PreamblePatcher::preamble_pages_ = NULL; 62long PreamblePatcher::granularity_ = 0; 63long PreamblePatcher::pagesize_ = 0; 64bool PreamblePatcher::initialized_ = false; 65 66static const unsigned int kPreamblePageMagic = 0x4347414D; // "MAGC" 67 68// Handle a special case that we see with functions that point into an 69// IAT table (including functions linked statically into the 70// application): these function already starts with ASM_JMP32*. For 71// instance, malloc() might be implemented as a JMP to __malloc(). 72// This function follows the initial JMPs for us, until we get to the 73// place where the actual code is defined. If we get to 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. 83void* PreamblePatcher::ResolveTargetImpl(unsigned char* target, 84 unsigned char* stop_before, 85 bool stop_before_trampoline) { 86 if (target == NULL) 87 return NULL; 88 while (1) { 89 unsigned char* new_target; 90 if (target[0] == ASM_JMP32REL) { 91 // target[1-4] holds the place the jmp goes to, but it's 92 // relative to the next instruction. 93 int relative_offset; // Windows guarantees int is 4 bytes 94 SIDESTEP_ASSERT(sizeof(relative_offset) == 4); 95 memcpy(reinterpret_cast<void*>(&relative_offset), 96 reinterpret_cast<void*>(target + 1), 4); 97 new_target = target + 5 + relative_offset; 98 } else if (target[0] == ASM_JMP8REL) { 99 // Visual Studio 7.1 implements new[] as an 8 bit jump to new 100 signed char relative_offset; 101 memcpy(reinterpret_cast<void*>(&relative_offset), 102 reinterpret_cast<void*>(target + 1), 1); 103 new_target = target + 2 + relative_offset; 104 } else if (target[0] == ASM_JMP32ABS_0 && 105 target[1] == ASM_JMP32ABS_1) { 106 // Visual studio seems to sometimes do it this way instead of the 107 // previous way. Not sure what the rules are, but it was happening 108 // with operator new in some binaries. 109 void** new_target_v; 110 if (kIs64BitBinary) { 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 } 120 new_target = reinterpret_cast<unsigned char*>(*new_target_v); 121 } else { 122 break; 123 } 124 if (new_target == stop_before) 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; 129 target = new_target; 130 } 131 return target; 132} 133 134// Special case scoped_ptr to avoid dependency on scoped_ptr below. 135class DeleteUnsignedCharArray { 136 public: 137 DeleteUnsignedCharArray(unsigned char* array) : array_(array) { 138 } 139 140 ~DeleteUnsignedCharArray() { 141 if (array_) { 142 PreamblePatcher::FreePreambleBlock(array_); 143 } 144 } 145 146 unsigned char* Release() { 147 unsigned char* temp = array_; 148 array_ = NULL; 149 return temp; 150 } 151 152 private: 153 unsigned char* array_; 154}; 155 156SideStepError PreamblePatcher::RawPatchWithStubAndProtections( 157 void* target_function, void *replacement_function, 158 unsigned char* preamble_stub, unsigned long stub_size, 159 unsigned long* bytes_needed) { 160 // We need to be able to write to a process-local copy of the first 161 // MAX_PREAMBLE_STUB_SIZE bytes of target_function 162 DWORD old_target_function_protect = 0; 163 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function), 164 MAX_PREAMBLE_STUB_SIZE, 165 PAGE_EXECUTE_READWRITE, 166 &old_target_function_protect); 167 if (!succeeded) { 168 SIDESTEP_ASSERT(false && "Failed to make page containing target function " 169 "copy-on-write."); 170 return SIDESTEP_ACCESS_DENIED; 171 } 172 173 SideStepError error_code = RawPatchWithStub(target_function, 174 replacement_function, 175 preamble_stub, 176 stub_size, 177 bytes_needed); 178 179 // Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of 180 // pTargetFunction to what they were before we started goofing around. 181 // We do this regardless of whether the patch succeeded or not. 182 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function), 183 MAX_PREAMBLE_STUB_SIZE, 184 old_target_function_protect, 185 &old_target_function_protect); 186 if (!succeeded) { 187 SIDESTEP_ASSERT(false && 188 "Failed to restore protection to target function."); 189 // We must not return an error here because the function has 190 // likely actually been patched, and returning an error might 191 // cause our client code not to unpatch it. So we just keep 192 // going. 193 } 194 195 if (SIDESTEP_SUCCESS != error_code) { // Testing RawPatchWithStub, above 196 SIDESTEP_ASSERT(false); 197 return error_code; 198 } 199 200 // Flush the instruction cache to make sure the processor doesn't execute the 201 // old version of the instructions (before our patch). 202 // 203 // FlushInstructionCache is actually a no-op at least on 204 // single-processor XP machines. I'm not sure why this is so, but 205 // it is, yet I want to keep the call to the API here for 206 // correctness in case there is a difference in some variants of 207 // Windows/hardware. 208 succeeded = ::FlushInstructionCache(::GetCurrentProcess(), 209 target_function, 210 MAX_PREAMBLE_STUB_SIZE); 211 if (!succeeded) { 212 SIDESTEP_ASSERT(false && "Failed to flush instruction cache."); 213 // We must not return an error here because the function has actually 214 // been patched, and returning an error would likely cause our client 215 // code not to unpatch it. So we just keep going. 216 } 217 218 return SIDESTEP_SUCCESS; 219} 220 221SideStepError PreamblePatcher::RawPatch(void* target_function, 222 void* replacement_function, 223 void** original_function_stub) { 224 if (!target_function || !replacement_function || !original_function_stub || 225 (*original_function_stub) || target_function == replacement_function) { 226 SIDESTEP_ASSERT(false && "Preconditions not met"); 227 return SIDESTEP_INVALID_PARAMETER; 228 } 229 230 BOOL succeeded = FALSE; 231 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); 247 if (!preamble_stub) { 248 SIDESTEP_ASSERT(false && "Unable to allocate preamble-stub."); 249 return SIDESTEP_INSUFFICIENT_BUFFER; 250 } 251 252 // Frees the array at end of scope. 253 DeleteUnsignedCharArray guard_preamble_stub(preamble_stub); 254 255 SideStepError error_code = RawPatchWithStubAndProtections( 256 target_function, replacement_function, preamble_stub, 257 MAX_PREAMBLE_STUB_SIZE, NULL); 258 259 if (SIDESTEP_SUCCESS != error_code) { 260 SIDESTEP_ASSERT(false); 261 return error_code; 262 } 263 264 // Flush the instruction cache to make sure the processor doesn't execute the 265 // old version of the instructions (before our patch). 266 // 267 // FlushInstructionCache is actually a no-op at least on 268 // single-processor XP machines. I'm not sure why this is so, but 269 // it is, yet I want to keep the call to the API here for 270 // correctness in case there is a difference in some variants of 271 // Windows/hardware. 272 succeeded = ::FlushInstructionCache(::GetCurrentProcess(), 273 target_function, 274 MAX_PREAMBLE_STUB_SIZE); 275 if (!succeeded) { 276 SIDESTEP_ASSERT(false && "Failed to flush instruction cache."); 277 // We must not return an error here because the function has actually 278 // been patched, and returning an error would likely cause our client 279 // code not to unpatch it. So we just keep going. 280 } 281 282 SIDESTEP_LOG("PreamblePatcher::RawPatch successfully patched."); 283 284 // detach the scoped pointer so the memory is not freed 285 *original_function_stub = 286 reinterpret_cast<void*>(guard_preamble_stub.Release()); 287 return SIDESTEP_SUCCESS; 288} 289 290SideStepError PreamblePatcher::Unpatch(void* target_function, 291 void* replacement_function, 292 void* original_function_stub) { 293 SIDESTEP_ASSERT(target_function && replacement_function && 294 original_function_stub); 295 if (!target_function || !replacement_function || 296 !original_function_stub) { 297 return SIDESTEP_INVALID_PARAMETER; 298 } 299 300 // Before unpatching, target_function should be a JMP to 301 // replacement_function. If it's not, then either it's an error, or 302 // we're falling into the case where the original instruction was a 303 // JMP, and we patched the jumped_to address rather than the JMP 304 // itself. (For instance, if malloc() is just a JMP to __malloc(), 305 // we patched __malloc() and not malloc().) 306 unsigned char* target = reinterpret_cast<unsigned char*>(target_function); 307 target = reinterpret_cast<unsigned char*>( 308 ResolveTargetImpl( 309 target, reinterpret_cast<unsigned char*>(replacement_function), 310 true)); 311 // We should end at the function we patched. When we patch, we insert 312 // a ASM_JMP32REL instruction, so look for that as a sanity check. 313 if (target[0] != ASM_JMP32REL) { 314 SIDESTEP_ASSERT(false && 315 "target_function does not look like it was patched."); 316 return SIDESTEP_INVALID_PARAMETER; 317 } 318 319 const unsigned int kRequiredTargetPatchBytes = 5; 320 321 // We need to be able to write to a process-local copy of the first 322 // kRequiredTargetPatchBytes bytes of target_function 323 DWORD old_target_function_protect = 0; 324 BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), 325 kRequiredTargetPatchBytes, 326 PAGE_EXECUTE_READWRITE, 327 &old_target_function_protect); 328 if (!succeeded) { 329 SIDESTEP_ASSERT(false && "Failed to make page containing target function " 330 "copy-on-write."); 331 return SIDESTEP_ACCESS_DENIED; 332 } 333 334 unsigned char* preamble_stub = reinterpret_cast<unsigned char*>( 335 original_function_stub); 336 337 // Disassemble the preamble of stub and copy the bytes back to target. 338 // If we've done any conditional jumps in the preamble we need to convert 339 // them back to the orignal REL8 jumps in the target. 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 } 388 389 FreePreambleBlock(reinterpret_cast<unsigned char*>(original_function_stub)); 390 391 // Restore the protection of the first kRequiredTargetPatchBytes bytes of 392 // target to what they were before we started goofing around. 393 succeeded = ::VirtualProtect(reinterpret_cast<void*>(target), 394 kRequiredTargetPatchBytes, 395 old_target_function_protect, 396 &old_target_function_protect); 397 398 // Flush the instruction cache to make sure the processor doesn't execute the 399 // old version of the instructions (before our patch). 400 // 401 // See comment on FlushInstructionCache elsewhere in this file. 402 succeeded = ::FlushInstructionCache(::GetCurrentProcess(), 403 target, 404 MAX_PREAMBLE_STUB_SIZE); 405 if (!succeeded) { 406 SIDESTEP_ASSERT(false && "Failed to flush instruction cache."); 407 return SIDESTEP_UNEXPECTED; 408 } 409 410 SIDESTEP_LOG("PreamblePatcher::Unpatch successfully unpatched."); 411 return SIDESTEP_SUCCESS; 412} 413 414void 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 424unsigned 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 466void 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 477void* 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 532bool PreamblePatcher::IsShortConditionalJump( 533 unsigned char* target, 534 unsigned int instruction_size) { 535 return (*(target) & 0x70) == 0x70 && instruction_size == 2; 536} 537 538bool 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 545bool PreamblePatcher::IsNearRelativeJump( 546 unsigned char* target, 547 unsigned int instruction_size) { 548 return *(target) == 0xe9 && instruction_size == 5; 549} 550 551bool 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 558bool PreamblePatcher::IsNearRelativeCall( 559 unsigned char* target, 560 unsigned int instruction_size) { 561 return *(target) == 0xe8 && instruction_size == 5; 562} 563 564bool 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 572SideStepError 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 603SideStepError 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 654SideStepError 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 684}; // namespace sidestep 685