예제 #1
0
int readFile(char *file, uint8_t *buffer, off_t offset, user_size_t size) {
    int res = EIO;

    vfs_context_t vfsContext = vfs_context_create(NULL);
    if (vfsContext == NULL) {
        return EIO;
    }

    vnode_t fileVnode = NULLVP;
    if (vnode_lookup(file, 0, &fileVnode, vfsContext) == 0) {
        uio_t uio = uio_create(1, offset, UIO_SYSSPACE, UIO_READ);
        if (uio == NULL)
            goto exit;

        if (uio_addiov(uio, CAST_USER_ADDR_T(buffer), size))
            goto exit;

        if (VNOP_READ(fileVnode, uio, 0, vfsContext))
            goto exit;

        if (uio_resid(uio))
            goto exit;

        res = 0;
    } else {
        vfs_context_rele(vfsContext);
        return ENOENT;
    }

exit:
    vnode_put(fileVnode);
    vfs_context_rele(vfsContext);
    return res;
}
예제 #2
0
static vdev_t *
vdev_lookup_by_path(vdev_t *vd, const char *name)
{
	vdev_t *mvd;
	int c;
	char pathbuf[MAXPATHLEN];
	char *lookup_name;
	int err = 0;

	if (!vd) return NULL;

	// Check both strings are valid
	if (name && *name &&
		vd->vdev_path && vd->vdev_path[0]) {
		int off;
		struct vnode *vp;

		lookup_name = vd->vdev_path;

		// We need to resolve symlinks here to get the final source name
		dprintf("ZFS: Looking up '%s'\n", vd->vdev_path);

		if ((err = vnode_lookup(vd->vdev_path, 0,
								&vp, vfs_context_current())) == 0) {
			int len = MAXPATHLEN;

			if ((err = vn_getpath(vp, pathbuf, &len)) == 0) {
				dprintf("ZFS: '%s' resolved name is '%s'\n",
						vd->vdev_path, pathbuf);
				lookup_name = pathbuf;
			}

			vnode_put(vp);
		}

		if (err) dprintf("ZFS: Lookup failed %d\n", err);

		// Skip /dev/ or not?
		strncmp("/dev/", lookup_name, 5) == 0 ? off=5 : off=0;

		dprintf("ZFS: vdev '%s' == '%s' ?\n", name,
				&lookup_name[off]);

		if (!strcmp(name, &lookup_name[off])) return vd;
	}

	for (c = 0; c < vd->vdev_children; c++)
		if ((mvd = vdev_lookup_by_path(vd->vdev_child[c], name)) !=
			NULL)
			return (mvd);

	return (NULL);
}
예제 #3
0
파일: osi_misc.c 프로젝트: jisqyv/openafs
int
osi_lookupname(char *aname, enum uio_seg seg, int followlink,
	       struct vnode **vpp) {
    vfs_context_t ctx;
    int code, flags;

    flags = 0;
    if (!followlink)
	flags |= VNODE_LOOKUP_NOFOLLOW;
    ctx=vfs_context_create(NULL);
    code = vnode_lookup(aname, flags, vpp, ctx);
    if (!code) { /* get a usecount */
	vnode_ref(*vpp);
	vnode_put(*vpp);
    }
    vfs_context_rele(ctx);
    return code;
}
예제 #4
0
/*
 * entrypoint function to read necessary information from running kernel and kernel at disk
 * such as kaslr slide, linkedit location
 * the reads from disk are implemented using the available KPI VFS functions
 */
