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_host.h>
13 #include <mach/mach_init.h>
14 #include <mach/vm_map.h>
15 #include <mach/vm_statistics.h>
18 #include <CoreServices/CoreServices.h>
20 /**************************
24 **************************/
26 #pragma mark (Constants)
28 #if defined(__ppc__) || defined(__POWERPC__)
30 long kIslandTemplate
[] = {
31 0x9001FFFC, // stw r0,-4(SP)
32 0x3C00DEAD, // lis r0,0xDEAD
33 0x6000BEEF, // ori r0,r0,0xBEEF
34 0x7C0903A6, // mtctr r0
35 0x8001FFFC, // lwz r0,-4(SP)
36 0x60000000, // nop ; optionally replaced
42 #define kInstructionHi 10
43 #define kInstructionLo 11
45 #elif defined(__i386__)
47 #define kOriginalInstructionsSize 16
49 char kIslandTemplate
[] = {
50 // kOriginalInstructionsSize nop instructions so that we
51 // should have enough space to host original instructions
52 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
53 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
54 // Now the real jump instruction
55 0xE9, 0xEF, 0xBE, 0xAD, 0xDE
58 #define kInstructions 0
59 #define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
60 #elif defined(__x86_64__)
62 #define kOriginalInstructionsSize 32
64 #define kJumpAddress kOriginalInstructionsSize + 6
66 char kIslandTemplate
[] = {
67 // kOriginalInstructionsSize nop instructions so that we
68 // should have enough space to host original instructions
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 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
73 // Now the real jump instruction
74 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00,
76 0x00, 0x00, 0x00, 0x00
81 #define kAllocateHigh 1
82 #define kAllocateNormal 0
84 /**************************
88 **************************/
90 #pragma mark (Data Types)
93 char instructions
[sizeof(kIslandTemplate
)];
97 /**************************
101 **************************/
103 #pragma mark (Funky Protos)
106 allocateBranchIsland(
107 BranchIsland
**island
,
109 void *originalFunctionAddress
);
113 BranchIsland
*island
);
115 #if defined(__ppc__) || defined(__POWERPC__)
117 setBranchIslandTarget(
118 BranchIsland
*island
,
119 const void *branchTo
,
123 #if defined(__i386__) || defined(__x86_64__)
125 setBranchIslandTarget_i386(
126 BranchIsland
*island
,
127 const void *branchTo
,
128 char* instructions
);
131 uint64_t *targetAddress
,
135 eatKnownInstructions(
137 uint64_t *newInstruction
,
139 char *originalInstructions
,
140 int *originalInstructionCount
,
141 uint8_t *originalInstructionSizes
);
145 void *originalFunction
,
147 void *instructionsToFix
,
148 int instructionCount
,
149 uint8_t *instructionSizes
);
152 /*******************************************************************************
156 *******************************************************************************/
158 #pragma mark (Interface)
160 #if defined(__i386__) || defined(__x86_64__)
161 mach_error_t
makeIslandExecutable(void *address
) {
162 mach_error_t err
= err_none
;
164 host_page_size( mach_host_self(), &pageSize
);
165 uintptr_t page
= (uintptr_t)address
& ~(uintptr_t)(pageSize
-1);
167 e
|= mprotect((void *)page
, pageSize
, PROT_EXEC
| PROT_READ
);
168 e
|= msync((void *)page
, pageSize
, MS_INVALIDATE
);
170 err
= err_cannot_override
;
178 void *originalFunctionAddress
,
179 const void *overrideFunctionAddress
,
180 void **originalFunctionReentryIsland
)
182 assert( originalFunctionAddress
);
183 assert( overrideFunctionAddress
);
185 // this addresses overriding such functions as AudioOutputUnitStart()
186 // test with modified DefaultOutputUnit project
187 #if defined(__x86_64__)
189 if(*(uint16_t*)originalFunctionAddress
==0x25FF) // jmp qword near [rip+0x????????]
190 originalFunctionAddress
=*(void**)((char*)originalFunctionAddress
+6+*(int32_t *)((uint16_t*)originalFunctionAddress
+1));
193 #elif defined(__i386__)
195 if(*(uint16_t*)originalFunctionAddress
==0x25FF) // jmp *0x????????
196 originalFunctionAddress
=**(void***)((uint16_t*)originalFunctionAddress
+1);
201 long *originalFunctionPtr
= (long*) originalFunctionAddress
;
202 mach_error_t err
= err_none
;
204 #if defined(__ppc__) || defined(__POWERPC__)
205 // Ensure first instruction isn't 'mfctr'.
206 #define kMFCTRMask 0xfc1fffff
207 #define kMFCTRInstruction 0x7c0903a6
209 long originalInstruction
= *originalFunctionPtr
;
210 if( !err
&& ((originalInstruction
& kMFCTRMask
) == kMFCTRInstruction
) )
211 err
= err_cannot_override
;
212 #elif defined(__i386__) || defined(__x86_64__)
214 int originalInstructionCount
= 0;
215 char originalInstructions
[kOriginalInstructionsSize
];
216 uint8_t originalInstructionSizes
[kOriginalInstructionsSize
];
217 uint64_t jumpRelativeInstruction
= 0; // JMP
219 Boolean overridePossible
= eatKnownInstructions ((unsigned char *)originalFunctionPtr
,
220 &jumpRelativeInstruction
, &eatenCount
,
221 originalInstructions
, &originalInstructionCount
,
222 originalInstructionSizes
);
223 if (eatenCount
> kOriginalInstructionsSize
) {
224 //printf ("Too many instructions eaten\n");
225 overridePossible
= false;
227 if (!overridePossible
) err
= err_cannot_override
;
228 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
231 // Make the original function implementation writable.
233 err
= vm_protect( mach_task_self(),
234 (vm_address_t
) originalFunctionPtr
, 8, false,
235 (VM_PROT_ALL
| VM_PROT_COPY
) );
237 err
= vm_protect( mach_task_self(),
238 (vm_address_t
) originalFunctionPtr
, 8, false,
239 (VM_PROT_DEFAULT
| VM_PROT_COPY
) );
241 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
243 // Allocate and target the escape island to the overriding function.
244 BranchIsland
*escapeIsland
= NULL
;
246 err
= allocateBranchIsland( &escapeIsland
, kAllocateHigh
, originalFunctionAddress
);
247 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
250 #if defined(__ppc__) || defined(__POWERPC__)
252 err
= setBranchIslandTarget( escapeIsland
, overrideFunctionAddress
, 0 );
254 // Build the branch absolute instruction to the escape island.
255 long branchAbsoluteInstruction
= 0; // Set to 0 just to silence warning.
257 long escapeIslandAddress
= ((long) escapeIsland
) & 0x3FFFFFF;
258 branchAbsoluteInstruction
= 0x48000002 | escapeIslandAddress
;
260 #elif defined(__i386__) || defined(__x86_64__)
261 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
264 err
= setBranchIslandTarget_i386( escapeIsland
, overrideFunctionAddress
, 0 );
266 if (err
) fprintf(stderr
, "err = %x %s:%d\n", err
, __FILE__
, __LINE__
);
267 // Build the jump relative instruction to the escape island
271 #if defined(__i386__) || defined(__x86_64__)
273 uint32_t addressOffset
= ((char*)escapeIsland
- (char*)originalFunctionPtr
- 5);
274 addressOffset
= OSSwapInt32(addressOffset
);
276 jumpRelativeInstruction
|= 0xE900000000000000LL
;
277 jumpRelativeInstruction
|= ((uint64_t)addressOffset
& 0xffffffff) << 24;
278 jumpRelativeInstruction
= OSSwapInt64(jumpRelativeInstruction
);
282 // Optionally allocate & return the reentry island. This may contain relocated
283 // jmp instructions and so has all the same addressing reachability requirements
284 // the escape island has to the original function, except the escape island is
285 // technically our original function.
286 BranchIsland
*reentryIsland
= NULL
;
287 if( !err
&& originalFunctionReentryIsland
) {
288 err
= allocateBranchIsland( &reentryIsland
, kAllocateHigh
, escapeIsland
);
290 *originalFunctionReentryIsland
= reentryIsland
;
293 #if defined(__ppc__) || defined(__POWERPC__)
295 // o If the reentry island was allocated:
296 // o Insert the original instruction into the reentry island.
297 // o Target the reentry island at the 2nd instruction of the
298 // original function.
299 // o Replace the original instruction with the branch absolute.
301 int escapeIslandEngaged
= false;
304 err
= setBranchIslandTarget( reentryIsland
,
305 (void*) (originalFunctionPtr
+1), originalInstruction
);
307 escapeIslandEngaged
= CompareAndSwap( originalInstruction
,
308 branchAbsoluteInstruction
,
309 (UInt32
*)originalFunctionPtr
);
310 if( !escapeIslandEngaged
) {
311 // Someone replaced the instruction out from under us,
312 // re-read the instruction, make sure it's still not
313 // 'mfctr' and try again.
314 originalInstruction
= *originalFunctionPtr
;
315 if( (originalInstruction
& kMFCTRMask
) == kMFCTRInstruction
)
316 err
= err_cannot_override
;
319 } while( !err
&& !escapeIslandEngaged
);
321 #elif defined(__i386__) || defined(__x86_64__)
323 // o If the reentry island was allocated:
324 // o Insert the original instructions into the reentry island.
325 // o Target the reentry island at the first non-replaced
326 // instruction of the original function.
327 // o Replace the original first instructions with the jump relative.
329 // Note that on i386, we do not support someone else changing the code under our feet
331 fixupInstructions(originalFunctionPtr
, reentryIsland
, originalInstructions
,
332 originalInstructionCount
, originalInstructionSizes
);
335 err
= setBranchIslandTarget_i386( reentryIsland
,
336 (void*) ((char *)originalFunctionPtr
+eatenCount
), originalInstructions
);
337 // try making islands executable before planting the jmp
338 #if defined(__x86_64__) || defined(__i386__)
340 err
= makeIslandExecutable(escapeIsland
);
341 if( !err
&& reentryIsland
)
342 err
= makeIslandExecutable(reentryIsland
);
345 atomic_mov64((uint64_t *)originalFunctionPtr
, jumpRelativeInstruction
);
347 mach_error_t prot_err
= err_none
;
348 prot_err
= vm_protect( mach_task_self(),
349 (vm_address_t
) originalFunctionPtr
, 8, false,
350 (VM_PROT_READ
| VM_PROT_EXECUTE
) );
351 if (prot_err
) fprintf(stderr
, "err = %x %s:%d\n", prot_err
, __FILE__
, __LINE__
);
355 // Clean up on error.
358 freeBranchIsland( reentryIsland
);
360 freeBranchIsland( escapeIsland
);
366 /*******************************************************************************
370 *******************************************************************************/
372 #pragma mark (Implementation)
374 /*******************************************************************************
375 Implementation: Allocates memory for a branch island.
377 @param island <- The allocated island.
378 @param allocateHigh -> Whether to allocate the island at the end of the
379 address space (for use with the branch absolute
381 @result <- mach_error_t
383 ***************************************************************************/
386 allocateBranchIsland(
387 BranchIsland
**island
,
389 void *originalFunctionAddress
)
393 mach_error_t err
= err_none
;
397 err
= host_page_size( mach_host_self(), &pageSize
);
399 assert( sizeof( BranchIsland
) <= pageSize
);
400 #if defined(__i386__)
401 vm_address_t page
= 0;
402 mach_error_t err
= vm_allocate( mach_task_self(), &page
, pageSize
, VM_FLAGS_ANYWHERE
);
403 if( err
== err_none
) {
404 *island
= (BranchIsland
*) page
;
410 #if defined(__ppc__) || defined(__POWERPC__)
411 vm_address_t first
= 0xfeffffff;
412 vm_address_t last
= 0xfe000000 + pageSize
;
413 #elif defined(__x86_64__)
414 vm_address_t first
= ((uint64_t)originalFunctionAddress
& ~(uint64_t)(((uint64_t)1 << 31) - 1)) | ((uint64_t)1 << 31); // start in the middle of the page?
415 vm_address_t last
= 0x0;
418 vm_address_t page
= first
;
420 vm_map_t task_self
= mach_task_self();
422 while( !err
&& !allocated
&& page
!= last
) {
424 err
= vm_allocate( task_self
, &page
, pageSize
, 0 );
425 if( err
== err_none
)
427 else if( err
== KERN_NO_SPACE
) {
428 #if defined(__x86_64__)
437 *island
= (BranchIsland
*) page
;
438 else if( !allocated
&& !err
)
443 void *block
= malloc( sizeof( BranchIsland
) );
450 (**island
).allocatedHigh
= allocateHigh
;
455 /*******************************************************************************
456 Implementation: Deallocates memory for a branch island.
458 @param island -> The island to deallocate.
459 @result <- mach_error_t
461 ***************************************************************************/
465 BranchIsland
*island
)
468 assert( (*(long*)&island
->instructions
[0]) == kIslandTemplate
[0] );
469 assert( island
->allocatedHigh
);
471 mach_error_t err
= err_none
;
473 if( island
->allocatedHigh
) {
475 err
= host_page_size( mach_host_self(), &pageSize
);
477 assert( sizeof( BranchIsland
) <= pageSize
);
480 (vm_address_t
) island
, pageSize
);
489 /*******************************************************************************
490 Implementation: Sets the branch island's target, with an optional
493 @param island -> The branch island to insert target into.
494 @param branchTo -> The address of the target.
495 @param instruction -> Optional instruction to execute prior to branch. Set
497 @result <- mach_error_t
499 ***************************************************************************/
500 #if defined(__ppc__) || defined(__POWERPC__)
502 setBranchIslandTarget(
503 BranchIsland
*island
,
504 const void *branchTo
,
507 // Copy over the template code.
508 bcopy( kIslandTemplate
, island
->instructions
, sizeof( kIslandTemplate
) );
510 // Fill in the address.
511 ((short*)island
->instructions
)[kAddressLo
] = ((long) branchTo
) & 0x0000FFFF;
512 ((short*)island
->instructions
)[kAddressHi
]
513 = (((long) branchTo
) >> 16) & 0x0000FFFF;
515 // Fill in the (optional) instuction.
516 if( instruction
!= 0 ) {
517 ((short*)island
->instructions
)[kInstructionLo
]
518 = instruction
& 0x0000FFFF;
519 ((short*)island
->instructions
)[kInstructionHi
]
520 = (instruction
>> 16) & 0x0000FFFF;
523 //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
524 msync( island
->instructions
, sizeof( kIslandTemplate
), MS_INVALIDATE
);
530 #if defined(__i386__)
532 setBranchIslandTarget_i386(
533 BranchIsland
*island
,
534 const void *branchTo
,
538 // Copy over the template code.
539 bcopy( kIslandTemplate
, island
->instructions
, sizeof( kIslandTemplate
) );
541 // copy original instructions
543 bcopy (instructions
, island
->instructions
+ kInstructions
, kOriginalInstructionsSize
);
546 // Fill in the address.
547 int32_t addressOffset
= (char *)branchTo
- (island
->instructions
+ kJumpAddress
+ 4);
548 *((int32_t *)(island
->instructions
+ kJumpAddress
)) = addressOffset
;
550 msync( island
->instructions
, sizeof( kIslandTemplate
), MS_INVALIDATE
);
554 #elif defined(__x86_64__)
556 setBranchIslandTarget_i386(
557 BranchIsland
*island
,
558 const void *branchTo
,
561 // Copy over the template code.
562 bcopy( kIslandTemplate
, island
->instructions
, sizeof( kIslandTemplate
) );
564 // Copy original instructions.
566 bcopy (instructions
, island
->instructions
, kOriginalInstructionsSize
);
569 // Fill in the address.
570 *((uint64_t *)(island
->instructions
+ kJumpAddress
)) = (uint64_t)branchTo
;
571 msync( island
->instructions
, sizeof( kIslandTemplate
), MS_INVALIDATE
);
578 #if defined(__i386__) || defined(__x86_64__)
580 eatKnownInstructions(
582 uint64_t *newInstruction
,
584 char *originalInstructions
,
585 int *originalInstructionCount
,
586 uint8_t *originalInstructionSizes
)
588 Boolean allInstructionsKnown
= true;
590 int remainsToEat
= 5; // a JMP instruction takes 5 bytes
591 int instructionIndex
= 0;
594 if (howManyEaten
) *howManyEaten
= 0;
595 if (originalInstructionCount
) *originalInstructionCount
= 0;
597 #if defined(__i386__)
598 ud_set_mode(&ud_obj
, 32);
600 ud_set_mode(&ud_obj
, 64);
602 ud_set_input_buffer(&ud_obj
, code
, 64); // Assume that 'code' points to at least 64bytes of data.
603 while (remainsToEat
> 0) {
604 if (!ud_disassemble(&ud_obj
)) {
605 allInstructionsKnown
= false;
606 fprintf(stderr
, "mach_override: some instructions unknown! Need to update libudis86\n");
610 // At this point, we've matched curInstr
611 int eaten
= ud_insn_len(&ud_obj
);
612 remainsToEat
-= eaten
;
615 if (originalInstructionSizes
) originalInstructionSizes
[instructionIndex
] = eaten
;
616 instructionIndex
+= 1;
617 if (originalInstructionCount
) *originalInstructionCount
= instructionIndex
;
621 if (howManyEaten
) *howManyEaten
= totalEaten
;
623 if (originalInstructions
) {
624 Boolean enoughSpaceForOriginalInstructions
= (totalEaten
< kOriginalInstructionsSize
);
626 if (enoughSpaceForOriginalInstructions
) {
627 memset(originalInstructions
, 0x90 /* NOP */, kOriginalInstructionsSize
); // fill instructions with NOP
628 bcopy(code
, originalInstructions
, totalEaten
);
630 // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
635 if (allInstructionsKnown
) {
636 // save last 3 bytes of first 64bits of codre we'll replace
637 uint64_t currentFirst64BitsOfCode
= *((uint64_t *)code
);
638 currentFirst64BitsOfCode
= OSSwapInt64(currentFirst64BitsOfCode
); // back to memory representation
639 currentFirst64BitsOfCode
&= 0x0000000000FFFFFFLL
;
641 // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
642 *newInstruction
&= 0xFFFFFFFFFF000000LL
; // clear last 3 bytes
643 *newInstruction
|= (currentFirst64BitsOfCode
& 0x0000000000FFFFFFLL
); // set last 3 bytes
646 return allInstructionsKnown
;
651 void *originalFunction
,
653 void *instructionsToFix
,
654 int instructionCount
,
655 uint8_t *instructionSizes
)
658 for (index
= 0;index
< instructionCount
;index
+= 1)
660 if (*(uint8_t*)instructionsToFix
== 0xE9) // 32-bit jump relative
662 uint32_t offset
= (uintptr_t)originalFunction
- (uintptr_t)escapeIsland
;
663 uint32_t *jumpOffsetPtr
= (uint32_t*)((uintptr_t)instructionsToFix
+ 1);
664 *jumpOffsetPtr
+= offset
;
667 originalFunction
= (void*)((uintptr_t)originalFunction
+ instructionSizes
[index
]);
668 escapeIsland
= (void*)((uintptr_t)escapeIsland
+ instructionSizes
[index
]);
669 instructionsToFix
= (void*)((uintptr_t)instructionsToFix
+ instructionSizes
[index
]);
673 #if defined(__i386__)
686 // atomic push of value to an address
687 // we use cmpxchg8b, which compares content of an address with
688 // edx:eax. If they are equal, it atomically puts 64bit value
689 // ecx:ebx in address.
690 // We thus put contents of address in edx:eax to force ecx:ebx
692 " mov 8(%ebp), %esi;" // esi contains target address
693 " mov 12(%ebp), %ebx;"
694 " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
696 " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
697 " lock; cmpxchg8b (%esi);" // atomic move.
708 #elif defined(__x86_64__)
710 uint64_t *targetAddress
,
713 *targetAddress
= value
;