1 // mach_override.c semver:1.2.0
2 // Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
3 // Some rights reserved: http://opensource.org/licenses/mit
4 // https://github.com/rentzsch/mach_override
6 #include "mach_override.h"
7 #if defined(__i386__) || defined(__x86_64__)
11 #include <mach-o/dyld.h>
12 #include <mach/mach_init.h>
13 #include <mach/vm_map.h>
14 #include <mach/vm_statistics.h>
17 #include <CoreServices/CoreServices.h>
19 /**************************
23 **************************/
25 #pragma mark (Constants)
27 #if defined(__ppc__) || defined(__POWERPC__)
29 long kIslandTemplate
[] = {
30 0x9001FFFC, // stw r0,-4(SP)
31 0x3C00DEAD, // lis r0,0xDEAD
32 0x6000BEEF, // ori r0,r0,0xBEEF
33 0x7C0903A6, // mtctr r0
34 0x8001FFFC, // lwz r0,-4(SP)
35 0x60000000, // nop ; optionally replaced
41 #define kInstructionHi 10
42 #define kInstructionLo 11
44 #elif defined(__i386__)
46 #define kOriginalInstructionsSize 16
48 char kIslandTemplate
[] = {
49 // kOriginalInstructionsSize nop instructions so that we
50 // should have enough space to host original instructions
51 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
52 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
53 // Now the real jump instruction
54 0xE9, 0xEF, 0xBE, 0xAD, 0xDE
57 #define kInstructions 0
58 #define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
59 #elif defined(__x86_64__)
61 #define kOriginalInstructionsSize 32
63 #define kJumpAddress kOriginalInstructionsSize + 6
65 char kIslandTemplate
[] = {
66 // kOriginalInstructionsSize nop instructions so that we
67 // should have enough space to host original instructions
68 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
69 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
70 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
71 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
72 // Now the real jump instruction
73 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00
80 #define kAllocateHigh 1
81 #define kAllocateNormal 0
83 /**************************
87 **************************/
89 #pragma mark (Data Types)
92 char instructions
[sizeof(kIslandTemplate
)];
96 /**************************
100 **************************/
102 #pragma mark (Funky Protos)
105 allocateBranchIsland(
106 BranchIsland
**island
,
108 void *originalFunctionAddress
);
112 BranchIsland
*island
);
114 #if defined(__ppc__) || defined(__POWERPC__)
116 setBranchIslandTarget(
117 BranchIsland
*island
,
118 const void *branchTo
,
122 #if defined(__i386__) || defined(__x86_64__)
124 setBranchIslandTarget_i386(
125 BranchIsland
*island
,
126 const void *branchTo
,
127 char* instructions
);
130 uint64_t *targetAddress
,
134 eatKnownInstructions(
136 uint64_t *newInstruction
,
138 char *originalInstructions
,
139 int *originalInstructionCount
,
140 uint8_t *originalInstructionSizes
);
144 void *originalFunction
,
146 void *instructionsToFix
,
147 int instructionCount
,
148 uint8_t *instructionSizes
);
151 /*******************************************************************************
155 *******************************************************************************/
157 #pragma mark (Interface)
159 #if defined(__i386__) || defined(__x86_64__)
160 mach_error_t
makeIslandExecutable(void *address
) {
161 mach_error_t err
= err_none
;
162 uintptr_t page
= (uintptr_t)address
& ~(uintptr_t)(PAGE_SIZE
- 1);
164 e
|= mprotect((void *)page
, PAGE_SIZE
, PROT_EXEC
| PROT_READ
);
165 e
|= msync((void *)page
, PAGE_SIZE
, MS_INVALIDATE
);
167 err
= err_cannot_override
;
175 void *originalFunctionAddress
,
176 const void *overrideFunctionAddress
,
177 void **originalFunctionReentryIsland
)
179 assert( originalFunctionAddress
);
180 assert( overrideFunctionAddress
);
182 // this addresses overriding such functions as AudioOutputUnitStart()
183 // test with modified DefaultOutputUnit project
184 #if defined(__x86_64__)
186 if(*(uint16_t*)originalFunctionAddress
==0x25FF) // jmp qword near [rip+0x????????]
187 originalFunctionAddress
=*(void**)((char*)originalFunctionAddress
+6+*(int32_t *)((uint16_t*)originalFunctionAddress
+1));
190 #elif defined(__i386__)
192 if(*(uint16_t*)originalFunctionAddress
==0x25FF) // jmp *0x????????
193 originalFunctionAddress
=**(void***)((uint16_t*)originalFunctionAddress
+1);
198 long *originalFunctionPtr
= (long*) originalFunctionAddress
;
199 mach_error_t err
= err_none
;
201 #if defined(__ppc__) || defined(__POWERPC__)
202 // Ensure first instruction isn't 'mfctr'.
203 #define kMFCTRMask 0xfc1fffff
204 #define kMFCTRInstruction 0x7c0903a6
206 long originalInstruction
= *originalFunctionPtr
;
207 if( !err
&& ((originalInstruction
& kMFCTRMask
) == kMFCTRInstruction
) )
208 err
= err_cannot_override
;
209 #elif defined(__i386__) || defined(__x86_64__)
211 int originalInstructionCount
= 0;
212 char originalInstructions
[kOriginalInstructionsSize
];
213 uint8_t originalInstructionSizes
[kOriginalInstructionsSize
];
214 uint64_t jumpRelativeInstruction
= 0; // JMP
216 Boolean overridePossible
= eatKnownInstructions ((unsigned char *)originalFunctionPtr
,
217 &jumpRelativeInstruction
, &eatenCount
,
218 originalInstructions
, &originalInstructionCount
,
219 originalInstructionSizes
);
220 if (eatenCount
> kOriginalInstructionsSize
) {
221 //printf ("Too many instructions eaten\n");
222 overridePossible
= false;
224 if (!overridePossible
) err
= err_cannot_override
;
225 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
228 // Make the original function implementation writable.
230 err
= vm_protect( mach_task_self(),
231 (vm_address_t
) originalFunctionPtr
, 8, false,
232 (VM_PROT_ALL
| VM_PROT_COPY
) );
234 err
= vm_protect( mach_task_self(),
235 (vm_address_t
) originalFunctionPtr
, 8, false,
236 (VM_PROT_DEFAULT
| VM_PROT_COPY
) );
238 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
240 // Allocate and target the escape island to the overriding function.
241 BranchIsland
*escapeIsland
= NULL
;
243 err
= allocateBranchIsland( &escapeIsland
, kAllocateHigh
, originalFunctionAddress
);
244 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
247 #if defined(__ppc__) || defined(__POWERPC__)
249 err
= setBranchIslandTarget( escapeIsland
, overrideFunctionAddress
, 0 );
251 // Build the branch absolute instruction to the escape island.
252 long branchAbsoluteInstruction
= 0; // Set to 0 just to silence warning.
254 long escapeIslandAddress
= ((long) escapeIsland
) & 0x3FFFFFF;
255 branchAbsoluteInstruction
= 0x48000002 | escapeIslandAddress
;
257 #elif defined(__i386__) || defined(__x86_64__)
258 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
261 err
= setBranchIslandTarget_i386( escapeIsland
, overrideFunctionAddress
, 0 );
263 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
264 // Build the jump relative instruction to the escape island
268 #if defined(__i386__) || defined(__x86_64__)
270 uint32_t addressOffset
= ((char*)escapeIsland
- (char*)originalFunctionPtr
- 5);
271 addressOffset
= OSSwapInt32(addressOffset
);
273 jumpRelativeInstruction
|= 0xE900000000000000LL
;
274 jumpRelativeInstruction
|= ((uint64_t)addressOffset
& 0xffffffff) << 24;
275 jumpRelativeInstruction
= OSSwapInt64(jumpRelativeInstruction
);
279 // Optionally allocate & return the reentry island. This may contain relocated
280 // jmp instructions and so has all the same addressing reachability requirements
281 // the escape island has to the original function, except the escape island is
282 // technically our original function.
283 BranchIsland
*reentryIsland
= NULL
;
284 if( !err
&& originalFunctionReentryIsland
) {
285 err
= allocateBranchIsland( &reentryIsland
, kAllocateHigh
, escapeIsland
);
287 *originalFunctionReentryIsland
= reentryIsland
;
290 #if defined(__ppc__) || defined(__POWERPC__)
292 // o If the reentry island was allocated:
293 // o Insert the original instruction into the reentry island.
294 // o Target the reentry island at the 2nd instruction of the
295 // original function.
296 // o Replace the original instruction with the branch absolute.
298 int escapeIslandEngaged
= false;
301 err
= setBranchIslandTarget( reentryIsland
,
302 (void*) (originalFunctionPtr
+1), originalInstruction
);
304 escapeIslandEngaged
= CompareAndSwap( originalInstruction
,
305 branchAbsoluteInstruction
,
306 (UInt32
*)originalFunctionPtr
);
307 if( !escapeIslandEngaged
) {
308 // Someone replaced the instruction out from under us,
309 // re-read the instruction, make sure it's still not
310 // 'mfctr' and try again.
311 originalInstruction
= *originalFunctionPtr
;
312 if( (originalInstruction
& kMFCTRMask
) == kMFCTRInstruction
)
313 err
= err_cannot_override
;
316 } while( !err
&& !escapeIslandEngaged
);
318 #elif defined(__i386__) || defined(__x86_64__)
320 // o If the reentry island was allocated:
321 // o Insert the original instructions into the reentry island.
322 // o Target the reentry island at the first non-replaced
323 // instruction of the original function.
324 // o Replace the original first instructions with the jump relative.
326 // Note that on i386, we do not support someone else changing the code under our feet
328 fixupInstructions(originalFunctionPtr
, reentryIsland
, originalInstructions
,
329 originalInstructionCount
, originalInstructionSizes
);
332 err
= setBranchIslandTarget_i386( reentryIsland
,
333 (void*) ((char *)originalFunctionPtr
+eatenCount
), originalInstructions
);
334 // try making islands executable before planting the jmp
335 #if defined(__x86_64__) || defined(__i386__)
337 err
= makeIslandExecutable(escapeIsland
);
338 if( !err
&& reentryIsland
)
339 err
= makeIslandExecutable(reentryIsland
);
342 atomic_mov64((uint64_t *)originalFunctionPtr
, jumpRelativeInstruction
);
343 mach_error_t prot_err
= err_none
;
344 prot_err
= vm_protect( mach_task_self(),
345 (vm_address_t
) originalFunctionPtr
, 8, false,
346 (VM_PROT_READ
| VM_PROT_EXECUTE
) );
347 if(prot_err
) fprintf(stderr
, "err = %x %s:%d\n", prot_err
, __FILE__
, __LINE__
);
351 // Clean up on error.
354 freeBranchIsland( reentryIsland
);
356 freeBranchIsland( escapeIsland
);
362 /*******************************************************************************
366 *******************************************************************************/
368 #pragma mark (Implementation)
370 /*******************************************************************************
371 Implementation: Allocates memory for a branch island.
373 @param island <- The allocated island.
374 @param allocateHigh -> Whether to allocate the island at the end of the
375 address space (for use with the branch absolute
377 @result <- mach_error_t
379 ***************************************************************************/
382 allocateBranchIsland(
383 BranchIsland
**island
,
385 void *originalFunctionAddress
)
389 mach_error_t err
= err_none
;
392 assert( sizeof( BranchIsland
) <= PAGE_SIZE
);
393 vm_address_t page
= 0;
394 #if defined(__i386__)
395 err
= vm_allocate( mach_task_self(), &page
, PAGE_SIZE
, VM_FLAGS_ANYWHERE
);
396 if( err
== err_none
)
397 *island
= (BranchIsland
*) page
;
400 #if defined(__ppc__) || defined(__POWERPC__)
401 vm_address_t first
= 0xfeffffff;
402 vm_address_t last
= 0xfe000000 + PAGE_SIZE
;
403 #elif defined(__x86_64__)
404 // 64-bit ASLR is in bits 13-28
405 vm_address_t first
= ((uint64_t)originalFunctionAddress
& ~( (0xFUL
<< 28) | (PAGE_SIZE
- 1) ) ) | (0x1UL
<< 31);
406 vm_address_t last
= (uint64_t)originalFunctionAddress
& ~((0x1UL
<< 32) - 1);
411 vm_map_t task_self
= mach_task_self();
413 while( !err
&& !allocated
&& page
!= last
) {
415 err
= vm_allocate( task_self
, &page
, PAGE_SIZE
, 0 );
416 if( err
== err_none
)
418 else if( err
== KERN_NO_SPACE
) {
419 #if defined(__x86_64__)
428 *island
= (BranchIsland
*) page
;
429 else if( !allocated
&& !err
)
433 void *block
= malloc( sizeof( BranchIsland
) );
440 (**island
).allocatedHigh
= allocateHigh
;
445 /*******************************************************************************
446 Implementation: Deallocates memory for a branch island.
448 @param island -> The island to deallocate.
449 @result <- mach_error_t
451 ***************************************************************************/
455 BranchIsland
*island
)
458 assert( (*(long*)&island
->instructions
[0]) == kIslandTemplate
[0] );
459 assert( island
->allocatedHigh
);
461 mach_error_t err
= err_none
;
463 if( island
->allocatedHigh
) {
464 assert( sizeof( BranchIsland
) <= PAGE_SIZE
);
465 err
= vm_deallocate(mach_task_self(), (vm_address_t
) island
, PAGE_SIZE
);
473 /*******************************************************************************
474 Implementation: Sets the branch island's target, with an optional
477 @param island -> The branch island to insert target into.
478 @param branchTo -> The address of the target.
479 @param instruction -> Optional instruction to execute prior to branch. Set
481 @result <- mach_error_t
483 ***************************************************************************/
484 #if defined(__ppc__) || defined(__POWERPC__)
486 setBranchIslandTarget(
487 BranchIsland
*island
,
488 const void *branchTo
,
491 // Copy over the template code.
492 bcopy( kIslandTemplate
, island
->instructions
, sizeof( kIslandTemplate
) );
494 // Fill in the address.
495 ((short*)island
->instructions
)[kAddressLo
] = ((long) branchTo
) & 0x0000FFFF;
496 ((short*)island
->instructions
)[kAddressHi
]
497 = (((long) branchTo
) >> 16) & 0x0000FFFF;
499 // Fill in the (optional) instuction.
500 if( instruction
!= 0 ) {
501 ((short*)island
->instructions
)[kInstructionLo
]
502 = instruction
& 0x0000FFFF;
503 ((short*)island
->instructions
)[kInstructionHi
]
504 = (instruction
>> 16) & 0x0000FFFF;
507 //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
508 msync( island
->instructions
, sizeof( kIslandTemplate
), MS_INVALIDATE
);
514 #if defined(__i386__)
516 setBranchIslandTarget_i386(
517 BranchIsland
*island
,
518 const void *branchTo
,
522 // Copy over the template code.
523 bcopy( kIslandTemplate
, island
->instructions
, sizeof( kIslandTemplate
) );
525 // copy original instructions
527 bcopy (instructions
, island
->instructions
+ kInstructions
, kOriginalInstructionsSize
);
530 // Fill in the address.
531 int32_t addressOffset
= (char *)branchTo
- (island
->instructions
+ kJumpAddress
+ 4);
532 *((int32_t *)(island
->instructions
+ kJumpAddress
)) = addressOffset
;
534 msync( island
->instructions
, sizeof( kIslandTemplate
), MS_INVALIDATE
);
538 #elif defined(__x86_64__)
540 setBranchIslandTarget_i386(
541 BranchIsland
*island
,
542 const void *branchTo
,
545 // Copy over the template code.
546 bcopy( kIslandTemplate
, island
->instructions
, sizeof( kIslandTemplate
) );
548 // Copy original instructions.
550 bcopy (instructions
, island
->instructions
, kOriginalInstructionsSize
);
553 // Fill in the address.
554 *((uint64_t *)(island
->instructions
+ kJumpAddress
)) = (uint64_t)branchTo
;
555 msync( island
->instructions
, sizeof( kIslandTemplate
), MS_INVALIDATE
);
562 #if defined(__i386__) || defined(__x86_64__)
564 eatKnownInstructions(
566 uint64_t *newInstruction
,
568 char *originalInstructions
,
569 int *originalInstructionCount
,
570 uint8_t *originalInstructionSizes
)
572 Boolean allInstructionsKnown
= true;
574 int remainsToEat
= 5; // a JMP instruction takes 5 bytes
575 int instructionIndex
= 0;
578 if (howManyEaten
) *howManyEaten
= 0;
579 if (originalInstructionCount
) *originalInstructionCount
= 0;
581 #if defined(__i386__)
582 ud_set_mode(&ud_obj
, 32);
584 ud_set_mode(&ud_obj
, 64);
586 ud_set_input_buffer(&ud_obj
, code
, 64); // Assume that 'code' points to at least 64bytes of data.
587 while (remainsToEat
> 0) {
588 if (!ud_disassemble(&ud_obj
)) {
589 allInstructionsKnown
= false;
590 fprintf(stderr
, "mach_override: some instructions unknown! Need to update libudis86\n");
594 // At this point, we've matched curInstr
595 int eaten
= ud_insn_len(&ud_obj
);
596 remainsToEat
-= eaten
;
599 if (originalInstructionSizes
) originalInstructionSizes
[instructionIndex
] = eaten
;
600 instructionIndex
+= 1;
601 if (originalInstructionCount
) *originalInstructionCount
= instructionIndex
;
605 if (howManyEaten
) *howManyEaten
= totalEaten
;
607 if (originalInstructions
) {
608 Boolean enoughSpaceForOriginalInstructions
= (totalEaten
< kOriginalInstructionsSize
);
610 if (enoughSpaceForOriginalInstructions
) {
611 memset(originalInstructions
, 0x90 /* NOP */, kOriginalInstructionsSize
); // fill instructions with NOP
612 bcopy(code
, originalInstructions
, totalEaten
);
614 // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
619 if (allInstructionsKnown
) {
620 // save last 3 bytes of first 64bits of codre we'll replace
621 uint64_t currentFirst64BitsOfCode
= *((uint64_t *)code
);
622 currentFirst64BitsOfCode
= OSSwapInt64(currentFirst64BitsOfCode
); // back to memory representation
623 currentFirst64BitsOfCode
&= 0x0000000000FFFFFFLL
;
625 // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
626 *newInstruction
&= 0xFFFFFFFFFF000000LL
; // clear last 3 bytes
627 *newInstruction
|= (currentFirst64BitsOfCode
& 0x0000000000FFFFFFLL
); // set last 3 bytes
630 return allInstructionsKnown
;
635 void *originalFunction
,
637 void *instructionsToFix
,
638 int instructionCount
,
639 uint8_t *instructionSizes
)
642 for (index
= 0;index
< instructionCount
;index
+= 1)
644 if (*(uint8_t*)instructionsToFix
== 0xE9) // 32-bit jump relative
646 uint32_t offset
= (uintptr_t)originalFunction
- (uintptr_t)escapeIsland
;
647 uint32_t *jumpOffsetPtr
= (uint32_t*)((uintptr_t)instructionsToFix
+ 1);
648 *jumpOffsetPtr
+= offset
;
651 originalFunction
= (void*)((uintptr_t)originalFunction
+ instructionSizes
[index
]);
652 escapeIsland
= (void*)((uintptr_t)escapeIsland
+ instructionSizes
[index
]);
653 instructionsToFix
= (void*)((uintptr_t)instructionsToFix
+ instructionSizes
[index
]);
657 #if defined(__i386__)
670 // atomic push of value to an address
671 // we use cmpxchg8b, which compares content of an address with
672 // edx:eax. If they are equal, it atomically puts 64bit value
673 // ecx:ebx in address.
674 // We thus put contents of address in edx:eax to force ecx:ebx
676 " mov 8(%ebp), %esi;" // esi contains target address
677 " mov 12(%ebp), %ebx;"
678 " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
680 " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
681 " lock; cmpxchg8b (%esi);" // atomic move.
692 #elif defined(__x86_64__)
694 uint64_t *targetAddress
,
697 *targetAddress
= value
;