#include #include #include #include #define printf(...) #define setvbuf(...) extern void *(*dlopen_ptr)(const char *path, int mode); extern void *(*dlsym_ptr)(void *handle, const char *symbol); __asm__(".include \"" CURRENT_DIR "/entry.s\""); inline void exit(int n) { printf("%d\n", n); } inline void memcpy(void *dst, void *src, size_t n) { char *dst_ = (char *)dst, *src_ = (char *)src; while(n--) *dst_++ = *src_++; } inline int memcmp(void *dst, void *src, size_t n) { char *dst_ = (char *)dst, *src_ = (char *)src; while(n--) if(*dst_++ != *src_++) return 1; return 0; } inline uint64_t read_uleb128(uint8_t*& p, uint8_t* end) { uint64_t result = 0; int bit = 0; do { if ( p == end ) { exit(1); break; } uint64_t slice = *p & 0x7f; if ( bit > 63 ) { exit(2); break; } else { result |= (slice << bit); bit += 7; } } while (*p++ & 0x80); return result; } inline void vm_(uint64_t base, void **libs, load_command **commands, void *mem, uint8_t *cmd, size_t size) { uint8_t *p = cmd, *end = cmd + size; int ordinal = 0, libIndex = 0; const char *symbolName; bool done = false; uint8_t segIndex; uintptr_t segOffset; off_t offset; int type; // ported from dyld while ( !done && (p < end) ) { uint8_t immediate = *p & BIND_IMMEDIATE_MASK; uint8_t opcode = *p & BIND_OPCODE_MASK; ++p; switch (opcode) { case BIND_OPCODE_DONE: break; case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: libIndex = immediate; break; case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: libIndex = (int)read_uleb128(p, end); break; case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: // the special ordinals are negative numbers if ( immediate == 0 ) ordinal = 0; else { int8_t signExtended = BIND_OPCODE_MASK | immediate; ordinal = signExtended; } break; case BIND_OPCODE_ADD_ADDR_ULEB: segOffset += read_uleb128(p, end); break; case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: symbolName = (char*)p; while (*p != '\0') ++p; ++p; break; case BIND_OPCODE_SET_TYPE_IMM: type = immediate; break; case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segIndex = immediate; segOffset = read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { uint64_t count = read_uleb128(p, end); uint64_t skip = read_uleb128(p, end); segOffset += count * (skip + sizeof(intptr_t)); break; } case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: case BIND_OPCODE_DO_BIND: { void *res = dlsym_ptr(libs[libIndex], symbolName + 1); offset = ((segment_command_64 *)commands[segIndex])->vmaddr + segOffset - base; printf("%llx (+%lx) %s %d\n", offset, segOffset, symbolName, type); printf("dlsym(libs[%d] == %p, \"%s\") == %p\n", libIndex, libs[libIndex], symbolName + 1, res); if(symbolName[0] == '_') *(void **)((char *)mem + offset) = res; // if not, it's from dyld I guess segOffset += 8; if(opcode == BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED) segOffset += immediate * 8; break; } default: printf("WARNING: unsupported command: 0x%x\n", opcode); // exit(-1); } } } inline void rebase_vm_(uint64_t base, void **libs, load_command **commands, void *map, uint8_t *cmd, size_t size) { uint8_t *p = cmd, *end = cmd + size; uint8_t type = 0; int segIndex = 0; uint64_t segOffset = 0; uint64_t count; uint64_t skip; bool segIndexSet = false; bool stop = false; int ptrSize = 8; while ( !stop && (p < end) ) { uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; uint8_t opcode = *p & REBASE_OPCODE_MASK; ++p; switch (opcode) { case REBASE_OPCODE_DONE: if ( (end - p) > 8 ) exit(100); stop = true; break; case REBASE_OPCODE_SET_TYPE_IMM: type = immediate; break; case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segIndex = immediate; segOffset = read_uleb128(p, end); segIndexSet = true; break; case REBASE_OPCODE_ADD_ADDR_ULEB: segOffset += read_uleb128(p, end); break; case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: segOffset += immediate*ptrSize; break; case REBASE_OPCODE_DO_REBASE_IMM_TIMES: case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: if(opcode == REBASE_OPCODE_DO_REBASE_IMM_TIMES) count = immediate; else count = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { uintptr_t offset = ((segment_command_64 *)commands[segIndex])->vmaddr + segOffset - base; printf("rebase %lx (+%llx)\n", offset, segOffset); *(uintptr_t *)((uintptr_t)map + offset) += ((uintptr_t)map - base); segOffset += ptrSize; } break; case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: { uintptr_t offset = ((segment_command_64 *)commands[segIndex])->vmaddr + segOffset - base; printf("rebase %lx (+%llx)\n", offset, segOffset); *(uintptr_t *)((uintptr_t)map + offset) += ((uintptr_t)map - base); segOffset += read_uleb128(p, end) + ptrSize; break; } case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: count = read_uleb128(p, end); skip = read_uleb128(p, end); for (uint32_t i=0; i < count; ++i) { uintptr_t offset = ((segment_command_64 *)commands[segIndex])->vmaddr + segOffset - base; printf("rebase %lx (+%llx)\n", offset, segOffset); *(uintptr_t *)((uintptr_t)map + offset) += ((uintptr_t)map - base); segOffset += skip + ptrSize; if ( stop ) break; } break; default: exit(101); } } } #define vm(offset, size) vm_(base, libs, commands, map, (uint8_t *)mem + offset, size) #define rebase_vm(offset, size) rebase_vm_(base, libs, commands, map, (uint8_t *)mem + offset, size) extern "C" void load(void *mem, void *args) { setvbuf(stdout, 0, _IONBF, 0); mach_header *header = (mach_header *)mem; load_command* startCmds = (load_command*)((char *)header + sizeof(mach_header_64)); load_command *cmd; printf("%x %x\n", header->magic, MH_MAGIC_64); size_t highest_address = 0; load_command *commands[0x80]; void *libs[0x80 + 1]; int libCount = 1; uint64_t base = 0; char pagezero[] = "__PAGEZERO"; #define LC cmd = startCmds; for (uint32_t i = 0; i < header->ncmds; ++i, cmd = (load_command*)((char *)cmd + cmd->cmdsize)) LC { if(cmd->cmd != LC_SEGMENT_64) continue; auto seg = (segment_command_64 *)cmd; size_t end = seg->vmaddr + seg->vmsize; if(!memcmp(seg->segname, (void *)pagezero, 11)) base = seg->vmsize; if(highest_address < end) { highest_address = end; } commands[i] = cmd; } highest_address -= base; commands[header->ncmds] = 0; printf("%lx\n", highest_address); void *map = mmap(NULL, highest_address, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE|MAP_JIT, -1, 0); uint64_t entry = 0; dysymtab_command *symtab; LC { if(cmd->cmd == LC_SEGMENT_64) { auto seg = (segment_command_64 *)cmd; memcpy((char *)map + seg->vmaddr - base, (char *)mem + seg->fileoff, seg->filesize); } if(cmd->cmd == 0x80000028) { auto entrycmd = (entry_point_command *)cmd; entry = entrycmd->entryoff; } if(cmd->cmd == LC_SYMTAB) { symtab = (dysymtab_command *)cmd; } if(cmd->cmd == LC_LOAD_DYLIB) { auto dylib = (dylib_command *)cmd; libs[libCount++] = dlopen_ptr((const char *)dylib + dylib->dylib.name.offset, RTLD_LAZY); } } LC { printf("cmd: %x\n", cmd->cmd); if(cmd->cmd == LC_DYLD_INFO_ONLY) { auto dyld = (dyld_info_command *)cmd; rebase_vm(dyld->rebase_off, dyld->rebase_size); vm(dyld->bind_off, dyld->bind_size); vm(dyld->lazy_bind_off, dyld->lazy_bind_size); } } if(!entry) { for(size_t i = 0; i < highest_address; i++) { int *cur = (int *)((char *)map + i); if(cur[0] == 0x13371337) { entry = i + 16; printf("%lx %llx\n", i, entry); break; } } } entry += (uint64_t)map; printf("%p\n", (void *)entry); ((void (*)(int, void *))(entry))(1, args); }