├── interpose.h ├── tester ├── testputs.c ├── tester.c ├── testlib.c └── Makefile ├── README.txt ├── inject.h ├── interpose.c └── inject.c /interpose.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | bool interpose(const char *name, void *impl); 4 | -------------------------------------------------------------------------------- /tester/testputs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | while(1) { 6 | puts("Hi!\n"); 7 | sleep(1); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Now deprecated in favor of Substitute: 2 | 3 | https://github.com/comex/substitute 4 | 5 | In particular: 6 | 7 | https://github.com/comex/substitute/blob/master/lib/darwin/inject.c 8 | https://github.com/comex/substitute/blob/master/lib/darwin/interpose.c 9 | -------------------------------------------------------------------------------- /tester/tester.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv) { 6 | pid_t pid = atoi(argv[1]); 7 | printf("pid=%d ", (int) pid); 8 | fflush(stdout); 9 | printf("kr=%x\n", (int) inject(pid, argv[2])); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /inject.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | kern_return_t inject(pid_t pid, const char *path); 5 | 6 | // The behavior is synchronous: when it returns, constructors have 7 | // already been called. 8 | 9 | // Bugs: Will fail, crash the target process, or even crash this process if the target task is weird. 10 | -------------------------------------------------------------------------------- /tester/testlib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int fake_puts(const char *s) { 6 | puts("whee"); 7 | puts(s); 8 | return 0; 9 | } 10 | 11 | __attribute__((constructor)) 12 | static void hello() { 13 | fprintf(stderr, "Someone loaded me\n"); 14 | fprintf(stderr, "%d\n", interpose("_puts", fake_puts)); 15 | } 16 | -------------------------------------------------------------------------------- /tester/Makefile: -------------------------------------------------------------------------------- 1 | GCC ?= gcc 2 | ARCH = 3 | ifeq "$(findstring -arch,$(GCC))" "" 4 | ARCH = -arch i386 -arch x86_64 -arch ppc #-arch ppc64 5 | endif 6 | 7 | all: testlib.dylib tester testputs 8 | 9 | testlib.dylib: testlib.c ../interpose.c ../interpose.h Makefile 10 | $(GCC) $(ARCH) -dynamiclib -I.. -o testlib.dylib testlib.c ../interpose.c -Wall -Wno-parentheses 11 | tester: tester.c ../inject.c ../inject.h Makefile 12 | $(GCC) -O3 -g3 -o tester -I.. tester.c ../inject.c -Wall -Wno-parentheses 13 | testputs: testputs.c Makefile 14 | $(GCC) -o testputs testputs.c 15 | clean: 16 | rm -f tester testlib.dylib testputs 17 | -------------------------------------------------------------------------------- /interpose.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef __LP64__ 11 | #define nlist_native nlist_64 12 | #define LC_SEGMENT_NATIVE LC_SEGMENT_64 13 | #define segment_command_native segment_command_64 14 | #define mach_header_native mach_header_64 15 | #define section_native section_64 16 | #define PAGEZERO_SIZE 0x100000000; 17 | #else 18 | #define nlist_native nlist 19 | #define LC_SEGMENT_NATIVE LC_SEGMENT 20 | #define segment_command_native segment_command 21 | #define mach_header_native mach_header 22 | #define section_native section 23 | #define PAGEZERO_SIZE 0x1000 24 | #endif 25 | 26 | __attribute__((noinline)) 27 | static void *find_lazy(uint32_t ncmds, const struct load_command *cmds, uintptr_t slide, const char *desired) { 28 | uint32_t symoff = 0, stroff = 0, isymoff = 0, lazy_index = 0, lazy_size = 0; 29 | void **lazy = 0; 30 | uint32_t cmdsleft; 31 | const struct load_command *lc; 32 | 33 | uintptr_t thisimage = (uintptr_t) &find_lazy - slide; 34 | 35 | for(lc = cmds, cmdsleft = ncmds; cmdsleft--;) { 36 | if(lc->cmd == LC_SYMTAB) { 37 | const struct symtab_command *sc = (void *) lc; 38 | stroff = sc->stroff; 39 | symoff = sc->symoff; 40 | } else if(lc->cmd == LC_DYSYMTAB) { 41 | const struct dysymtab_command *dc = (void *) lc; 42 | isymoff = dc->indirectsymoff; 43 | } else if(lc->cmd == LC_SEGMENT_NATIVE) { 44 | const struct segment_command_native *sc = (void *) lc; 45 | const struct section_native *sect = (void *) (sc + 1); 46 | uint32_t i; 47 | if(sc->vmaddr <= thisimage && thisimage < (sc->vmaddr + sc->vmsize)) return 0; 48 | for(i = 0; i < sc->nsects; i++) { 49 | if((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { 50 | lazy_index = sect->reserved1; 51 | lazy_size = sect->size / sizeof(*lazy); 52 | lazy = (void *) sect->addr + slide; 53 | } 54 | sect++; 55 | } 56 | } 57 | lc = (void *) ((char *) lc + lc->cmdsize); 58 | } 59 | 60 | if(!stroff || !symoff || !isymoff || !lazy_index) return 0; 61 | 62 | #define CATCH(off, addr) if(sc->fileoff <= (off) && (sc->fileoff + sc->filesize) >= (off)) (addr) = (void *) (sc->vmaddr + slide + (off) - sc->fileoff); 63 | struct nlist_native *syms = 0; 64 | const char *strs = 0; 65 | uint32_t *isyms = 0; 66 | 67 | for(lc = cmds, cmdsleft = ncmds; cmdsleft--;) { 68 | if(lc->cmd == LC_SEGMENT_NATIVE) { 69 | struct segment_command_native *sc = (void *) lc; 70 | CATCH(symoff, syms); 71 | CATCH(stroff, strs); 72 | CATCH(isymoff, isyms); 73 | } 74 | lc = (void *) ((char *) lc + lc->cmdsize); 75 | } 76 | 77 | if(!syms || !strs || !isyms) return 0; 78 | 79 | uint32_t i; 80 | for(i = lazy_index; i < lazy_index + lazy_size; i++) { 81 | const struct nlist_native *sym = syms + isyms[i]; 82 | if(!strcmp(strs + sym->n_un.n_strx, desired)) { 83 | return lazy; 84 | } 85 | lazy++; 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | bool interpose(const char *name, void *impl) { 92 | const struct mach_header_native *mach_hdr; 93 | bool result = false; 94 | uint32_t i; 95 | for(i = 0; mach_hdr = (void *) _dyld_get_image_header(i); i++) { 96 | void **lazy = find_lazy(mach_hdr->ncmds, (void *) (mach_hdr + 1), _dyld_get_image_vmaddr_slide(i), name); 97 | if(lazy) { 98 | result = true; 99 | *lazy = impl; 100 | } 101 | } 102 | return true; 103 | } 104 | -------------------------------------------------------------------------------- /inject.c: -------------------------------------------------------------------------------- 1 | // I think this file has more useless features than the Toshiba Tablet. 2 | 3 | #include "inject.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | struct dyld_all_image_infos { 18 | uint32_t version; 19 | uint32_t infoArrayCount; 20 | uint32_t infoArray; 21 | uint32_t notification; 22 | uint8_t processDetachedFromSharedRegion; 23 | uint8_t libSystemInitialized; 24 | uint8_t pad[2]; 25 | uint32_t dyldImageLoadAddress; 26 | }; 27 | 28 | struct dyld_all_image_infos_64 { 29 | uint32_t version; 30 | uint32_t infoArrayCount; 31 | uint64_t infoArray; 32 | uint64_t notification; 33 | uint8_t processDetachedFromSharedRegion; 34 | uint8_t libSystemInitialized; 35 | uint8_t pad[6]; 36 | uint64_t dyldImageLoadAddress; 37 | }; 38 | 39 | #define ARM_THREAD_STATE 1 40 | struct arm_thread_state { 41 | uint32_t r[13]; 42 | uint32_t sp; 43 | uint32_t lr; 44 | uint32_t pc; 45 | uint32_t cpsr; 46 | }; 47 | 48 | #define x86_THREAD_STATE32 1 49 | struct x86_thread_state32 { 50 | uint32_t eax, ebx, ecx, edx, 51 | edi, esi, ebp, esp, 52 | ss, eflags, eip, 53 | cs, ds, es, fs, gs; 54 | }; 55 | 56 | #define x86_THREAD_STATE64 4 57 | struct x86_thread_state64 { 58 | uint64_t rax, rbx, rcx, rdx, 59 | rdi, rsi, rbp, rsp, 60 | r8, r9, r10, r11, 61 | r12, r13, r14, r15, 62 | rip, rflags, 63 | cs, fs, gs; 64 | }; 65 | 66 | #define PPC_THREAD_STATE64 5 67 | struct ppc_thread_state64 { 68 | uint64_t srr0, srr1; 69 | uint64_t r[32]; 70 | uint32_t cr; 71 | uint64_t xer, lr, ctr; 72 | uint32_t vrsave; 73 | }; 74 | 75 | #pragma pack(4) 76 | struct exception_message { 77 | mach_msg_header_t Head; 78 | mach_msg_body_t msgh_body; 79 | mach_msg_port_descriptor_t thread; 80 | mach_msg_port_descriptor_t task; 81 | NDR_record_t NDR; 82 | exception_type_t exception; 83 | mach_msg_type_number_t codeCnt; 84 | integer_t code[2]; 85 | int flavor; 86 | mach_msg_type_number_t old_stateCnt; 87 | natural_t old_state[144]; 88 | }; 89 | 90 | struct exception_reply { 91 | mach_msg_header_t Head; 92 | NDR_record_t NDR; 93 | kern_return_t RetCode; 94 | int flavor; 95 | mach_msg_type_number_t new_stateCnt; 96 | natural_t new_state[144]; 97 | }; 98 | #pragma pack() 99 | 100 | static const mach_vm_size_t stack_size = 32*1024; 101 | 102 | struct addr_bundle { 103 | mach_vm_address_t dlopen; 104 | mach_vm_address_t syscall; 105 | }; 106 | 107 | struct symtab_bundle { 108 | mach_vm_address_t symaddr; 109 | uint32_t nsyms; 110 | mach_vm_address_t straddr; 111 | uint32_t strsize; 112 | }; 113 | 114 | 115 | #define TRY(x) do { if(kr = x) { fprintf(stderr, "fail on line %d: %s\n", __LINE__, #x); goto bad; } } while(0) 116 | #define ASSERT(x) ASSERTR(x, KERN_INVALID_ARGUMENT) 117 | #define ASSERTR(x, err) do { if(!(x)) { fprintf(stderr, "assertion failed on line %d: %s\n", __LINE__, #x); kr = err; goto bad; } } while(0) 118 | #define address_cast(x) ((mach_vm_address_t) (uintptr_t) (x)) 119 | #define SWAP(x) (swap ? __builtin_bswap32(x) : (x)) 120 | #define SWAP64(x) (swap ? __builtin_bswap64(x) : (x)) 121 | 122 | static inline void handle_sym(const char *sym, uint32_t size, mach_vm_address_t value, struct addr_bundle *bundle) { 123 | switch(sym[1]) { 124 | case 'd': 125 | if(!strncmp(sym, "_dlopen", size)) bundle->dlopen = value; 126 | break; 127 | case 's': 128 | if(!strncmp(sym, "_syscall", size)) bundle->syscall = value; 129 | break; 130 | } 131 | } 132 | 133 | static kern_return_t find_symtab_addrs(mach_vm_address_t dyldImageLoadAddress, uint32_t ncmds, mach_vm_size_t sizeofcmds, struct load_command *cmds, bool swap, size_t nlist_size, struct symtab_bundle *symtab, mach_vm_address_t *slide_) { 134 | kern_return_t kr = 0; 135 | struct load_command *lc; 136 | uint32_t symoff = 0, stroff = 0; 137 | uint32_t cmdsleft; 138 | 139 | memset(symtab, 0, sizeof(*symtab)); 140 | 141 | mach_vm_address_t vma = 0; 142 | 143 | lc = cmds; 144 | for(cmdsleft = ncmds; cmdsleft--;) { 145 | uint32_t cmdsize = SWAP(lc->cmdsize); 146 | ASSERT(sizeofcmds >= sizeof(struct load_command) && sizeofcmds >= cmdsize); 147 | sizeofcmds -= cmdsize; 148 | if(!vma && SWAP(lc->cmd) == LC_SEGMENT) { 149 | struct segment_command *sc = (void *) lc; 150 | ASSERT(cmdsize >= sizeof(*sc)); 151 | vma = SWAP(sc->vmaddr); 152 | } else if(!vma && SWAP(lc->cmd) == LC_SEGMENT_64) { 153 | struct segment_command_64 *sc = (void *) lc; 154 | ASSERT(cmdsize >= sizeof(*sc)); 155 | vma = SWAP64(sc->vmaddr); 156 | } else if(SWAP(lc->cmd) == LC_SYMTAB) { 157 | struct symtab_command *sc = (void *) lc; 158 | ASSERT(cmdsize >= sizeof(*sc)); 159 | symoff = SWAP(sc->symoff); 160 | symtab->nsyms = SWAP(sc->nsyms); 161 | stroff = SWAP(sc->stroff); 162 | symtab->strsize = SWAP(sc->strsize); 163 | ASSERT(symtab->strsize < 10000000 && symtab->nsyms < 10000000); 164 | } 165 | lc = (void *) ((char *) lc + SWAP(lc->cmdsize)); 166 | } 167 | 168 | ASSERT(symoff); 169 | ASSERT(vma); 170 | 171 | mach_vm_address_t slide = dyldImageLoadAddress - vma; 172 | *slide_ = slide; 173 | 174 | #define CATCH(SWAP, off, size, addr) ASSERT(SWAP(sc->fileoff) + SWAP(sc->filesize) >= SWAP(sc->fileoff)); if(SWAP(sc->fileoff) <= (off) && (SWAP(sc->fileoff) + SWAP(sc->filesize) - (off)) >= (size)) (addr) = SWAP(sc->vmaddr) + slide + (off) - SWAP(sc->fileoff); 175 | 176 | lc = cmds; 177 | for(cmdsleft = ncmds; cmdsleft--;) { 178 | if(SWAP(lc->cmd) == LC_SEGMENT) { 179 | struct segment_command *sc = (void *) lc; 180 | if(!vma) vma = SWAP(sc->vmaddr); 181 | CATCH(SWAP, symoff, symtab->nsyms * nlist_size, symtab->symaddr); 182 | CATCH(SWAP, stroff, symtab->strsize, symtab->straddr); 183 | } else if(SWAP(lc->cmd) == LC_SEGMENT_64) { 184 | struct segment_command_64 *sc = (void *) lc; 185 | CATCH(SWAP64, symoff, symtab->nsyms * nlist_size, symtab->symaddr); 186 | CATCH(SWAP64, stroff, symtab->strsize, symtab->straddr); 187 | } 188 | lc = (void *) ((char *) lc + SWAP(lc->cmdsize)); 189 | } 190 | 191 | ASSERT(symtab->straddr); 192 | ASSERT(symtab->symaddr); 193 | 194 | bad: 195 | return kr; 196 | } 197 | 198 | static kern_return_t get_stuff(task_t task, cpu_type_t *cputype, struct addr_bundle *addrs) { 199 | kern_return_t kr = 0; 200 | 201 | char *strs = 0; void *syms = 0; 202 | struct load_command *cmds = 0; 203 | 204 | *cputype = 0; // make the optimizer happy 205 | 206 | task_dyld_info_data_t info; 207 | mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; 208 | union { 209 | struct dyld_all_image_infos data; 210 | struct dyld_all_image_infos_64 data64; 211 | } u; 212 | mach_vm_size_t data_size; 213 | 214 | 215 | TRY(task_info(task, TASK_DYLD_INFO, (task_info_t) &info, &count)); 216 | 217 | data_size = sizeof(u); 218 | if(info.all_image_info_size < data_size) data_size = info.all_image_info_size; 219 | 220 | TRY(mach_vm_read_overwrite(task, info.all_image_info_addr, data_size, address_cast(&u), &data_size)); 221 | 222 | if(u.data.version <= 1) return KERN_NO_SPACE; 223 | 224 | #if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) 225 | // Try to guess whether the process is 64-bit, 226 | bool proc64 = info.all_image_info_addr > 0; 227 | #else 228 | bool proc64 = false; 229 | #endif 230 | mach_vm_address_t dyldImageLoadAddress = proc64 ? u.data64.dyldImageLoadAddress : u.data.dyldImageLoadAddress; 231 | 232 | struct mach_header mach_hdr; 233 | TRY(mach_vm_read_overwrite(task, dyldImageLoadAddress, sizeof(mach_hdr), address_cast(&mach_hdr), &data_size)); 234 | 235 | bool swap = mach_hdr.magic == MH_CIGAM || mach_hdr.magic == MH_CIGAM_64; 236 | bool mh64 = mach_hdr.magic == MH_MAGIC_64 || mach_hdr.magic == MH_CIGAM_64; 237 | 238 | *cputype = SWAP(mach_hdr.cputype); 239 | 240 | size_t nlist_size = mh64 ? sizeof(struct nlist_64) : sizeof(struct nlist); 241 | 242 | mach_vm_size_t sizeofcmds = SWAP(mach_hdr.sizeofcmds); 243 | cmds = malloc(sizeofcmds); 244 | 245 | TRY(mach_vm_read_overwrite(task, dyldImageLoadAddress + (mh64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header)), sizeofcmds, address_cast(cmds), &sizeofcmds)); 246 | 247 | mach_vm_address_t slide; 248 | struct symtab_bundle symtab; 249 | TRY(find_symtab_addrs(dyldImageLoadAddress, mach_hdr.ncmds, sizeofcmds, cmds, swap, nlist_size, &symtab, &slide)); 250 | 251 | strs = malloc(symtab.strsize); 252 | syms = malloc(symtab.nsyms * nlist_size); 253 | 254 | TRY(mach_vm_read_overwrite(task, symtab.straddr, symtab.strsize, address_cast(strs), &data_size)); 255 | TRY(mach_vm_read_overwrite(task, symtab.symaddr, symtab.nsyms * nlist_size, address_cast(syms), &data_size)); 256 | 257 | memset(addrs, 0, sizeof(*addrs)); 258 | 259 | if(mh64) { 260 | const struct nlist_64 *nl = syms; 261 | while(symtab.nsyms--) { 262 | uint32_t strx = (uint32_t) SWAP(nl->n_un.n_strx); 263 | ASSERT(strx < symtab.strsize); 264 | handle_sym(strs + strx, symtab.strsize - strx, (mach_vm_address_t) SWAP64(nl->n_value) + slide, addrs); 265 | nl++; 266 | } 267 | } else { 268 | const struct nlist *nl = syms; 269 | while(symtab.nsyms--) { 270 | uint32_t strx = SWAP(nl->n_un.n_strx); 271 | ASSERT(strx < symtab.strsize); 272 | handle_sym(strs + strx, symtab.strsize - strx, (mach_vm_address_t) SWAP(nl->n_value) + slide, addrs); 273 | nl++; 274 | } 275 | } 276 | 277 | ASSERT(addrs->dlopen); 278 | ASSERT(addrs->syscall); 279 | 280 | bad: 281 | if(cmds) free(cmds); 282 | if(strs) free(strs); 283 | if(syms) free(syms); 284 | return kr; 285 | } 286 | 287 | kern_return_t inject(pid_t pid, const char *path) { 288 | kern_return_t kr = 0; 289 | 290 | mach_vm_address_t stack_address = 0; 291 | mach_port_t exc = 0; 292 | task_t task = 0; 293 | thread_act_t thread = 0; 294 | 295 | char path_real[PATH_MAX]; 296 | if(!realpath(path, path_real)) { 297 | perror("realpath"); 298 | ASSERT(0); 299 | } 300 | 301 | TRY(task_for_pid(mach_task_self(), (int) pid, &task)); 302 | 303 | cpu_type_t cputype; 304 | struct addr_bundle addrs; 305 | TRY(get_stuff(task, &cputype, &addrs)); 306 | 307 | TRY(mach_vm_allocate(task, &stack_address, stack_size, VM_FLAGS_ANYWHERE)); 308 | 309 | mach_vm_address_t stack_end = stack_address + stack_size - 0x100; 310 | 311 | TRY(mach_vm_write(task, stack_address, address_cast(path_real), strlen(path_real) + 1)); 312 | 313 | // the first one is the return address 314 | uint32_t args_32[] = {0, 360, 0xdeadbeef, 0xdeadbeef, 128*1024, 0, 0}; 315 | uint64_t args_64[] = {0, 360, 0xdeadbeef, 0xdeadbeef, 128*1024, 0, 0}; 316 | 317 | union { 318 | struct arm_thread_state arm; 319 | struct x86_thread_state32 x86; 320 | struct x86_thread_state64 x64; 321 | struct ppc_thread_state64 ppc; 322 | natural_t nat; 323 | } state; 324 | thread_state_flavor_t state_flavor; 325 | mach_msg_type_number_t state_count; 326 | 327 | memset(&state, 0, sizeof(state)); 328 | 329 | //printf("dlopen = %llx\n", addrs.dlopen); 330 | 331 | switch(cputype) { 332 | #ifdef __arm__ 333 | case CPU_TYPE_ARM: 334 | (void) args_64; 335 | memcpy(&state.arm.r[0], args_32 + 1, 4*4); 336 | TRY(mach_vm_write(task, stack_end, address_cast(args_32 + 5), 2*4)); 337 | 338 | state.arm.sp = (uint32_t) stack_end; 339 | state.arm.pc = (uint32_t) addrs.syscall; 340 | state.arm.lr = (uint32_t) args_32[0]; 341 | 342 | state_flavor = ARM_THREAD_STATE; 343 | state_count = sizeof(state.arm) / sizeof(state.nat); 344 | break; 345 | #endif 346 | #if defined(__i386__) || defined(__x86_64__) 347 | case CPU_TYPE_X86: 348 | TRY(mach_vm_write(task, stack_end, address_cast(args_32), 7*4)); 349 | 350 | state.x86.esp = state.x86.ebp = (uint32_t) stack_end; 351 | state.x86.eip = (uint32_t) addrs.syscall; 352 | 353 | state_flavor = x86_THREAD_STATE32; 354 | state_count = sizeof(state.x86) / sizeof(state.nat); 355 | break; 356 | case CPU_TYPE_X86_64: 357 | state.x64.rdi = args_64[1]; 358 | state.x64.rsi = args_64[2]; 359 | state.x64.rdx = args_64[3]; 360 | state.x64.rcx = args_64[4]; 361 | state.x64.r8 = args_64[5]; 362 | state.x64.r9 = args_64[6]; 363 | 364 | state.x64.rsp = state.x64.rbp = stack_end; 365 | state.x64.rip = addrs.syscall; 366 | 367 | state_flavor = x86_THREAD_STATE64; 368 | state_count = sizeof(state.x64) / sizeof(state.nat); 369 | break; 370 | #endif 371 | #ifdef __ppc__ 372 | case CPU_TYPE_POWERPC: 373 | case CPU_TYPE_POWERPC64: 374 | fprintf(stderr, "ppc is untested\n"); 375 | state.ppc.r[1] = stack_end; 376 | memcpy(&state.ppc.r[3], args_64 + 1, 6*8); 377 | state.ppc.srr0 = addrs.syscall; 378 | 379 | state_flavor = PPC_THREAD_STATE64; 380 | state_count = sizeof(state.ppc) / sizeof(state.nat); 381 | break; 382 | #endif 383 | default: 384 | abort(); 385 | } 386 | 387 | TRY(thread_create(task, &thread)); 388 | 389 | mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exc); 390 | 391 | TRY(mach_port_insert_right(mach_task_self(), exc, exc, MACH_MSG_TYPE_MAKE_SEND)); 392 | 393 | exception_mask_t em[2]; 394 | exception_handler_t eh[2]; 395 | exception_behavior_t eb[2]; 396 | thread_state_flavor_t ef[2]; 397 | mach_msg_type_number_t em_count = 2; 398 | 399 | TRY(task_swap_exception_ports(task, EXC_MASK_BAD_ACCESS, exc, EXCEPTION_STATE_IDENTITY, state_flavor, em, &em_count, eh, eb, ef)); 400 | ASSERTR(em_count <= 1, KERN_FAILURE); 401 | 402 | TRY(thread_set_state(thread, state_flavor, &state.nat, state_count)); 403 | 404 | TRY(thread_resume(thread)); 405 | 406 | // We expect three exceptions: one from thread when it returns, one from the new thread when it calls the fake handler, and one from the new thread when it returns from dlopen. 407 | bool started_dlopen = false; 408 | while(1) { 409 | struct exception_message msg; 410 | TRY(mach_msg_overwrite(NULL, MACH_RCV_MSG, 0, sizeof(msg), exc, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, (void *) &msg, sizeof(msg))); 411 | //fprintf(stderr, "got a message\n"); 412 | ASSERTR((msg.Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && 413 | (msg.msgh_body.msgh_descriptor_count != 0) && 414 | (msg.Head.msgh_size >= offsetof(struct exception_message, old_state)) && 415 | (msg.old_stateCnt == state_count) && 416 | (msg.Head.msgh_size >= offsetof(struct exception_message, old_state) + msg.old_stateCnt * sizeof(natural_t)), KERN_FAILURE); 417 | memcpy(&state, msg.old_state, sizeof(state)); 418 | 419 | if(msg.thread.name == thread) { 420 | TRY(thread_terminate(thread)); 421 | } else { 422 | bool cond = false; 423 | 424 | switch(cputype) { 425 | #ifdef __arm__ 426 | case CPU_TYPE_ARM: cond = (state.arm.pc & ~1) == 0xdeadbeee; break; 427 | #endif 428 | #if defined(__i386__) || defined(__x86_64__) 429 | case CPU_TYPE_X86: cond = state.x86.eip == 0xdeadbeef; break; 430 | case CPU_TYPE_X86_64: cond = state.x64.rip == 0xdeadbeef; break; 431 | #endif 432 | #ifdef __ppc__ 433 | case CPU_TYPE_POWERPC: 434 | case CPU_TYPE_POWERPC64: cond = state.ppc.srr0 == 0xdeadbeef; break; 435 | #endif 436 | } 437 | 438 | if(!cond) { 439 | // let the normal crash mechanism handle it 440 | task_set_exception_ports(task, em[0], eh[0], eb[0], ef[0]); 441 | ASSERTR(0, KERN_FAILURE); 442 | } else if(started_dlopen) { 443 | TRY(thread_terminate(msg.thread.name)); 444 | break; 445 | } else { 446 | switch(cputype) { 447 | #ifdef __arm__ 448 | case CPU_TYPE_ARM: 449 | state.arm.r[0] = (uint32_t) stack_address; 450 | state.arm.r[1] = RTLD_LAZY; 451 | state.arm.pc = (uint32_t) addrs.dlopen; 452 | state.arm.lr = 0xdeadbeef; 453 | break; 454 | #endif 455 | #if defined(__i386__) || defined(__x86_64__) 456 | case CPU_TYPE_X86: 457 | { 458 | uint32_t stack_stuff[3] = {0xdeadbeef, (uint32_t) stack_address, RTLD_LAZY}; 459 | TRY(mach_vm_write(task, state.x86.esp, address_cast(&stack_stuff), sizeof(stack_stuff))); 460 | } 461 | state.x86.eip = (uint32_t) addrs.dlopen; 462 | break; 463 | case CPU_TYPE_X86_64: 464 | { 465 | uint64_t stack_stuff = 0xdeadbeef; 466 | TRY(mach_vm_write(task, state.x64.rsp, address_cast(&stack_stuff), sizeof(stack_stuff))); 467 | } 468 | state.x64.rip = addrs.dlopen; 469 | state.x64.rdi = stack_address; 470 | state.x64.rsi = RTLD_LAZY; 471 | break; 472 | #endif 473 | #ifdef __ppc__ 474 | case CPU_TYPE_POWERPC: 475 | case CPU_TYPE_POWERPC64: 476 | cond = state.ppc.srr0 == 0xdeadbeef; 477 | state.ppc.srr0 = addrs.dlopen; 478 | state.ppc.r[3] = stack_address; 479 | state.ppc.r[4] = RTLD_LAZY; 480 | state.ppc.lr = 0xdeadbeef; 481 | break; 482 | #endif 483 | } 484 | 485 | struct exception_reply reply; 486 | memcpy(&reply.Head, &msg.Head, sizeof(mach_msg_header_t)); 487 | reply.Head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; 488 | reply.Head.msgh_size = offsetof(struct exception_reply, new_state) + state_count * sizeof(natural_t); 489 | reply.Head.msgh_id += 100; 490 | memcpy(&reply.NDR, &msg.NDR, sizeof(NDR_record_t)); 491 | reply.RetCode = 0; 492 | reply.flavor = state_flavor; 493 | reply.new_stateCnt = state_count; 494 | memcpy(&reply.new_state, &state, sizeof(state)); 495 | 496 | TRY(thread_set_state(msg.thread.name, state_flavor, &state.nat, state_count)); 497 | TRY(mach_msg(&reply.Head, MACH_SEND_MSG, reply.Head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL)); 498 | started_dlopen = true; 499 | } 500 | } 501 | } 502 | 503 | bad: 504 | if(stack_address) vm_deallocate(task, stack_address, stack_size); 505 | if(thread) { 506 | thread_terminate(thread); 507 | mach_port_deallocate(mach_task_self(), thread); 508 | } 509 | if(task) mach_port_deallocate(mach_task_self(), task); 510 | if(exc) mach_port_deallocate(mach_task_self(), exc); 511 | return kr; 512 | } 513 | --------------------------------------------------------------------------------