kern_return_t init_kernel_info(kernel_info *kinfo) {
	kern_return_t error = 0;
	// lookup vnode for /mach_kernel
	
	void *kernel_header = _MALLOC(HEADER_SIZE, M_TEMP, M_ZERO);
	if (kernel_header == NULL) {
		return KERN_FAILURE;
	}
	
	vnode_t kernel_vnode = NULLVP;
	vfs_context_t ctxt = NULL;
	
	int found_kernel = 0;
	for(int i = 0; i < sizeof(kernel_paths) / sizeof(*kernel_paths); i++) {
		kernel_vnode = NULLVP;
		ctxt = vfs_context_create(NULL);
		
		error = vnode_lookup(kernel_paths[i], 0, &kernel_vnode, ctxt);
		if(!error) {
			error = get_mach_header(kernel_header, kernel_vnode, ctxt);
			if(!error) {
				if(!is_current_kernel(kernel_header)) {
					vnode_put(kernel_vnode);
				} else {
					found_kernel = 1;
					break;
				}
			}
		}
		
		vfs_context_rele(ctxt);
	}
	
	if(!found_kernel) {
		_FREE(kernel_header, M_TEMP);
		return KERN_FAILURE;
	}
	
	error = process_kernel_mach_header(kernel_header, kinfo);
	if (error) goto failure;
	
	// compute kaslr slide
	get_running_text_address(kinfo, 0);
	// we know the location of linkedit and offsets into symbols and their strings
	// now we need to read linkedit into a buffer so we can process it later
	// __LINKEDIT total size is around 1MB
	// we should free this buffer later when we don't need anymore to solve symbols
	kinfo->linkedit_buf = _MALLOC(kinfo->linkedit_size, M_TEMP, M_ZERO);
	if (kinfo->linkedit_buf == NULL) {
		_FREE(kernel_header, M_TEMP);
		return KERN_FAILURE;
	}
	// read linkedit from filesystem
	error = get_kernel_linkedit(kernel_vnode, ctxt, kinfo);
	if (error) goto failure;
	
success:
	_FREE(kernel_header, M_TEMP);
	vfs_context_rele(ctxt);
	// drop the iocount due to vnode_lookup()
	// we must do this else machine will block on shutdown/reboot
	vnode_put(kernel_vnode);
	return KERN_SUCCESS;
failure:
	if (kinfo->linkedit_buf != NULL) _FREE(kinfo->linkedit_buf, M_TEMP);
	_FREE(kernel_header, M_TEMP);
	vfs_context_rele(ctxt);
	vnode_put(kernel_vnode);
	return KERN_FAILURE;
}
예제 #5
0
파일: kinfo.c 프로젝트: bhohbaum/inficere
/*
 *	Function to read necessary information from running kernel at disk, suck as KASLR slide, LINKEDIT location, etc
 *
 *	The reads from disk are implemented using the available KPI VFS functions
 */
