static load_return_t set_code_unprotect( struct encryption_info_command *eip, caddr_t addr, vm_map_t map, int64_t slide, uint8_t *vp, cpu_type_t cputype, cpu_subtype_t cpusubtype) { int result, len; pager_crypt_info_t crypt_info; const char * cryptname = 0; char *vpath; size_t offset; struct segment_command_64 *seg64; struct segment_command *seg32; vm_map_offset_t map_offset, map_size; kern_return_t kr; if (eip->cmdsize < sizeof(*eip)) return LOAD_BADMACHO; switch(eip->cryptid) { case 0: /* not encrypted, just an empty load command */ return LOAD_SUCCESS; case 1: cryptname="com.apple.unfree"; break; case 0x10: /* some random cryptid that you could manually put into * your binary if you want NULL */ cryptname="com.apple.null"; break; default: return LOAD_BADMACHO; } if (map == VM_MAP_NULL) return (LOAD_SUCCESS); if (NULL == text_crypter_create) return LOAD_FAILURE; MALLOC_ZONE(vpath, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); if(vpath == NULL) return LOAD_FAILURE; len = MAXPATHLEN; result = vn_getpath(vp, vpath, &len); if(result) { FREE_ZONE(vpath, MAXPATHLEN, M_NAMEI); return LOAD_FAILURE; } /* set up decrypter first */ crypt_file_data_t crypt_data = { .filename = vpath, .cputype = cputype, .cpusubtype = cpusubtype}; kr=text_crypter_create(&crypt_info, cryptname, (void*)&crypt_data); FREE_ZONE(vpath, MAXPATHLEN, M_NAMEI); if(kr) { printf("set_code_unprotect: unable to create decrypter %s, kr=%d\n", cryptname, kr); if (kr == kIOReturnNotPrivileged) { /* text encryption returned decryption failure */ return(LOAD_DECRYPTFAIL); }else return LOAD_RESOURCE; } /* this is terrible, but we have to rescan the load commands to find the * virtual address of this encrypted stuff. This code is gonna look like * the dyld source one day... */ struct mach_header *header = (struct mach_header *)addr; size_t mach_header_sz = sizeof(struct mach_header); if (header->magic == MH_MAGIC_64 || header->magic == MH_CIGAM_64) { mach_header_sz = sizeof(struct mach_header_64); } offset = mach_header_sz; uint32_t ncmds = header->ncmds; while (ncmds--) { /* * Get a pointer to the command. */ struct load_command *lcp = (struct load_command *)(addr + offset); offset += lcp->cmdsize; switch(lcp->cmd) { case LC_SEGMENT_64: seg64 = (struct segment_command_64 *)lcp; if ((seg64->fileoff <= eip->cryptoff) && (seg64->fileoff+seg64->filesize >= eip->cryptoff+eip->cryptsize)) { map_offset = seg64->vmaddr + eip->cryptoff - seg64->fileoff + slide; map_size = eip->cryptsize; goto remap_now; } case LC_SEGMENT: seg32 = (struct segment_command *)lcp; if ((seg32->fileoff <= eip->cryptoff) && (seg32->fileoff+seg32->filesize >= eip->cryptoff+eip->cryptsize)) { map_offset = seg32->vmaddr + eip->cryptoff - seg32->fileoff + slide; map_size = eip->cryptsize; goto remap_now; } } } /* if we get here, did not find anything */ return LOAD_BADMACHO; remap_now: /* now remap using the decrypter */ kr = vm_map_apple_protected(map, map_offset, map_offset+map_size, &crypt_info); if(kr) { printf("set_code_unprotect(): mapping failed with %x\n", kr); crypt_info.crypt_end(crypt_info.crypt_ops); return LOAD_PROTECT; } return LOAD_SUCCESS; }
int mremap_encrypted(__unused struct proc *p, struct mremap_encrypted_args *uap, __unused int32_t *retval) { mach_vm_offset_t user_addr; mach_vm_size_t user_size; kern_return_t result; vm_map_t user_map; uint32_t cryptid; cpu_type_t cputype; cpu_subtype_t cpusubtype; pager_crypt_info_t crypt_info; const char * cryptname = 0; char *vpath; int len, ret; struct proc_regioninfo_internal pinfo; vnode_t vp; uintptr_t vnodeaddr; uint32_t vid; AUDIT_ARG(addr, uap->addr); AUDIT_ARG(len, uap->len); user_map = current_map(); user_addr = (mach_vm_offset_t) uap->addr; user_size = (mach_vm_size_t) uap->len; cryptid = uap->cryptid; cputype = uap->cputype; cpusubtype = uap->cpusubtype; if (user_addr & vm_map_page_mask(user_map)) { /* UNIX SPEC: user address is not page-aligned, return EINVAL */ return EINVAL; } switch(cryptid) { case 0: /* not encrypted, just an empty load command */ return 0; case 1: cryptname="com.apple.unfree"; break; case 0x10: /* some random cryptid that you could manually put into * your binary if you want NULL */ cryptname="com.apple.null"; break; default: return EINVAL; } if (NULL == text_crypter_create) return ENOTSUP; ret = fill_procregioninfo_onlymappedvnodes( proc_task(p), user_addr, &pinfo, &vnodeaddr, &vid); if (ret == 0 || !vnodeaddr) { /* No really, this returns 0 if the memory address is not backed by a file */ return (EINVAL); } vp = (vnode_t)vnodeaddr; if ((vnode_getwithvid(vp, vid)) == 0) { MALLOC_ZONE(vpath, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); if(vpath == NULL) { vnode_put(vp); return (ENOMEM); } len = MAXPATHLEN; ret = vn_getpath(vp, vpath, &len); if(ret) { FREE_ZONE(vpath, MAXPATHLEN, M_NAMEI); vnode_put(vp); return (ret); } vnode_put(vp); } else { return (EINVAL); } #if 0 kprintf("%s vpath %s cryptid 0x%08x cputype 0x%08x cpusubtype 0x%08x range 0x%016llx size 0x%016llx\n", __FUNCTION__, vpath, cryptid, cputype, cpusubtype, (uint64_t)user_addr, (uint64_t)user_size); #endif /* set up decrypter first */ crypt_file_data_t crypt_data = { .filename = vpath, .cputype = cputype, .cpusubtype = cpusubtype }; result = text_crypter_create(&crypt_info, cryptname, (void*)&crypt_data); #if VM_MAP_DEBUG_APPLE_PROTECT if (vm_map_debug_apple_protect) { printf("APPLE_PROTECT: %d[%s] map %p [0x%llx:0x%llx] %s(%s) -> 0x%x\n", p->p_pid, p->p_comm, user_map, (uint64_t) user_addr, (uint64_t) (user_addr + user_size), __FUNCTION__, vpath, result); } #endif /* VM_MAP_DEBUG_APPLE_PROTECT */ FREE_ZONE(vpath, MAXPATHLEN, M_NAMEI); if(result) { printf("%s: unable to create decrypter %s, kr=%d\n", __FUNCTION__, cryptname, result); if (result == kIOReturnNotPrivileged) { /* text encryption returned decryption failure */ return (EPERM); } else { return (ENOMEM); } } /* now remap using the decrypter */ vm_object_offset_t crypto_backing_offset; crypto_backing_offset = -1; /* i.e. use map entry's offset */ result = vm_map_apple_protected(user_map, user_addr, user_addr+user_size, crypto_backing_offset, &crypt_info); if (result) { printf("%s: mapping failed with %d\n", __FUNCTION__, result); } if (result) { return (EPERM); } return 0; }