1/* 2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#include <CoreFoundation/CoreFoundation.h> 24#include <IOKit/kext/OSKext.h> 25#include <IOKit/kext/OSKextPrivate.h> 26#include <IOKit/kext/fat_util.h> 27 28#include <System/libkern/mkext.h> 29#include <sys/types.h> 30#include <sys/stat.h> 31#include <sys/mman.h> 32#include <unistd.h> 33#include <libc.h> 34#include <mach-o/arch.h> 35#include <mach-o/fat.h> 36 37#include <mach/mach.h> 38#include <mach/mach_types.h> 39#include <mach/kmod.h> 40#include "kext_tools_util.h" 41 42// not a utility.[ch] customer yet 43static const char * progname = "mkextunpack"; 44static Boolean gVerbose = false; 45 46u_int32_t local_adler32(u_int8_t *buffer, int32_t length); 47 48Boolean getMkextDataForArch( 49 u_int8_t * fileData, 50 size_t fileSize, 51 const NXArchInfo * archInfo, 52 void ** mkextStart, 53 void ** mkextEnd, 54 uint32_t * mkextVersion); 55 56CFArrayRef copyKextsFromMkext2( 57 void * mkextStart, 58 void * mkextEnd, 59 const NXArchInfo * archInfo); 60Boolean writeMkext2EntriesToDirectory( 61 CFArrayRef kexts, 62 char * outputDirectory); 63Boolean writeMkext2ToDirectory( 64 OSKextRef aKext, 65 const char * outputDirectory, 66 CFMutableSetRef kextNames); 67 68CFDictionaryRef extractEntriesFromMkext1( 69 void * mkextStart, 70 void * mkextEnd); 71Boolean uncompressMkext1Entry( 72 void * mkext_base_address, 73 mkext_file * entry_address, 74 CFDataRef * uncompressedEntry); 75Boolean writeMkext1EntriesToDirectory(CFDictionaryRef entries, 76 char * outputDirectory); 77CFStringRef createKextNameFromPlist( 78 CFDictionaryRef entries, CFDictionaryRef kextPlist); 79int getBundleIDAndVersion(CFDictionaryRef kextPlist, unsigned index, 80 char ** bundle_id_out, char ** bundle_version_out); 81 82Boolean writeFileInDirectory( 83 const char * basePath, 84 char * subPath, 85 const char * fileName, 86 const char * fileData, 87 size_t fileLength); 88 89#if 0 90/******************************************************************************* 91* NOTES! 92*******************************************************************************/ 93How to unpack all arches from an mkext: 94 95Unpack each arch in a fat mkext to a separate array of kexts. 96For each arch: 97 - Get kext plist 98 - Check {kext path, id, vers} against assembled kexts: 99 - Already have? 100 - Plist identical: 101 - "lipo" executable into assembled 102 - Add all resources IF NOT PRESENT (check for different?) 103 - Plist different: 104 - cannot handle, error; or save to separate path 105 - Else add kext to assembled list 106 107 - Need to save infoDict, executable, resources 108#endif /* 0 */ 109 110/******************************************************************************* 111*******************************************************************************/ 112void usage(int num) { 113 fprintf(stderr, "usage: %s [-v] [-a arch] [-d output_dir] mkextfile\n", progname); 114 fprintf(stderr, " -d output_dir: where to put kexts (must exist)\n"); 115 fprintf(stderr, " -a arch: pick architecture from fat mkext file\n"); 116 fprintf(stderr, " -v: verbose output; list kexts in mkextfile\n"); 117 return; 118} 119 120/******************************************************************************* 121*******************************************************************************/ 122int main (int argc, const char * argv[]) { 123 int exit_code = 0; 124 125 char optchar; 126 char * outputDirectory = NULL; 127 const char * mkextFile = NULL; 128 int mkextFileFD; 129 struct stat stat_buf; 130 uint8_t * mkextFileContents = NULL; 131 void * mkextStart = NULL; 132 void * mkextEnd = NULL; 133 CFDictionaryRef entries = NULL; 134 CFArrayRef oskexts = NULL; 135 const NXArchInfo * archInfo = NULL; 136 uint32_t mkextVersion; 137 138 progname = argv[0]; 139 140 /* Set the OSKext log callback right away. 141 */ 142 OSKextSetLogOutputFunction(&tool_log); 143 144 while ((optchar = getopt(argc, (char * const *)argv, "a:d:hv")) != -1) { 145 switch (optchar) { 146 case 'd': 147 if (!optarg) { 148 fprintf(stderr, "no argument for -d\n"); 149 usage(0); 150 exit_code = 1; 151 goto finish; 152 } 153 outputDirectory = optarg; 154 break; 155 case 'h': 156 usage(1); 157 exit_code = 0; 158 goto finish; 159 break; 160 case 'v': 161 gVerbose = true; 162 break; 163 case 'a': 164 if (archInfo != NULL) { 165 fprintf(stderr, "architecture already specified; replacing\n"); 166 } 167 if (!optarg) { 168 fprintf(stderr, "no argument for -a\n"); 169 usage(0); 170 exit_code = 1; 171 goto finish; 172 } 173 archInfo = NXGetArchInfoFromName(optarg); 174 if (!archInfo) { 175 fprintf(stderr, "architecture '%s' not found\n", optarg); 176 exit_code = 1; 177 goto finish; 178 } 179 break; 180 } 181 } 182 183 /* Update argc, argv based on option processing. 184 */ 185 argc -= optind; 186 argv += optind; 187 188 if (argc == 0 || argc > 1) { 189 usage(0); 190 exit_code = 1; 191 goto finish; 192 } 193 194 mkextFile = argv[0]; 195 196 if (!outputDirectory && !gVerbose) { 197 fprintf(stderr, "no work to do; please specify -d or -v\n"); 198 usage(0); 199 exit_code = 1; 200 goto finish; 201 } 202 203 if (!mkextFile) { 204 fprintf(stderr, "no mkext file given\n"); 205 usage(0); 206 exit_code = 1; 207 goto finish; 208 } 209 210 if (outputDirectory) { 211 if (stat(outputDirectory, &stat_buf) < 0) { 212 fprintf(stderr, "can't stat directory %s\n", 213 outputDirectory); 214 exit_code = 1; 215 goto finish; 216 } 217 218 if ((stat_buf.st_mode & S_IFMT) != S_IFDIR) { 219 fprintf(stderr, "%s is not a directory\n", 220 outputDirectory); 221 exit_code = 1; 222 goto finish; 223 } 224 225 if (access(outputDirectory, W_OK) == -1) { 226 fprintf(stderr, "can't write into directory %s " 227 "(permission denied)\n", outputDirectory); 228 exit_code = 1; 229 goto finish; 230 } 231 } 232 233 if (stat(mkextFile, &stat_buf) < 0) { 234 fprintf(stderr, "can't stat file %s\n", mkextFile); 235 exit_code = 1; 236 goto finish; 237 } 238 239 if (access(mkextFile, R_OK) == -1) { 240 fprintf(stderr, "can't read file %s (permission denied)\n", 241 mkextFile); 242 exit_code = 1; 243 goto finish; 244 } 245 246 mkextFileFD = open(mkextFile, O_RDONLY, 0); 247 if (mkextFileFD < 0) { 248 fprintf(stderr, "can't open file %s\n", mkextFile); 249 exit_code = 1; 250 goto finish; 251 } 252 253 if ( !(stat_buf.st_mode & S_IFREG) ) { 254 fprintf(stderr, "%s is not a regular file\n", 255 mkextFile); 256 exit_code = 1; 257 goto finish; 258 } 259 260 if (!stat_buf.st_size) { 261 fprintf(stderr, "%s is an empty file\n", 262 mkextFile); 263 exit_code = 1; 264 goto finish; 265 } 266 267 mkextFileContents = mmap(0, (size_t)stat_buf.st_size, PROT_READ, 268 MAP_FILE|MAP_PRIVATE, mkextFileFD, 0); 269 if (mkextFileContents == (u_int8_t *)-1) { 270 fprintf(stderr, "can't map file %s\n", mkextFile); 271 exit_code = 1; 272 goto finish; 273 } 274 275 if (!getMkextDataForArch(mkextFileContents, (size_t)stat_buf.st_size, 276 archInfo, &mkextStart, &mkextEnd, &mkextVersion)) { 277 278 exit_code = 1; 279 goto finish; 280 } 281 282 if (mkextVersion == MKEXT_VERS_2) { 283 oskexts = copyKextsFromMkext2(mkextStart, mkextEnd, archInfo); 284 if (!oskexts) { 285 exit_code = 1; 286 goto finish; 287 } 288 289 if (outputDirectory && 290 !writeMkext2EntriesToDirectory(oskexts, outputDirectory)) { 291 exit_code = 1; 292 goto finish; 293 } 294 } else if (mkextVersion == MKEXT_VERS_1) { 295 entries = extractEntriesFromMkext1(mkextStart, mkextEnd); 296 if (!entries) { 297 fprintf(stderr, "can't unpack file %s\n", mkextFile); 298 exit_code = 1; 299 goto finish; 300 } 301 302 if (outputDirectory && 303 !writeMkext1EntriesToDirectory(entries, outputDirectory)) { 304 305 exit_code = 1; 306 goto finish; 307 } 308 } 309 310finish: 311 SAFE_RELEASE(oskexts); 312 exit(exit_code); 313 return exit_code; 314} 315 316/******************************************************************************* 317*******************************************************************************/ 318Boolean getMkextDataForArch( 319 u_int8_t * fileData, 320 size_t fileSize, 321 const NXArchInfo * archInfo, 322 void ** mkextStart, 323 void ** mkextEnd, 324 uint32_t * mkextVersion) 325{ 326 Boolean result = false; 327 uint32_t magic; 328 fat_iterator fatIterator = NULL; // must fat_iterator_close() 329 mkext_header * mkextHeader = NULL; // do not free 330 uint8_t * crc_address = NULL; // do not free 331 uint32_t checksum; 332 333 *mkextStart = *mkextEnd = NULL; 334 *mkextVersion = 0; 335 336 magic = MAGIC32(fileData); 337 if (ISFAT(magic)) { 338 if (!archInfo) { 339 archInfo = NXGetLocalArchInfo(); 340 } 341 fatIterator = fat_iterator_for_data(fileData, 342 fileData + fileSize, 343 1 /* mach-o only */); 344 if (!fatIterator) { 345 OSKextLog(/* kext */ NULL, 346 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 347 "Can't read mkext fat header."); 348 goto finish; 349 } 350 *mkextStart = fat_iterator_find_arch(fatIterator, 351 archInfo->cputype, archInfo->cpusubtype, mkextEnd); 352 if (!*mkextStart) { 353 OSKextLog(/* kext */ NULL, 354 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 355 "Architecture %s not found in mkext.", 356 archInfo->name); 357 goto finish; 358 } 359 } else { 360 *mkextStart = fileData; 361 *mkextEnd = fileData + fileSize; 362 } 363 364 mkextHeader = (mkext_header *)*mkextStart; 365 366 if ((MKEXT_GET_MAGIC(mkextHeader) != MKEXT_MAGIC) || 367 (MKEXT_GET_SIGNATURE(mkextHeader) != MKEXT_SIGN)) { 368 369 OSKextLog(/* kext */ NULL, 370 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 371 "Bad mkext magic/signature."); 372 goto finish; 373 } 374 if (MKEXT_GET_LENGTH(mkextHeader) != 375 (*mkextEnd - *mkextStart)) { 376 377 OSKextLog(/* kext */ NULL, 378 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 379 "Mkext length field %d does not match mkext actual size %d.", 380 MKEXT_GET_LENGTH(mkextHeader), 381 (int)(*mkextEnd - *mkextStart)); 382 goto finish; 383 } 384 if (archInfo && MKEXT_GET_CPUTYPE(mkextHeader) != archInfo->cputype) { 385 386 OSKextLog(/* kext */ NULL, 387 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 388 "Mkext cputype %d does not match requested type %d (%s).", 389 MKEXT_GET_CPUTYPE(mkextHeader), 390 archInfo->cputype, 391 archInfo->name); 392 goto finish; 393 } 394 395 crc_address = (uint8_t *)&mkextHeader->version; 396 checksum = local_adler32(crc_address, 397 (int32_t)((uintptr_t)mkextHeader + 398 MKEXT_GET_LENGTH(mkextHeader) - (uintptr_t)crc_address)); 399 400 if (MKEXT_GET_CHECKSUM(mkextHeader) != checksum) { 401 OSKextLog(/* kext */ NULL, 402 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 403 "Mkext checksum error."); 404 goto finish; 405 } 406 407 *mkextVersion = MKEXT_GET_VERSION(mkextHeader); 408 409 result = true; 410 411finish: 412 if (fatIterator) { 413 fat_iterator_close(fatIterator); 414 } 415 return result; 416} 417 418/******************************************************************************* 419*******************************************************************************/ 420CFArrayRef copyKextsFromMkext2( 421 void * mkextStart, 422 void * mkextEnd, 423 const NXArchInfo * archInfo) 424{ 425 CFArrayRef result = NULL; // release on error 426 Boolean ok = false; 427 CFDataRef mkextDataObject = NULL; // must release 428 char kextPath[PATH_MAX]; 429 char * kextIdentifier = NULL; // must free 430 char kextVersion[kOSKextVersionMaxLength]; 431 432 if (!archInfo) { 433 archInfo = NXGetLocalArchInfo(); 434 } 435 436 OSKextSetArchitecture(archInfo); 437 mkextDataObject = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 438 (UInt8 *)mkextStart, mkextEnd - mkextStart, NULL); 439 if (!mkextDataObject) { 440 OSKextLogMemError(); 441 goto finish; 442 } 443 result = OSKextCreateKextsFromMkextData(kCFAllocatorDefault, mkextDataObject); 444 if (!result) { 445 OSKextLog(/* kext */ NULL, 446 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 447 "Can't read mkext2 archive."); 448 goto finish; 449 } 450 451 if (gVerbose) { 452 CFIndex count, i; 453 454 count = CFArrayGetCount(result); 455 fprintf(stdout, "Found %d kexts:\n", (int)count); 456 for (i = 0; i < count; i++) { 457 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(result, i); 458 459 SAFE_FREE(kextIdentifier); 460 461 if (!CFURLGetFileSystemRepresentation(OSKextGetURL(theKext), 462 /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) { 463 464 OSKextLogStringError(theKext); 465 goto finish; 466 } 467 kextIdentifier = createUTF8CStringForCFString(OSKextGetIdentifier(theKext)); 468 OSKextVersionGetString(OSKextGetVersion(theKext), kextVersion, 469 sizeof(kextVersion)); 470 fprintf(stdout, "%s - %s (%s)\n", kextPath, kextIdentifier, 471 kextVersion); 472 } 473 } 474 475 ok = true; 476 477finish: 478 if (!ok) { 479 SAFE_RELEASE_NULL(result); 480 } 481 SAFE_RELEASE(mkextDataObject); 482 return result; 483} 484 485/******************************************************************************* 486*******************************************************************************/ 487Boolean writeMkext2EntriesToDirectory( 488 CFArrayRef kexts, 489 char * outputDirectory) 490{ 491 Boolean result = false; 492 CFMutableSetRef kextNames = NULL; // must release 493 CFIndex count, i; 494 495 if (!createCFMutableSet(&kextNames, &kCFTypeSetCallBacks)) { 496 OSKextLogMemError(); 497 } 498 499 count = CFArrayGetCount(kexts); 500 for (i = 0; i < count; i++) { 501 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i); 502 503 if (!writeMkext2ToDirectory(theKext, outputDirectory, kextNames)) { 504 goto finish; 505 } 506 } 507 508finish: 509 return result; 510} 511 512/******************************************************************************* 513*******************************************************************************/ 514Boolean writeMkext2ToDirectory( 515 OSKextRef aKext, 516 const char * outputDirectory, 517 CFMutableSetRef kextNames) 518{ 519 Boolean result = false; 520 CFDictionaryRef infoDict = NULL; // must release 521 CFDataRef infoDictData = NULL; // must release 522 CFErrorRef error = NULL; // must release 523 CFDataRef executable = NULL; // must release 524 CFURLRef kextURL = NULL; // do not release 525 CFStringRef kextName = NULL; // must release 526 CFStringRef executableName = NULL; // do not release 527 uint32_t pathLength; 528 char kextPath[PATH_MAX]; // gets trashed during use 529 char * kextNameCString = NULL; // do not free 530 char * kextNameSuffix = NULL; // do not free 531 char * kextNameCStringAlloced = NULL; // must free 532 char * executableNameCStringAlloced = NULL; // must free 533 const void * file_data = NULL; // do not free 534 char subPath[PATH_MAX]; 535 CFIndex i; 536 537 infoDict = OSKextCopyInfoDictionary(aKext); 538 executable = OSKextCopyExecutableForArchitecture(aKext, NULL); 539 if (!infoDict) { 540 OSKextLogMemError(); 541 goto finish; 542 } 543 544 kextURL = OSKextGetURL(aKext); 545 if (!CFURLGetFileSystemRepresentation(kextURL, /* resolveToBase */ true, 546 (UInt8 *)kextPath, sizeof(kextPath))) { 547 548 OSKextLogStringError(aKext); 549 goto finish; 550 } 551 pathLength = (uint32_t)strlen(kextPath); 552 if (pathLength && kextPath[pathLength-1] == '/') { 553 kextPath[pathLength-1] = '\0'; 554 } 555 kextNameCString = rindex(kextPath, '/'); 556 if (kextNameCString) { 557 kextNameCString++; 558 } 559 560 SAFE_RELEASE_NULL(kextName); 561 kextName = CFStringCreateWithCString(kCFAllocatorDefault, 562 kextNameCString, kCFStringEncodingUTF8); 563 if (!kextName) { 564 OSKextLogMemError(); 565 goto finish; 566 } 567 568 /* Splat off the ".kext" suffix if needed so we can build 569 * numbered variants. 570 */ 571 if (CFSetContainsValue(kextNames, kextName)) { 572 kextNameSuffix = strstr(kextNameCString, ".kext"); 573 if (!kextNameSuffix) { 574 OSKextLog(/* kext */ NULL, 575 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 576 "Bad mkext data; kext missing suffix."); 577 goto finish; 578 } 579 *kextNameSuffix = '\0'; // truncate at the period 580 } 581 582 i = 1; 583 while (CFSetContainsValue(kextNames, kextName)) { 584 SAFE_RELEASE_NULL(kextName); 585 kextName = CFStringCreateWithFormat(kCFAllocatorDefault, 586 /* formatOptions */ NULL, CFSTR("%s-%zd.kext"), 587 kextNameCString, i); 588 i++; 589 } 590 CFSetAddValue(kextNames, kextName); 591 592 kextNameCStringAlloced = createUTF8CStringForCFString(kextName); 593 594 /***** 595 * Write the plist file. 596 */ 597 infoDictData = CFPropertyListCreateData(kCFAllocatorDefault, 598 infoDict, kCFPropertyListXMLFormat_v1_0, /* options */ 0, &error); 599 if (!infoDictData) { 600 OSKextLog(/* kext */ NULL, 601 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 602 "Can't serialize kext info dictionary."); 603 goto finish; 604 } 605 606 file_data = (u_int8_t *)CFDataGetBytePtr(infoDictData); 607 if (!file_data) { 608 OSKextLog(/* kext */ NULL, 609 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 610 "Internal error; info dictionary has no data."); 611 goto finish; 612 } 613 614 if (snprintf(subPath, sizeof(subPath), 615 "%s/Contents", kextNameCStringAlloced) >= sizeof(subPath) - 1) { 616 OSKextLog(/* kext */ NULL, 617 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag, 618 "Output path is too long - %s.", subPath); 619 goto finish; 620 } 621 if (!writeFileInDirectory(outputDirectory, subPath, "Info.plist", 622 file_data, CFDataGetLength(infoDictData))) { 623 624 goto finish; 625 } 626 627 if (!executable) { 628 result = true; 629 goto finish; 630 } 631 632 /***** 633 * Write the executable file. 634 */ 635 file_data = (u_int8_t *)CFDataGetBytePtr(executable); 636 if (!file_data) { 637 OSKextLog(/* kext */ NULL, 638 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 639 "Internal error; executable has no data."); 640 goto finish; 641 } 642 643 if (snprintf(subPath, sizeof(subPath), 644 "%s/Contents/MacOS", kextNameCStringAlloced) >= sizeof(subPath) - 1) { 645 OSKextLog(/* kext */ NULL, 646 kOSKextLogErrorLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag, 647 "Output path is too long - %s.", subPath); 648 goto finish; 649 } 650 executableName = CFDictionaryGetValue(infoDict, CFSTR("CFBundleExecutable")); 651 if (!executableName) { 652 OSKextLog(/* kext */ NULL, 653 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 654 "Kext %s has an executable but no CFBundleExecutable property.", 655 kextNameCStringAlloced); 656 goto finish; 657 } 658 executableNameCStringAlloced = createUTF8CStringForCFString(executableName); 659 if (!executableNameCStringAlloced) { 660 OSKextLogMemError(); 661 goto finish; 662 } 663 if (!writeFileInDirectory(outputDirectory, subPath, 664 executableNameCStringAlloced, 665 file_data, CFDataGetLength(executable))) { 666 667 goto finish; 668 } 669 670 result = true; 671 672finish: 673 SAFE_RELEASE(infoDict); 674 SAFE_RELEASE(infoDictData); 675 SAFE_RELEASE(error); 676 SAFE_RELEASE(executable); 677 SAFE_RELEASE(kextName); 678 SAFE_FREE(kextNameCStringAlloced); 679 SAFE_FREE(executableNameCStringAlloced); 680 return result; 681} 682 683/******************************************************************************* 684*******************************************************************************/ 685static Boolean CaseInsensitiveEqual(CFTypeRef left, CFTypeRef right) 686{ 687 CFComparisonResult compResult; 688 689 compResult = CFStringCompare(left, right, kCFCompareCaseInsensitive); 690 if (compResult == kCFCompareEqualTo) { 691 return true; 692 } 693 return false; 694} 695 696/******************************************************************************* 697*******************************************************************************/ 698static CFHashCode CaseInsensitiveHash(const void *key) 699{ 700 return (CFStringGetLength(key)); 701} 702 703/******************************************************************************* 704*******************************************************************************/ 705CFDictionaryRef extractEntriesFromMkext1( 706 void * mkextStart, 707 void * mkextEnd) 708{ 709 CFMutableDictionaryRef entries = NULL; // returned 710 Boolean error = false; 711 712 unsigned int i; 713 714 mkext1_header * mkextHeader = NULL; // don't free 715 mkext_kext * onekext_data = 0; // don't free 716 mkext_file * plist_file = 0; // don't free 717 mkext_file * module_file = 0; // don't free 718 CFStringRef entryName = NULL; // must release 719 CFMutableDictionaryRef entryDict = NULL; // must release 720 CFDataRef kextPlistDataObject = 0; // must release 721 CFDictionaryRef kextPlist = 0; // must release 722 CFStringRef errorString = NULL; // must release 723 CFDataRef kextExecutable = 0; // must release 724 CFDictionaryKeyCallBacks keyCallBacks; 725 726 727 keyCallBacks = kCFTypeDictionaryKeyCallBacks; 728 keyCallBacks.equal = &CaseInsensitiveEqual; 729 keyCallBacks.hash = &CaseInsensitiveHash; 730 entries = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 731 &keyCallBacks, 732 &kCFTypeDictionaryValueCallBacks); 733 if (!entries) { 734 goto finish; 735 } 736 737 mkextHeader = (mkext1_header *)mkextStart; 738 739 if (gVerbose) { 740 fprintf(stdout, "Found %u kexts:\n", MKEXT_GET_COUNT(mkextHeader)); 741 } 742 743 for (i = 0; i < MKEXT_GET_COUNT(mkextHeader); i++) { 744 if (entryName) { 745 CFRelease(entryName); 746 entryName = NULL; 747 } 748 if (entryDict) { 749 CFRelease(entryDict); 750 entryDict = NULL; 751 } 752 if (kextPlistDataObject) { 753 CFRelease(kextPlistDataObject); 754 kextPlistDataObject = NULL; 755 } 756 if (kextPlist) { 757 CFRelease(kextPlist); 758 kextPlist = NULL; 759 } 760 if (errorString) { 761 CFRelease(errorString); 762 errorString = NULL; 763 } 764 if (kextExecutable) { 765 CFRelease(kextExecutable); 766 kextExecutable = NULL; 767 } 768 769 entryDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 770 &kCFTypeDictionaryKeyCallBacks, 771 &kCFTypeDictionaryValueCallBacks); 772 if (!entryDict) { 773 fprintf(stderr, "internal error.\n"); 774 error = true; 775 goto finish; 776 } 777 778 onekext_data = &mkextHeader->kext[i]; 779 plist_file = &onekext_data->plist; 780 module_file = &onekext_data->module; 781 782 /***** 783 * Get the plist 784 */ 785 if (!uncompressMkext1Entry(mkextStart, 786 plist_file, &kextPlistDataObject) || !kextPlistDataObject) { 787 788 fprintf(stderr, "couldn't uncompress plist at index %d.\n", i); 789 continue; 790 } 791 792 CFErrorRef error; 793 kextPlist = CFPropertyListCreateWithData(kCFAllocatorDefault, 794 kextPlistDataObject, 795 kCFPropertyListImmutable, 796 NULL, 797 &error); 798 if (!kextPlist) { 799 errorString = CFErrorCopyDescription(error); 800 CFRelease(error); 801 if (errorString) { 802 CFIndex bufsize = CFStringGetMaximumSizeForEncoding( 803 CFStringGetLength(errorString), kCFStringEncodingUTF8); 804 char * error_string = (char *)malloc((1+bufsize) * sizeof(char)); 805 if (!error_string) { 806 fprintf(stderr, "memory allocation failure\n"); 807 error = true; 808 goto finish; 809 } 810 if (CFStringGetCString(errorString, error_string, 811 bufsize, kCFStringEncodingUTF8)) { 812 fprintf(stderr, "error reading plist: %s", 813 error_string); 814 } 815 free(error_string); 816 } else { 817 fprintf(stderr, "error reading plist"); 818 } 819 goto finish; 820 } 821 822 entryName = createKextNameFromPlist(entries, kextPlist); 823 if (!entryName) { 824 fprintf(stderr, "internal error.\n"); 825 error = true; 826 goto finish; 827 } 828 829 CFDictionarySetValue(entryDict, CFSTR("plistData"), kextPlistDataObject); 830 CFDictionarySetValue(entryDict, CFSTR("plist"), kextPlist); 831 832 if (gVerbose) { 833 char kext_name[PATH_MAX]; 834 char * bundle_id = NULL; // don't free 835 char * bundle_vers = NULL; // don't free 836 837 if (!CFStringGetCString(entryName, kext_name, sizeof(kext_name) - 1, 838 kCFStringEncodingUTF8)) { 839 fprintf(stderr, "memory or string conversion error\n"); 840 } else { 841 switch (getBundleIDAndVersion(kextPlist, i, &bundle_id, 842 &bundle_vers)) { 843 844 case 1: 845 fprintf(stdout, "%s - %s (%s)\n", kext_name, bundle_id, 846 bundle_vers); 847 break; 848 case 0: 849 continue; 850 case -1: 851 default: 852 error = true; 853 goto finish; 854 break; 855 } 856 } 857 } 858 859 /***** 860 * Get the executable 861 */ 862 if (OSSwapBigToHostInt32(module_file->offset) || 863 OSSwapBigToHostInt32(module_file->compsize) || 864 OSSwapBigToHostInt32(module_file->realsize) || 865 OSSwapBigToHostInt32(module_file->modifiedsecs)) { 866 867 if (!uncompressMkext1Entry(mkextStart, 868 module_file, &kextExecutable)) { 869 870 fprintf(stderr, "couldn't uncompress executable at index %d.\n", 871 i); 872 continue; 873 } 874 875 if (kextExecutable) { 876 CFDictionarySetValue(entryDict, CFSTR("executable"), 877 kextExecutable); 878 } 879 } 880 881 CFDictionarySetValue(entries, entryName, entryDict); 882 } 883 884finish: 885 if (error) { 886 if (entries) { 887 CFRelease(entries); 888 entries = NULL; 889 } 890 } 891 if (entryName) CFRelease(entryName); 892 if (entryDict) CFRelease(entryDict); 893 if (kextPlistDataObject) CFRelease(kextPlistDataObject); 894 if (kextPlist) CFRelease(kextPlist); 895 if (errorString) CFRelease(errorString); 896 if (kextExecutable) CFRelease(kextExecutable); 897 898 return entries; 899} 900 901/******************************************************************************* 902*******************************************************************************/ 903Boolean uncompressMkext1Entry( 904 void * mkext_base_address, 905 mkext_file * entry_address, 906 CFDataRef * uncompressedEntry) 907{ 908 Boolean result = true; 909 910 u_int8_t * uncompressed_data = NULL; // must free 911 CFDataRef uncompressedData = NULL; // returned 912 size_t uncompressed_size = 0; 913 914 size_t offset = OSSwapBigToHostInt32(entry_address->offset); 915 size_t compsize = OSSwapBigToHostInt32(entry_address->compsize); 916 size_t realsize = OSSwapBigToHostInt32(entry_address->realsize); 917 time_t modifiedsecs = OSSwapBigToHostInt32(entry_address->modifiedsecs); 918 919 *uncompressedEntry = NULL; 920 921 /* If realsize is 0 there is nothing to uncompress or if any of the other 922 * three fields are 0, but that isn't an error. 923 */ 924 if (realsize == 0 || 925 (offset == 0 && compsize == 0 && modifiedsecs == 0)) { 926 goto finish; 927 } 928 929 uncompressed_data = malloc(realsize); 930 if (!uncompressed_data) { 931 fprintf(stderr, "malloc failure\n"); 932 result = false; 933 goto finish; 934 } 935 936 if (compsize != 0) { 937 uncompressed_size = decompress_lzss(uncompressed_data, 938 (u_int32_t)realsize, 939 mkext_base_address + offset, 940 (u_int32_t)compsize); 941 if (uncompressed_size != realsize) { 942 fprintf(stderr, "uncompressed file is not the length " 943 "recorded.\n"); 944 result = false; 945 goto finish; 946 } 947 } else { 948 bcopy(mkext_base_address + offset, uncompressed_data, 949 realsize); 950 } 951 952 uncompressedData = CFDataCreate(kCFAllocatorDefault, 953 (const UInt8 *)uncompressed_data, realsize); 954 if (!uncompressedData) { 955 fprintf(stderr, "malloc failure\n"); 956 result = false; 957 goto finish; 958 } 959 *uncompressedEntry = uncompressedData; 960 961finish: 962 if (uncompressed_data) free(uncompressed_data); 963 964 return result; 965} 966 967/******************************************************************************* 968*******************************************************************************/ 969Boolean writeMkext1EntriesToDirectory(CFDictionaryRef entryDict, 970 char * outputDirectory) 971{ 972 Boolean result = false; 973 CFStringRef * kextNames = NULL; // must free 974 CFDictionaryRef * entries = NULL; // must free 975 char * kext_name = NULL; // must free 976 char * executable_name = NULL; // must free 977 char subPath[PATH_MAX]; 978 unsigned int count, i; 979 980 count = (unsigned int)CFDictionaryGetCount(entryDict); 981 982 kextNames = (CFStringRef *)malloc(count * sizeof(CFStringRef)); 983 entries = (CFDictionaryRef *)malloc(count * sizeof(CFDictionaryRef)); 984 if (!kextNames || !entries) { 985 fprintf(stderr, "malloc failure\n"); 986 result = false; 987 goto finish; 988 } 989 990 CFDictionaryGetKeysAndValues(entryDict, (const void **)kextNames, 991 (const void **)entries); 992 993 for (i = 0; i < count; i++) { 994 CFStringRef kextName = kextNames[i]; 995 CFDictionaryRef kextEntry = entries[i]; 996 CFStringRef executableName = NULL; // do not release 997 CFDataRef fileData = NULL; // do not release 998 CFDictionaryRef plist; 999 1000 const void * file_data = NULL; 1001 1002 SAFE_FREE_NULL(kext_name); 1003 SAFE_FREE_NULL(executable_name); 1004 1005 kext_name = createUTF8CStringForCFString(kextName); 1006 if (!kext_name) { 1007 OSKextLogMemError(); 1008 goto finish; 1009 } 1010 1011 /***** 1012 * Write the plist file. 1013 */ 1014 fileData = CFDictionaryGetValue(kextEntry, CFSTR("plistData")); 1015 if (!fileData) { 1016 OSKextLog(/* kext */ NULL, 1017 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1018 "Kext entry %d has no plist.", i); 1019 continue; 1020 } 1021 file_data = (u_int8_t *)CFDataGetBytePtr(fileData); 1022 if (!file_data) { 1023 OSKextLog(/* kext */ NULL, 1024 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1025 "Kext %s has no plist.", kext_name); 1026 continue; 1027 } 1028 1029 if (snprintf(subPath, sizeof(subPath), 1030 "%s.kext/Contents", kext_name) >= sizeof(subPath) - 1) { 1031 OSKextLog(/* kext */ NULL, 1032 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1033 "Output path is too long - %s.", subPath); 1034 goto finish; 1035 } 1036 if (!writeFileInDirectory(outputDirectory, subPath, "Info.plist", 1037 file_data, CFDataGetLength(fileData))) { 1038 1039 goto finish; 1040 } 1041 1042 /***** 1043 * Write the executable file. 1044 */ 1045 fileData = CFDictionaryGetValue(kextEntry, CFSTR("executable")); 1046 if (!fileData) { 1047 continue; 1048 } 1049 file_data = (u_int8_t *)CFDataGetBytePtr(fileData); 1050 if (!file_data) { 1051 OSKextLog(/* kext */ NULL, 1052 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1053 "Executable missing from kext %s.", 1054 kext_name); 1055 continue; 1056 } 1057 1058 if (snprintf(subPath, sizeof(subPath), 1059 "%s.kext/Contents/MacOS", kext_name) >= sizeof(subPath) - 1) { 1060 OSKextLog(/* kext */ NULL, 1061 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1062 "Output path is too long - %s.", subPath); 1063 goto finish; 1064 } 1065 plist = CFDictionaryGetValue(kextEntry, CFSTR("plist")); 1066 executableName = CFDictionaryGetValue(plist, CFSTR("CFBundleExecutable")); 1067 if (!executableName) { 1068 OSKextLog(/* kext */ NULL, 1069 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 1070 "Kext %s has an executable but no CFBundleExecutable property.", 1071 kext_name); 1072 continue; 1073 } 1074 executable_name = createUTF8CStringForCFString(executableName); 1075 if (!executable_name) { 1076 OSKextLogMemError(); 1077 goto finish; 1078 } 1079 if (!writeFileInDirectory(outputDirectory, subPath, executable_name, 1080 file_data, CFDataGetLength(fileData))) { 1081 1082 goto finish; 1083 } 1084 } 1085 1086 result = true; 1087 1088finish: 1089 if (kextNames) free(kextNames); 1090 if (entries) free(entries); 1091 return result; 1092} 1093 1094/******************************************************************************* 1095*******************************************************************************/ 1096CFStringRef createKextNameFromPlist( 1097 CFDictionaryRef entries, CFDictionaryRef kextPlist) 1098{ 1099 CFStringRef result = NULL; // returned 1100 CFStringRef bundleName = NULL; // don't release 1101 CFStringRef bundleID = NULL; // don't release 1102 static unsigned int nameUnknownIndex = 1; 1103 unsigned int dupIndex = 1; 1104 CFArrayRef idParts = NULL; // must release 1105 1106 /* First see if there's a CFBundleExecutable. 1107 */ 1108 bundleName = CFDictionaryGetValue(kextPlist, CFSTR("CFBundleExecutable")); 1109 if (!bundleName) { 1110 1111 /* No? Try for CFBundleIdentifier and get the last component. 1112 */ 1113 bundleID = CFDictionaryGetValue(kextPlist, CFSTR("CFBundleIdentifier")); 1114 if (bundleID) { 1115 CFIndex length; 1116 1117 idParts = CFStringCreateArrayBySeparatingStrings( 1118 kCFAllocatorDefault, bundleID, CFSTR(".")); 1119 length = CFArrayGetCount(idParts); 1120 bundleName = (CFStringRef)CFArrayGetValueAtIndex(idParts, length - 1); 1121 1122 /* Identifier ends with a period? We got no name, then. 1123 */ 1124 if (!CFStringGetLength(bundleName)) { 1125 bundleName = NULL; 1126 } 1127 } 1128 1129 /* If we didn't find a name to use, conjure one up. 1130 */ 1131 if (!bundleName) { 1132 result = CFStringCreateWithFormat(kCFAllocatorDefault, 1133 NULL, CFSTR("NameUnknown-%d"), nameUnknownIndex); 1134 nameUnknownIndex++; 1135 goto finish; 1136 } 1137 } 1138 1139 /* See if we already have the name we found based on executable/bundle ID 1140 * (as opposed to making up with NameUnknown); if so, add numbers until we get a unique. 1141 */ 1142 if (bundleName) { 1143 if (CFDictionaryGetValue(entries, bundleName)) { 1144 for ( ; ; dupIndex++) { 1145 result = CFStringCreateWithFormat(kCFAllocatorDefault, 1146 NULL, CFSTR("%@-%d"), bundleName, dupIndex); 1147 if (!CFDictionaryGetValue(entries, result)) { 1148 goto finish; 1149 } 1150 CFRelease(result); 1151 result = NULL; 1152 } 1153 } else { 1154 result = CFRetain(bundleName); 1155 goto finish; 1156 } 1157 } 1158finish: 1159 if (idParts) CFRelease(idParts); 1160 return result; 1161} 1162 1163/******************************************************************************* 1164*******************************************************************************/ 1165int getBundleIDAndVersion(CFDictionaryRef kextPlist, unsigned index, 1166 char ** bundle_id_out, char ** bundle_version_out) 1167{ 1168 int result = 1; 1169 1170 CFStringRef bundleID = NULL; // don't release 1171 CFStringRef bundleVersion = NULL; // don't release 1172 static char bundle_id[KMOD_MAX_NAME]; 1173 static char bundle_vers[KMOD_MAX_NAME]; 1174 1175 bundleID = CFDictionaryGetValue(kextPlist, CFSTR("CFBundleIdentifier")); 1176 if (!bundleID) { 1177 fprintf(stderr, "kext entry %d has no CFBundleIdentifier\n", index); 1178 result = 0; 1179 goto finish; 1180 } else if (CFGetTypeID(bundleID) != CFStringGetTypeID()) { 1181 fprintf(stderr, "kext entry %d, CFBundleIdentifier is not a string\n", index); 1182 result = 0; 1183 goto finish; 1184 } else { 1185 if (!CFStringGetCString(bundleID, bundle_id, sizeof(bundle_id) - 1, 1186 kCFStringEncodingUTF8)) { 1187 fprintf(stderr, "memory or string conversion error\n"); 1188 result = -1; 1189 goto finish; 1190 } 1191 } 1192 1193 bundleVersion = CFDictionaryGetValue(kextPlist, 1194 CFSTR("CFBundleVersion")); 1195 if (!bundleVersion) { 1196 fprintf(stderr, "kext entry %d has no CFBundleVersion\n", index); 1197 result = 0; 1198 goto finish; 1199 } else if (CFGetTypeID(bundleVersion) != CFStringGetTypeID()) { 1200 fprintf(stderr, "kext entry %d, CFBundleVersion is not a string\n", index); 1201 result = 0; 1202 goto finish; 1203 } else { 1204 if (!CFStringGetCString(bundleVersion, bundle_vers, 1205 sizeof(bundle_vers) - 1, kCFStringEncodingUTF8)) { 1206 fprintf(stderr, "memory or string conversion error\n"); 1207 result = -1; 1208 goto finish; 1209 } 1210 } 1211 1212 if (bundle_id_out) { 1213 *bundle_id_out = bundle_id; 1214 } 1215 1216 if (bundle_version_out) { 1217 *bundle_version_out = bundle_vers; 1218 } 1219 1220 1221finish: 1222 1223 return result; 1224} 1225 1226/******************************************************************************* 1227*******************************************************************************/ 1228Boolean writeFileInDirectory( 1229 const char * basePath, 1230 char * subPath, 1231 const char * fileName, 1232 const char * fileData, 1233 size_t fileLength) 1234{ 1235 Boolean result = false; 1236 char path[PATH_MAX]; 1237 char * pathComponent = NULL; // do not free 1238 char * pathComponentEnd = NULL; // do not free 1239 int fd = -1; 1240 uint32_t bytesWritten; 1241 1242 if (strlcpy(path, basePath, sizeof(path)) >= sizeof(path)) { 1243 OSKextLog(/* kext */ NULL, 1244 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1245 "Output path is too long - %s.", basePath); 1246 goto finish; 1247 } 1248 1249 pathComponent = subPath; 1250 while (pathComponent) { 1251 pathComponentEnd = index(pathComponent, '/'); 1252 if (pathComponentEnd) { 1253 *pathComponentEnd = '\0'; 1254 } 1255 if (strlcat(path, "/", sizeof(path)) >= sizeof(path) || 1256 strlcat(path, pathComponent, sizeof(path)) >= sizeof(path)) { 1257 1258 OSKextLog(/* kext */ NULL, 1259 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1260 "Output path is too long - %s.", path); 1261 goto finish; 1262 } 1263 1264 if ((mkdir(path, 0777) < 0) && (errno != EEXIST)) { 1265 OSKextLog(/* kext */ NULL, 1266 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1267 "Can't create directory %s - %s", path, strerror(errno)); 1268 goto finish; 1269 } 1270 if (pathComponentEnd) { 1271 *pathComponentEnd = '/'; 1272 pathComponent = pathComponentEnd + 1; 1273 pathComponentEnd = NULL; 1274 } else { 1275 break; 1276 } 1277 } 1278 1279 if (strlcat(path, "/", sizeof(path)) >= sizeof(path) || 1280 strlcat(path, fileName, sizeof(path)) >= sizeof(path)) { 1281 1282 OSKextLog(/* kext */ NULL, 1283 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1284 "Output path is too long - %s.", path); 1285 goto finish; 1286 } 1287 1288 fd = open(path, O_WRONLY | O_CREAT, 0777); 1289 if (fd < 0) { 1290 OSKextLog(/* kext */ NULL, 1291 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1292 "Can't open %s for writing - %s.", path, strerror(errno)); 1293 goto finish; 1294 } 1295 1296 bytesWritten = 0; 1297 while (bytesWritten < fileLength) { 1298 int writeResult; 1299 writeResult = (int)write(fd, fileData + bytesWritten, 1300 fileLength - bytesWritten); 1301 if (writeResult < 0) { 1302 OSKextLog(/* kext */ NULL, 1303 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1304 "Write failed for %s - %s.", path, strerror(errno)); 1305 goto finish; 1306 } 1307 bytesWritten += writeResult; 1308 } 1309 1310 result = true; 1311 1312finish: 1313 if (fd != -1) { 1314 close(fd); 1315 } 1316 return result; 1317 1318} 1319