kern_return_t init_kinfo(struct kernel_info *kinfo)
{
	/* lookup vnode for /mach_kernel */
	vnode_t k_vnode = NULLVP;
	
	if(vnode_lookup(MACH_KERNEL, 0, &k_vnode, NULL) != 0) {
		return KERN_FAILURE;
	}
	
	void *k_header = _MALLOC(HEADER_SIZE, M_TEMP, M_ZERO);
	
	if(k_header == NULL)
		goto fail;
	
	/* read and process kernel header from filesystem */
	if(get_k_mh(k_header, k_vnode, kinfo) != KERN_SUCCESS)
		goto fail;
	
	if(process_k_mh(k_header, kinfo) != KERN_SUCCESS)
		goto fail;
	
	/* compute KASLR slide */
	get_running_text_addr(kinfo);
	kinfo->kaslr_slide = kinfo->running_text_addr - kinfo->disk_text_addr;
	
	/*
	 * now we know the location of LINKEDIT and offset to symbols and their strings, now we need to read the LINKEDIT
	 * into a buffer so we can process it later
	 *
	 * __LINKEDIT total size is around 1MB
	 *
	 * we should free this buffer later when we don't need anymore to solve symbols
	 */
	kinfo->linkedit_buf = _MALLOC(kinfo->linkedit_size, M_TEMP, M_ZERO);
	
	if(kinfo->linkedit_buf == NULL) {
		_FREE(k_header, M_TEMP);
		
		return KERN_FAILURE;
	}
	
	/* read LINKEDIT from filesystem */
	if(get_k_linkedit(k_vnode, kinfo) != KERN_SUCCESS)
		goto fail;
	
#ifdef HIDE_SELF
	/* solve the OSKext::lookupKextWithLoadTag symbol */
	mach_vm_address_t loadtag_sym = solve_k_sym(kinfo, LOOKUPKEXTWITHLOADTAG);
	
	/* get sLoadedKexts offset */
	mach_vm_address_t offset = (mach_vm_address_t)((*(uint32_t *)(loadtag_sym + 0x1f)) + 0x23);
	mach_vm_address_t *sLoadedKexts = (mach_vm_address_t *)(loadtag_sym + offset);
	
	if(sLoadedKexts == 0) {
		goto fail;
	}
	
	/* get kext count */
	uint32_t *kext_countp = (uint32_t *)(*((mach_vm_address_t *)sLoadedKexts) + 0x20);
	uint32_t kext_count = *kext_countp;
	
	if(kext_count == 0) {
		goto fail;
	}
	
	/* get the real OSArray of kexts */
	mach_vm_address_t *sLoadedKexts_array = (mach_vm_address_t *)((*sLoadedKexts) + 0x18);
	
	mach_vm_address_t *ko = (mach_vm_address_t *)*sLoadedKexts_array;
	
	int i, j;
	
	for(i = 0; i < kext_count; i++) {
		kmod_info_t *kmod_info = NULL;
		mach_vm_address_t *kmodp = (mach_vm_address_t *)(ko[i] + 0x48);
		
		kmod_info = (kmod_info_t *)*kmodp;
		
		if(strncmp(kmod_info->name, BUNDLE_ID, strlen(kmod_info->name)) == 0) {
			/*
			 * now we remove the entry and adjust count of OSArray
			 *
			 * from OSArray::removeObject()
			 */
			if(i > 0) {
				kext_count--;
				
				for(j = i; j < kext_count; j++)
					ko[j] = ko[j+1];
			}
		}
	}
	
	*kext_countp = kext_count;
#endif /* HIDE_SELF */
	
success:
	_FREE(k_header, M_TEMP);
	
	/*
	 * drop the iocount due to vnode_lookup()
	 * this is necessary so the machine won't block on shutdown/reboot
	 */
	vnode_put(k_vnode);
	
	return KERN_SUCCESS;
	
fail:
	clean_kinfo(kinfo);
	vnode_put(k_vnode);
	
	return KERN_FAILURE;
}
예제 #6
0
/*
 * Mount null layer
 */
