mach_error_t defaultIslandFree( void *ptr) { return freeBranchIsland(ptr); }
mach_error_t mach_override_ptr( void *originalFunctionAddress, const void *overrideFunctionAddress, void **originalFunctionReentryIsland ) { assert( originalFunctionAddress ); assert( overrideFunctionAddress ); // this addresses overriding such functions as AudioOutputUnitStart() // test with modified DefaultOutputUnit project #if defined(__x86_64__) for(;;){ if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????] originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1)); else break; } #elif defined(__i386__) for(;;){ if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1); else break; } #endif long *originalFunctionPtr = (long*) originalFunctionAddress; mach_error_t err = err_none; #if defined(__ppc__) || defined(__POWERPC__) // Ensure first instruction isn't 'mfctr'. #define kMFCTRMask 0xfc1fffff #define kMFCTRInstruction 0x7c0903a6 long originalInstruction = *originalFunctionPtr; if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) err = err_cannot_override; #elif defined(__i386__) || defined(__x86_64__) int eatenCount = 0; int originalInstructionCount = 0; char originalInstructions[kOriginalInstructionsSize]; uint8_t originalInstructionSizes[kOriginalInstructionsSize]; uint64_t jumpRelativeInstruction = 0; // JMP Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, &jumpRelativeInstruction, &eatenCount, originalInstructions, &originalInstructionCount, originalInstructionSizes ); if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) { //printf ("Too many instructions eaten\n"); overridePossible = false; } if (!overridePossible) err = err_cannot_override; if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); #endif // Make the original function implementation writable. if( !err ) { err = vm_protect( mach_task_self(), (vm_address_t) originalFunctionPtr, 8, false, (VM_PROT_ALL | VM_PROT_COPY) ); if( err ) err = vm_protect( mach_task_self(), (vm_address_t) originalFunctionPtr, 8, false, (VM_PROT_DEFAULT | VM_PROT_COPY) ); } if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); // Allocate and target the escape island to the overriding function. BranchIsland *escapeIsland = NULL; if( !err ) err = allocateBranchIsland( &escapeIsland, originalFunctionAddress ); if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); #if defined(__ppc__) || defined(__POWERPC__) if( !err ) err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); // Build the branch absolute instruction to the escape island. long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. if( !err ) { long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; } #elif defined(__i386__) || defined(__x86_64__) if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); if( !err ) err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); // Build the jump relative instruction to the escape island #endif #if defined(__i386__) || defined(__x86_64__) if (!err) { uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5); addressOffset = OSSwapInt32(addressOffset); jumpRelativeInstruction |= 0xE900000000000000LL; jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); } #endif // Optionally allocate & return the reentry island. This may contain relocated // jmp instructions and so has all the same addressing reachability requirements // the escape island has to the original function, except the escape island is // technically our original function. BranchIsland *reentryIsland = NULL; if( !err && originalFunctionReentryIsland ) { err = allocateBranchIsland( &reentryIsland, escapeIsland); if( !err ) *originalFunctionReentryIsland = reentryIsland; } #if defined(__ppc__) || defined(__POWERPC__) // Atomically: // o If the reentry island was allocated: // o Insert the original instruction into the reentry island. // o Target the reentry island at the 2nd instruction of the // original function. // o Replace the original instruction with the branch absolute. if( !err ) { int escapeIslandEngaged = false; do { if( reentryIsland ) err = setBranchIslandTarget( reentryIsland, (void*) (originalFunctionPtr+1), originalInstruction ); if( !err ) { escapeIslandEngaged = CompareAndSwap( originalInstruction, branchAbsoluteInstruction, (UInt32*)originalFunctionPtr ); if( !escapeIslandEngaged ) { // Someone replaced the instruction out from under us, // re-read the instruction, make sure it's still not // 'mfctr' and try again. originalInstruction = *originalFunctionPtr; if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) err = err_cannot_override; } } } while( !err && !escapeIslandEngaged ); } #elif defined(__i386__) || defined(__x86_64__) // Atomically: // o If the reentry island was allocated: // o Insert the original instructions into the reentry island. // o Target the reentry island at the first non-replaced // instruction of the original function. // o Replace the original first instructions with the jump relative. // // Note that on i386, we do not support someone else changing the code under our feet if ( !err ) { uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland; fixupInstructions(offset, originalInstructions, originalInstructionCount, originalInstructionSizes ); if( reentryIsland ) err = setBranchIslandTarget_i386( reentryIsland, (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); // try making islands executable before planting the jmp #if defined(__x86_64__) || defined(__i386__) if( !err ) err = makeIslandExecutable(escapeIsland); if( !err && reentryIsland ) err = makeIslandExecutable(reentryIsland); #endif if ( !err ) atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); } #endif // Clean up on error. if( err ) { if( reentryIsland ) freeBranchIsland( reentryIsland ); if( escapeIsland ) freeBranchIsland( escapeIsland ); } return err; }
mach_error_t mach_override_ptr( void *originalFunctionAddress, const void *overrideFunctionAddress, void **originalFunctionReentryIsland ) { assert( originalFunctionAddress ); assert( overrideFunctionAddress ); long *originalFunctionPtr = (long*) originalFunctionAddress; mach_error_t err = err_none; #if defined(__ppc__) || defined(__POWERPC__) // Ensure first instruction isn't 'mfctr'. #define kMFCTRMask 0xfc1fffff #define kMFCTRInstruction 0x7c0903a6 long originalInstruction = *originalFunctionPtr; if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) err = err_cannot_override; #elif defined(__i386__) || defined(__x86_64__) int eatenCount = 0; char originalInstructions[kOriginalInstructionsSize]; uint64_t jumpRelativeInstruction = 0; // JMP Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, &jumpRelativeInstruction, &eatenCount, originalInstructions); if (eatenCount > kOriginalInstructionsSize) { //printf ("Too many instructions eaten\n"); overridePossible = false; } if (!overridePossible) err = err_cannot_override; if (err) printf("err = %x %d\n", err, __LINE__); #endif // Make the original function implementation writable. if( !err ) { err = vm_protect( mach_task_self(), (vm_address_t) originalFunctionPtr, sizeof(long), false, (VM_PROT_ALL | VM_PROT_COPY) ); if( err ) err = vm_protect( mach_task_self(), (vm_address_t) originalFunctionPtr, sizeof(long), false, (VM_PROT_DEFAULT | VM_PROT_COPY) ); } if (err) printf("err = %x %d\n", err, __LINE__); // Allocate and target the escape island to the overriding function. BranchIsland *escapeIsland = NULL; if( !err ) err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress ); if (err) printf("err = %x %d\n", err, __LINE__); #if defined(__ppc__) || defined(__POWERPC__) if( !err ) err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); // Build the branch absolute instruction to the escape island. long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. if( !err ) { long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; } #elif defined(__i386__) || defined(__x86_64__) if (err) printf("err = %x %d\n", err, __LINE__); if( !err ) err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); if (err) printf("err = %x %d\n", err, __LINE__); // Build the jump relative instruction to the escape island #endif #if defined(__i386__) || defined(__x86_64__) if (!err) { uint32_t addressOffset = ((void*)escapeIsland - (void*)originalFunctionPtr - 5); addressOffset = OSSwapInt32(addressOffset); jumpRelativeInstruction |= 0xE900000000000000LL; jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); } #endif // Optionally allocate & return the reentry island. BranchIsland *reentryIsland = NULL; if( !err && originalFunctionReentryIsland ) { err = allocateBranchIsland( &reentryIsland, kAllocateNormal, NULL); if( !err ) *originalFunctionReentryIsland = reentryIsland; } #if defined(__ppc__) || defined(__POWERPC__) // Atomically: // o If the reentry island was allocated: // o Insert the original instruction into the reentry island. // o Target the reentry island at the 2nd instruction of the // original function. // o Replace the original instruction with the branch absolute. if( !err ) { int escapeIslandEngaged = false; do { if( reentryIsland ) err = setBranchIslandTarget( reentryIsland, (void*) (originalFunctionPtr+1), originalInstruction ); if( !err ) { escapeIslandEngaged = CompareAndSwap( originalInstruction, branchAbsoluteInstruction, (UInt32*)originalFunctionPtr ); if( !escapeIslandEngaged ) { // Someone replaced the instruction out from under us, // re-read the instruction, make sure it's still not // 'mfctr' and try again. originalInstruction = *originalFunctionPtr; if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) err = err_cannot_override; } } } while( !err && !escapeIslandEngaged ); } #elif defined(__i386__) || defined(__x86_64__) // Atomically: // o If the reentry island was allocated: // o Insert the original instructions into the reentry island. // o Target the reentry island at the first non-replaced // instruction of the original function. // o Replace the original first instructions with the jump relative. // // Note that on i386, we do not support someone else changing the code under our feet if ( !err ) { if( reentryIsland ) err = setBranchIslandTarget_i386( reentryIsland, (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); if ( !err ) atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); } #endif // Clean up on error. if( err ) { if( reentryIsland ) freeBranchIsland( reentryIsland ); if( escapeIsland ) freeBranchIsland( escapeIsland ); } #if defined(__x86_64__) err = makeIslandExecutable(escapeIsland); err = makeIslandExecutable(reentryIsland); #endif return err; }