Beispiel #1
0
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;
}
Beispiel #2
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;
}