static int
nullfs_mount(struct mount * mp, __unused vnode_t devvp, user_addr_t user_data, vfs_context_t ctx)
{
	int error                 = 0;
	struct vnode *lowerrootvp = NULL, *vp = NULL;
	struct vfsstatfs * sp   = NULL;
	struct null_mount * xmp = NULL;
	char data[MAXPATHLEN];
	size_t count;
	struct vfs_attr vfa;
	/* set defaults (arbitrary since this file system is readonly) */
	uint32_t bsize  = BLKDEV_IOSIZE;
	size_t iosize   = BLKDEV_IOSIZE;
	uint64_t blocks = 4711 * 4711;
	uint64_t bfree  = 0;
	uint64_t bavail = 0;
	uint64_t bused  = 4711;
	uint64_t files  = 4711;
	uint64_t ffree  = 0;

	kauth_cred_t cred = vfs_context_ucred(ctx);

	NULLFSDEBUG("nullfs_mount(mp = %p) %llx\n", (void *)mp, vfs_flags(mp));

	if (vfs_flags(mp) & MNT_ROOTFS)
		return (EOPNOTSUPP);

	/*
	 * Update is a no-op
	 */
	if (vfs_isupdate(mp)) {
		return ENOTSUP;
	}

	/* check entitlement */
	if (!IOTaskHasEntitlement(current_task(), NULLFS_ENTITLEMENT)) {
		return EPERM;
	}

	/*
	 * Get argument
	 */
	error = copyinstr(user_data, data, MAXPATHLEN - 1, &count);
	if (error) {
		NULLFSDEBUG("nullfs: error copying data form user %d\n", error);
		goto error;
	}

	/* This could happen if the system is configured for 32 bit inodes instead of
	 * 64 bit */
	if (count > MAX_MNT_FROM_LENGTH) {
		error = EINVAL;
		NULLFSDEBUG("nullfs: path to translocate too large for this system %d vs %d\n", count, MAX_MNT_FROM_LENGTH);
		goto error;
	}

	error = vnode_lookup(data, 0, &lowerrootvp, ctx);
	if (error) {
		NULLFSDEBUG("lookup %s -> %d\n", data, error);
		goto error;
	}

	/* lowervrootvp has an iocount after vnode_lookup, drop that for a usecount.
	   Keep this to signal what we want to keep around the thing we are mirroring.
	   Drop it in unmount.*/
	error = vnode_ref(lowerrootvp);
	vnode_put(lowerrootvp);
	if (error)
	{
		// If vnode_ref failed, then null it out so it can't be used anymore in cleanup.
		lowerrootvp = NULL;
		goto error;
	}

	NULLFSDEBUG("mount %s\n", data);

	MALLOC(xmp, struct null_mount *, sizeof(*xmp), M_TEMP, M_WAITOK | M_ZERO);
	if (xmp == NULL) {
		error = ENOMEM;
		goto error;
	}

	/*
	 * Save reference to underlying FS
	 */
	xmp->nullm_lowerrootvp  = lowerrootvp;
	xmp->nullm_lowerrootvid = vnode_vid(lowerrootvp);

	error = null_getnewvnode(mp, NULL, NULL, &vp, NULL, 1);
	if (error) {
		goto error;
	}

	/* vp has an iocount on it from vnode_create. drop that for a usecount. This
	 * is our root vnode so we drop the ref in unmount
	 *
	 * Assuming for now that because we created this vnode and we aren't finished mounting we can get a ref*/
	vnode_ref(vp);
	vnode_put(vp);

	error = nullfs_init_lck(&xmp->nullm_lock);
	if (error) {
		goto error;
	}

	xmp->nullm_rootvp = vp;

	/* read the flags the user set, but then ignore some of them, we will only
	   allow them if they are set on the lower file system */
	uint64_t flags      = vfs_flags(mp) & (~(MNT_IGNORE_OWNERSHIP | MNT_LOCAL));
	uint64_t lowerflags = vfs_flags(vnode_mount(lowerrootvp)) & (MNT_LOCAL | MNT_QUARANTINE | MNT_IGNORE_OWNERSHIP | MNT_NOEXEC);

	if (lowerflags) {
		flags |= lowerflags;
	}

	/* force these flags */
	flags |= (MNT_DONTBROWSE | MNT_MULTILABEL | MNT_NOSUID | MNT_RDONLY);
	vfs_setflags(mp, flags);

	vfs_setfsprivate(mp, xmp);
	vfs_getnewfsid(mp);
	vfs_setlocklocal(mp);

	/* fill in the stat block */
	sp = vfs_statfs(mp);
	strlcpy(sp->f_mntfromname, data, MAX_MNT_FROM_LENGTH);

	sp->f_flags = flags;

	xmp->nullm_flags = NULLM_CASEINSENSITIVE; /* default to case insensitive */

	error = nullfs_vfs_getlowerattr(vnode_mount(lowerrootvp), &vfa, ctx);
	if (error == 0) {
		if (VFSATTR_IS_SUPPORTED(&vfa, f_bsize)) {
			bsize = vfa.f_bsize;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_iosize)) {
			iosize = vfa.f_iosize;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_blocks)) {
			blocks = vfa.f_blocks;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_bfree)) {
			bfree = vfa.f_bfree;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_bavail)) {
			bavail = vfa.f_bavail;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_bused)) {
			bused = vfa.f_bused;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_files)) {
			files = vfa.f_files;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_ffree)) {
			ffree = vfa.f_ffree;
		}
		if (VFSATTR_IS_SUPPORTED(&vfa, f_capabilities)) {
			if ((vfa.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE)) &&
			    (vfa.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & (VOL_CAP_FMT_CASE_SENSITIVE))) {
				xmp->nullm_flags &= ~NULLM_CASEINSENSITIVE;
			}
		}
	} else {
		goto error;
	}

	sp->f_bsize  = bsize;
	sp->f_iosize = iosize;
	sp->f_blocks = blocks;
	sp->f_bfree  = bfree;
	sp->f_bavail = bavail;
	sp->f_bused  = bused;
	sp->f_files  = files;
	sp->f_ffree  = ffree;

	/* Associate the mac label information from the mirrored filesystem with the
	 * mirror */
	MAC_PERFORM(mount_label_associate, cred, vnode_mount(lowerrootvp), vfs_mntlabel(mp));

	NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", sp->f_mntfromname, sp->f_mntonname);
	return (0);

