hacks/dump-imessages/iphone-dataprotection/ramdisk_tools/ioflash/ioflash.c

557 lines
20 KiB
C

#include <stdio.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CommonCrypto/CommonCryptor.h>
#include <IOKit/IOKitLib.h>
#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;
}