Index: third_party/mach_override/mach_override.c |
=================================================================== |
--- third_party/mach_override/mach_override.c (revision 150086) |
+++ third_party/mach_override/mach_override.c (working copy) |
@@ -23,6 +23,7 @@ |
#pragma mark - |
#pragma mark (Constants) |
+#define kPageSize 4096 |
#if defined(__ppc__) || defined(__POWERPC__) |
long kIslandTemplate[] = { |
@@ -76,9 +77,6 @@ |
#endif |
-#define kAllocateHigh 1 |
-#define kAllocateNormal 0 |
- |
/************************** |
* |
* Data Types |
@@ -89,7 +87,6 @@ |
typedef struct { |
char instructions[sizeof(kIslandTemplate)]; |
- int allocatedHigh; |
} BranchIsland; |
/************************** |
@@ -103,7 +100,6 @@ |
mach_error_t |
allocateBranchIsland( |
BranchIsland **island, |
- int allocateHigh, |
void *originalFunctionAddress); |
mach_error_t |
@@ -134,7 +130,17 @@ |
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 |
/******************************************************************************* |
@@ -148,12 +154,10 @@ |
#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 ); |
- uintptr_t page = (uintptr_t)address & ~(uintptr_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; |
} |
@@ -172,17 +176,16 @@ |
// this addresses overriding such functions as AudioOutputUnitStart() |
// test with modified DefaultOutputUnit project |
-#if defined(__x86_64__) || defined(__i386__) |
+#if defined(__x86_64__) |
for(;;){ |
- if(*(unsigned char*)originalFunctionAddress==0xE9) // jmp .+0x???????? |
- originalFunctionAddress=(void*)((char*)originalFunctionAddress+5+*(int32_t *)((char*)originalFunctionAddress+1)); |
-#if defined(__x86_64__) |
- else if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????] |
+ 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__) |
- else if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? |
+ for(;;){ |
+ if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? |
originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1); |
-#endif |
else break; |
} |
#endif |
@@ -200,11 +203,15 @@ |
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; |
@@ -228,7 +235,7 @@ |
// Allocate and target the escape island to the overriding function. |
BranchIsland *escapeIsland = NULL; |
if( !err ) |
- err = allocateBranchIsland( &escapeIsland, kAllocateHigh, originalFunctionAddress ); |
+ err = allocateBranchIsland( &escapeIsland, originalFunctionAddress ); |
if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); |
@@ -264,10 +271,13 @@ |
} |
#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, kAllocateHigh, NULL); |
+ err = allocateBranchIsland( &reentryIsland, escapeIsland); |
if( !err ) |
*originalFunctionReentryIsland = reentryIsland; |
} |
@@ -310,6 +320,9 @@ |
// |
// 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 ); |
@@ -348,9 +361,6 @@ |
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 |
***************************************************************************/ |
@@ -358,60 +368,63 @@ |
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; |
+ assert( sizeof( BranchIsland ) <= kPageSize ); |
+ |
+ 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; |
+ |
+ 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 |
- vm_address_t first = 0xffc00000; |
- vm_address_t last = 0xfffe0000; |
+ 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 |
+ if (kr != KERN_SUCCESS) |
+ return kr; |
- vm_address_t page = first; |
- int allocated = 0; |
- vm_map_t task_self = mach_task_self(); |
- |
- while( !err && !allocated && page != last ) { |
+ // 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; |
- 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; |
-#else |
- page += pageSize; |
+ // Go back one page. |
+ vm_address_t new_address = address - kPageSize; |
+#if __WORDSIZE == 64 |
+ if(original_address - new_address - 5 > INT32_MAX) |
+ break; |
#endif |
- err = err_none; |
- } |
- } |
- if( allocated ) |
- *island = (BranchIsland*) page; |
- else if( !allocated && !err ) |
- err = KERN_NO_SPACE; |
+ 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; |
} |
/***************************************************************************//** |
@@ -428,24 +441,9 @@ |
{ |
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 ); |
} |
/***************************************************************************//** |
@@ -547,6 +545,7 @@ |
#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 |
@@ -560,19 +559,24 @@ |
{ 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 |
@@ -597,17 +601,21 @@ |
#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; |
@@ -630,6 +638,10 @@ |
ptr += eaten; |
remainsToEat -= eaten; |
totalEaten += eaten; |
+ |
+ if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; |
+ instructionIndex += 1; |
+ if (originalInstructionCount) *originalInstructionCount = instructionIndex; |
} |
@@ -660,6 +672,30 @@ |
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__) |