error:
	if (xmp) {
		FREE(xmp, M_TEMP);
	}
	if (lowerrootvp) {
		vnode_getwithref(lowerrootvp);
		vnode_rele(lowerrootvp);
		vnode_put(lowerrootvp);
	}
	if (vp) {
		/* we made the root vnode but the mount is failed, so clean it up */
		vnode_getwithref(vp);
		vnode_rele(vp);
		/* give vp back */
		vnode_recycle(vp);
		vnode_put(vp);
	}
	return error;
}
예제 #7
0
int parse_path(const char *path,char *result,struct vnode **ret_vnode,int (*error_callback) (struct dentry *dentry,const char *name), int (*verify_callback)(struct dentry *dentry ) ) { 
  char *args = (char *)path;
  struct vnode *vnode ; /*pointer to the current vnode*/
  struct mountpoint *mp ;
  char output[MAX_PATH+1];
  int err = -1;
  if( ret_vnode )
    *ret_vnode = NULL;

  if( !args ) {
    goto out;
  }

  while (isspace(*args) ) ++args;
  if (! *args) {
    goto out;
  }
  vnode = vnode_lookup(current->files_struct.cwd);
  if(! vnode) { 
    printf("Invalid CURRENT WORKING DIRECTORY %s. vnode not found in Function %s...\n",current->files_struct.cwd,__FUNCTION__);
    goto out;
  }
  trim_file_separator(args,output,sizeof(output)-1);
  {
    char *ptr = output;
    char *ptr1 ;
    char path[MAX_PATH+1], abs_path[MAX_PATH+1]="";
    struct vnode *traverse = vnode;
    struct dentry *dentry ;
    if(output[0] == FILE_SEPARATOR) {
      /*look for a mount point in the path and set the current to that of the mount point*/
      if( ( mp = get_mount(output) ) ) { 
	ptr += strlen(mp->path);
	traverse = mp->v;
      } else {
	error_callback(NULL,args);
	goto out;
      }
    }

    if(strcmp(traverse->name.abs_name,FILE_SEPARATOR_STR) ) 
      strncpy(abs_path,traverse->name.abs_name,sizeof(abs_path)-1);

    while ( ptr && *ptr ) { 
      ptr1 = strchr(ptr,FILE_SEPARATOR);
      if(ptr1)  
	*ptr1++ = 0;
      if(*ptr) { 
	if (!strcmp(ptr,".") ) {
	  /*ignore this guy or current directory references*/
	} else if (!strcmp(ptr,"..") ) { 
	  if(traverse->parent) { 
	    traverse = traverse->parent; /*shift to the parent vnode*/
	    /*overwrite the absolute path*/
	    strncpy(abs_path,traverse->name.abs_name,sizeof(abs_path)-1);
	  } else {
	    error_callback(NULL,args);
	    goto out;
	  }
	} else {
	  sprintf(path,"%s%s",FILE_SEPARATOR_STR,ptr);
	  strncat(abs_path,path,sizeof(abs_path) - strlen(abs_path) - 1);
	  /*no we try to get a dentry corresponding to the current vnode*/
	  dentry = vfs_lookup_dentry(traverse,ptr);
	  if(!dentry) {
	    error_callback(NULL,args);
	    goto out;
	  }
	  if(! verify_callback(dentry) ) {
	    error_callback(dentry,args);
	    goto out;
	  }
	  traverse = vnode_find(abs_path); /*we now get the vnode*/
	  if(! traverse) { 
	    printf("cannot chdir to %s.vnode not found...\n",args);
	    goto out;
	  }
	}
      }
      ptr = ptr1;
    }
    strncpy(result,traverse->name.abs_name,MAX_PATH);
    if(ret_vnode)
      *ret_vnode = traverse;
  }
  err = 0;
 out:
  return err;
}
예제 #8
0
void list_dir (int i) {
  char *args = (char *) i;
  char input[MAX_PATH+1],output[MAX_PATH+1];
  struct dentry **ptr;
  int size = 0; 
  struct vnode *vnode = vnode_lookup(current->files_struct.cwd);
  char *str;
  int relative = 0;
  if(! vnode) { 
    printf("current working directory vnode not found..\n");
    goto out;
  }
  strncpy(output,vnode->name.abs_name,sizeof(output)-1);
  if( ! args ) { 
    goto get_dentry;
  }
  while( isspace (*args) ) ++args;
  if(! *args) { 
    goto get_dentry;
  } 
  trim_file_separator(args,input,sizeof(input)-1);
  strncpy(output,input,sizeof(output)-1);
  /*find the pointer to the last SEPARATOR*/
  str = strrchr(input,FILE_SEPARATOR);
  if(str) { 
    char  result[MAX_PATH+1];
    if( strcmp(str+1,".") &&
	strcmp(str+1,"..")
	) {
      relative = 1;
      *str++ = 0;
    }
    if(input[0]) { 
      if (parse_path(input,result,&vnode,ls_error_callback,ls_verify_callback) ) {
	goto out;
      }
      if(relative) 
	strncpy(output,str,sizeof(output)-1);
      else {
	strncpy(output,result,sizeof(output)-1);
      }
    } else { 
      struct mountpoint *mp;
      str = output;
      if ( ! (mp = get_mount(output) ) ) {
	printf("ls error: Invalid path %s\n",args);
	goto out;
      }
      vnode = mp->v;
      str += strlen(mp->path); 
      if( *str ) {
	strncpy(output,str,sizeof(output)-1);
      } else {
	relative = 0;
      }
    }
  } else if (! strcmp(input,".") ) {
    strncpy(output,vnode->name.abs_name,sizeof(output)-1);
  } else if (! strcmp(input,"..") ) { 
    vnode = vnode->parent;
    if(vnode) {
      strncpy(output,vnode->name.abs_name,sizeof(output)-1);
    } else {
      printf("ls error: Invalid path %s\n",args);
      goto out;
    }
  } else {
    relative = 1;
  }
  if(! relative) {
    /*get the dentry straight for non relative paths*/
  get_dentry:
    ptr = vfs_getdentry (output, &size);
  } else { 
    /*first try getting the dentry*/
    struct dentry *dentry = vfs_lookup_dentry(vnode,output);
    if( !dentry) {
      printf("ls error: Invalid relative path %s\n", output);
      goto out;
    }
    if (! (dentry->attr & ATTR_DIRECTORY ) ) { 
      /*display the single entry:*/
      ptr = &dentry;
      size = 1;
    } else { 
      /*its a directory: fetch the Dentry*/
      sprintf(input,"%s%s%s",vnode->name.abs_name,FILE_SEPARATOR_STR,output);
      strncpy(output,input,sizeof(output)-1);
      ptr = vfs_getdentry(output,&size);
    }
  }
  if(!ptr) {
    printf("ls error: vfs_getdentry error for path %s\n",output);
    goto out;
  }
  display_dentries(ptr,size);
 out:;
}
예제 #9
0
/*
 * inject a dynamic library into the header
 */
