00001
00002
00003
00004
00005
00006
00007
00008 #include "mach_override.h"
00009
00010 #include <mach-o/dyld.h>
00011 #include <mach/mach_host.h>
00012 #include <mach/mach_init.h>
00013 #include <mach/vm_map.h>
00014 #include <sys/mman.h>
00015
00016 #include <CoreServices/CoreServices.h>
00017
00018
00019
00020
00021
00022
00023 #pragma mark -
00024 #pragma mark (Constants)
00025
00026 #if !defined(__ppc__) && !defined(__POWERPC__)
00027 #error "this code is currently PowerPC-only"
00028 #endif
00029
00030 long kIslandTemplate[] = {
00031 0x9001FFFC,
00032 0x3C00DEAD,
00033 0x6000BEEF,
00034 0x7C0903A6,
00035 0x8001FFFC,
00036 0x60000000,
00037 0x4E800420
00038 };
00039 #define kAddressHi 3
00040 #define kAddressLo 5
00041 #define kInstructionHi 10
00042 #define kInstructionLo 11
00043
00044 #define kAllocateHigh 1
00045 #define kAllocateNormal 0
00046
00047
00048
00049
00050
00051
00052 #pragma mark -
00053 #pragma mark (Data Types)
00054
00055 typedef struct {
00056 char instructions[sizeof(kIslandTemplate)];
00057 int allocatedHigh;
00058 } BranchIsland;
00059
00060
00061
00062
00063
00064
00065 #pragma mark -
00066 #pragma mark (Funky Protos)
00067
00068 mach_error_t
00069 allocateBranchIsland(
00070 BranchIsland **island,
00071 int allocateHigh );
00072
00073 mach_error_t
00074 freeBranchIsland(
00075 BranchIsland *island );
00076
00077 mach_error_t
00078 setBranchIslandTarget(
00079 BranchIsland *island,
00080 const void *branchTo,
00081 long instruction );
00082
00083
00084
00085
00086
00087
00088 #pragma mark -
00089 #pragma mark (Interface)
00090
00091 mach_error_t
00092 mach_override(
00093 char *originalFunctionSymbolName,
00094 const char *originalFunctionLibraryNameHint,
00095 const void *overrideFunctionAddress,
00096 void **originalFunctionReentryIsland )
00097 {
00098 assert( originalFunctionSymbolName );
00099 assert( strlen( originalFunctionSymbolName ) );
00100 assert( overrideFunctionAddress );
00101
00102 mach_error_t err = err_none;
00103
00104
00105 long *originalFunctionPtr;
00106 if( originalFunctionLibraryNameHint )
00107 _dyld_lookup_and_bind_with_hint(
00108 originalFunctionSymbolName,
00109 originalFunctionLibraryNameHint,
00110 (void*) &originalFunctionPtr,
00111 NULL );
00112 else
00113 _dyld_lookup_and_bind(
00114 originalFunctionSymbolName,
00115 (void*) &originalFunctionPtr,
00116 NULL );
00117
00118
00119 #define kMFCTRMask 0xfc1fffff
00120 #define kMFCTRInstruction 0x7c0903a6
00121
00122 long originalInstruction = *originalFunctionPtr;
00123 if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
00124 err = err_cannot_override;
00125
00126
00127 if( !err ) {
00128 err = vm_protect( mach_task_self(),
00129 (vm_address_t) originalFunctionPtr,
00130 sizeof(long), false, (VM_PROT_ALL | VM_PROT_COPY) );
00131 if( err )
00132 err = vm_protect( mach_task_self(),
00133 (vm_address_t) originalFunctionPtr, sizeof(long), false,
00134 (VM_PROT_DEFAULT | VM_PROT_COPY) );
00135 }
00136
00137
00138 BranchIsland *escapeIsland = NULL;
00139 if( !err )
00140 err = allocateBranchIsland( &escapeIsland, kAllocateHigh );
00141 if( !err )
00142 err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
00143
00144
00145 long branchAbsoluteInstruction = 0;
00146 if( !err ) {
00147 long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
00148 branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
00149 }
00150
00151
00152 BranchIsland *reentryIsland = NULL;
00153 if( !err && originalFunctionReentryIsland ) {
00154 err = allocateBranchIsland( &reentryIsland, kAllocateNormal );
00155 if( !err )
00156 *originalFunctionReentryIsland = reentryIsland;
00157 }
00158
00159
00160
00161
00162
00163
00164
00165 if( !err ) {
00166 int escapeIslandEngaged = false;
00167 do {
00168 if( reentryIsland )
00169 err = setBranchIslandTarget( reentryIsland,
00170 (void*) (originalFunctionPtr+1), originalInstruction );
00171 if( !err ) {
00172 escapeIslandEngaged = CompareAndSwap( originalInstruction,
00173 branchAbsoluteInstruction,
00174 (UInt32*)originalFunctionPtr );
00175 if( !escapeIslandEngaged ) {
00176
00177
00178
00179 originalInstruction = *originalFunctionPtr;
00180 if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
00181 err = err_cannot_override;
00182 }
00183 }
00184 } while( !err && !escapeIslandEngaged );
00185 }
00186
00187
00188 if( err ) {
00189 if( reentryIsland )
00190 freeBranchIsland( reentryIsland );
00191 if( escapeIsland )
00192 freeBranchIsland( escapeIsland );
00193 }
00194
00195 return err;
00196 }
00197
00198
00199
00200
00201
00202
00203 #pragma mark -
00204 #pragma mark (Implementation)
00205
00206
00217 mach_error_t
00218 allocateBranchIsland(
00219 BranchIsland **island,
00220 int allocateHigh )
00221 {
00222 assert( island );
00223
00224 mach_error_t err = err_none;
00225
00226 if( allocateHigh ) {
00227 vm_size_t pageSize;
00228 err = host_page_size( mach_host_self(), &pageSize );
00229 if( !err ) {
00230 assert( sizeof( BranchIsland ) <= pageSize );
00231 vm_address_t first = 0xfeffffff;
00232 vm_address_t last = 0xfe000000 + pageSize;
00233 vm_address_t page = first;
00234 int allocated = 0;
00235 vm_map_t task_self = mach_task_self();
00236
00237 while( !err && !allocated && page != last ) {
00238 err = vm_allocate( task_self, &page, pageSize, 0 );
00239 if( err == err_none )
00240 allocated = 1;
00241 else if( err == KERN_NO_SPACE ) {
00242 page += pageSize;
00243 err = err_none;
00244 }
00245 }
00246 if( allocated )
00247 *island = (void*) page;
00248 else if( !allocated && !err )
00249 err = KERN_NO_SPACE;
00250 }
00251 } else {
00252 void *block = malloc( sizeof( BranchIsland ) );
00253 if( block )
00254 *island = block;
00255 else
00256 err = KERN_NO_SPACE;
00257 }
00258 if( !err )
00259 (**island).allocatedHigh = allocateHigh;
00260
00261 return err;
00262 }
00263
00264
00272 mach_error_t
00273 freeBranchIsland(
00274 BranchIsland *island )
00275 {
00276 assert( island );
00277 assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
00278 assert( island->allocatedHigh );
00279
00280 mach_error_t err = err_none;
00281
00282 if( island->allocatedHigh ) {
00283 vm_size_t pageSize;
00284 err = host_page_size( mach_host_self(), &pageSize );
00285 if( !err ) {
00286 assert( sizeof( BranchIsland ) <= pageSize );
00287 err = vm_deallocate(
00288 mach_task_self(),
00289 (vm_address_t) island, pageSize );
00290 }
00291 } else {
00292 free( island );
00293 }
00294
00295 return err;
00296 }
00297
00298
00310 mach_error_t
00311 setBranchIslandTarget(
00312 BranchIsland *island,
00313 const void *branchTo,
00314 long instruction )
00315 {
00316
00317 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
00318
00319
00320 ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
00321 ((short*)island->instructions)[kAddressHi]
00322 = (((long) branchTo) >> 16) & 0x0000FFFF;
00323
00324
00325 if( instruction != 0 ) {
00326 ((short*)island->instructions)[kInstructionLo]
00327 = instruction & 0x0000FFFF;
00328 ((short*)island->instructions)[kInstructionHi]
00329 = (instruction >> 16) & 0x0000FFFF;
00330 }
00331
00332
00333 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
00334
00335 return err_none;
00336 }