1/* 2 * kclist_main.c 3 * kext_tools 4 * 5 * Created by Nik Gervae on 2010 10 04. 6 * Copyright 2010 Apple Computer, Inc. All rights reserved. 7 * 8 */ 9 10#include <CoreFoundation/CoreFoundation.h> 11#include <System/libkern/OSKextLibPrivate.h> 12#include <System/libkern/prelink.h> 13#include <IOKit/kext/OSKext.h> 14#include <IOKit/kext/OSKextPrivate.h> 15#include <IOKit/IOCFUnserialize.h> 16#include <IOKit/kext/macho_util.h> 17 18#include <architecture/byte_order.h> 19#include <errno.h> 20#include <libc.h> 21#include <mach-o/fat.h> 22#include <mach-o/loader.h> 23#include <sys/mman.h> 24#include <uuid/uuid.h> 25 26#include "kclist_main.h" 27#include "compression.h" 28 29/******************************************************************************* 30*******************************************************************************/ 31extern void printPList_new(FILE * stream, CFPropertyListRef plist, int style); 32 33/******************************************************************************* 34* Program Globals 35*******************************************************************************/ 36const char * progname = "(unknown)"; 37 38/******************************************************************************* 39*******************************************************************************/ 40int main(int argc, char * const argv[]) 41{ 42 ExitStatus result = EX_SOFTWARE; 43 KclistArgs toolArgs; 44 int kernelcache_fd = -1; // must close() 45 void * fat_header = NULL; // must unmapFatHeaderPage() 46 struct fat_arch * fat_arch = NULL; 47 CFDataRef rawKernelcache = NULL; // must release 48 CFDataRef kernelcacheImage = NULL; // must release 49 const UInt8 * kernelcacheStart = NULL; 50 51 void * prelinkInfoSect = NULL; 52 const char * prelinkInfoBytes = NULL; 53 CFPropertyListRef prelinkInfoPlist = NULL; // must release 54 55 void * prelinkTextSect = NULL; 56 const char * prelinkTextBytes = NULL; 57 uint64_t prelinkTextSourceAddress = 0; 58 uint64_t prelinkTextSourceSize = 0; 59 60 bzero(&toolArgs, sizeof(toolArgs)); 61 62 /***** 63 * Find out what the program was invoked as. 64 */ 65 progname = rindex(argv[0], '/'); 66 if (progname) { 67 progname++; // go past the '/' 68 } else { 69 progname = (char *)argv[0]; 70 } 71 72 /* Set the OSKext log callback right away. 73 */ 74 OSKextSetLogOutputFunction(&tool_log); 75 76 /***** 77 * Process args & check for permission to load. 78 */ 79 result = readArgs(&argc, &argv, &toolArgs); 80 if (result != EX_OK) { 81 if (result == kKclistExitHelp) { 82 result = EX_OK; 83 } 84 goto finish; 85 } 86 87 result = checkArgs(&toolArgs); 88 if (result != EX_OK) { 89 if (result == kKclistExitHelp) { 90 result = EX_OK; 91 } 92 goto finish; 93 } 94 95 kernelcache_fd = open(toolArgs.kernelcachePath, O_RDONLY); 96 if (kernelcache_fd == -1) { 97 OSKextLog(/* kext */ NULL, 98 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 99 "Can't open %s: %s.", toolArgs.kernelcachePath, strerror(errno)); 100 result = EX_OSERR; 101 goto finish; 102 } 103 fat_header = mapAndSwapFatHeaderPage(kernelcache_fd); 104 if (!fat_header) { 105 OSKextLog(/* kext */ NULL, 106 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 107 "Can't map %s: %s.", toolArgs.kernelcachePath, strerror(errno)); 108 result = EX_OSERR; 109 goto finish; 110 } 111 112 fat_arch = getFirstFatArch(fat_header); 113 if (fat_arch && !toolArgs.archInfo) { 114 toolArgs.archInfo = NXGetArchInfoFromCpuType(fat_arch->cputype, fat_arch->cpusubtype); 115 } 116 117 rawKernelcache = readMachOSliceForArch(toolArgs.kernelcachePath, toolArgs.archInfo, 118 /* checkArch */ FALSE); 119 if (!rawKernelcache) { 120 OSKextLog(/* kext */ NULL, 121 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 122 "Can't read arch %s from %s.", toolArgs.archInfo->name, toolArgs.kernelcachePath); 123 goto finish; 124 } 125 126 if (MAGIC32(CFDataGetBytePtr(rawKernelcache)) == OSSwapHostToBigInt32('comp')) { 127 kernelcacheImage = uncompressPrelinkedSlice(rawKernelcache); 128 if (!kernelcacheImage) { 129 OSKextLog(/* kext */ NULL, 130 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 131 "Can't uncompress kernelcache slice."); 132 goto finish; 133 } 134 } else { 135 kernelcacheImage = CFRetain(rawKernelcache); 136 } 137 138 kernelcacheStart = CFDataGetBytePtr(kernelcacheImage); 139 140 if (ISMACHO64(MAGIC32(kernelcacheStart))) { 141 prelinkInfoSect = (void *)macho_get_section_by_name_64( 142 (struct mach_header_64 *)kernelcacheStart, 143 kPrelinkInfoSegment, kPrelinkInfoSection); 144 prelinkTextSect = (void *)macho_get_section_by_name_64( 145 (struct mach_header_64 *)kernelcacheStart, 146 kPrelinkTextSegment, kPrelinkTextSection); 147 } else { 148 prelinkInfoSect = (void *)macho_get_section_by_name( 149 (struct mach_header *)kernelcacheStart, 150 kPrelinkInfoSegment, kPrelinkInfoSection); 151 prelinkTextSect = (void *)macho_get_section_by_name( 152 (struct mach_header *)kernelcacheStart, 153 kPrelinkTextSegment, kPrelinkTextSection); 154 } 155 156 if (!prelinkInfoSect) { 157 OSKextLog(/* kext */ NULL, 158 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 159 "Can't find prelink info section."); 160 goto finish; 161 } 162 163 if (!prelinkTextSect) { 164 OSKextLog(/* kext */ NULL, 165 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 166 "Can't find prelink text section."); 167 goto finish; 168 } 169 170 if (ISMACHO64(MAGIC32(kernelcacheStart))) { 171 prelinkInfoBytes = ((char *)kernelcacheStart) + 172 ((struct section_64 *)prelinkInfoSect)->offset; 173 prelinkTextBytes = ((char *)kernelcacheStart) + 174 ((struct section_64 *)prelinkTextSect)->offset; 175 prelinkTextSourceAddress = ((struct section_64 *)prelinkTextSect)->addr; 176 prelinkTextSourceSize = ((struct section_64 *)prelinkTextSect)->size; 177 } else { 178 prelinkInfoBytes = ((char *)kernelcacheStart) + 179 ((struct section *)prelinkInfoSect)->offset; 180 prelinkTextBytes = ((char *)kernelcacheStart) + 181 ((struct section *)prelinkTextSect)->offset; 182 prelinkTextSourceAddress = ((struct section *)prelinkTextSect)->addr; 183 prelinkTextSourceSize = ((struct section *)prelinkTextSect)->size; 184 } 185 186 prelinkInfoPlist = (CFPropertyListRef)IOCFUnserialize(prelinkInfoBytes, 187 kCFAllocatorDefault, /* options */ 0, /* errorString */ NULL); 188 if (!prelinkInfoPlist) { 189 OSKextLog(/* kext */ NULL, 190 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 191 "Can't unserialize prelink info."); 192 goto finish; 193 } 194 195 listPrelinkedKexts(&toolArgs, prelinkInfoPlist, prelinkTextBytes, prelinkTextSourceAddress, prelinkTextSourceSize); 196 197 result = EX_OK; 198 199finish: 200 201 SAFE_RELEASE(prelinkInfoPlist); 202 SAFE_RELEASE(kernelcacheImage); 203 SAFE_RELEASE(rawKernelcache); 204 205 if (fat_header) { 206 unmapFatHeaderPage(fat_header); 207 } 208 209 if (kernelcache_fd != -1) { 210 close(kernelcache_fd); 211 } 212 return result; 213} 214 215/******************************************************************************* 216*******************************************************************************/ 217ExitStatus readArgs( 218 int * argc, 219 char * const ** argv, 220 KclistArgs * toolArgs) 221{ 222 ExitStatus result = EX_USAGE; 223 ExitStatus scratchResult = EX_USAGE; 224 int optchar = 0; 225 int longindex = -1; 226 227 bzero(toolArgs, sizeof(*toolArgs)); 228 229 /***** 230 * Allocate collection objects. 231 */ 232 if (!createCFMutableSet(&toolArgs->kextIDs, &kCFTypeSetCallBacks)) { 233 234 OSKextLogMemError(); 235 result = EX_OSERR; 236 exit(result); 237 } 238 239 /***** 240 * Process command line arguments. 241 */ 242 while ((optchar = getopt_long_only(*argc, *argv, 243 kOptChars, sOptInfo, &longindex)) != -1) { 244 245 switch (optchar) { 246 247 case kOptArch: 248 toolArgs->archInfo = NXGetArchInfoFromName(optarg); 249 if (!toolArgs->archInfo) { 250 OSKextLog(/* kext */ NULL, 251 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 252 "Unknown architecture %s.", optarg); 253 goto finish; 254 } 255 break; 256 257 case kOptHelp: 258 usage(kUsageLevelFull); 259 result = kKclistExitHelp; 260 goto finish; 261 262 case kOptUUID: 263 toolArgs->printUUIDs = true; 264 break; 265 266 case kOptVerbose: 267 toolArgs->verbose = true; 268 break; 269 270 default: 271 OSKextLog(/* kext */ NULL, 272 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 273 "unrecognized option %s", (*argv)[optind-1]); 274 goto finish; 275 break; 276 277 } 278 279 /* Reset longindex, because getopt_long_only() is stupid and doesn't. 280 */ 281 longindex = -1; 282 } 283 284 /***** 285 * Record remaining args from the command line. 286 */ 287 for ( /* optind already set */ ; optind < *argc; optind++) { 288 if (!toolArgs->kernelcachePath) { 289 toolArgs->kernelcachePath = (*argv)[optind]; 290 } else { 291 CFStringRef scratchString = CFStringCreateWithCString(kCFAllocatorDefault, 292 (*argv)[optind], kCFStringEncodingUTF8); 293 if (!scratchString) { 294 result = EX_OSERR; 295 OSKextLogMemError(); 296 goto finish; 297 } 298 CFSetAddValue(toolArgs->kextIDs, scratchString); 299 CFRelease(scratchString); 300 } 301 } 302 303 /* Update the argc & argv seen by main() so that boot<>root calls 304 * handle remaining args. 305 */ 306 *argc -= optind; 307 *argv += optind; 308 309 result = EX_OK; 310 311finish: 312 if (result == EX_USAGE) { 313 usage(kUsageLevelBrief); 314 } 315 return result; 316} 317 318/******************************************************************************* 319*******************************************************************************/ 320ExitStatus checkArgs(KclistArgs * toolArgs) 321{ 322 ExitStatus result = EX_USAGE; 323 324 if (!toolArgs->kernelcachePath) { 325 326 OSKextLog(/* kext */ NULL, 327 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 328 "No kernelcache file specified."); 329 goto finish; 330 } 331 332 result = EX_OK; 333 334finish: 335 if (result == EX_USAGE) { 336 usage(kUsageLevelBrief); 337 } 338 return result; 339} 340 341/******************************************************************************* 342*******************************************************************************/ 343void listPrelinkedKexts(KclistArgs * toolArgs, CFPropertyListRef kcInfoPlist, const char *prelinkTextBytes, uint64_t prelinkTextSourceAddress, uint64_t prelinkTextSourceSize) 344{ 345 CFIndex i, count; 346 Boolean haveIDs = CFSetGetCount(toolArgs->kextIDs) > 0 ? TRUE : FALSE; 347 CFArrayRef kextPlistArray = NULL; 348 349 if (CFArrayGetTypeID() == CFGetTypeID(kcInfoPlist)) { 350 kextPlistArray = (CFArrayRef)kcInfoPlist; 351 } else if (CFDictionaryGetTypeID() == CFGetTypeID(kcInfoPlist)){ 352 kextPlistArray = (CFArrayRef)CFDictionaryGetValue(kcInfoPlist, 353 CFSTR("_PrelinkInfoDictionary")); 354 } else { 355 OSKextLog(/* kext */ NULL, 356 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 357 "Unrecognized kernelcache plist data."); 358 goto finish; 359 } 360 361 count = CFArrayGetCount(kextPlistArray); 362 for (i = 0; i < count; i++) { 363 CFDictionaryRef kextPlist = (CFDictionaryRef)CFArrayGetValueAtIndex(kextPlistArray, i); 364 CFStringRef kextIdentifier = (CFStringRef)CFDictionaryGetValue(kextPlist, kCFBundleIdentifierKey); 365 CFNumberRef kextSourceAddress = (CFNumberRef)CFDictionaryGetValue(kextPlist, CFSTR(kPrelinkExecutableSourceKey)); 366 CFNumberRef kextSourceSize = (CFNumberRef)CFDictionaryGetValue(kextPlist, CFSTR(kPrelinkExecutableSizeKey)); 367 const char *kextTextBytes = NULL; 368 369 if (haveIDs && !CFSetContainsValue(toolArgs->kextIDs, kextIdentifier)) { 370 continue; 371 } 372 373 if (kextSourceAddress && CFNumberGetTypeID() == CFGetTypeID(kextSourceAddress) && 374 kextSourceSize && CFNumberGetTypeID() == CFGetTypeID(kextSourceSize)) { 375 uint64_t sourceAddress; 376 uint64_t sourceSize; 377 378 CFNumberGetValue(kextSourceAddress, kCFNumberSInt64Type, &sourceAddress); 379 CFNumberGetValue(kextSourceSize, kCFNumberSInt64Type, &sourceSize); 380 if ((sourceAddress >= prelinkTextSourceAddress) && 381 ((sourceAddress+sourceSize) <= (prelinkTextSourceAddress + prelinkTextSourceSize))) { 382 kextTextBytes = prelinkTextBytes + (ptrdiff_t)(sourceAddress - prelinkTextSourceAddress); 383 } 384 } 385 386 printKextInfo(kextPlist, toolArgs->verbose, toolArgs->printUUIDs, kextTextBytes); 387 } 388 389finish: 390 return; 391} 392 393/******************************************************************************* 394*******************************************************************************/ 395void printKextInfo(CFDictionaryRef kextPlist, Boolean beVerbose, Boolean printUUIDs, const char *kextTextBytes) 396{ 397 CFStringRef kextIdentifier = (CFStringRef)CFDictionaryGetValue(kextPlist, kCFBundleIdentifierKey); 398 CFStringRef kextVersion = (CFStringRef)CFDictionaryGetValue(kextPlist, kCFBundleVersionKey); 399 CFStringRef kextPath = (CFStringRef)CFDictionaryGetValue(kextPlist, CFSTR("_PrelinkBundlePath")); 400 char idBuffer[KMOD_MAX_NAME]; 401 char versionBuffer[KMOD_MAX_NAME]; 402 char pathBuffer[PATH_MAX]; 403 404 CFNumberRef cfNum; 405 uint64_t kextLoadAddress = 0x0; 406 uint64_t kextSourceAddress = 0x0; 407 uint64_t kextExecutableSize = 0; 408 uint64_t kextKmodInfoAddress = 0x0; 409 410 struct load_command *lcp; 411 struct uuid_command *uuid_cmd = NULL; 412 uint32_t ncmds, cmd_i; 413 414 if (!kextIdentifier || !kextVersion || !kextPath) { 415 OSKextLog(/* kext */ NULL, 416 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 417 "Missing identifier, version, or path."); 418 goto finish; 419 } 420 CFStringGetCString(kextIdentifier, idBuffer, sizeof(idBuffer), kCFStringEncodingUTF8); 421 CFStringGetCString(kextVersion, versionBuffer, sizeof(versionBuffer), kCFStringEncodingUTF8); 422 CFStringGetCString(kextPath, pathBuffer, sizeof(pathBuffer), kCFStringEncodingUTF8); 423 424 if (NULL != (cfNum = CFDictionaryGetValue(kextPlist, CFSTR(kPrelinkExecutableLoadKey)))) 425 CFNumberGetValue(cfNum, kCFNumberSInt64Type, &kextLoadAddress); 426 if (NULL != (cfNum = CFDictionaryGetValue(kextPlist, CFSTR(kPrelinkExecutableSourceKey)))) 427 CFNumberGetValue(cfNum, kCFNumberSInt64Type, &kextSourceAddress); 428 if (NULL != (cfNum = CFDictionaryGetValue(kextPlist, CFSTR(kPrelinkExecutableSizeKey)))) 429 CFNumberGetValue(cfNum, kCFNumberSInt64Type, &kextExecutableSize); 430 if (NULL != (cfNum = CFDictionaryGetValue(kextPlist, CFSTR(kPrelinkKmodInfoKey)))) 431 CFNumberGetValue(cfNum, kCFNumberSInt64Type, &kextKmodInfoAddress); 432 433 if (kextTextBytes) { 434 if (ISMACHO64(MAGIC32(kextTextBytes))) { 435 struct mach_header_64 *mhp64 = (struct mach_header_64 *)kextTextBytes; 436 ncmds = mhp64->ncmds; 437 lcp = (struct load_command *)(void *)(mhp64 + 1); 438 } else { 439 struct mach_header *mhp = (struct mach_header *)kextTextBytes; 440 ncmds = mhp->ncmds; 441 lcp = (struct load_command *)(void *)(mhp + 1); 442 } 443 444 for (cmd_i = 0; cmd_i < ncmds; cmd_i++) { 445 if (lcp->cmd == LC_UUID) { 446 uuid_cmd = (struct uuid_command *)lcp; 447 break; 448 } 449 lcp = (struct load_command *)((uintptr_t)lcp + lcp->cmdsize); 450 } 451 } 452 453 if (printUUIDs) { 454 455 if (uuid_cmd) { 456 uuid_string_t uuid_string; 457 458 uuid_unparse(*(uuid_t *)uuid_cmd->uuid, uuid_string); 459 printf("%s\t%s\t%s\t0x%llx\t0x%llx\t%s\n", idBuffer, versionBuffer, uuid_string, kextLoadAddress, kextExecutableSize, pathBuffer); 460 } else { 461 printf("%s\t%s\t\t\t\t%s\n", idBuffer, versionBuffer, pathBuffer); 462 } 463 } else { 464 printf("%s\t%s\t%s\n", idBuffer, versionBuffer, pathBuffer); 465 } 466 467 if (beVerbose) { 468 printf("\t-> load address: 0x%0.8llx, " 469 "size = 0x%0.8llx,\n" 470 "\t-> source address: 0x%0.8llx, " 471 "kmod_info address = 0x%0.8llx\n", 472 kextLoadAddress, kextExecutableSize, kextSourceAddress, kextKmodInfoAddress); 473 } 474 475finish: 476 return; 477} 478 479/******************************************************************************* 480*******************************************************************************/ 481CFComparisonResult compareIdentifiers(const void * val1, const void * val2, void * context __unused) 482{ 483 CFDictionaryRef dict1 = (CFDictionaryRef)val1; 484 CFDictionaryRef dict2 = (CFDictionaryRef)val2; 485 486 CFStringRef id1 = CFDictionaryGetValue(dict1, kCFBundleIdentifierKey); 487 CFStringRef id2 = CFDictionaryGetValue(dict2, kCFBundleIdentifierKey); 488 489 return CFStringCompare(id1, id2, 0); 490} 491 492/******************************************************************************* 493* usage() 494*******************************************************************************/ 495void usage(UsageLevel usageLevel) 496{ 497 fprintf(stderr, 498 "usage: %1$s [-arch archname] [-u] [-v] [--] kernelcache [bundle-id ...]\n" 499 "usage: %1$s -help\n" 500 "\n", 501 progname); 502 503 if (usageLevel == kUsageLevelBrief) { 504 fprintf(stderr, "use %s -%s for an explanation of each option\n", 505 progname, kOptNameHelp); 506 } 507 508 if (usageLevel == kUsageLevelBrief) { 509 return; 510 } 511 512 fprintf(stderr, "-%s <archname>:\n" 513 " list info for architecture <archname>\n", 514 kOptNameArch); 515 fprintf(stderr, "-%s (-%c):\n" 516 " print kext load addresses and UUIDs\n", 517 kOptNameUUID, kOptUUID); 518 fprintf(stderr, "-%s (-%c):\n" 519 " emit additional information about kext load addresses and sizes\n", 520 kOptNameVerbose, kOptVerbose); 521 fprintf(stderr, "\n"); 522 523 fprintf(stderr, "-%s (-%c): print this message and exit\n", 524 kOptNameHelp, kOptHelp); 525 526 return; 527} 528