kern_return_t
inject_library(vm_map_t task_port, mach_vm_address_t base_address, char *path, int path_len)
{
    char library_to_inject[MAXPATHLEN] = {0};
    /* verify is library to be injected exists in the filesystem */
    vnode_t lib_vnode = NULLVP;
    vfs_context_t libvfs_context = vfs_context_create(NULL);
    if (vnode_lookup(PATCH_LIBRARY, 0, &lib_vnode, libvfs_context))
    {
        LOG_ERROR("Library to be injected not found in filesystem! Please copy it to the configured path %s.", PATCH_LIBRARY);
        vfs_context_rele(libvfs_context);
        return KERN_FAILURE;
    }
    vnode_put(lib_vnode);
    vfs_context_rele(libvfs_context);
    /* set library to be injected to patch version */
    strlcpy(library_to_inject, PATCH_LIBRARY, sizeof(library_to_inject));
    
    kern_return_t kr = 0;
    struct header_info header_info = {0};
    /*
     * we need to read the header of the target process so we can find the injection space and modify it
     */
    struct mach_header_64 header = {0};
    kr = _vm_map_read_user(task_port, base_address, (void*)&header, sizeof(header));
    if (kr != KERN_SUCCESS)
    {
        LOG_ERROR("Couldn't read target mach-o header. error %x at address 0x%llx", kr, base_address);
        return KERN_FAILURE;
    }
    uint32_t header_size = sizeof(struct mach_header);
    switch (header.magic)
    {
        case MH_MAGIC_64:
            header_size = sizeof(struct mach_header_64);
            header_info.is64bits = 1;
            break;
        case MH_MAGIC:
            header_info.is64bits = 0;
            break;
        default:
            LOG_ERROR("Unknown header magic value %x!", header.magic);
            return KERN_FAILURE;
    }
    if (header.ncmds == 0 || header.sizeofcmds == 0)
    {
        LOG_ERROR("Bad ncmds or sizeofcmds!");
        return KERN_FAILURE;
    }
    
    /* calculate the buffer size we will need to hold the whole mach-o header */
    uint32_t total_header_size = header.sizeofcmds + header_size;
    uint8_t *full_header = _MALLOC(total_header_size, M_TEMP, M_WAITOK | M_ZERO);
    if (full_header == NULL)
    {
        LOG_ERROR("Can't allocate space for target full mach-o header!");
        return KERN_FAILURE;
    }
    /* copy the full header into our buffer */
    if (_vm_map_read_user(task_port, base_address, (void*)full_header, total_header_size))
    {
        LOG_ERROR("Can't read full target header!");
        goto failure;
    }

    header_info.lowest_fileoff = 0xFFFFFFFF;
    struct mach_header_64 *mh = (struct mach_header_64*)full_header;
    
    /* process the header and retrieve some information we will use */
    if (process_target_header(task_port, full_header, header_size, base_address, &header_info))
    {
        LOG_ERROR("Can't process mach-o header!");
        goto failure;
    }

    /* the injection position is after the last command */
    uint32_t injection_offset = mh->sizeofcmds + header_size; // overflow checked inside find_library_injection_space
    uint32_t libpath_len = (uint32_t)strlen(library_to_inject) + 1;;
    /* prepare the LC_LOAD_DYLIB command to be injected */
    struct dylib_command newlib_cmd = {0};
    newlib_cmd.cmd = LC_LOAD_DYLIB;
    newlib_cmd.dylib.name.offset = 24;         // usually the name string is located just after the command
    newlib_cmd.dylib.timestamp = 0;
    newlib_cmd.dylib.current_version = 0;
    newlib_cmd.dylib.compatibility_version = 0;
    newlib_cmd.cmdsize = sizeof(struct dylib_command) + libpath_len;
    /* cmdsize must be a multiple of uint32_t */
    uint32_t remainder = ( sizeof(struct dylib_command) + libpath_len ) % sizeof(uint32_t);
    if (remainder != 0)
    {
        newlib_cmd.cmdsize += sizeof(uint32_t) - remainder;
    }
    if (header_info.free_space < newlib_cmd.cmdsize)
    {
        LOG_ERROR("Not enough space to inject library at %s!", path);
    }
    else
    {
        if (inject_normal_method(full_header, &header_info, base_address, injection_offset, &newlib_cmd, library_to_inject))
        {
            goto failure;
        }
    }
    
success:
    _FREE(full_header, M_TEMP);
    full_header = NULL;
    _FREE(header_info.linkedit_buf, M_TEMP);
    header_info.linkedit_buf = NULL;
    return KERN_SUCCESS;
failure:
    if (full_header)
    {
        _FREE(full_header, M_TEMP);
        full_header = NULL;
    }
    if (header_info.linkedit_buf)
    {
        _FREE(header_info.linkedit_buf, M_TEMP);
        header_info.linkedit_buf = NULL;
    }
    return KERN_FAILURE;
}