static int repack(bool flatten, const char *dstpath, const char *srcpath) { char *src_zip = (char *)map_file(srcpath); if (!src_zip) { printf("Could not open zip file.\n"); return -1; } int fd = creat(dstpath, 0777); if (fd == -1) { printf("can't open output file\n"); return -1; } struct cdir_end *dirend = (struct cdir_end *)(src_zip + zip_size - sizeof(*dirend)); while ((void *)dirend > src_zip && le32toh(dirend->signature) != 0x06054b50) dirend = (struct cdir_end *)((char *)dirend - 1); if (le32toh(dirend->signature) != 0x06054b50) { printf("couldn't find end of central directory record!\n"); return -1; } uint32_t cdir_offset = le32toh(dirend->cdir_offset); uint16_t cdir_entries = le16toh(dirend->cdir_entries); uint32_t cdir_size = le32toh(dirend->cdir_size); TRACE("Found %d entries. cdir offset at %d\n", cdir_entries, cdir_offset); struct cdir_entry *cdir_start = (struct cdir_entry *)(src_zip + cdir_offset); struct cdir_entry *new_cdir_start = (struct cdir_entry *)malloc(cdir_size); if (!new_cdir_start) { TRACE("couldn't allocate central directory copy\n"); return -1; } memcpy(new_cdir_start, cdir_start, cdir_size); uint32_t lowest_offset = find_lowest_offset(cdir_start, cdir_entries); uint32_t out_offset = simple_write(fd, src_zip, lowest_offset); struct cdir_entry *current_entry = new_cdir_start; uint16_t i = cdir_entries; while (i--) { struct local_file_header *file = (struct local_file_header *)(src_zip + le32toh(current_entry->offset)); int rc; if (flatten) rc = flatten_entry(fd, file, current_entry, out_offset); else rc = squeeze_entry(fd, file, current_entry, out_offset); if (rc) return rc; current_entry = (struct cdir_entry *)((char *)current_entry + cdir_entry_size(current_entry)); } uint32_t new_cdir_offset; if (cdir_offset < lowest_offset) { TRACE("Doing in place cdir replacement at %d\n", cdir_offset); new_cdir_offset = cdir_offset; lseek(fd, SEEK_SET, cdir_offset); simple_write(fd, (char *)new_cdir_start, cdir_size); lseek(fd, SEEK_END, 0); } else { new_cdir_offset = out_offset; TRACE("Appending cdir at %d\n", new_cdir_offset); simple_write(fd, (char *)new_cdir_start, cdir_size); } struct cdir_end end; memcpy(&end, dirend, sizeof(end)); end.cdir_offset = htole32(new_cdir_offset); simple_write(fd, (char *)&end, sizeof(end)); close(fd); munmap(src_zip, zip_size); return 0; }
/* * process the target mach-o header and retrieve all information we will need later on */ static kern_return_t process_target_header(vm_map_t task_port, uint8_t *header, uint32_t header_size, mach_vm_address_t base_address, struct header_info *header_info) { struct mach_header_64 *mh = (struct mach_header_64*)header; uint8_t lib_found = 0; if (UINT_MAX - mh->sizeofcmds < header_size) { LOG_ERROR("overflow?"); return KERN_FAILURE; } if (mh->ncmds == 0 || mh->sizeofcmds == 0) { LOG_ERROR("Bad ncmds or sizeofcmds"); return KERN_FAILURE; } /* find the last command offset */ struct load_command *load_cmd = NULL; uint8_t *load_cmd_addr = (uint8_t*)header + header_size; for (uint32_t i = 0; i < mh->ncmds; i++) { load_cmd = (struct load_command*)load_cmd_addr; /* * 64 bits segment commands */ if (load_cmd->cmd == LC_SEGMENT_64) { struct segment_command_64 *seg_cmd = (struct segment_command_64*)load_cmd_addr; if (seg_cmd->vmaddr > header_info->highestvmaddr) { header_info->highestvmaddr = seg_cmd->vmaddr; header_info->highestvmsize = seg_cmd->vmsize; } /* lookup this section to find out global lowest file offset */ find_lowest_offset(load_cmd_addr, header_info); if (strncmp(seg_cmd->segname, "__TEXT", 16) == 0) { // address of the first section uint8_t *section_addr = load_cmd_addr + sizeof(struct segment_command_64); for (uint32_t x = 0; x < seg_cmd->nsects; x++) { struct section_64 *section_cmd = (struct section_64*)section_addr; if (strncmp(section_cmd->sectname, "__text", 16) == 0) { header_info->text_section_off = section_cmd->offset; break; } section_addr += sizeof(struct section_64); } /* the vmaddr value of __TEXT segment starts at the mach-o header location */ /* base address is memory address where process is loaded */ if (seg_cmd->vmaddr > base_address) { LOG_ERROR("overflow?"); return KERN_FAILURE; } header_info->aslr_slide = base_address - seg_cmd->vmaddr; } else if (strncmp(seg_cmd->segname, "__LINKEDIT", 16) == 0) { header_info->linkedit_size = seg_cmd->vmsize; header_info->linkedit_addr = seg_cmd->vmaddr; header_info->linkedit_offset = seg_cmd->fileoff; } } /* * all other commands we are interested in */ /* find first location of a dynamic library */ // XXX: test the other two commads! else if (load_cmd->cmd == LC_LOAD_DYLIB || load_cmd->cmd == LC_LOAD_WEAK_DYLIB || load_cmd->cmd == LC_REEXPORT_DYLIB) { if (lib_found == 0) { if ((uint32_t)header > (uint32_t)load_cmd_addr) { LOG_ERROR("overflow?"); return KERN_FAILURE; } header_info->first_lib_offset = (uint32_t)(load_cmd_addr - header); lib_found = 1; } header_info->lib_count += 1; } else if (load_cmd->cmd == LC_SYMTAB) { header_info->symtab_cmd = (struct symtab_command*)load_cmd; } else if (load_cmd->cmd == LC_DYLD_INFO_ONLY) { header_info->dyldinfo_cmd = (struct dyld_info_command*)load_cmd; } /* other commands that have file offset information */ else if (load_cmd->cmd == LC_ENCRYPTION_INFO) { struct encryption_info_command *tcmd = (struct encryption_info_command*)load_cmd; if ( tcmd->cryptoff != 0 && ( tcmd->cryptoff < header_info->lowest_fileoff) ) { header_info->lowest_fileoff = tcmd->cryptoff; } } // advance to next command, size field holds the total size of each command, including sections load_cmd_addr += load_cmd->cmdsize; } /* * verify if there's enough free header space to inject the new command * between mach-o header + sizeofcmds and lowest data/code file offset * we found out the lowest file offset above so the difference between the two is the free header space */ if ((mh->sizeofcmds + header_size) > header_info->lowest_fileoff) { LOG_ERROR("overflow?"); return KERN_FAILURE; } header_info->free_space = header_info->lowest_fileoff - (mh->sizeofcmds + header_size); /* read whole __LINKEDIT */ header_info->linkedit_buf = _MALLOC(header_info->linkedit_size, M_TEMP, M_WAITOK); if (header_info->linkedit_buf == NULL) { LOG_ERROR("Can't allocate buffer for __LINKEDIT!"); return KERN_FAILURE; } if (_vm_map_read_user(task_port, header_info->linkedit_addr + header_info->aslr_slide, (void*)header_info->linkedit_buf, header_info->linkedit_size)) { LOG_ERROR("Can't read __LINKEDIT from target!"); /* free memory and return the pointer in a clean state */ _FREE(header_info->linkedit_buf, M_TEMP); header_info->linkedit_buf = NULL; return KERN_FAILURE; } /* set the task port */ header_info->taskport = task_port; return KERN_SUCCESS; }