#include #include #include #include #include #include #include #include #include #include "ioflash.h" /** used to decrypt special pages when checking if the physical banks parameter is correct when reading/dumping pages are not decrypted **/ uint8_t META_KEY[16] = {0x92, 0xa7, 0x42, 0xab, 0x08, 0xc9, 0x69, 0xbf, 0x00, 0x6c, 0x94, 0x12, 0xd3, 0xcc, 0x79, 0xa5}; //uint8_t FILESYSTEM_KEY[16] = {0xf6, 0x5d, 0xae, 0x95, 0x0e, 0x90, 0x6c, 0x42, 0xb2, 0x54, 0xcc, 0x58, 0xfc, 0x78, 0xee, 0xce}; uint32_t gDeviceReadID = 0; uint32_t gCECount = 0; uint32_t gBlocksPerCE = 0; uint32_t gPagesPerBlock = 0; uint32_t gBytesPerPage = 0; uint32_t gBytesPerSpare = 0; uint32_t gBootloaderBytes = 0; uint32_t gIsBootFromNand = 0; uint32_t gPagesPerCE = 0; uint32_t gTotalBlocks = 0; uint32_t metaPerLogicalPage = 0; uint32_t validmetaPerLogicalPage = 0; uint32_t banksPerCE = 0; uint32_t use_4k_aes_chain = 0; uint32_t gFSStartBlock= 0; uint32_t gDumpPageSize = 0; uint32_t gPPNdevice = 0; uint32_t banksPerCEphysical = 1; uint32_t bank_address_space = 0; uint32_t blocks_per_bank = 0; //from ioflashstoragetool io_service_t fsdService = 0; io_connect_t fsdConnection = 0; CFMutableDictionaryRef MakeOneStringProp(CFStringRef key, CFStringRef value) { CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(dict, key, value); return dict; } io_service_t _wait_for_io_service_matching_dict(CFDictionaryRef matching) { io_service_t service = 0; /* while(!service) { */CFRetain(matching); service = IOServiceGetMatchingService(kIOMasterPortDefault, matching); //if(service) break; /*sleep(1); //CFRelease(matching); } CFRelease(matching);*/ return service; } int FSDConnect(const char* name) { CFMutableDictionaryRef matching; matching = IOServiceMatching(name); fsdService = IOServiceGetMatchingService(kIOMasterPortDefault, matching); IOServiceOpen(fsdService, mach_task_self(), 0, &fsdConnection); return fsdConnection != 0; } int FSDGetPropertyForKey(io_object_t obj, CFStringRef name, void* out, uint32_t outLen, CFMutableDictionaryRef dict) { CFTypeRef data = IORegistryEntryCreateCFProperty(obj, name, kCFAllocatorDefault, 0); if (!data) { return 0; } if (dict != NULL) { CFDictionaryAddValue(dict, name, data); } if (out == NULL) return 0; if(CFGetTypeID(data) == CFNumberGetTypeID()) { CFNumberGetValue((CFNumberRef)data, kCFNumberIntType, out); return 1; } else if(CFGetTypeID(data) == CFDataGetTypeID()) { CFIndex dataLen = CFDataGetLength(data); CFDataGetBytes(data, CFRangeMake(0,dataLen < outLen ? dataLen : outLen), out); return 1; } return 0; } IOReturn FSDReadPageHelper(struct kIOFlashControllerReadPageIn* in, struct kIOFlashControllerOut* out) { size_t outLen = sizeof(struct kIOFlashControllerOut); IOConnectCallStructMethod(fsdConnection, kIOFlashControllerReadPage, in, sizeof(struct kIOFlashControllerReadPageIn), out, &outLen); // fprintf(stderr, "%x %x %x %x %x\n", in->page, in->ce, r, out->ret1, out->ret2); return out->ret1; } IOReturn FSDReadPageWithOptions(uint32_t ceNum, uint32_t pageNum, void* buffer, void* spareBuffer, uint32_t spareSize, uint32_t options, struct kIOFlashControllerOut* out ) { struct kIOFlashControllerReadPageIn in; in.page = pageNum; in.ce = ceNum; in.options = options; in.buffer = buffer; in.bufferSize = gBytesPerPage; in.spare = spareBuffer; in.spareSize = spareSize; return FSDReadPageHelper(&in, out); } IOReturn FSDReadBootPage(uint32_t ceNum, uint32_t pageNum,uint32_t* buffer, struct kIOFlashControllerOut* out) { return FSDReadPageWithOptions(ceNum, pageNum, buffer, NULL, 0, kIOFlashStorageOptionBootPageIO, out); } void findPartitionLocation(CFStringRef contentHint, CFMutableDictionaryRef dict) { const void* keys[2] = {CFSTR("Block Count"), CFSTR("Block Offset")}; const void* values[2] = {NULL, NULL}; CFMutableDictionaryRef match = MakeOneStringProp(CFSTR("IOProviderClass"), CFSTR("IOFlashStoragePartition")); CFMutableDictionaryRef iopmatch = MakeOneStringProp(CFSTR("Content Hint"), contentHint); CFDictionarySetValue(match, CFSTR("IOPropertyMatch"), iopmatch); CFRelease(iopmatch); io_service_t service = _wait_for_io_service_matching_dict(match); if (service) { CFNumberRef blockCount = (CFNumberRef) IORegistryEntryCreateCFProperty(service, CFSTR("Block Count"), kCFAllocatorDefault, 0); CFNumberRef blockOffset = (CFNumberRef) IORegistryEntryCreateCFProperty(service, CFSTR("Block Offset"), kCFAllocatorDefault, 0); if (dict != NULL) { values[0] = (void*) blockCount; values[1] = (void*) blockOffset; CFDictionaryRef d = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (d != NULL) CFDictionaryAddValue(dict, contentHint, d); } if( CFStringCompare(contentHint, CFSTR("Filesystem"), 0) == kCFCompareEqualTo) { CFNumberGetValue(blockOffset, kCFNumberIntType, &gFSStartBlock); } } CFRelease(match); } //openiBoot/util.c uint32_t next_power_of_two(uint32_t n) { uint32_t val = 1 << (31 - __builtin_clz(n)); if (n % val) val *= 2; return val; } void generate_IV(unsigned long lbn, unsigned long *iv) { int i; for(i = 0; i < 4; i++) { if(lbn & 1) lbn = 0x80000061 ^ (lbn >> 1); else lbn = lbn >> 1; iv[i] = lbn; } } void decrypt_page(uint8_t* data, uint32_t dataLength, uint8_t* key, uint32_t keyLength, uint32_t pn) { char iv[16]; size_t dataOutMoved=dataLength; generate_IV(pn, (unsigned long*) iv); CCCryptorStatus s = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, 0, (const void*) key, keyLength, (const void*) iv, (const void*) data, dataLength, (const void*) data, dataLength, &dataOutMoved); if (s != kCCSuccess) { fprintf(stderr, "decrypt_page: CCCrypt error %x\n", s); } } void set_physical_banks(int n) { banksPerCEphysical = n; blocks_per_bank = gBlocksPerCE / banksPerCEphysical; if((gBlocksPerCE & (gBlocksPerCE-1)) == 0) { // Already a power of two. bank_address_space = blocks_per_bank; //total_block_space = gBlocksPerCE; } else { // Calculate the bank address space. bank_address_space = next_power_of_two(blocks_per_bank); //total_block_space = ((banksPerCEphysical-1)*bank_address_space) + blocks_per_bank; } } //"bruteforce" the number of physical banks //except for PPN devices, DEVICEINFOBBT special pages should always be somewhere at the end void check_special_pages() { if(gPPNdevice) { fprintf(stderr, "PPN device, i don't know how to guess the number of physical banks, assuming 1\n"); set_physical_banks(1); return; } uint32_t i,x=1; uint32_t ok = 0; uint32_t bank, block, page; uint8_t* pageBuffer = (uint8_t*) valloc(gDumpPageSize); printf("Searching for correct banksPerCEphysical value ...\n"); while(!ok && x < 10) { set_physical_banks(x); bank = banksPerCEphysical - 1; for(block = blocks_per_bank-1; !ok && block > blocks_per_bank-10 ; block--) { page = (bank_address_space * bank + block) * gPagesPerBlock; struct kIOFlashControllerOut *out = (&pageBuffer[gBytesPerPage + metaPerLogicalPage]); for(i=0; i < gPagesPerBlock; i++) { if(FSDReadPageWithOptions(0, page + i, pageBuffer, &pageBuffer[gBytesPerPage], metaPerLogicalPage, 0, out)) continue; if(pageBuffer[gBytesPerPage] != 0xA5) continue; if(!memcmp(pageBuffer, "DEVICEINFOBBT", 13)) { printf("Found cleartext DEVICEINFOBBT at block %d page %d with banksPerCEphyiscal=%d\n", blocks_per_bank*bank +block, i, banksPerCEphysical); ok = 1; break; } decrypt_page(pageBuffer, gBytesPerPage, META_KEY, kCCKeySizeAES128, page + i); if(!memcmp(pageBuffer, "DEVICEINFOBBT", 13)) { printf("Found encrypted DEVICEINFOBBT at block %d page %d with banksPerCEphyiscal=%d\n", blocks_per_bank*bank +block, i, banksPerCEphysical); ok = 1; break; } } } x++; } if(!ok) { fprintf(stderr, "Couldnt guess the number of physical banks, exiting\n"); exit(0); } free(pageBuffer); return; } //XXX dont read the NAND from this function as it can be called from multiple processes CFMutableDictionaryRef FSDGetInfo(int printInfo) { io_iterator_t iterator = 0; io_object_t obj = 0; FSDConnect("IOFlashController"); if(IORegistryEntryCreateIterator(fsdService, "IOService",0, &iterator)) return NULL; obj = IOIteratorNext(iterator); if (!obj) return NULL; CFMutableDictionaryRef dict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); FSDGetPropertyForKey(obj, CFSTR("device-readid"), &gDeviceReadID, sizeof(gDeviceReadID), dict); FSDGetPropertyForKey(obj, CFSTR("#ce"), &gCECount, sizeof(gCECount), dict); FSDGetPropertyForKey(obj, CFSTR("#ce-blocks"), &gBlocksPerCE, sizeof(gBlocksPerCE), dict); FSDGetPropertyForKey(obj, CFSTR("#block-pages"), &gPagesPerBlock, sizeof(gPagesPerBlock), dict); FSDGetPropertyForKey(obj, CFSTR("#page-bytes"), &gBytesPerPage, sizeof(gBytesPerPage), dict); FSDGetPropertyForKey(obj, CFSTR("#spare-bytes"), &gBytesPerSpare, sizeof(gBytesPerSpare), dict); FSDGetPropertyForKey(obj, CFSTR("#bootloader-bytes"), &gBootloaderBytes, sizeof(gBootloaderBytes), dict); FSDGetPropertyForKey(obj, CFSTR("metadata-whitening"), NULL, 0, dict); FSDGetPropertyForKey(obj, CFSTR("name"), NULL, 0, dict); FSDGetPropertyForKey(obj, CFSTR("is-bfn-partitioned"), NULL, 0, dict); FSDGetPropertyForKey(obj, CFSTR("bbt-format"), NULL, 0, dict); //FSDGetPropertyForKey(obj, CFSTR("channels"), NULL, 0, dict); FSDGetPropertyForKey(obj, CFSTR("vendor-type"), NULL, 0, dict); FSDGetPropertyForKey(obj, CFSTR("ppn-device"), &gPPNdevice, sizeof(gPPNdevice), dict); FSDGetPropertyForKey(obj, CFSTR("valid-meta-per-logical-page"), &validmetaPerLogicalPage, sizeof(gBytesPerSpare), dict); FSDGetPropertyForKey(obj, CFSTR("meta-per-logical-page"), &metaPerLogicalPage, sizeof(gBytesPerSpare), dict); if (metaPerLogicalPage == 0) { metaPerLogicalPage = 12;//default value? } //XXX: returns (possibly wrong) default value (1) when nand-disable-driver is set, use vendor-type + info from openiboot to get correct value : bank-per-ce VFL (!=physical banks) FSDGetPropertyForKey(obj, CFSTR("banks-per-ce"), &banksPerCE, sizeof(gBytesPerSpare), dict); FSDGetPropertyForKey(obj, CFSTR("use-4k-aes-chain"), &use_4k_aes_chain, sizeof(gBytesPerSpare), dict); gPagesPerCE = gBlocksPerCE * gPagesPerBlock; gTotalBlocks = gCECount * gBlocksPerCE; FSDGetPropertyForKey(obj, CFSTR("boot-from-nand"), &gIsBootFromNand, sizeof(gIsBootFromNand), dict); CFMutableDictionaryRef dictPartitions = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); findPartitionLocation(CFSTR("Boot Block"), dictPartitions); findPartitionLocation(CFSTR("Effaceable"), dictPartitions); findPartitionLocation(CFSTR("NVRAM"), dictPartitions); findPartitionLocation(CFSTR("Firmware"), dictPartitions); findPartitionLocation(CFSTR("Filesystem"), dictPartitions); /*findPartitionLocation(CFSTR("Diagnostic Data")); findPartitionLocation(CFSTR("System Config")); findPartitionLocation(CFSTR("Bad Block Table"));*/ //findPartitionLocation(CFSTR("Unpartitioned"));//never matches CFDictionaryAddValue(dict, CFSTR("partitions"), dictPartitions); IOObjectRelease(obj); IOObjectRelease(iterator); gDumpPageSize = gBytesPerPage + metaPerLogicalPage + sizeof(struct kIOFlashControllerOut); CFNumberRef n = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &gDumpPageSize); CFDictionarySetValue(dict, CFSTR("dumpedPageSize"), n); CFRelease(n); if (printInfo) { uint64_t total_size = ((uint64_t)gBytesPerPage) * ((uint64_t) (gPagesPerBlock * gBlocksPerCE * gCECount)); total_size /= 1024*1024*1024; fprintf(stderr, "NAND configuration: %uGiB (%d CEs of %d blocks of %d pages of %d bytes data, %d bytes spare\n", (uint32_t) total_size, gCECount, gBlocksPerCE, gPagesPerBlock, gBytesPerPage, gBytesPerSpare); } set_physical_banks(1); return dict; } CFDictionaryRef nand_dump(int fd) { uint64_t totalSize = (uint64_t)gPagesPerBlock * (uint64_t)gBlocksPerCE * (uint64_t)gCECount * (uint64_t)gDumpPageSize; write(fd, &totalSize, sizeof(uint64_t)); dump_nand_to_socket(fd); return NULL; } int dump_nand_to_socket(int fd) { int ceNum=0,pageNum,bankNum; IOReturn r; uint64_t totalPages = gPagesPerBlock * gBlocksPerCE * gCECount; uint64_t validPages = 0; uint64_t blankPages = 0; uint64_t errorPages = 0; uint64_t otherPages = 0; uint64_t counter = 0; //page data + spare metadata + kernel return values uint8_t* pageBuffer = (uint8_t*) valloc(gDumpPageSize); if (pageBuffer == NULL) { fprintf(stderr, "valloc(%d) FAIL", gDumpPageSize); return 0; } struct kIOFlashControllerOut *out = (&pageBuffer[gBytesPerPage + metaPerLogicalPage]); time_t start = time(NULL); for(bankNum=0; bankNum < banksPerCEphysical; bankNum++) { uint32_t start_page = bank_address_space * bankNum * gPagesPerBlock; uint32_t end_page = start_page + gPagesPerBlock * blocks_per_bank; for(pageNum=start_page; pageNum < end_page; pageNum++) { for(ceNum=0; ceNum < gCECount; ceNum++) { uint32_t blockNum = pageNum / gPagesPerBlock; uint32_t boot = (blockNum < gFSStartBlock); if(boot) r = FSDReadBootPage(ceNum, pageNum, pageBuffer, out); else r = FSDReadPageWithOptions(ceNum, pageNum, pageBuffer, &pageBuffer[gBytesPerPage], metaPerLogicalPage, 0, out); //r = FSDReadPageWithOptions(ceNum, pageNum, pageBuffer,NULL, 0, 0x100, out); if(r == 0) { validPages++; } else { if (r == kIOReturnBadMedia) { fprintf(stderr, "CE %x page %x : uncorrectable ECC error\n", ceNum, pageNum); errorPages++; } else if (r == kIOReturnUnformattedMedia) { memset(pageBuffer, 0xFF, gBytesPerPage + metaPerLogicalPage); blankPages++; } else if (r == 0xdeadbeef) { fprintf(stderr, "0xdeadbeef return code, something is wrong with injected kernel code\n"); exit(0); } else if (r == kIOReturnBadArgument || r == kIOReturnNotPrivileged) { fprintf(stderr, "Error 0x%x (kIOReturnBadArgument/kIOReturnNotPrivileged)\n", r); exit(0); } else { fprintf(stderr, "CE %x page %x : unknown return code 0x%x\n", ceNum, pageNum, r); otherPages++; } } out->ret2 = 0; if(write(fd, pageBuffer, gDumpPageSize) != gDumpPageSize) { pageNum = gPagesPerBlock * gBlocksPerCE; fprintf(stderr, "Abort dump\n"); break; } if (ceNum == 0 && pageNum % gPagesPerBlock == 0) { fprintf(stderr, "Block %d/%d (%d%%)\n", (counter/gPagesPerBlock), gBlocksPerCE, (counter*100)/(gPagesPerBlock*gBlocksPerCE)); } } counter++; } } if (ceNum == gCECount && pageNum == (gPagesPerBlock * gBlocksPerCE)) { time_t duration = time(NULL) - start; fprintf(stderr, "Finished NAND dump in %lu hours %lu minutes %lu seconds\n", duration / 3600, (duration % 3600) / 60, (duration % 3600) % 60); fprintf(stderr, "Total pages %llu\n", totalPages); fprintf(stderr, "In-use pages %llu (%d%%)\n", validPages, (int) (validPages * 100 / totalPages)); fprintf(stderr, "Blank pages %llu (%d%%)\n", blankPages, (int) (blankPages * 100 / totalPages)); fprintf(stderr, "Error pages %llu (%d%%)\n", errorPages, (int) (errorPages * 100 / totalPages)); fprintf(stderr, "Other pages %llu (%d%%)\n", otherPages, (int) (otherPages * 100 / totalPages)); } free(pageBuffer); return 0; } int nand_proxy(int fd) { struct kIOFlashControllerOut out; proxy_read_cmd cmd; uint8_t* pageBuffer = (uint8_t*) valloc(gBytesPerPage); if( pageBuffer == NULL) { fprintf(stderr, "pageBuffer = valloc(%d) failed\n", gBytesPerPage); return 0; } while(1) { int z = read(fd, &cmd, sizeof(proxy_read_cmd)); if (z != sizeof(proxy_read_cmd)) break; void* spareBuf = NULL; uint32_t blockNum = cmd.page / gPagesPerBlock; uint32_t boot = (blockNum < gFSStartBlock); if(boot) { cmd.spareSize = 0; cmd.options |= kIOFlashStorageOptionBootPageIO; } else if (cmd.spareSize) { spareBuf = valloc(cmd.spareSize); } FSDReadPageWithOptions(cmd.ce, cmd.page, pageBuffer, spareBuf, cmd.spareSize, cmd.options, &out); write(fd, pageBuffer, gBytesPerPage); if (spareBuf != NULL) { write(fd, spareBuf, cmd.spareSize); } write(fd, &out, sizeof(struct kIOFlashControllerOut)); if (spareBuf != NULL) { free(spareBuf); } } free(pageBuffer); return 0; }