From 40e716984528e79d7fbed003e9fe5e9511d5b44a Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 11:42:42 -0700 Subject: [PATCH 1/8] build for 64-bit only --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9df28d9..3184118 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ STRIP ?= strip # Android SDK provides i386 executables, which will fail from dyld failures # from loading the libauthbind.dylib library if that library is only built # for e.g. the x86_64 arch. -ARCH=-arch i386 -arch x86_64 +ARCH=-arch x86_64 OSX_CFLAGS=-flat_namespace OSX_LDFLAGS=$(ARCH) -dynamiclib -dynamic -flat_namespace From eda7eb2a7c50f0cc2c085215992a4b0db49eb50d Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 11:43:14 -0700 Subject: [PATCH 2/8] import mach_override from https://github.com/rentzsch/mach_override --- mach_override.c | 700 ++++++++++++++++++++++++++++++++++++++++++++++++ mach_override.h | 76 ++++++ 2 files changed, 776 insertions(+) create mode 100644 mach_override.c create mode 100644 mach_override.h diff --git a/mach_override.c b/mach_override.c new file mode 100644 index 0000000..85a75e5 --- /dev/null +++ b/mach_override.c @@ -0,0 +1,700 @@ +// mach_override.c semver:1.2.0 +// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/mit +// https://github.com/rentzsch/mach_override + +#include "mach_override.h" +#if defined(__i386__) || defined(__x86_64__) +#include "udis86.h" +#endif + +#include +#include +#include +#include +#include + +#include + +/************************** +* +* Constants +* +**************************/ +#pragma mark - +#pragma mark (Constants) + +#if defined(__ppc__) || defined(__POWERPC__) + +long kIslandTemplate[] = { + 0x9001FFFC, // stw r0,-4(SP) + 0x3C00DEAD, // lis r0,0xDEAD + 0x6000BEEF, // ori r0,r0,0xBEEF + 0x7C0903A6, // mtctr r0 + 0x8001FFFC, // lwz r0,-4(SP) + 0x60000000, // nop ; optionally replaced + 0x4E800420 // bctr +}; + +#define kAddressHi 3 +#define kAddressLo 5 +#define kInstructionHi 10 +#define kInstructionLo 11 + +#elif defined(__i386__) + +#define kOriginalInstructionsSize 16 + +char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xE9, 0xEF, 0xBE, 0xAD, 0xDE +}; + +#define kInstructions 0 +#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 +#elif defined(__x86_64__) + +#define kOriginalInstructionsSize 32 + +#define kJumpAddress kOriginalInstructionsSize + 6 + +char kIslandTemplate[] = { + // kOriginalInstructionsSize nop instructions so that we + // should have enough space to host original instructions + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + // Now the real jump instruction + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +#endif + +#define kAllocateHigh 1 +#define kAllocateNormal 0 + +/************************** +* +* Data Types +* +**************************/ +#pragma mark - +#pragma mark (Data Types) + +typedef struct { + char instructions[sizeof(kIslandTemplate)]; + int allocatedHigh; +} BranchIsland; + +/************************** +* +* Funky Protos +* +**************************/ +#pragma mark - +#pragma mark (Funky Protos) + + mach_error_t +allocateBranchIsland( + BranchIsland **island, + int allocateHigh, + void *originalFunctionAddress); + + mach_error_t +freeBranchIsland( + BranchIsland *island ); + +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ); +#endif + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ); +void +atomic_mov64( + uint64_t *targetAddress, + uint64_t value ); + + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ); + + static void +fixupInstructions( + void *originalFunction, + void *escapeIsland, + void *instructionsToFix, + int instructionCount, + uint8_t *instructionSizes ); +#endif + +/******************************************************************************* +* +* Interface +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Interface) + +#if defined(__i386__) || defined(__x86_64__) +mach_error_t makeIslandExecutable(void *address) { + mach_error_t err = err_none; + uintptr_t page = (uintptr_t)address & ~(uintptr_t)(PAGE_SIZE - 1); + int e = err_none; + e |= mprotect((void *)page, PAGE_SIZE, PROT_EXEC | PROT_READ); + e |= msync((void *)page, PAGE_SIZE, MS_INVALIDATE ); + if (e) { + err = err_cannot_override; + } + return err; +} +#endif + + 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 > 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, kAllocateHigh, 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, kAllocateHigh, 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 ) { + 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); + mach_error_t prot_err = err_none; + prot_err = vm_protect( mach_task_self(), + (vm_address_t) originalFunctionPtr, 8, false, + (VM_PROT_READ | VM_PROT_EXECUTE) ); + if(prot_err) fprintf(stderr, "err = %x %s:%d\n", prot_err, __FILE__, __LINE__); + } +#endif + + // Clean up on error. + if( err ) { + if( reentryIsland ) + freeBranchIsland( reentryIsland ); + if( escapeIsland ) + freeBranchIsland( escapeIsland ); + } + + return err; +} + +/******************************************************************************* +* +* Implementation +* +*******************************************************************************/ +#pragma mark - +#pragma mark (Implementation) + +/******************************************************************************* + 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 + + ***************************************************************************/ + + mach_error_t +allocateBranchIsland( + BranchIsland **island, + int allocateHigh, + void *originalFunctionAddress) +{ + assert( island ); + + mach_error_t err = err_none; + + if( allocateHigh ) { + assert( sizeof( BranchIsland ) <= PAGE_SIZE ); + vm_address_t page = 0; +#if defined(__i386__) + err = vm_allocate( mach_task_self(), &page, PAGE_SIZE, VM_FLAGS_ANYWHERE ); + if( err == err_none ) + *island = (BranchIsland*) page; +#else + +#if defined(__ppc__) || defined(__POWERPC__) + vm_address_t first = 0xfeffffff; + vm_address_t last = 0xfe000000 + PAGE_SIZE; +#elif defined(__x86_64__) + // 64-bit ASLR is in bits 13-28 + vm_address_t first = ((uint64_t)originalFunctionAddress & ~( (0xFUL << 28) | (PAGE_SIZE - 1) ) ) | (0x1UL << 31); + vm_address_t last = (uint64_t)originalFunctionAddress & ~((0x1UL << 32) - 1); +#endif + + page = first; + int allocated = 0; + vm_map_t task_self = mach_task_self(); + + while( !err && !allocated && page != last ) { + + err = vm_allocate( task_self, &page, PAGE_SIZE, 0 ); + if( err == err_none ) + allocated = 1; + else if( err == KERN_NO_SPACE ) { +#if defined(__x86_64__) + page -= PAGE_SIZE; +#else + page += PAGE_SIZE; +#endif + err = err_none; + } + } + if( allocated ) + *island = (BranchIsland*) page; + else if( !allocated && !err ) + err = KERN_NO_SPACE; +#endif + } else { + void *block = malloc( sizeof( BranchIsland ) ); + if( block ) + *island = block; + else + err = KERN_NO_SPACE; + } + if( !err ) + (**island).allocatedHigh = allocateHigh; + + return err; +} + +/******************************************************************************* + Implementation: Deallocates memory for a branch island. + + @param island -> The island to deallocate. + @result <- mach_error_t + + ***************************************************************************/ + + mach_error_t +freeBranchIsland( + BranchIsland *island ) +{ + assert( island ); + assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); + assert( island->allocatedHigh ); + + mach_error_t err = err_none; + + if( island->allocatedHigh ) { + assert( sizeof( BranchIsland ) <= PAGE_SIZE ); + err = vm_deallocate(mach_task_self(), (vm_address_t) island, PAGE_SIZE ); + } else { + free( island ); + } + + return err; +} + +/******************************************************************************* + Implementation: Sets the branch island's target, with an optional + instruction. + + @param island -> The branch island to insert target into. + @param branchTo -> The address of the target. + @param instruction -> Optional instruction to execute prior to branch. Set + to zero for nop. + @result <- mach_error_t + + ***************************************************************************/ +#if defined(__ppc__) || defined(__POWERPC__) + mach_error_t +setBranchIslandTarget( + BranchIsland *island, + const void *branchTo, + long instruction ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Fill in the address. + ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; + ((short*)island->instructions)[kAddressHi] + = (((long) branchTo) >> 16) & 0x0000FFFF; + + // Fill in the (optional) instuction. + if( instruction != 0 ) { + ((short*)island->instructions)[kInstructionLo] + = instruction & 0x0000FFFF; + ((short*)island->instructions)[kInstructionHi] + = (instruction >> 16) & 0x0000FFFF; + } + + //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + +#if defined(__i386__) + mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // copy original instructions + if (instructions) { + bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); + } + + // Fill in the address. + int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); + *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; + + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + return err_none; +} + +#elif defined(__x86_64__) +mach_error_t +setBranchIslandTarget_i386( + BranchIsland *island, + const void *branchTo, + char* instructions ) +{ + // Copy over the template code. + bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); + + // Copy original instructions. + if (instructions) { + bcopy (instructions, island->instructions, kOriginalInstructionsSize); + } + + // Fill in the address. + *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; + msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); + + return err_none; +} +#endif + + +#if defined(__i386__) || defined(__x86_64__) + static Boolean +eatKnownInstructions( + unsigned char *code, + uint64_t *newInstruction, + int *howManyEaten, + char *originalInstructions, + int *originalInstructionCount, + uint8_t *originalInstructionSizes ) +{ + Boolean allInstructionsKnown = true; + int totalEaten = 0; + int remainsToEat = 5; // a JMP instruction takes 5 bytes + int instructionIndex = 0; + ud_t ud_obj; + + if (howManyEaten) *howManyEaten = 0; + if (originalInstructionCount) *originalInstructionCount = 0; + ud_init(&ud_obj); +#if defined(__i386__) + ud_set_mode(&ud_obj, 32); +#else + ud_set_mode(&ud_obj, 64); +#endif + ud_set_input_buffer(&ud_obj, code, 64); // Assume that 'code' points to at least 64bytes of data. + while (remainsToEat > 0) { + if (!ud_disassemble(&ud_obj)) { + allInstructionsKnown = false; + fprintf(stderr, "mach_override: some instructions unknown! Need to update libudis86\n"); + break; + } + + // At this point, we've matched curInstr + int eaten = ud_insn_len(&ud_obj); + remainsToEat -= eaten; + totalEaten += eaten; + + if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; + instructionIndex += 1; + if (originalInstructionCount) *originalInstructionCount = instructionIndex; + } + + + if (howManyEaten) *howManyEaten = totalEaten; + + if (originalInstructions) { + Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); + + if (enoughSpaceForOriginalInstructions) { + memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP + bcopy(code, originalInstructions, totalEaten); + } else { + // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); + return false; + } + } + + if (allInstructionsKnown) { + // save last 3 bytes of first 64bits of codre we'll replace + uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); + currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation + currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; + + // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr + *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes + *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes + } + + 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]); + } +} + +#if defined(__i386__) +__asm( + ".text;" + ".align 2, 0x90;" + "_atomic_mov64:;" + " pushl %ebp;" + " movl %esp, %ebp;" + " pushl %esi;" + " pushl %ebx;" + " pushl %ecx;" + " pushl %eax;" + " pushl %edx;" + + // atomic push of value to an address + // we use cmpxchg8b, which compares content of an address with + // edx:eax. If they are equal, it atomically puts 64bit value + // ecx:ebx in address. + // We thus put contents of address in edx:eax to force ecx:ebx + // in address + " mov 8(%ebp), %esi;" // esi contains target address + " mov 12(%ebp), %ebx;" + " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address + " mov (%esi), %eax;" + " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address + " lock; cmpxchg8b (%esi);" // atomic move. + + // restore registers + " popl %edx;" + " popl %eax;" + " popl %ecx;" + " popl %ebx;" + " popl %esi;" + " popl %ebp;" + " ret" +); +#elif defined(__x86_64__) +void atomic_mov64( + uint64_t *targetAddress, + uint64_t value ) +{ + *targetAddress = value; +} +#endif +#endif diff --git a/mach_override.h b/mach_override.h new file mode 100644 index 0000000..ecd319c --- /dev/null +++ b/mach_override.h @@ -0,0 +1,76 @@ +// mach_override.h semver:1.2.0 +// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/mit +// https://github.com/rentzsch/mach_override + +#ifndef _mach_override_ +#define _mach_override_ + +#include +#include + +#define err_cannot_override (err_local|1) + +__BEGIN_DECLS + +/**************************************************************************************** + Dynamically overrides the function implementation referenced by + originalFunctionAddress with the implentation pointed to by overrideFunctionAddress. + Optionally returns a pointer to a "reentry island" which, if jumped to, will resume + the original implementation. + + @param originalFunctionAddress -> Required address of the function to + override (with overrideFunctionAddress). + @param overrideFunctionAddress -> Required address to the overriding + function. + @param originalFunctionReentryIsland <- Optional pointer to pointer to the + reentry island. Can be NULL. + @result <- err_cannot_override if the original + function's implementation begins with + the 'mfctr' instruction. + + ************************************************************************************/ + + mach_error_t +mach_override_ptr( + void *originalFunctionAddress, + const void *overrideFunctionAddress, + void **originalFunctionReentryIsland ); + +__END_DECLS + +/**************************************************************************************** + If you're using C++ this macro will ease the tedium of typedef'ing, naming, keeping + track of reentry islands and defining your override code. See test_mach_override.cp + for example usage. + + ************************************************************************************/ + +#ifdef __cplusplus +#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \ +{ \ + static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \ + static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \ + class mach_override_class__##ORIGINAL_FUNCTION_NAME { \ + public: \ + static kern_return_t override(void *originalFunctionPtr) { \ + kern_return_t result = err_none; \ + if (!ORIGINAL_FUNCTION_NAME##_overriden) { \ + ORIGINAL_FUNCTION_NAME##_overriden = true; \ + result = mach_override_ptr( (void*)originalFunctionPtr, \ + (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \ + (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \ + } \ + return result; \ + } \ + static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS { + +#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \ + } \ + }; \ + \ + err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \ +} +#endif + +#endif // _mach_override_ From de3d9871e3e549b123f5d02cfda60b1113c848e9 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 11:45:02 -0700 Subject: [PATCH 3/8] compile against mach_override for 10.11 --- Makefile | 6 +++--- libauthbind.c | 29 +++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 3184118..d2bdb4b 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ OSX_LDFLAGS=$(ARCH) -dynamiclib -dynamic -flat_namespace OPTIMISE=-O2 LDFLAGS=$(OSX_LDFLAGS) -LIBS=-ldl +LIBS=-ldl -ludis86 CFLAGS=$(ARCH) -g $(OPTIMISE) -D_REENTRANT \ -Wall -Wwrite-strings -Wpointer-arith -Wimplicit -Wnested-externs \ -Wmissing-prototypes -Wstrict-prototypes $(OSX_CFLAGS) @@ -106,8 +106,8 @@ authbind: authbind.o helper: helper.o $(CC) -o $@ $< -$(LIBRARY): - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@$(LIBEXT) $@.c $(LIBS) +$(LIBRARY): mach_override.o + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@$(LIBEXT) $@.c mach_override.o $(LIBS) clean distclean: $(RM) $(TARGETS) *.o *~ ./#*# *.bak *.new core diff --git a/libauthbind.c b/libauthbind.c index 704470a..ad33b44 100644 --- a/libauthbind.c +++ b/libauthbind.c @@ -34,6 +34,8 @@ static const char *rcsid="$Id: libauthbind.c,v 1.8 2004-04-02 18:54:27 ian Exp $"; #include "authbind.h" +#include "mach_override.h" +#include "mach-o/dyld.h" typedef void anyfn_type(void); typedef int bindfn_type(int fd, const struct sockaddr *addr, socklen_t addrlen); @@ -143,8 +145,24 @@ static void removepreload(void) { return; } -int _init(void); -int _init(void) { +long *orig_bind_ptr; +void (*orig_bind)() = 0; +int mybind(int fd, const struct sockaddr *addr, socklen_t addrlen); + +void my_init(void) __attribute__ ((constructor)); +void my_init(void) { + _dyld_lookup_and_bind( + "_bind", + (void**) &orig_bind_ptr, + NULL); + + orig_bind = (void (*)())orig_bind_ptr; + + mach_override_ptr( + orig_bind_ptr, + (void*)&mybind, + (void**)&orig_bind); + char *levels; int levelno; @@ -157,23 +175,22 @@ int _init(void) { */ levels= getenv(AUTHBIND_LEVELS_VAR); if (levels) { - if (levels[0]=='y') return 0; + if (levels[0]=='y') return; levelno= atoi(levels); if (levelno > 0) { levelno--; if (levelno > 0) sprintf(levels,"%d",levelno); else unsetenv(AUTHBIND_LEVELS_VAR); - return 0; + return; } unsetenv(AUTHBIND_LEVELS_VAR); } removepreload(); - return 0; } static const int evilsignals[]= { SIGFPE, SIGILL, SIGSEGV, SIGBUS, 0 }; -int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { +int mybind(int fd, const struct sockaddr *addr, socklen_t addrlen) { pid_t child, rchild; char portarg[5], addrarg[9]; int r, status; From 2a15d429b83826295fd9773774ad729c4ccf49df Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 11:59:29 -0700 Subject: [PATCH 4/8] use dlsym instead of deprecated _dyld_lookup_and_bind --- libauthbind.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/libauthbind.c b/libauthbind.c index ad33b44..ee51c4c 100644 --- a/libauthbind.c +++ b/libauthbind.c @@ -34,8 +34,9 @@ static const char *rcsid="$Id: libauthbind.c,v 1.8 2004-04-02 18:54:27 ian Exp $"; #include "authbind.h" +#ifdef __APPLE__ #include "mach_override.h" -#include "mach-o/dyld.h" +#endif typedef void anyfn_type(void); typedef int bindfn_type(int fd, const struct sockaddr *addr, socklen_t addrlen); @@ -145,23 +146,20 @@ static void removepreload(void) { return; } -long *orig_bind_ptr; -void (*orig_bind)() = 0; int mybind(int fd, const struct sockaddr *addr, socklen_t addrlen); void my_init(void) __attribute__ ((constructor)); void my_init(void) { - _dyld_lookup_and_bind( - "_bind", - (void**) &orig_bind_ptr, - NULL); - - orig_bind = (void (*)())orig_bind_ptr; +#ifdef __APPLE__ + anyfn_type *anyfn; + anyfn = find_any("bind"); + old_bind = (bindfn_type *) anyfn; mach_override_ptr( - orig_bind_ptr, + anyfn, (void*)&mybind, - (void**)&orig_bind); + (void**)&old_bind); +#endif char *levels; int levelno; From 5fe088fb95cd3d0d078043e31b8e9e7f513128b0 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 12:01:40 -0700 Subject: [PATCH 5/8] remove compiler warning --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d2bdb4b..a1c35fe 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ STRIP ?= strip # from loading the libauthbind.dylib library if that library is only built # for e.g. the x86_64 arch. ARCH=-arch x86_64 -OSX_CFLAGS=-flat_namespace +OSX_CFLAGS= OSX_LDFLAGS=$(ARCH) -dynamiclib -dynamic -flat_namespace OPTIMISE=-O2 From e4f0f28946ff431774c79459198c8012182e2dd7 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 12:27:48 -0700 Subject: [PATCH 6/8] make sure helper is setuid --- helper.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/helper.c b/helper.c index c4c3d16..2ab2f14 100644 --- a/helper.c +++ b/helper.c @@ -63,7 +63,7 @@ static void authorised(void) { } int main(int argc, const char *const *argv) { - uid_t uid; + uid_t uid, euid; char fnbuf[100]; char *ep; const char *np; @@ -85,6 +85,18 @@ int main(int argc, const char *const *argv) { perrorfail("chdir " CONFIGDIR); } + euid = geteuid(); + if (euid == (uid_t)-1) { + perrorfail("geteuid"); + } + + if (euid != (uid_t)0) { + fprintf(stderr, "Error: libauthbind's helper needs root privileges. Please run:\n"); + fprintf(stderr, " $ chmod u+s %s\n", argv[0]); + fprintf(stderr, " $ sudo chown root %s\n\n", argv[0]); + exiterrno(EPERM); + } + fnbuf[sizeof(fnbuf)-1] = 0; memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; From 47db8be2c2d238351249d394e3ef4c123870b630 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 13:03:46 -0700 Subject: [PATCH 7/8] unnecessary prefix --- helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper.c b/helper.c index 2ab2f14..948d048 100644 --- a/helper.c +++ b/helper.c @@ -55,7 +55,7 @@ static struct sockaddr_in saddr; static void authorised(void) { if (bind(0, (struct sockaddr *) &saddr, sizeof(saddr))) { - perrorfail("libauthbind's helper: bind() failed"); + perrorfail("bind() failed"); } else { _exit(0); From e1b0017e4c20d71106bd0e16d275e31148af6f83 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Tue, 29 Sep 2015 13:12:20 -0700 Subject: [PATCH 8/8] remove mach_override dependency --- Makefile | 8 +- libauthbind.c | 27 +- mach_override.c | 700 ------------------------------------------------ mach_override.h | 76 ------ 4 files changed, 10 insertions(+), 801 deletions(-) delete mode 100644 mach_override.c delete mode 100644 mach_override.h diff --git a/Makefile b/Makefile index a1c35fe..9ac6daa 100644 --- a/Makefile +++ b/Makefile @@ -48,13 +48,13 @@ STRIP ?= strip # Android SDK provides i386 executables, which will fail from dyld failures # from loading the libauthbind.dylib library if that library is only built # for e.g. the x86_64 arch. -ARCH=-arch x86_64 +ARCH=-arch i386 -arch x86_64 OSX_CFLAGS= OSX_LDFLAGS=$(ARCH) -dynamiclib -dynamic -flat_namespace OPTIMISE=-O2 LDFLAGS=$(OSX_LDFLAGS) -LIBS=-ldl -ludis86 +LIBS=-ldl CFLAGS=$(ARCH) -g $(OPTIMISE) -D_REENTRANT \ -Wall -Wwrite-strings -Wpointer-arith -Wimplicit -Wnested-externs \ -Wmissing-prototypes -Wstrict-prototypes $(OSX_CFLAGS) @@ -106,8 +106,8 @@ authbind: authbind.o helper: helper.o $(CC) -o $@ $< -$(LIBRARY): mach_override.o - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@$(LIBEXT) $@.c mach_override.o $(LIBS) +$(LIBRARY): + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@$(LIBEXT) $@.c $(LIBS) clean distclean: $(RM) $(TARGETS) *.o *~ ./#*# *.bak *.new core diff --git a/libauthbind.c b/libauthbind.c index ee51c4c..704470a 100644 --- a/libauthbind.c +++ b/libauthbind.c @@ -34,9 +34,6 @@ static const char *rcsid="$Id: libauthbind.c,v 1.8 2004-04-02 18:54:27 ian Exp $"; #include "authbind.h" -#ifdef __APPLE__ -#include "mach_override.h" -#endif typedef void anyfn_type(void); typedef int bindfn_type(int fd, const struct sockaddr *addr, socklen_t addrlen); @@ -146,21 +143,8 @@ static void removepreload(void) { return; } -int mybind(int fd, const struct sockaddr *addr, socklen_t addrlen); - -void my_init(void) __attribute__ ((constructor)); -void my_init(void) { -#ifdef __APPLE__ - anyfn_type *anyfn; - anyfn = find_any("bind"); - old_bind = (bindfn_type *) anyfn; - - mach_override_ptr( - anyfn, - (void*)&mybind, - (void**)&old_bind); -#endif - +int _init(void); +int _init(void) { char *levels; int levelno; @@ -173,22 +157,23 @@ void my_init(void) { */ levels= getenv(AUTHBIND_LEVELS_VAR); if (levels) { - if (levels[0]=='y') return; + if (levels[0]=='y') return 0; levelno= atoi(levels); if (levelno > 0) { levelno--; if (levelno > 0) sprintf(levels,"%d",levelno); else unsetenv(AUTHBIND_LEVELS_VAR); - return; + return 0; } unsetenv(AUTHBIND_LEVELS_VAR); } removepreload(); + return 0; } static const int evilsignals[]= { SIGFPE, SIGILL, SIGSEGV, SIGBUS, 0 }; -int mybind(int fd, const struct sockaddr *addr, socklen_t addrlen) { +int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { pid_t child, rchild; char portarg[5], addrarg[9]; int r, status; diff --git a/mach_override.c b/mach_override.c deleted file mode 100644 index 85a75e5..0000000 --- a/mach_override.c +++ /dev/null @@ -1,700 +0,0 @@ -// mach_override.c semver:1.2.0 -// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com -// Some rights reserved: http://opensource.org/licenses/mit -// https://github.com/rentzsch/mach_override - -#include "mach_override.h" -#if defined(__i386__) || defined(__x86_64__) -#include "udis86.h" -#endif - -#include -#include -#include -#include -#include - -#include - -/************************** -* -* Constants -* -**************************/ -#pragma mark - -#pragma mark (Constants) - -#if defined(__ppc__) || defined(__POWERPC__) - -long kIslandTemplate[] = { - 0x9001FFFC, // stw r0,-4(SP) - 0x3C00DEAD, // lis r0,0xDEAD - 0x6000BEEF, // ori r0,r0,0xBEEF - 0x7C0903A6, // mtctr r0 - 0x8001FFFC, // lwz r0,-4(SP) - 0x60000000, // nop ; optionally replaced - 0x4E800420 // bctr -}; - -#define kAddressHi 3 -#define kAddressLo 5 -#define kInstructionHi 10 -#define kInstructionLo 11 - -#elif defined(__i386__) - -#define kOriginalInstructionsSize 16 - -char kIslandTemplate[] = { - // kOriginalInstructionsSize nop instructions so that we - // should have enough space to host original instructions - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - // Now the real jump instruction - 0xE9, 0xEF, 0xBE, 0xAD, 0xDE -}; - -#define kInstructions 0 -#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 -#elif defined(__x86_64__) - -#define kOriginalInstructionsSize 32 - -#define kJumpAddress kOriginalInstructionsSize + 6 - -char kIslandTemplate[] = { - // kOriginalInstructionsSize nop instructions so that we - // should have enough space to host original instructions - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - // Now the real jump instruction - 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; - -#endif - -#define kAllocateHigh 1 -#define kAllocateNormal 0 - -/************************** -* -* Data Types -* -**************************/ -#pragma mark - -#pragma mark (Data Types) - -typedef struct { - char instructions[sizeof(kIslandTemplate)]; - int allocatedHigh; -} BranchIsland; - -/************************** -* -* Funky Protos -* -**************************/ -#pragma mark - -#pragma mark (Funky Protos) - - mach_error_t -allocateBranchIsland( - BranchIsland **island, - int allocateHigh, - void *originalFunctionAddress); - - mach_error_t -freeBranchIsland( - BranchIsland *island ); - -#if defined(__ppc__) || defined(__POWERPC__) - mach_error_t -setBranchIslandTarget( - BranchIsland *island, - const void *branchTo, - long instruction ); -#endif - -#if defined(__i386__) || defined(__x86_64__) -mach_error_t -setBranchIslandTarget_i386( - BranchIsland *island, - const void *branchTo, - char* instructions ); -void -atomic_mov64( - uint64_t *targetAddress, - uint64_t value ); - - static Boolean -eatKnownInstructions( - unsigned char *code, - uint64_t *newInstruction, - int *howManyEaten, - char *originalInstructions, - int *originalInstructionCount, - uint8_t *originalInstructionSizes ); - - static void -fixupInstructions( - void *originalFunction, - void *escapeIsland, - void *instructionsToFix, - int instructionCount, - uint8_t *instructionSizes ); -#endif - -/******************************************************************************* -* -* Interface -* -*******************************************************************************/ -#pragma mark - -#pragma mark (Interface) - -#if defined(__i386__) || defined(__x86_64__) -mach_error_t makeIslandExecutable(void *address) { - mach_error_t err = err_none; - uintptr_t page = (uintptr_t)address & ~(uintptr_t)(PAGE_SIZE - 1); - int e = err_none; - e |= mprotect((void *)page, PAGE_SIZE, PROT_EXEC | PROT_READ); - e |= msync((void *)page, PAGE_SIZE, MS_INVALIDATE ); - if (e) { - err = err_cannot_override; - } - return err; -} -#endif - - 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 > 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, kAllocateHigh, 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, kAllocateHigh, 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 ) { - 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); - mach_error_t prot_err = err_none; - prot_err = vm_protect( mach_task_self(), - (vm_address_t) originalFunctionPtr, 8, false, - (VM_PROT_READ | VM_PROT_EXECUTE) ); - if(prot_err) fprintf(stderr, "err = %x %s:%d\n", prot_err, __FILE__, __LINE__); - } -#endif - - // Clean up on error. - if( err ) { - if( reentryIsland ) - freeBranchIsland( reentryIsland ); - if( escapeIsland ) - freeBranchIsland( escapeIsland ); - } - - return err; -} - -/******************************************************************************* -* -* Implementation -* -*******************************************************************************/ -#pragma mark - -#pragma mark (Implementation) - -/******************************************************************************* - 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 - - ***************************************************************************/ - - mach_error_t -allocateBranchIsland( - BranchIsland **island, - int allocateHigh, - void *originalFunctionAddress) -{ - assert( island ); - - mach_error_t err = err_none; - - if( allocateHigh ) { - assert( sizeof( BranchIsland ) <= PAGE_SIZE ); - vm_address_t page = 0; -#if defined(__i386__) - err = vm_allocate( mach_task_self(), &page, PAGE_SIZE, VM_FLAGS_ANYWHERE ); - if( err == err_none ) - *island = (BranchIsland*) page; -#else - -#if defined(__ppc__) || defined(__POWERPC__) - vm_address_t first = 0xfeffffff; - vm_address_t last = 0xfe000000 + PAGE_SIZE; -#elif defined(__x86_64__) - // 64-bit ASLR is in bits 13-28 - vm_address_t first = ((uint64_t)originalFunctionAddress & ~( (0xFUL << 28) | (PAGE_SIZE - 1) ) ) | (0x1UL << 31); - vm_address_t last = (uint64_t)originalFunctionAddress & ~((0x1UL << 32) - 1); -#endif - - page = first; - int allocated = 0; - vm_map_t task_self = mach_task_self(); - - while( !err && !allocated && page != last ) { - - err = vm_allocate( task_self, &page, PAGE_SIZE, 0 ); - if( err == err_none ) - allocated = 1; - else if( err == KERN_NO_SPACE ) { -#if defined(__x86_64__) - page -= PAGE_SIZE; -#else - page += PAGE_SIZE; -#endif - err = err_none; - } - } - if( allocated ) - *island = (BranchIsland*) page; - else if( !allocated && !err ) - err = KERN_NO_SPACE; -#endif - } else { - void *block = malloc( sizeof( BranchIsland ) ); - if( block ) - *island = block; - else - err = KERN_NO_SPACE; - } - if( !err ) - (**island).allocatedHigh = allocateHigh; - - return err; -} - -/******************************************************************************* - Implementation: Deallocates memory for a branch island. - - @param island -> The island to deallocate. - @result <- mach_error_t - - ***************************************************************************/ - - mach_error_t -freeBranchIsland( - BranchIsland *island ) -{ - assert( island ); - assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); - assert( island->allocatedHigh ); - - mach_error_t err = err_none; - - if( island->allocatedHigh ) { - assert( sizeof( BranchIsland ) <= PAGE_SIZE ); - err = vm_deallocate(mach_task_self(), (vm_address_t) island, PAGE_SIZE ); - } else { - free( island ); - } - - return err; -} - -/******************************************************************************* - Implementation: Sets the branch island's target, with an optional - instruction. - - @param island -> The branch island to insert target into. - @param branchTo -> The address of the target. - @param instruction -> Optional instruction to execute prior to branch. Set - to zero for nop. - @result <- mach_error_t - - ***************************************************************************/ -#if defined(__ppc__) || defined(__POWERPC__) - mach_error_t -setBranchIslandTarget( - BranchIsland *island, - const void *branchTo, - long instruction ) -{ - // Copy over the template code. - bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); - - // Fill in the address. - ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; - ((short*)island->instructions)[kAddressHi] - = (((long) branchTo) >> 16) & 0x0000FFFF; - - // Fill in the (optional) instuction. - if( instruction != 0 ) { - ((short*)island->instructions)[kInstructionLo] - = instruction & 0x0000FFFF; - ((short*)island->instructions)[kInstructionHi] - = (instruction >> 16) & 0x0000FFFF; - } - - //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); - msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); - - return err_none; -} -#endif - -#if defined(__i386__) - mach_error_t -setBranchIslandTarget_i386( - BranchIsland *island, - const void *branchTo, - char* instructions ) -{ - - // Copy over the template code. - bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); - - // copy original instructions - if (instructions) { - bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); - } - - // Fill in the address. - int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); - *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; - - msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); - return err_none; -} - -#elif defined(__x86_64__) -mach_error_t -setBranchIslandTarget_i386( - BranchIsland *island, - const void *branchTo, - char* instructions ) -{ - // Copy over the template code. - bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); - - // Copy original instructions. - if (instructions) { - bcopy (instructions, island->instructions, kOriginalInstructionsSize); - } - - // Fill in the address. - *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; - msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); - - return err_none; -} -#endif - - -#if defined(__i386__) || defined(__x86_64__) - static Boolean -eatKnownInstructions( - unsigned char *code, - uint64_t *newInstruction, - int *howManyEaten, - char *originalInstructions, - int *originalInstructionCount, - uint8_t *originalInstructionSizes ) -{ - Boolean allInstructionsKnown = true; - int totalEaten = 0; - int remainsToEat = 5; // a JMP instruction takes 5 bytes - int instructionIndex = 0; - ud_t ud_obj; - - if (howManyEaten) *howManyEaten = 0; - if (originalInstructionCount) *originalInstructionCount = 0; - ud_init(&ud_obj); -#if defined(__i386__) - ud_set_mode(&ud_obj, 32); -#else - ud_set_mode(&ud_obj, 64); -#endif - ud_set_input_buffer(&ud_obj, code, 64); // Assume that 'code' points to at least 64bytes of data. - while (remainsToEat > 0) { - if (!ud_disassemble(&ud_obj)) { - allInstructionsKnown = false; - fprintf(stderr, "mach_override: some instructions unknown! Need to update libudis86\n"); - break; - } - - // At this point, we've matched curInstr - int eaten = ud_insn_len(&ud_obj); - remainsToEat -= eaten; - totalEaten += eaten; - - if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; - instructionIndex += 1; - if (originalInstructionCount) *originalInstructionCount = instructionIndex; - } - - - if (howManyEaten) *howManyEaten = totalEaten; - - if (originalInstructions) { - Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); - - if (enoughSpaceForOriginalInstructions) { - memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP - bcopy(code, originalInstructions, totalEaten); - } else { - // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); - return false; - } - } - - if (allInstructionsKnown) { - // save last 3 bytes of first 64bits of codre we'll replace - uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); - currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation - currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; - - // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr - *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes - *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes - } - - 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]); - } -} - -#if defined(__i386__) -__asm( - ".text;" - ".align 2, 0x90;" - "_atomic_mov64:;" - " pushl %ebp;" - " movl %esp, %ebp;" - " pushl %esi;" - " pushl %ebx;" - " pushl %ecx;" - " pushl %eax;" - " pushl %edx;" - - // atomic push of value to an address - // we use cmpxchg8b, which compares content of an address with - // edx:eax. If they are equal, it atomically puts 64bit value - // ecx:ebx in address. - // We thus put contents of address in edx:eax to force ecx:ebx - // in address - " mov 8(%ebp), %esi;" // esi contains target address - " mov 12(%ebp), %ebx;" - " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address - " mov (%esi), %eax;" - " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address - " lock; cmpxchg8b (%esi);" // atomic move. - - // restore registers - " popl %edx;" - " popl %eax;" - " popl %ecx;" - " popl %ebx;" - " popl %esi;" - " popl %ebp;" - " ret" -); -#elif defined(__x86_64__) -void atomic_mov64( - uint64_t *targetAddress, - uint64_t value ) -{ - *targetAddress = value; -} -#endif -#endif diff --git a/mach_override.h b/mach_override.h deleted file mode 100644 index ecd319c..0000000 --- a/mach_override.h +++ /dev/null @@ -1,76 +0,0 @@ -// mach_override.h semver:1.2.0 -// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com -// Some rights reserved: http://opensource.org/licenses/mit -// https://github.com/rentzsch/mach_override - -#ifndef _mach_override_ -#define _mach_override_ - -#include -#include - -#define err_cannot_override (err_local|1) - -__BEGIN_DECLS - -/**************************************************************************************** - Dynamically overrides the function implementation referenced by - originalFunctionAddress with the implentation pointed to by overrideFunctionAddress. - Optionally returns a pointer to a "reentry island" which, if jumped to, will resume - the original implementation. - - @param originalFunctionAddress -> Required address of the function to - override (with overrideFunctionAddress). - @param overrideFunctionAddress -> Required address to the overriding - function. - @param originalFunctionReentryIsland <- Optional pointer to pointer to the - reentry island. Can be NULL. - @result <- err_cannot_override if the original - function's implementation begins with - the 'mfctr' instruction. - - ************************************************************************************/ - - mach_error_t -mach_override_ptr( - void *originalFunctionAddress, - const void *overrideFunctionAddress, - void **originalFunctionReentryIsland ); - -__END_DECLS - -/**************************************************************************************** - If you're using C++ this macro will ease the tedium of typedef'ing, naming, keeping - track of reentry islands and defining your override code. See test_mach_override.cp - for example usage. - - ************************************************************************************/ - -#ifdef __cplusplus -#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \ -{ \ - static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \ - static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \ - class mach_override_class__##ORIGINAL_FUNCTION_NAME { \ - public: \ - static kern_return_t override(void *originalFunctionPtr) { \ - kern_return_t result = err_none; \ - if (!ORIGINAL_FUNCTION_NAME##_overriden) { \ - ORIGINAL_FUNCTION_NAME##_overriden = true; \ - result = mach_override_ptr( (void*)originalFunctionPtr, \ - (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \ - (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \ - } \ - return result; \ - } \ - static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS { - -#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \ - } \ - }; \ - \ - err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \ -} -#endif - -#endif // _mach_override_