mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
macx/overlay: sync with latest mach_override.
This commit is contained in:
parent
19577ae7b2
commit
96fd34d36e
@ -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__)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user