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