|
1 // Copied from upstream at revision 195c13743fe0ebc658714e2a9567d86529f20443. |
|
2 // mach_override.c semver:1.2.0 |
|
3 // Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com |
|
4 // Some rights reserved: http://opensource.org/licenses/mit |
|
5 // https://github.com/rentzsch/mach_override |
|
6 |
|
7 #include "mach_override.h" |
|
8 |
|
9 #include <mach-o/dyld.h> |
|
10 #include <mach/mach_host.h> |
|
11 #include <mach/mach_init.h> |
|
12 #include <mach/vm_map.h> |
|
13 #include <sys/mman.h> |
|
14 |
|
15 #include <CoreServices/CoreServices.h> |
|
16 |
|
17 /************************** |
|
18 * |
|
19 * Constants |
|
20 * |
|
21 **************************/ |
|
22 #pragma mark - |
|
23 #pragma mark (Constants) |
|
24 |
|
25 #define kPageSize 4096 |
|
26 #if defined(__ppc__) || defined(__POWERPC__) |
|
27 |
|
28 long kIslandTemplate[] = { |
|
29 0x9001FFFC, // stw r0,-4(SP) |
|
30 0x3C00DEAD, // lis r0,0xDEAD |
|
31 0x6000BEEF, // ori r0,r0,0xBEEF |
|
32 0x7C0903A6, // mtctr r0 |
|
33 0x8001FFFC, // lwz r0,-4(SP) |
|
34 0x60000000, // nop ; optionally replaced |
|
35 0x4E800420 // bctr |
|
36 }; |
|
37 |
|
38 #define kAddressHi 3 |
|
39 #define kAddressLo 5 |
|
40 #define kInstructionHi 10 |
|
41 #define kInstructionLo 11 |
|
42 |
|
43 #elif defined(__i386__) |
|
44 |
|
45 #define kOriginalInstructionsSize 16 |
|
46 // On X86 we migh need to instert an add with a 32 bit immediate after the |
|
47 // original instructions. |
|
48 #define kMaxFixupSizeIncrease 5 |
|
49 |
|
50 unsigned char kIslandTemplate[] = { |
|
51 // kOriginalInstructionsSize nop instructions so that we |
|
52 // should have enough space to host original instructions |
|
53 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, |
|
54 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, |
|
55 // Now the real jump instruction |
|
56 0xE9, 0xEF, 0xBE, 0xAD, 0xDE |
|
57 }; |
|
58 |
|
59 #define kInstructions 0 |
|
60 #define kJumpAddress kInstructions + kOriginalInstructionsSize + 1 |
|
61 #elif defined(__x86_64__) |
|
62 |
|
63 #define kOriginalInstructionsSize 32 |
|
64 // On X86-64 we never need to instert a new instruction. |
|
65 #define kMaxFixupSizeIncrease 0 |
|
66 |
|
67 #define kJumpAddress kOriginalInstructionsSize + 6 |
|
68 |
|
69 unsigned char kIslandTemplate[] = { |
|
70 // kOriginalInstructionsSize nop instructions so that we |
|
71 // should have enough space to host original instructions |
|
72 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, |
|
73 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, |
|
74 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, |
|
75 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, |
|
76 // Now the real jump instruction |
|
77 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, |
|
78 0x00, 0x00, 0x00, 0x00, |
|
79 0x00, 0x00, 0x00, 0x00 |
|
80 }; |
|
81 |
|
82 #endif |
|
83 |
|
84 /************************** |
|
85 * |
|
86 * Data Types |
|
87 * |
|
88 **************************/ |
|
89 #pragma mark - |
|
90 #pragma mark (Data Types) |
|
91 |
|
92 typedef struct { |
|
93 char instructions[sizeof(kIslandTemplate)]; |
|
94 } BranchIsland; |
|
95 |
|
96 /************************** |
|
97 * |
|
98 * Funky Protos |
|
99 * |
|
100 **************************/ |
|
101 #pragma mark - |
|
102 #pragma mark (Funky Protos) |
|
103 |
|
104 static mach_error_t |
|
105 allocateBranchIsland( |
|
106 BranchIsland **island, |
|
107 void *originalFunctionAddress); |
|
108 |
|
109 mach_error_t |
|
110 freeBranchIsland( |
|
111 BranchIsland *island ); |
|
112 |
|
113 #if defined(__ppc__) || defined(__POWERPC__) |
|
114 mach_error_t |
|
115 setBranchIslandTarget( |
|
116 BranchIsland *island, |
|
117 const void *branchTo, |
|
118 long instruction ); |
|
119 #endif |
|
120 |
|
121 #if defined(__i386__) || defined(__x86_64__) |
|
122 mach_error_t |
|
123 setBranchIslandTarget_i386( |
|
124 BranchIsland *island, |
|
125 const void *branchTo, |
|
126 char* instructions ); |
|
127 void |
|
128 atomic_mov64( |
|
129 uint64_t *targetAddress, |
|
130 uint64_t value ); |
|
131 |
|
132 static Boolean |
|
133 eatKnownInstructions( |
|
134 unsigned char *code, |
|
135 uint64_t *newInstruction, |
|
136 int *howManyEaten, |
|
137 char *originalInstructions, |
|
138 int *originalInstructionCount, |
|
139 uint8_t *originalInstructionSizes ); |
|
140 |
|
141 static void |
|
142 fixupInstructions( |
|
143 uint32_t offset, |
|
144 void *instructionsToFix, |
|
145 int instructionCount, |
|
146 uint8_t *instructionSizes ); |
|
147 #endif |
|
148 |
|
149 /******************************************************************************* |
|
150 * |
|
151 * Interface |
|
152 * |
|
153 *******************************************************************************/ |
|
154 #pragma mark - |
|
155 #pragma mark (Interface) |
|
156 |
|
157 #if defined(__i386__) || defined(__x86_64__) |
|
158 mach_error_t makeIslandExecutable(void *address) { |
|
159 mach_error_t err = err_none; |
|
160 uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1); |
|
161 int e = err_none; |
|
162 e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE); |
|
163 e |= msync((void *)page, kPageSize, MS_INVALIDATE ); |
|
164 if (e) { |
|
165 err = err_cannot_override; |
|
166 } |
|
167 return err; |
|
168 } |
|
169 #endif |
|
170 |
|
171 mach_error_t |
|
172 mach_override_ptr( |
|
173 void *originalFunctionAddress, |
|
174 const void *overrideFunctionAddress, |
|
175 void **originalFunctionReentryIsland ) |
|
176 { |
|
177 assert( originalFunctionAddress ); |
|
178 assert( overrideFunctionAddress ); |
|
179 |
|
180 // this addresses overriding such functions as AudioOutputUnitStart() |
|
181 // test with modified DefaultOutputUnit project |
|
182 #if defined(__x86_64__) |
|
183 for(;;){ |
|
184 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????] |
|
185 originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1)); |
|
186 else break; |
|
187 } |
|
188 #elif defined(__i386__) |
|
189 for(;;){ |
|
190 if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x???????? |
|
191 originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1); |
|
192 else break; |
|
193 } |
|
194 #endif |
|
195 |
|
196 long *originalFunctionPtr = (long*) originalFunctionAddress; |
|
197 mach_error_t err = err_none; |
|
198 |
|
199 #if defined(__ppc__) || defined(__POWERPC__) |
|
200 // Ensure first instruction isn't 'mfctr'. |
|
201 #define kMFCTRMask 0xfc1fffff |
|
202 #define kMFCTRInstruction 0x7c0903a6 |
|
203 |
|
204 long originalInstruction = *originalFunctionPtr; |
|
205 if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) ) |
|
206 err = err_cannot_override; |
|
207 #elif defined(__i386__) || defined(__x86_64__) |
|
208 int eatenCount = 0; |
|
209 int originalInstructionCount = 0; |
|
210 char originalInstructions[kOriginalInstructionsSize]; |
|
211 uint8_t originalInstructionSizes[kOriginalInstructionsSize]; |
|
212 uint64_t jumpRelativeInstruction = 0; // JMP |
|
213 |
|
214 Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr, |
|
215 &jumpRelativeInstruction, &eatenCount, |
|
216 originalInstructions, &originalInstructionCount, |
|
217 originalInstructionSizes ); |
|
218 if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) { |
|
219 //printf ("Too many instructions eaten\n"); |
|
220 overridePossible = false; |
|
221 } |
|
222 if (!overridePossible) err = err_cannot_override; |
|
223 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); |
|
224 #endif |
|
225 |
|
226 // Make the original function implementation writable. |
|
227 if( !err ) { |
|
228 err = vm_protect( mach_task_self(), |
|
229 (vm_address_t) originalFunctionPtr, 8, false, |
|
230 (VM_PROT_ALL | VM_PROT_COPY) ); |
|
231 if( err ) |
|
232 err = vm_protect( mach_task_self(), |
|
233 (vm_address_t) originalFunctionPtr, 8, false, |
|
234 (VM_PROT_DEFAULT | VM_PROT_COPY) ); |
|
235 } |
|
236 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); |
|
237 |
|
238 // Allocate and target the escape island to the overriding function. |
|
239 BranchIsland *escapeIsland = NULL; |
|
240 if( !err ) |
|
241 err = allocateBranchIsland( &escapeIsland, originalFunctionAddress ); |
|
242 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); |
|
243 |
|
244 |
|
245 #if defined(__ppc__) || defined(__POWERPC__) |
|
246 if( !err ) |
|
247 err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 ); |
|
248 |
|
249 // Build the branch absolute instruction to the escape island. |
|
250 long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning. |
|
251 if( !err ) { |
|
252 long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF; |
|
253 branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress; |
|
254 } |
|
255 #elif defined(__i386__) || defined(__x86_64__) |
|
256 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); |
|
257 |
|
258 if( !err ) |
|
259 err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 ); |
|
260 |
|
261 if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__); |
|
262 // Build the jump relative instruction to the escape island |
|
263 #endif |
|
264 |
|
265 |
|
266 #if defined(__i386__) || defined(__x86_64__) |
|
267 if (!err) { |
|
268 uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5); |
|
269 addressOffset = OSSwapInt32(addressOffset); |
|
270 |
|
271 jumpRelativeInstruction |= 0xE900000000000000LL; |
|
272 jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24; |
|
273 jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction); |
|
274 } |
|
275 #endif |
|
276 |
|
277 // Optionally allocate & return the reentry island. This may contain relocated |
|
278 // jmp instructions and so has all the same addressing reachability requirements |
|
279 // the escape island has to the original function, except the escape island is |
|
280 // technically our original function. |
|
281 BranchIsland *reentryIsland = NULL; |
|
282 if( !err && originalFunctionReentryIsland ) { |
|
283 err = allocateBranchIsland( &reentryIsland, escapeIsland); |
|
284 if( !err ) |
|
285 *originalFunctionReentryIsland = reentryIsland; |
|
286 } |
|
287 |
|
288 #if defined(__ppc__) || defined(__POWERPC__) |
|
289 // Atomically: |
|
290 // o If the reentry island was allocated: |
|
291 // o Insert the original instruction into the reentry island. |
|
292 // o Target the reentry island at the 2nd instruction of the |
|
293 // original function. |
|
294 // o Replace the original instruction with the branch absolute. |
|
295 if( !err ) { |
|
296 int escapeIslandEngaged = false; |
|
297 do { |
|
298 if( reentryIsland ) |
|
299 err = setBranchIslandTarget( reentryIsland, |
|
300 (void*) (originalFunctionPtr+1), originalInstruction ); |
|
301 if( !err ) { |
|
302 escapeIslandEngaged = CompareAndSwap( originalInstruction, |
|
303 branchAbsoluteInstruction, |
|
304 (UInt32*)originalFunctionPtr ); |
|
305 if( !escapeIslandEngaged ) { |
|
306 // Someone replaced the instruction out from under us, |
|
307 // re-read the instruction, make sure it's still not |
|
308 // 'mfctr' and try again. |
|
309 originalInstruction = *originalFunctionPtr; |
|
310 if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction) |
|
311 err = err_cannot_override; |
|
312 } |
|
313 } |
|
314 } while( !err && !escapeIslandEngaged ); |
|
315 } |
|
316 #elif defined(__i386__) || defined(__x86_64__) |
|
317 // Atomically: |
|
318 // o If the reentry island was allocated: |
|
319 // o Insert the original instructions into the reentry island. |
|
320 // o Target the reentry island at the first non-replaced |
|
321 // instruction of the original function. |
|
322 // o Replace the original first instructions with the jump relative. |
|
323 // |
|
324 // Note that on i386, we do not support someone else changing the code under our feet |
|
325 if ( !err ) { |
|
326 uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland; |
|
327 fixupInstructions(offset, originalInstructions, |
|
328 originalInstructionCount, originalInstructionSizes ); |
|
329 |
|
330 if( reentryIsland ) |
|
331 err = setBranchIslandTarget_i386( reentryIsland, |
|
332 (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions ); |
|
333 // try making islands executable before planting the jmp |
|
334 #if defined(__x86_64__) || defined(__i386__) |
|
335 if( !err ) |
|
336 err = makeIslandExecutable(escapeIsland); |
|
337 if( !err && reentryIsland ) |
|
338 err = makeIslandExecutable(reentryIsland); |
|
339 #endif |
|
340 if ( !err ) |
|
341 atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction); |
|
342 } |
|
343 #endif |
|
344 |
|
345 // Clean up on error. |
|
346 if( err ) { |
|
347 if( reentryIsland ) |
|
348 freeBranchIsland( reentryIsland ); |
|
349 if( escapeIsland ) |
|
350 freeBranchIsland( escapeIsland ); |
|
351 } |
|
352 |
|
353 return err; |
|
354 } |
|
355 |
|
356 /******************************************************************************* |
|
357 * |
|
358 * Implementation |
|
359 * |
|
360 *******************************************************************************/ |
|
361 #pragma mark - |
|
362 #pragma mark (Implementation) |
|
363 |
|
364 static bool jump_in_range(intptr_t from, intptr_t to) { |
|
365 intptr_t field_value = to - from - 5; |
|
366 int32_t field_value_32 = field_value; |
|
367 return field_value == field_value_32; |
|
368 } |
|
369 |
|
370 /******************************************************************************* |
|
371 Implementation: Allocates memory for a branch island. |
|
372 |
|
373 @param island <- The allocated island. |
|
374 @result <- mach_error_t |
|
375 |
|
376 ***************************************************************************/ |
|
377 |
|
378 static mach_error_t |
|
379 allocateBranchIslandAux( |
|
380 BranchIsland **island, |
|
381 void *originalFunctionAddress, |
|
382 bool forward) |
|
383 { |
|
384 assert( island ); |
|
385 assert( sizeof( BranchIsland ) <= kPageSize ); |
|
386 |
|
387 vm_map_t task_self = mach_task_self(); |
|
388 vm_address_t original_address = (vm_address_t) originalFunctionAddress; |
|
389 vm_address_t address = original_address; |
|
390 |
|
391 for (;;) { |
|
392 vm_size_t vmsize = 0; |
|
393 memory_object_name_t object = 0; |
|
394 kern_return_t kr = 0; |
|
395 vm_region_flavor_t flavor = VM_REGION_BASIC_INFO; |
|
396 // Find the region the address is in. |
|
397 #if __WORDSIZE == 32 |
|
398 vm_region_basic_info_data_t info; |
|
399 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT; |
|
400 kr = vm_region(task_self, &address, &vmsize, flavor, |
|
401 (vm_region_info_t)&info, &info_count, &object); |
|
402 #else |
|
403 vm_region_basic_info_data_64_t info; |
|
404 mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64; |
|
405 kr = vm_region_64(task_self, &address, &vmsize, flavor, |
|
406 (vm_region_info_t)&info, &info_count, &object); |
|
407 #endif |
|
408 if (kr != KERN_SUCCESS) |
|
409 return kr; |
|
410 assert((address & (kPageSize - 1)) == 0); |
|
411 |
|
412 // Go to the first page before or after this region |
|
413 vm_address_t new_address = forward ? address + vmsize : address - kPageSize; |
|
414 #if __WORDSIZE == 64 |
|
415 if(!jump_in_range(original_address, new_address)) |
|
416 break; |
|
417 #endif |
|
418 address = new_address; |
|
419 |
|
420 // Try to allocate this page. |
|
421 kr = vm_allocate(task_self, &address, kPageSize, 0); |
|
422 if (kr == KERN_SUCCESS) { |
|
423 *island = (BranchIsland*) address; |
|
424 return err_none; |
|
425 } |
|
426 if (kr != KERN_NO_SPACE) |
|
427 return kr; |
|
428 } |
|
429 |
|
430 return KERN_NO_SPACE; |
|
431 } |
|
432 |
|
433 static mach_error_t |
|
434 allocateBranchIsland( |
|
435 BranchIsland **island, |
|
436 void *originalFunctionAddress) |
|
437 { |
|
438 mach_error_t err = |
|
439 allocateBranchIslandAux(island, originalFunctionAddress, true); |
|
440 if (!err) |
|
441 return err; |
|
442 return allocateBranchIslandAux(island, originalFunctionAddress, false); |
|
443 } |
|
444 |
|
445 |
|
446 /******************************************************************************* |
|
447 Implementation: Deallocates memory for a branch island. |
|
448 |
|
449 @param island -> The island to deallocate. |
|
450 @result <- mach_error_t |
|
451 |
|
452 ***************************************************************************/ |
|
453 |
|
454 mach_error_t |
|
455 freeBranchIsland( |
|
456 BranchIsland *island ) |
|
457 { |
|
458 assert( island ); |
|
459 assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] ); |
|
460 assert( sizeof( BranchIsland ) <= kPageSize ); |
|
461 return vm_deallocate( mach_task_self(), (vm_address_t) island, |
|
462 kPageSize ); |
|
463 } |
|
464 |
|
465 /******************************************************************************* |
|
466 Implementation: Sets the branch island's target, with an optional |
|
467 instruction. |
|
468 |
|
469 @param island -> The branch island to insert target into. |
|
470 @param branchTo -> The address of the target. |
|
471 @param instruction -> Optional instruction to execute prior to branch. Set |
|
472 to zero for nop. |
|
473 @result <- mach_error_t |
|
474 |
|
475 ***************************************************************************/ |
|
476 #if defined(__ppc__) || defined(__POWERPC__) |
|
477 mach_error_t |
|
478 setBranchIslandTarget( |
|
479 BranchIsland *island, |
|
480 const void *branchTo, |
|
481 long instruction ) |
|
482 { |
|
483 // Copy over the template code. |
|
484 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); |
|
485 |
|
486 // Fill in the address. |
|
487 ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF; |
|
488 ((short*)island->instructions)[kAddressHi] |
|
489 = (((long) branchTo) >> 16) & 0x0000FFFF; |
|
490 |
|
491 // Fill in the (optional) instuction. |
|
492 if( instruction != 0 ) { |
|
493 ((short*)island->instructions)[kInstructionLo] |
|
494 = instruction & 0x0000FFFF; |
|
495 ((short*)island->instructions)[kInstructionHi] |
|
496 = (instruction >> 16) & 0x0000FFFF; |
|
497 } |
|
498 |
|
499 //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) ); |
|
500 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); |
|
501 |
|
502 return err_none; |
|
503 } |
|
504 #endif |
|
505 |
|
506 #if defined(__i386__) |
|
507 mach_error_t |
|
508 setBranchIslandTarget_i386( |
|
509 BranchIsland *island, |
|
510 const void *branchTo, |
|
511 char* instructions ) |
|
512 { |
|
513 |
|
514 // Copy over the template code. |
|
515 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); |
|
516 |
|
517 // copy original instructions |
|
518 if (instructions) { |
|
519 bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize); |
|
520 } |
|
521 |
|
522 // Fill in the address. |
|
523 int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4); |
|
524 *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset; |
|
525 |
|
526 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); |
|
527 return err_none; |
|
528 } |
|
529 |
|
530 #elif defined(__x86_64__) |
|
531 mach_error_t |
|
532 setBranchIslandTarget_i386( |
|
533 BranchIsland *island, |
|
534 const void *branchTo, |
|
535 char* instructions ) |
|
536 { |
|
537 // Copy over the template code. |
|
538 bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) ); |
|
539 |
|
540 // Copy original instructions. |
|
541 if (instructions) { |
|
542 bcopy (instructions, island->instructions, kOriginalInstructionsSize); |
|
543 } |
|
544 |
|
545 // Fill in the address. |
|
546 *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo; |
|
547 msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE ); |
|
548 |
|
549 return err_none; |
|
550 } |
|
551 #endif |
|
552 |
|
553 |
|
554 #if defined(__i386__) || defined(__x86_64__) |
|
555 // simplistic instruction matching |
|
556 typedef struct { |
|
557 unsigned int length; // max 15 |
|
558 unsigned char mask[15]; // sequence of bytes in memory order |
|
559 unsigned char constraint[15]; // sequence of bytes in memory order |
|
560 } AsmInstructionMatch; |
|
561 |
|
562 #if defined(__i386__) |
|
563 static AsmInstructionMatch possibleInstructions[] = { |
|
564 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? |
|
565 { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret |
|
566 { 0x1, {0xFF}, {0x90} }, // nop |
|
567 { 0x1, {0xFF}, {0x55} }, // push %esp |
|
568 { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp |
|
569 { 0x1, {0xFF}, {0x53} }, // push %ebx |
|
570 { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp |
|
571 { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate |
|
572 { 0x1, {0xFF}, {0x57} }, // push %edi |
|
573 { 0x1, {0xFF}, {0x56} }, // push %esi |
|
574 { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax |
|
575 { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg |
|
576 { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg |
|
577 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx |
|
578 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax |
|
579 { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax |
|
580 { 0x0 } |
|
581 }; |
|
582 #elif defined(__x86_64__) |
|
583 static AsmInstructionMatch possibleInstructions[] = { |
|
584 { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x???????? |
|
585 { 0x1, {0xFF}, {0x90} }, // nop |
|
586 { 0x1, {0xF8}, {0x50} }, // push %rX |
|
587 { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp |
|
588 { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp |
|
589 { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp |
|
590 { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx |
|
591 { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX |
|
592 { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX |
|
593 { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg |
|
594 { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi) |
|
595 { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax |
|
596 { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax |
|
597 |
|
598 //leaq offset(%rip),%rax |
|
599 { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} }, |
|
600 |
|
601 { 0x0 } |
|
602 }; |
|
603 #endif |
|
604 |
|
605 static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction) |
|
606 { |
|
607 Boolean match = true; |
|
608 |
|
609 size_t i; |
|
610 for (i=0; i<instruction->length; i++) { |
|
611 unsigned char mask = instruction->mask[i]; |
|
612 unsigned char constraint = instruction->constraint[i]; |
|
613 unsigned char codeValue = code[i]; |
|
614 |
|
615 match = ((codeValue & mask) == constraint); |
|
616 if (!match) break; |
|
617 } |
|
618 |
|
619 return match; |
|
620 } |
|
621 |
|
622 #if defined(__i386__) || defined(__x86_64__) |
|
623 static Boolean |
|
624 eatKnownInstructions( |
|
625 unsigned char *code, |
|
626 uint64_t *newInstruction, |
|
627 int *howManyEaten, |
|
628 char *originalInstructions, |
|
629 int *originalInstructionCount, |
|
630 uint8_t *originalInstructionSizes ) |
|
631 { |
|
632 Boolean allInstructionsKnown = true; |
|
633 int totalEaten = 0; |
|
634 unsigned char* ptr = code; |
|
635 int remainsToEat = 5; // a JMP instruction takes 5 bytes |
|
636 int instructionIndex = 0; |
|
637 |
|
638 if (howManyEaten) *howManyEaten = 0; |
|
639 if (originalInstructionCount) *originalInstructionCount = 0; |
|
640 while (remainsToEat > 0) { |
|
641 Boolean curInstructionKnown = false; |
|
642 |
|
643 // See if instruction matches one we know |
|
644 AsmInstructionMatch* curInstr = possibleInstructions; |
|
645 do { |
|
646 if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break; |
|
647 curInstr++; |
|
648 } while (curInstr->length > 0); |
|
649 |
|
650 // if all instruction matches failed, we don't know current instruction then, stop here |
|
651 if (!curInstructionKnown) { |
|
652 allInstructionsKnown = false; |
|
653 fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n"); |
|
654 break; |
|
655 } |
|
656 |
|
657 // At this point, we've matched curInstr |
|
658 int eaten = curInstr->length; |
|
659 ptr += eaten; |
|
660 remainsToEat -= eaten; |
|
661 totalEaten += eaten; |
|
662 |
|
663 if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten; |
|
664 instructionIndex += 1; |
|
665 if (originalInstructionCount) *originalInstructionCount = instructionIndex; |
|
666 } |
|
667 |
|
668 |
|
669 if (howManyEaten) *howManyEaten = totalEaten; |
|
670 |
|
671 if (originalInstructions) { |
|
672 Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize); |
|
673 |
|
674 if (enoughSpaceForOriginalInstructions) { |
|
675 memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP |
|
676 bcopy(code, originalInstructions, totalEaten); |
|
677 } else { |
|
678 // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n"); |
|
679 return false; |
|
680 } |
|
681 } |
|
682 |
|
683 if (allInstructionsKnown) { |
|
684 // save last 3 bytes of first 64bits of codre we'll replace |
|
685 uint64_t currentFirst64BitsOfCode = *((uint64_t *)code); |
|
686 currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation |
|
687 currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL; |
|
688 |
|
689 // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr |
|
690 *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes |
|
691 *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes |
|
692 } |
|
693 |
|
694 return allInstructionsKnown; |
|
695 } |
|
696 |
|
697 static void |
|
698 fixupInstructions( |
|
699 uint32_t offset, |
|
700 void *instructionsToFix, |
|
701 int instructionCount, |
|
702 uint8_t *instructionSizes ) |
|
703 { |
|
704 // The start of "leaq offset(%rip),%rax" |
|
705 static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05}; |
|
706 |
|
707 int index; |
|
708 for (index = 0;index < instructionCount;index += 1) |
|
709 { |
|
710 if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative |
|
711 { |
|
712 uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1); |
|
713 *jumpOffsetPtr += offset; |
|
714 } |
|
715 |
|
716 // leaq offset(%rip),%rax |
|
717 if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) { |
|
718 uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3); |
|
719 *LeaqOffsetPtr += offset; |
|
720 } |
|
721 |
|
722 // 32-bit call relative to the next addr; pop %eax |
|
723 if (*(uint8_t*)instructionsToFix == 0xE8) |
|
724 { |
|
725 // Just this call is larger than the jump we use, so we |
|
726 // know this is the last instruction. |
|
727 assert(index == (instructionCount - 1)); |
|
728 assert(instructionSizes[index] == 6); |
|
729 |
|
730 // Insert "addl $offset, %eax" in the end so that when |
|
731 // we jump to the rest of the function %eax has the |
|
732 // value it would have if eip had been pushed by the |
|
733 // call in its original position. |
|
734 uint8_t *op = instructionsToFix; |
|
735 op += 6; |
|
736 *op = 0x05; // addl |
|
737 uint32_t *addImmPtr = (uint32_t*)(op + 1); |
|
738 *addImmPtr = offset; |
|
739 } |
|
740 |
|
741 instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]); |
|
742 } |
|
743 } |
|
744 #endif |
|
745 |
|
746 #if defined(__i386__) |
|
747 __asm( |
|
748 ".text;" |
|
749 ".align 2, 0x90;" |
|
750 "_atomic_mov64:;" |
|
751 " pushl %ebp;" |
|
752 " movl %esp, %ebp;" |
|
753 " pushl %esi;" |
|
754 " pushl %ebx;" |
|
755 " pushl %ecx;" |
|
756 " pushl %eax;" |
|
757 " pushl %edx;" |
|
758 |
|
759 // atomic push of value to an address |
|
760 // we use cmpxchg8b, which compares content of an address with |
|
761 // edx:eax. If they are equal, it atomically puts 64bit value |
|
762 // ecx:ebx in address. |
|
763 // We thus put contents of address in edx:eax to force ecx:ebx |
|
764 // in address |
|
765 " mov 8(%ebp), %esi;" // esi contains target address |
|
766 " mov 12(%ebp), %ebx;" |
|
767 " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address |
|
768 " mov (%esi), %eax;" |
|
769 " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address |
|
770 " lock; cmpxchg8b (%esi);" // atomic move. |
|
771 |
|
772 // restore registers |
|
773 " popl %edx;" |
|
774 " popl %eax;" |
|
775 " popl %ecx;" |
|
776 " popl %ebx;" |
|
777 " popl %esi;" |
|
778 " popl %ebp;" |
|
779 " ret" |
|
780 ); |
|
781 #elif defined(__x86_64__) |
|
782 void atomic_mov64( |
|
783 uint64_t *targetAddress, |
|
784 uint64_t value ) |
|
785 { |
|
786 *targetAddress = value; |
|
787 } |
|
788 #endif |
|
789 #endif |