Main Page | Class List | File List | Class Members | File Members | Related Pages

mach_override.c

Go to the documentation of this file.
00001 /*******************************************************************************
00002     mach_override.c
00003         Copyright (c) 2003-2005 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
00004         Some rights reserved: <http://creativecommons.org/licenses/by/2.0/>
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 *   Constants
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, //  stw     r0,-4(SP)
00032     0x3C00DEAD, //  lis     r0,0xDEAD
00033     0x6000BEEF, //  ori     r0,r0,0xBEEF
00034     0x7C0903A6, //  mtctr   r0
00035     0x8001FFFC, //  lwz     r0,-4(SP)
00036     0x60000000, //  nop     ; optionally replaced
00037     0x4E800420  //  bctr
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 *   Data Types
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 *   Funky Protos
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 *   Interface
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     //  Lookup the original function's code pointer.
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     //  Ensure first instruction isn't 'mfctr'.
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     //  Make the original function implementation writable.
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     //  Allocate and target the escape island to the overriding function.
00138     BranchIsland    *escapeIsland = NULL;
00139     if( !err )  
00140         err = allocateBranchIsland( &escapeIsland, kAllocateHigh );
00141     if( !err )
00142         err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
00143     
00144     //  Build the branch absolute instruction to the escape island.
00145     long    branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
00146     if( !err ) {
00147         long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
00148         branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
00149     }
00150     
00151     //  Optionally allocate & return the reentry island.
00152     BranchIsland    *reentryIsland = NULL;
00153     if( !err && originalFunctionReentryIsland ) {
00154         err = allocateBranchIsland( &reentryIsland, kAllocateNormal );
00155         if( !err )
00156             *originalFunctionReentryIsland = reentryIsland;
00157     }
00158     
00159     //  Atomically:
00160     //  o If the reentry island was allocated:
00161     //      o Insert the original instruction into the reentry island.
00162     //      o Target the reentry island at the 2nd instruction of the
00163     //        original function.
00164     //  o Replace the original instruction with the branch absolute.
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                     //  Someone replaced the instruction out from under us,
00177                     //  re-read the instruction, make sure it's still not
00178                     //  'mfctr' and try again.
00179                     originalInstruction = *originalFunctionPtr;
00180                     if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
00181                         err = err_cannot_override;
00182                 }
00183             }
00184         } while( !err && !escapeIslandEngaged );
00185     }
00186     
00187     //  Clean up on error.
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 *   Implementation
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     //  Copy over the template code.
00317     bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
00318     
00319     //  Fill in the address.
00320     ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
00321     ((short*)island->instructions)[kAddressHi]
00322         = (((long) branchTo) >> 16) & 0x0000FFFF;
00323     
00324     //  Fill in the (optional) instuction.
00325     if( instruction != 0 ) {
00326         ((short*)island->instructions)[kInstructionLo]
00327             = instruction & 0x0000FFFF;
00328         ((short*)island->instructions)[kInstructionHi]
00329             = (instruction >> 16) & 0x0000FFFF;
00330     }
00331     
00332     //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
00333     msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
00334     
00335     return err_none;
00336 }

Generated on Sun Jun 12 22:09:33 2005 for mach_override by doxygen 1.3.4