mach_override.c revision 5967c3de18a658e052c07a2af69f02823115655b
1/******************************************************************************* 2 mach_override.c 3 Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com> 4 Some rights reserved: <http://opensource.org/licenses/mit-license.php> 5 6 ***************************************************************************/ 7#ifdef __APPLE__ 8 9#include "mach_override.h" 10 11#include <mach-o/dyld.h> 12#include <mach/mach_host.h> 13#include <mach/mach_init.h> 14#include <mach/vm_map.h> 15#include <sys/mman.h> 16 17#include <CoreServices/CoreServices.h> 18 19//#define DEBUG_DISASM 1 20#undef DEBUG_DISASM 21 22/************************** 23* 24* Constants 25* 26**************************/ 27#pragma mark - 28#pragma mark (Constants) 29 30#if defined(__ppc__) || defined(__POWERPC__) 31 32long kIslandTemplate[] = { 33 0x9001FFFC, // stw r0,-4(SP) 34 0x3C00DEAD, // lis r0,0xDEAD 35 0x6000BEEF, // ori r0,r0,0xBEEF 36 0x7C0903A6, // mtctr r0 37 0x8001FFFC, // lwz r0,-4(SP) 38 0x60000000, // nop ; optionally replaced 39 0x4E800420 // bctr 40}; 41 42#define kAddressHi 3 43#define kAddressLo 5 44#define kInstructionHi 10 45#define kInstructionLo 11 46 47#elif defined(__i386__) 48 49#define kOriginalInstructionsSize 16 50 51char kIslandTemplate[] = { 52 // kOriginalInstructionsSize nop instructions so that we 53 // should have enough space to host original instructions 54 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 55 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 56 // Now the real jump instruction 57 0xE9, 0xEF, 0xBE, 0xAD, 0xDE 58}; 59 60#define kInstructions 0 61#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 62#elif defined(__x86_64__) 63 64#define kOriginalInstructionsSize 32 65 66#define kJumpAddress kOriginalInstructionsSize + 6 67 68char kIslandTemplate[] = { 69 // kOriginalInstructionsSize nop instructions so that we 70 // should have enough space to host original instructions 71 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 72 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 73 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 74 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 75 // Now the real jump instruction 76 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, 77 0x00, 0x00, 0x00, 0x00, 78 0x00, 0x00, 0x00, 0x00 79}; 80 81#endif 82 83#define kAllocateHigh 1 84#define kAllocateNormal 0 85 86/************************** 87* 88* Data Types 89* 90**************************/ 91#pragma mark - 92#pragma mark (Data Types) 93 94typedef struct { 95 char instructions[sizeof(kIslandTemplate)]; 96 int allocatedHigh; 97} BranchIsland; 98 99/************************** 100* 101* Funky Protos 102* 103**************************/ 104#pragma mark - 105#pragma mark (Funky Protos) 106 107 mach_error_t 108allocateBranchIsland( 109 BranchIsland **island, 110 int allocateHigh, 111 void *originalFunctionAddress) __attribute__((visibility("hidden"))); 112 113 mach_error_t 114freeBranchIsland( 115 BranchIsland *island ) __attribute__((visibility("hidden"))); 116 117 mach_error_t 118defaultIslandMalloc( 119 void **ptr, size_t unused_size, void *hint) __attribute__((visibility("hidden"))); 120 121 mach_error_t 122defaultIslandFree( 123 void *ptr) __attribute__((visibility("hidden"))); 124 125#if defined(__ppc__) || defined(__POWERPC__) 126 mach_error_t 127setBranchIslandTarget( 128 BranchIsland *island, 129 const void *branchTo, 130 long instruction ) __attribute__((visibility("hidden"))); 131#endif 132 133#if defined(__i386__) || defined(__x86_64__) 134mach_error_t 135setBranchIslandTarget_i386( 136 BranchIsland *island, 137 const void *branchTo, 138 char* instructions ) __attribute__((visibility("hidden"))); 139void 140atomic_mov64( 141 uint64_t *targetAddress, 142 uint64_t value ) __attribute__((visibility("hidden"))); 143 144 static Boolean 145eatKnownInstructions( 146 unsigned char *code, 147 uint64_t *newInstruction, 148 int *howManyEaten, 149 char *originalInstructions, 150 int *originalInstructionCount, 151 uint8_t *originalInstructionSizes ) __attribute__((visibility("hidden"))); 152 153 static void 154fixupInstructions( 155 void *originalFunction, 156 void *escapeIsland, 157 void *instructionsToFix, 158 int instructionCount, 159 uint8_t *instructionSizes ) __attribute__((visibility("hidden"))); 160#endif 161 162/******************************************************************************* 163* 164* Interface 165* 166*******************************************************************************/ 167#pragma mark - 168#pragma mark (Interface) 169 170#if defined(__i386__) || defined(__x86_64__) 171mach_error_t makeIslandExecutable(void *address) { 172 mach_error_t err = err_none; 173 vm_size_t pageSize; 174 host_page_size( mach_host_self(), &pageSize ); 175 uintptr_t page = (uintptr_t)address & ~(uintptr_t)(pageSize-1); 176 int e = err_none; 177 e |= mprotect((void *)page, pageSize, PROT_EXEC | PROT_READ | PROT_WRITE); 178 e |= msync((void *)page, pageSize, MS_INVALIDATE ); 179 if (e) { 180 err = err_cannot_override; 181 } 182 return err; 183} 184#endif 185 186 mach_error_t 187defaultIslandMalloc( 188 void **ptr, size_t unused_size, void *hint) { 189 return allocateBranchIsland( (BranchIsland**)ptr, kAllocateHigh, hint ); 190} 191 mach_error_t 192defaultIslandFree( 193 void *ptr) { 194 return freeBranchIsland(ptr); 195} 196 197 mach_error_t 198__asan_mach_override_ptr( 199 void *originalFunctionAddress, 200 const void *overrideFunctionAddress, 201 void **originalFunctionReentryIsland ) 202{ 203 return __asan_mach_override_ptr_custom(originalFunctionAddress, 204 overrideFunctionAddress, 205 originalFunctionReentryIsland, 206 defaultIslandMalloc, 207 defaultIslandFree); 208} 209 210 mach_error_t 211__asan_mach_override_ptr_custom( 212 void *originalFunctionAddress, 213 const void *overrideFunctionAddress, 214 void **originalFunctionReentryIsland, 215 island_malloc *alloc, 216 island_free *dealloc) 217{ 218 assert( originalFunctionAddress ); 219 assert( overrideFunctionAddress ); 220 221 // this addresses overriding such functions as AudioOutputUnitStart() 222 // test with modified DefaultOutputUnit project 223#if defined(__x86_64__) 224 for(;;){ 225 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????] 226 originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1)); 227 else break; 228 } 229#elif defined(__i386__) 230 for(;;){ 231 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? 232 originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1); 233 else break; 234 } 235#endif 236#ifdef DEBUG_DISASM 237 { 238 fprintf(stderr, "Replacing function at %p\n", originalFunctionAddress); 239 fprintf(stderr, "First 16 bytes of the function: "); 240 unsigned char *orig = (unsigned char *)originalFunctionAddress; 241 int i; 242 for (i = 0; i < 16; i++) { 243 fprintf(stderr, "%x ", (unsigned int) orig[i]); 244 } 245 fprintf(stderr, "\n"); 246 fprintf(stderr, 247 "To disassemble, save the following function as disas.c" 248 " and run:\n gcc -c disas.c && gobjdump -d disas.o\n" 249 "The first 16 bytes of the original function will start" 250 " after four nop instructions.\n"); 251 fprintf(stderr, "\nvoid foo() {\n asm volatile(\"nop;nop;nop;nop;\");\n"); 252 int j = 0; 253 for (j = 0; j < 2; j++) { 254 fprintf(stderr, " asm volatile(\".byte "); 255 for (i = 8 * j; i < 8 * (j+1) - 1; i++) { 256 fprintf(stderr, "0x%x, ", (unsigned int) orig[i]); 257 } 258 fprintf(stderr, "0x%x;\");\n", (unsigned int) orig[8 * (j+1) - 1]); 259 } 260 fprintf(stderr, "}\n\n"); 261 } 262#endif 263 264 long *originalFunctionPtr = (long*) originalFunctionAddress; 265 mach_error_t err = err_none; 266 267#if defined(__ppc__) || defined(__POWERPC__) 268 // Ensure first instruction isn't 'mfctr'. 269 #define kMFCTRMask 0xfc1fffff 270 #define kMFCTRInstruction 0x7c0903a6 271 272 long originalInstruction = *originalFunctionPtr; 273 if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) 274 err = err_cannot_override; 275#elif defined(__i386__) || defined(__x86_64__) 276 int eatenCount = 0; 277 int originalInstructionCount = 0; 278 char originalInstructions[kOriginalInstructionsSize]; 279 uint8_t originalInstructionSizes[kOriginalInstructionsSize]; 280 uint64_t jumpRelativeInstruction = 0; // JMP 281 282 Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, 283 &jumpRelativeInstruction, &eatenCount, 284 originalInstructions, &originalInstructionCount, 285 originalInstructionSizes ); 286#ifdef DEBUG_DISASM 287 if (!overridePossible) fprintf(stderr, "overridePossible = false @%d\n", __LINE__); 288#endif 289 if (eatenCount > kOriginalInstructionsSize) { 290#ifdef DEBUG_DISASM 291 fprintf(stderr, "Too many instructions eaten\n"); 292#endif 293 overridePossible = false; 294 } 295 if (!overridePossible) err = err_cannot_override; 296 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 297#endif 298 299 // Make the original function implementation writable. 300 if( !err ) { 301 err = vm_protect( mach_task_self(), 302 (vm_address_t) originalFunctionPtr, 8, false, 303 (VM_PROT_ALL | VM_PROT_COPY) ); 304 if( err ) 305 err = vm_protect( mach_task_self(), 306 (vm_address_t) originalFunctionPtr, 8, false, 307 (VM_PROT_DEFAULT | VM_PROT_COPY) ); 308 } 309 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 310 311 // Allocate and target the escape island to the overriding function. 312 BranchIsland *escapeIsland = NULL; 313 if( !err ) 314 err = alloc( (void**)&escapeIsland, sizeof(BranchIsland), originalFunctionAddress ); 315 if ( err ) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 316 317#if defined(__ppc__) || defined(__POWERPC__) 318 if( !err ) 319 err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); 320 321 // Build the branch absolute instruction to the escape island. 322 long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. 323 if( !err ) { 324 long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; 325 branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; 326 } 327#elif defined(__i386__) || defined(__x86_64__) 328 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 329 330 if( !err ) 331 err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); 332 333 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); 334 // Build the jump relative instruction to the escape island 335#endif 336 337 338#if defined(__i386__) || defined(__x86_64__) 339 if (!err) { 340 uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5); 341 addressOffset = OSSwapInt32(addressOffset); 342 343 jumpRelativeInstruction |= 0xE900000000000000LL; 344 jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; 345 jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); 346 } 347#endif 348 349 // Optionally allocate & return the reentry island. This may contain relocated 350 // jmp instructions and so has all the same addressing reachability requirements 351 // the escape island has to the original function, except the escape island is 352 // technically our original function. 353 BranchIsland *reentryIsland = NULL; 354 if( !err && originalFunctionReentryIsland ) { 355 err = alloc( (void**)&reentryIsland, sizeof(BranchIsland), escapeIsland); 356 if( !err ) 357 *originalFunctionReentryIsland = reentryIsland; 358 } 359 360#if defined(__ppc__) || defined(__POWERPC__) 361 // Atomically: 362 // o If the reentry island was allocated: 363 // o Insert the original instruction into the reentry island. 364 // o Target the reentry island at the 2nd instruction of the 365 // original function. 366 // o Replace the original instruction with the branch absolute. 367 if( !err ) { 368 int escapeIslandEngaged = false; 369 do { 370 if( reentryIsland ) 371 err = setBranchIslandTarget( reentryIsland, 372 (void*) (originalFunctionPtr+1), originalInstruction ); 373 if( !err ) { 374 escapeIslandEngaged = CompareAndSwap( originalInstruction, 375 branchAbsoluteInstruction, 376 (UInt32*)originalFunctionPtr ); 377 if( !escapeIslandEngaged ) { 378 // Someone replaced the instruction out from under us, 379 // re-read the instruction, make sure it's still not 380 // 'mfctr' and try again. 381 originalInstruction = *originalFunctionPtr; 382 if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) 383 err = err_cannot_override; 384 } 385 } 386 } while( !err && !escapeIslandEngaged ); 387 } 388#elif defined(__i386__) || defined(__x86_64__) 389 // Atomically: 390 // o If the reentry island was allocated: 391 // o Insert the original instructions into the reentry island. 392 // o Target the reentry island at the first non-replaced 393 // instruction of the original function. 394 // o Replace the original first instructions with the jump relative. 395 // 396 // Note that on i386, we do not support someone else changing the code under our feet 397 if ( !err ) { 398 fixupInstructions(originalFunctionPtr, reentryIsland, originalInstructions, 399 originalInstructionCount, originalInstructionSizes ); 400 401 if( reentryIsland ) 402 err = setBranchIslandTarget_i386( reentryIsland, 403 (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); 404 // try making islands executable before planting the jmp 405#if defined(__x86_64__) || defined(__i386__) 406 if( !err ) 407 err = makeIslandExecutable(escapeIsland); 408 if( !err && reentryIsland ) 409 err = makeIslandExecutable(reentryIsland); 410#endif 411 if ( !err ) 412 atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); 413 } 414#endif 415 416 // Clean up on error. 417 if( err ) { 418 if( reentryIsland ) 419 dealloc( reentryIsland ); 420 if( escapeIsland ) 421 dealloc( escapeIsland ); 422 } 423 424#ifdef DEBUG_DISASM 425 { 426 fprintf(stderr, "First 16 bytes of the function after slicing: "); 427 unsigned char *orig = (unsigned char *)originalFunctionAddress; 428 int i; 429 for (i = 0; i < 16; i++) { 430 fprintf(stderr, "%x ", (unsigned int) orig[i]); 431 } 432 fprintf(stderr, "\n"); 433 } 434#endif 435 return err; 436} 437 438/******************************************************************************* 439* 440* Implementation 441* 442*******************************************************************************/ 443#pragma mark - 444#pragma mark (Implementation) 445 446/***************************************************************************//** 447 Implementation: Allocates memory for a branch island. 448 449 @param island <- The allocated island. 450 @param allocateHigh -> Whether to allocate the island at the end of the 451 address space (for use with the branch absolute 452 instruction). 453 @result <- mach_error_t 454 455 ***************************************************************************/ 456 457 mach_error_t 458allocateBranchIsland( 459 BranchIsland **island, 460 int allocateHigh, 461 void *originalFunctionAddress) 462{ 463 assert( island ); 464 465 mach_error_t err = err_none; 466 467 if( allocateHigh ) { 468 vm_size_t pageSize; 469 err = host_page_size( mach_host_self(), &pageSize ); 470 if( !err ) { 471 assert( sizeof( BranchIsland ) <= pageSize ); 472#if defined(__ppc__) || defined(__POWERPC__) 473 vm_address_t first = 0xfeffffff; 474 vm_address_t last = 0xfe000000 + pageSize; 475#elif defined(__x86_64__) 476 vm_address_t first = ((uint64_t)originalFunctionAddress & ~(uint64_t)(((uint64_t)1 << 31) - 1)) | ((uint64_t)1 << 31); // start in the middle of the page? 477 vm_address_t last = 0x0; 478#else 479 vm_address_t first = 0xffc00000; 480 vm_address_t last = 0xfffe0000; 481#endif 482 483 vm_address_t page = first; 484 int allocated = 0; 485 vm_map_t task_self = mach_task_self(); 486 487 while( !err && !allocated && page != last ) { 488 489 err = vm_allocate( task_self, &page, pageSize, 0 ); 490 if( err == err_none ) 491 allocated = 1; 492 else if( err == KERN_NO_SPACE ) { 493#if defined(__x86_64__) 494 page -= pageSize; 495#else 496 page += pageSize; 497#endif 498 err = err_none; 499 } 500 } 501 if( allocated ) 502 *island = (BranchIsland*) page; 503 else if( !allocated && !err ) 504 err = KERN_NO_SPACE; 505 } 506 } else { 507 void *block = malloc( sizeof( BranchIsland ) ); 508 if( block ) 509 *island = block; 510 else 511 err = KERN_NO_SPACE; 512 } 513 if( !err ) 514 (**island).allocatedHigh = allocateHigh; 515 516 return err; 517} 518 519/***************************************************************************//** 520 Implementation: Deallocates memory for a branch island. 521 522 @param island -> The island to deallocate. 523 @result <- mach_error_t 524 525 ***************************************************************************/ 526 527 mach_error_t 528freeBranchIsland( 529 BranchIsland *island ) 530{ 531 assert( island ); 532 assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); 533 assert( island->allocatedHigh ); 534 535 mach_error_t err = err_none; 536 537 if( island->allocatedHigh ) { 538 vm_size_t pageSize; 539 err = host_page_size( mach_host_self(), &pageSize ); 540 if( !err ) { 541 assert( sizeof( BranchIsland ) <= pageSize ); 542 err = vm_deallocate( 543 mach_task_self(), 544 (vm_address_t) island, pageSize ); 545 } 546 } else { 547 free( island ); 548 } 549 550 return err; 551} 552 553/***************************************************************************//** 554 Implementation: Sets the branch island's target, with an optional 555 instruction. 556 557 @param island -> The branch island to insert target into. 558 @param branchTo -> The address of the target. 559 @param instruction -> Optional instruction to execute prior to branch. Set 560 to zero for nop. 561 @result <- mach_error_t 562 563 ***************************************************************************/ 564#if defined(__ppc__) || defined(__POWERPC__) 565 mach_error_t 566setBranchIslandTarget( 567 BranchIsland *island, 568 const void *branchTo, 569 long instruction ) 570{ 571 // Copy over the template code. 572 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); 573 574 // Fill in the address. 575 ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; 576 ((short*)island->instructions)[kAddressHi] 577 = (((long) branchTo) >> 16) & 0x0000FFFF; 578 579 // Fill in the (optional) instuction. 580 if( instruction != 0 ) { 581 ((short*)island->instructions)[kInstructionLo] 582 = instruction & 0x0000FFFF; 583 ((short*)island->instructions)[kInstructionHi] 584 = (instruction >> 16) & 0x0000FFFF; 585 } 586 587 //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); 588 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); 589 590 return err_none; 591} 592#endif 593 594#if defined(__i386__) 595 mach_error_t 596setBranchIslandTarget_i386( 597 BranchIsland *island, 598 const void *branchTo, 599 char* instructions ) 600{ 601 602 // Copy over the template code. 603 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); 604 605 // copy original instructions 606 if (instructions) { 607 bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); 608 } 609 610 // Fill in the address. 611 int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); 612 *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; 613 614 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); 615 return err_none; 616} 617 618#elif defined(__x86_64__) 619mach_error_t 620setBranchIslandTarget_i386( 621 BranchIsland *island, 622 const void *branchTo, 623 char* instructions ) 624{ 625 // Copy over the template code. 626 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); 627 628 // Copy original instructions. 629 if (instructions) { 630 bcopy (instructions, island->instructions, kOriginalInstructionsSize); 631 } 632 633 // Fill in the address. 634 *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; 635 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); 636 637 return err_none; 638} 639#endif 640 641 642#if defined(__i386__) || defined(__x86_64__) 643// simplistic instruction matching 644typedef struct { 645 unsigned int length; // max 15 646 unsigned char mask[15]; // sequence of bytes in memory order 647 unsigned char constraint[15]; // sequence of bytes in memory order 648} AsmInstructionMatch; 649 650#if defined(__i386__) 651static AsmInstructionMatch possibleInstructions[] = { 652 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? 653 { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %esp; mov %esp,%ebp; leave; ret 654 { 0x1, {0xFF}, {0x90} }, // nop 655 { 0x1, {0xF8}, {0x50} }, // push %reg 656 { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp 657 { 0x3, {0xFF, 0xFF, 0xFF}, {0x89, 0x1C, 0x24} }, // mov %ebx,(%esp) 658 { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp 659 { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate 660 { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax 661 { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg 662 { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg 663 { 0x3, {0xFF, 0xCF, 0x00}, {0x8B, 0x4D, 0x00} }, // mov $imm(%rpb), %reg 664 { 0x3, {0xFF, 0x4F, 0x00}, {0x8A, 0x4D, 0x00} }, // mov $imm(%ebp), %cl 665 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx 666 { 0x4, {0xFF, 0x00, 0x00, 0x00}, {0x8B, 0x00, 0x00, 0x00} }, // mov r16,r/m16 or r32,r/m32 667 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB9, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %ecx 668 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax 669 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1 670 { 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit 671 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE8, 0x00, 0x00, 0x00, 0x00} }, // call $imm 672 { 0x0 } 673}; 674#elif defined(__x86_64__) 675// TODO(glider): disassembling the "0x48, 0x89" sequences is trickier than it's done below. 676// If it stops working, refer to http://ref.x86asm.net/geek.html#modrm_byte_32_64 to do it 677// more accurately. 678// Note: 0x48 is in fact the REX.W prefix, but it might be wrong to treat it as a separate 679// instruction. 680static AsmInstructionMatch possibleInstructions[] = { 681 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? 682 { 0x1, {0xFF}, {0x90} }, // nop 683 { 0x1, {0xF8}, {0x50} }, // push %rX 684 { 0x1, {0xFF}, {0x65} }, // GS prefix 685 { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp 686 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp 687 { 0x4, {0xFB, 0xFF, 0x07, 0x00}, {0x48, 0x89, 0x05, 0x00} }, // move onto rbp 688 { 0x3, {0xFB, 0xFF, 0x00}, {0x48, 0x89, 0x00} }, // mov %reg, %reg 689 { 0x3, {0xFB, 0xFF, 0x00}, {0x49, 0x89, 0x00} }, // mov %reg, %reg (REX.WB) 690 { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX 691 { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX 692 { 0x2, {0xFF, 0x00}, {0x77, 0x00} }, // ja $i8 693 { 0x2, {0xFF, 0x00}, {0x74, 0x00} }, // je $i8 694 { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg 695 { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi) 696 { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax 697 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0x25, 0x00, 0x00, 0x00, 0x00} }, // and $imm, %eax 698 { 0x3, {0xFF, 0xFF, 0xFF}, {0x80, 0x3F, 0x00} }, // cmpb $imm, (%rdi) 699 700 { 0x8, {0xFF, 0xFF, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0x00}, 701 {0x48, 0x8B, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00}, }, // mov $imm, %{rax,rdx,rsp,rsi} 702 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xFA, 0x00}, }, // cmp $i8, %rdx 703 { 0x4, {0xFF, 0xFF, 0x00, 0x00}, {0x83, 0x7f, 0x00, 0x00}, }, // cmpl $imm, $imm(%rdi) 704 { 0xa, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 705 {0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %rax 706 { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, 707 {0x81, 0xE6, 0x00, 0x00, 0x00, 0x00} }, // and $imm, %esi 708 { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, 709 {0xFF, 0x25, 0x00, 0x00, 0x00, 0x00} }, // jmpq *(%rip) 710 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1 711 { 0x2, {0xFF, 0x00}, {0x89, 0x00} }, // mov r/m32,r32 or r/m16,r16 712 { 0x3, {0xFF, 0xFF, 0xFF}, {0x49, 0x89, 0xF8} }, // mov %rdi,%r8 713 { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi) 714 { 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit 715 { 0x0 } 716}; 717#endif 718 719static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction) 720{ 721 Boolean match = true; 722 723 size_t i; 724 assert(instruction); 725#ifdef DEBUG_DISASM 726 fprintf(stderr, "Matching: "); 727#endif 728 for (i=0; i<instruction->length; i++) { 729 unsigned char mask = instruction->mask[i]; 730 unsigned char constraint = instruction->constraint[i]; 731 unsigned char codeValue = code[i]; 732#ifdef DEBUG_DISASM 733 fprintf(stderr, "%x ", (unsigned)codeValue); 734#endif 735 match = ((codeValue & mask) == constraint); 736 if (!match) break; 737 } 738#ifdef DEBUG_DISASM 739 if (match) { 740 fprintf(stderr, " OK\n"); 741 } else { 742 fprintf(stderr, " FAIL\n"); 743 } 744#endif 745 return match; 746} 747 748#if defined(__i386__) || defined(__x86_64__) 749 static Boolean 750eatKnownInstructions( 751 unsigned char *code, 752 uint64_t *newInstruction, 753 int *howManyEaten, 754 char *originalInstructions, 755 int *originalInstructionCount, 756 uint8_t *originalInstructionSizes ) 757{ 758 Boolean allInstructionsKnown = true; 759 int totalEaten = 0; 760 unsigned char* ptr = code; 761 int remainsToEat = 5; // a JMP instruction takes 5 bytes 762 int instructionIndex = 0; 763 764 if (howManyEaten) *howManyEaten = 0; 765 if (originalInstructionCount) *originalInstructionCount = 0; 766 while (remainsToEat > 0) { 767 Boolean curInstructionKnown = false; 768 769 // See if instruction matches one we know 770 AsmInstructionMatch* curInstr = possibleInstructions; 771 do { 772 if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break; 773 curInstr++; 774 } while (curInstr->length > 0); 775 776 // if all instruction matches failed, we don't know current instruction then, stop here 777 if (!curInstructionKnown) { 778 allInstructionsKnown = false; 779 fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n"); 780 break; 781 } 782 783 // At this point, we've matched curInstr 784 int eaten = curInstr->length; 785 ptr += eaten; 786 remainsToEat -= eaten; 787 totalEaten += eaten; 788 789 if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; 790 instructionIndex += 1; 791 if (originalInstructionCount) *originalInstructionCount = instructionIndex; 792 } 793 794 795 if (howManyEaten) *howManyEaten = totalEaten; 796 797 if (originalInstructions) { 798 Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); 799 800 if (enoughSpaceForOriginalInstructions) { 801 memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP 802 bcopy(code, originalInstructions, totalEaten); 803 } else { 804#ifdef DEBUG_DISASM 805 fprintf(stderr, "Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); 806#endif 807 return false; 808 } 809 } 810 811 if (allInstructionsKnown) { 812 // save last 3 bytes of first 64bits of codre we'll replace 813 uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); 814 currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation 815 currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; 816 817 // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr 818 *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes 819 *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes 820 } 821 822 return allInstructionsKnown; 823} 824 825 static void 826fixupInstructions( 827 void *originalFunction, 828 void *escapeIsland, 829 void *instructionsToFix, 830 int instructionCount, 831 uint8_t *instructionSizes ) 832{ 833 int index; 834 for (index = 0;index < instructionCount;index += 1) 835 { 836 if ((*(uint8_t*)instructionsToFix == 0xE9) || // 32-bit jump relative 837 (*(uint8_t*)instructionsToFix == 0xE8)) // 32-bit call relative 838 { 839 uint32_t offset = (uintptr_t)originalFunction - (uintptr_t)escapeIsland; 840 uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1); 841 *jumpOffsetPtr += offset; 842 } 843 844 845 originalFunction = (void*)((uintptr_t)originalFunction + instructionSizes[index]); 846 escapeIsland = (void*)((uintptr_t)escapeIsland + instructionSizes[index]); 847 instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]); 848 } 849} 850#endif 851 852#if defined(__i386__) 853__asm( 854 ".text;" 855 ".align 2, 0x90;" 856 "_atomic_mov64:;" 857 " pushl %ebp;" 858 " movl %esp, %ebp;" 859 " pushl %esi;" 860 " pushl %ebx;" 861 " pushl %ecx;" 862 " pushl %eax;" 863 " pushl %edx;" 864 865 // atomic push of value to an address 866 // we use cmpxchg8b, which compares content of an address with 867 // edx:eax. If they are equal, it atomically puts 64bit value 868 // ecx:ebx in address. 869 // We thus put contents of address in edx:eax to force ecx:ebx 870 // in address 871 " mov 8(%ebp), %esi;" // esi contains target address 872 " mov 12(%ebp), %ebx;" 873 " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address 874 " mov (%esi), %eax;" 875 " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address 876 " lock; cmpxchg8b (%esi);" // atomic move. 877 878 // restore registers 879 " popl %edx;" 880 " popl %eax;" 881 " popl %ecx;" 882 " popl %ebx;" 883 " popl %esi;" 884 " popl %ebp;" 885 " ret" 886); 887#elif defined(__x86_64__) 888void atomic_mov64( 889 uint64_t *targetAddress, 890 uint64_t value ) 891{ 892 *targetAddress = value; 893} 894#endif 895#endif 896#endif // __APPLE__ 897