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; }
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); }
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; }
/* * 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; }
/* * 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; }
/* * 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; }
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; }
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:; }
/* * 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; }