From 96fd34d36ea6fd80507a10fdee83bda1c10a04c2 Mon Sep 17 00:00:00 2001 From: Mikkel Krautz Date: Sun, 5 Aug 2012 20:09:01 +0200 Subject: [PATCH] macx/overlay: sync with latest mach_override. --- macx/overlay/mach_override.c | 262 ++++++++++++++++++++++------------- 1 file changed, 162 insertions(+), 100 deletions(-) diff --git a/macx/overlay/mach_override.c b/macx/overlay/mach_override.c index 790e2698e..fc04cc212 100644 --- a/macx/overlay/mach_override.c +++ b/macx/overlay/mach_override.c @@ -23,6 +23,7 @@ #pragma mark - #pragma mark (Constants) +#define kPageSize 4096 #if defined(__ppc__) || defined(__POWERPC__) long kIslandTemplate[] = { @@ -76,9 +77,6 @@ char kIslandTemplate[] = { #endif -#define kAllocateHigh 1 -#define kAllocateNormal 0 - /************************** * * Data Types @@ -89,7 +87,6 @@ char kIslandTemplate[] = { typedef struct { char instructions[sizeof(kIslandTemplate)]; - int allocatedHigh; } BranchIsland; /************************** @@ -103,7 +100,6 @@ typedef struct { mach_error_t allocateBranchIsland( BranchIsland **island, - int allocateHigh, void *originalFunctionAddress); mach_error_t @@ -134,7 +130,17 @@ eatKnownInstructions( unsigned char *code, uint64_t *newInstruction, int *howManyEaten, - char *originalInstructions ); + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ); + + static void +fixupInstructions( + void *originalFunction, + void *escapeIsland, + void *instructionsToFix, + int instructionCount, + uint8_t *instructionSizes ); #endif /******************************************************************************* @@ -145,15 +151,13 @@ eatKnownInstructions( #pragma mark - #pragma mark (Interface) -#if defined(__x86_64__) +#if defined(__i386__) || defined(__x86_64__) mach_error_t makeIslandExecutable(void *address) { mach_error_t err = err_none; - vm_size_t pageSize; - host_page_size( mach_host_self(), &pageSize ); - uint64_t page = (uint64_t)address & ~(uint64_t)(pageSize-1); + uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1); int e = err_none; - e |= mprotect((void *)page, pageSize, PROT_EXEC | PROT_READ | PROT_WRITE); - e |= msync((void *)page, pageSize, MS_INVALIDATE ); + e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE); + e |= msync((void *)page, kPageSize, MS_INVALIDATE ); if (e) { err = err_cannot_override; } @@ -170,6 +174,22 @@ mach_override_ptr( 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; @@ -183,36 +203,40 @@ mach_override_ptr( 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); + &jumpRelativeInstruction, &eatenCount, + originalInstructions, &originalInstructionCount, + originalInstructionSizes ); 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__); + 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, - sizeof(uint64_t), false, (VM_PROT_ALL | VM_PROT_COPY) ); + (vm_address_t) originalFunctionPtr, 8, false, + (VM_PROT_ALL | VM_PROT_COPY) ); if( err ) err = vm_protect( mach_task_self(), - (vm_address_t) originalFunctionPtr, sizeof(uint64_t), false, + (vm_address_t) originalFunctionPtr, 8, false, (VM_PROT_DEFAULT | VM_PROT_COPY) ); } - if (err) printf("err = %x %d\n", err, __LINE__); + 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, kAllocateHigh, originalFunctionAddress ); - if (err) printf("err = %x %d\n", err, __LINE__); + err = allocateBranchIsland( &escapeIsland, originalFunctionAddress ); + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); #if defined(__ppc__) || defined(__POWERPC__) @@ -226,19 +250,19 @@ mach_override_ptr( branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; } #elif defined(__i386__) || defined(__x86_64__) - if (err) printf("err = %x %d\n", err, __LINE__); + if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); if( !err ) err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); - if (err) printf("err = %x %d\n", err, __LINE__); + 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 = ((void*)escapeIsland - (void*)originalFunctionPtr - 5); + uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5); addressOffset = OSSwapInt32(addressOffset); jumpRelativeInstruction |= 0xE900000000000000LL; @@ -247,10 +271,13 @@ mach_override_ptr( } #endif - // Optionally allocate & return the reentry island. + // 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, kAllocateNormal, NULL); + err = allocateBranchIsland( &reentryIsland, escapeIsland); if( !err ) *originalFunctionReentryIsland = reentryIsland; } @@ -293,9 +320,19 @@ mach_override_ptr( // // Note that on i386, we do not support someone else changing the code under our feet if ( !err ) { + fixupInstructions(originalFunctionPtr, reentryIsland, 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); } @@ -309,11 +346,6 @@ mach_override_ptr( freeBranchIsland( escapeIsland ); } -#if defined(__x86_64__) - err = makeIslandExecutable(escapeIsland); - err = makeIslandExecutable(reentryIsland); -#endif - return err; } @@ -329,9 +361,6 @@ mach_override_ptr( Implementation: Allocates memory for a branch island. @param island <- The allocated island. - @param allocateHigh -> Whether to allocate the island at the end of the - address space (for use with the branch absolute - instruction). @result <- mach_error_t ***************************************************************************/ @@ -339,60 +368,63 @@ mach_override_ptr( mach_error_t allocateBranchIsland( BranchIsland **island, - int allocateHigh, void *originalFunctionAddress) { assert( island ); - - mach_error_t err = err_none; - - if( allocateHigh ) { - vm_size_t pageSize; - err = host_page_size( mach_host_self(), &pageSize ); - if( !err ) { - assert( sizeof( BranchIsland ) <= pageSize ); -#if defined(__x86_64__) - 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? - vm_address_t last = 0x0; -#else - vm_address_t first = 0xfeffffff; - vm_address_t last = 0xfe000000 + pageSize; -#endif + assert( sizeof( BranchIsland ) <= kPageSize ); - vm_address_t page = first; - int allocated = 0; - vm_map_t task_self = mach_task_self(); - - while( !err && !allocated && page != last ) { + vm_map_t task_self = mach_task_self(); + vm_address_t original_address = (vm_address_t) originalFunctionAddress; + static vm_address_t last_allocated = 0; + vm_address_t address = + last_allocated ? last_allocated : original_address; - err = vm_allocate( task_self, &page, pageSize, 0 ); - if( err == err_none ) - allocated = 1; - else if( err == KERN_NO_SPACE ) { -#if defined(__x86_64__) - page -= pageSize; + for (;;) { + vm_size_t vmsize = 0; + memory_object_name_t object = 0; + kern_return_t kr = 0; + vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; + // Find the page the address is in. +#if __WORDSIZE == 32 + vm_region_basic_info_data_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; + kr = vm_region(task_self, &address, &vmsize, flavor, + (vm_region_info_t)&info, &info_count, &object); #else - page += pageSize; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; + kr = vm_region_64(task_self, &address, &vmsize, flavor, + (vm_region_info_t)&info, &info_count, &object); #endif - err = err_none; - } - } - if( allocated ) - *island = (void*) page; - else if( !allocated && !err ) - err = KERN_NO_SPACE; + if (kr != KERN_SUCCESS) + return kr; + + // Don't underflow. This could be made to work, but this is a + // convenient place to give up. + assert((address & (kPageSize - 1)) == 0); + if (address == 0) + break; + + // Go back one page. + vm_address_t new_address = address - kPageSize; +#if __WORDSIZE == 64 + if(original_address - new_address - 5 > INT32_MAX) + break; +#endif + address = new_address; + + // Try to allocate this page. + kr = vm_allocate(task_self, &address, kPageSize, 0); + if (kr == KERN_SUCCESS) { + *island = (BranchIsland*) address; + last_allocated = address; + return err_none; } - } else { - void *block = malloc( sizeof( BranchIsland ) ); - if( block ) - *island = block; - else - err = KERN_NO_SPACE; + if (kr != KERN_NO_SPACE) + return kr; } - if( !err ) - (**island).allocatedHigh = allocateHigh; - - return err; + + return KERN_NO_SPACE; } /***************************************************************************//** @@ -409,24 +441,9 @@ freeBranchIsland( { assert( island ); assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); - assert( island->allocatedHigh ); - - mach_error_t err = err_none; - - if( island->allocatedHigh ) { - vm_size_t pageSize; - err = host_page_size( mach_host_self(), &pageSize ); - if( !err ) { - assert( sizeof( BranchIsland ) <= pageSize ); - err = vm_deallocate( - mach_task_self(), - (vm_address_t) island, pageSize ); - } - } else { - free( island ); - } - - return err; + assert( sizeof( BranchIsland ) <= kPageSize ); + return vm_deallocate( mach_task_self(), (vm_address_t) island, + kPageSize ); } /***************************************************************************//** @@ -528,6 +545,8 @@ typedef struct { #if defined(__i386__) static AsmInstructionMatch possibleInstructions[] = { + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? + { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret { 0x1, {0xFF}, {0x90} }, // nop { 0x1, {0xFF}, {0x55} }, // push %esp { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp @@ -537,17 +556,27 @@ static AsmInstructionMatch possibleInstructions[] = { { 0x1, {0xFF}, {0x57} }, // push %edi { 0x1, {0xFF}, {0x56} }, // push %esi { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg + { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg + { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax { 0x0 } }; #elif defined(__x86_64__) static AsmInstructionMatch possibleInstructions[] = { + { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? { 0x1, {0xFF}, {0x90} }, // nop { 0x1, {0xF8}, {0x50} }, // push %rX { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp + { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX + { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg + { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi) + { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax + { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax { 0x0 } }; #endif @@ -572,17 +601,21 @@ static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* #if defined(__i386__) || defined(__x86_64__) static Boolean eatKnownInstructions( - unsigned char *code, - uint64_t* newInstruction, - int* howManyEaten, - char* originalInstructions ) + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ) { Boolean allInstructionsKnown = true; int totalEaten = 0; unsigned char* ptr = code; int remainsToEat = 5; // a JMP instruction takes 5 bytes + int instructionIndex = 0; if (howManyEaten) *howManyEaten = 0; + if (originalInstructionCount) *originalInstructionCount = 0; while (remainsToEat > 0) { Boolean curInstructionKnown = false; @@ -596,6 +629,7 @@ eatKnownInstructions( // if all instruction matches failed, we don't know current instruction then, stop here if (!curInstructionKnown) { allInstructionsKnown = false; + fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n"); break; } @@ -604,6 +638,10 @@ eatKnownInstructions( ptr += eaten; remainsToEat -= eaten; totalEaten += eaten; + + if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; + instructionIndex += 1; + if (originalInstructionCount) *originalInstructionCount = instructionIndex; } @@ -634,6 +672,30 @@ eatKnownInstructions( return allInstructionsKnown; } + + static void +fixupInstructions( + void *originalFunction, + void *escapeIsland, + void *instructionsToFix, + int instructionCount, + uint8_t *instructionSizes ) +{ + int index; + for (index = 0;index < instructionCount;index += 1) + { + if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative + { + uint32_t offset = (uintptr_t)originalFunction - (uintptr_t)escapeIsland; + uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1); + *jumpOffsetPtr += offset; + } + + originalFunction = (void*)((uintptr_t)originalFunction + instructionSizes[index]); + escapeIsland = (void*)((uintptr_t)escapeIsland + instructionSizes[index]); + instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]); + } +} #endif #if defined(__i386__)