#include #include #include #include #include #include #include #include "ioflash.h" CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); mach_port_t kernel_task=0; void* externalMethod_original = NULL; void** externalMethod_ptr = NULL; void* (*IOMemoryDescriptor__withAddress)(void*, uint32_t, uint32_t, uint32_t) = 0x0; typedef int (*methodptr)(void*, ...); #define CALL_VTABLE(this, vtableoff, ...) \ ((methodptr) (*(uint32_t**)this)[vtableoff/4]) (this, ##__VA_ARGS__) //IOReturn externalMethod( uint32_t selector, IOExternalMethodArguments * arguments,IOExternalMethodDispatch * dispatch = 0, OSObject * target = 0, void * reference = 0 ); int myIOFlashStorage_externalMethod(uint32_t* this, uint32_t selector, IOExternalMethodArguments* arguments) { if (selector == kIOFlashControllerDisableKeepout) { void* obj2 = (void*) this[0x78/4]; //AppleIOPFMI object CALL_VTABLE(obj2, 0x35C, 0, 0, 1); return 0x0; } if (selector != kIOFlashControllerReadPage) //only support read return 0xE00002C2; if (IOMemoryDescriptor__withAddress == 0x0) return 0xdeadbeef; struct IOFlashCommandStruct command = {0}; uint32_t** bufferDesc = NULL; uint32_t** spareDesc = NULL; uint32_t** md = NULL; void* obj1 = (void*) this[0x78/4]; //AppleIOPFMI object struct kIOFlashControllerReadPageIn* in = (struct kIOFlashControllerReadPageIn*) arguments->structureInput; struct kIOFlashControllerOut* out = (struct kIOFlashControllerOut*) arguments->structureOutput; uint32_t page = in->page; uint32_t ce = in->ce; command.flags1 = 1; if (in->options & kIOFlashStorageOptionBootPageIO) { if(in->spare != NULL) return 0xE00002C2; //spare buffer is not used with bootloader page I/O command.flags0 = 9; } else { if( !(in->options & kIOFlashStorageOptionRawPageIO) && in->spare) { command.flags0 = in->options & 2; } else { return 0xdeadbeef; //raw page io } } command.flags1 |= in->options & 4; command.field8 = 1; command.fieldC = 0; command.page_ptr = &page; bufferDesc = IOMemoryDescriptor__withAddress(in->buffer, in->bufferSize, 1, this[0x7C/4]); if (bufferDesc == NULL) return 0xE00002C2; command.bufferDesc = bufferDesc; command.field18 = 0; command.errorCode = 0; if (in->spare != NULL) { spareDesc = IOMemoryDescriptor__withAddress(in->spare, in-> spareSize, 1, this[0x7C/4]); if (spareDesc == NULL) return 0xE00002C2; //0xAC -> desc2 __ZN25IOGeneralMemoryDescriptor5doMapEP7_vm_mapPjmmm //virtual IOMemoryMap * map( IOOptionBits options = 0 ); md = (void*) CALL_VTABLE(spareDesc, 0x98); //IOGeneralMemoryDescriptor_map command.spareSize = in->spareSize; command.spareVA = (void*) CALL_VTABLE(md, 0x38); } else { command.spareSize = 0; command.spareVA = NULL; } command.field34 = 0; command.ce_ptr = &ce; out->ret1 = CALL_VTABLE(obj1, 0x344, 0, &command); CALL_VTABLE(bufferDesc, 0x14); //IOGeneralMemoryDescriptor_release if (md != NULL) { CALL_VTABLE(md, 0x14); //IOGeneralMemoryDescriptor_release } if (spareDesc != NULL) { CALL_VTABLE(spareDesc, 0x14); //IOGeneralMemoryDescriptor_release } out->ret2 = command.errorCode; return 0; } kern_return_t write_kernel(mach_port_t p, void* addr, uint32_t value) { kern_return_t r = vm_write(p, (vm_address_t)addr, (vm_address_t)&value, sizeof(value )); if (r) fprintf(stderr, "vm_write into kernel_task failed\n"); else fprintf(stderr, "vm_write into kernel_task OK\n"); return r; } void __attribute__((destructor)) restore_handler() { if (kernel_task != 0 && externalMethod_ptr != NULL && externalMethod_original != NULL) { fprintf(stderr, "Restoring IOFlashStorageControler::externalMethod ptr\n"); write_kernel(kernel_task, externalMethod_ptr, (uint32_t) externalMethod_original+1); } } void signal_handler(int sig) { restore_handler(); signal(sig, SIG_DFL); raise(sig); } int IOFlashStorage_kernel_patch() { CFStringRef version = NULL; CFDictionaryRef versionDict = _CFCopySystemVersionDictionary(); if (versionDict != NULL) { version = CFDictionaryGetValue(versionDict, _kCFSystemVersionProductVersionKey); } if (version == NULL) { fprintf(stderr, "FAILed to get current version\n"); return 0; } if (CFStringCompare(version, CFSTR("4.3.5"), 0) <= 0) { fprintf(stderr, "iOS 4 kernel detected, no kernel patching required\n"); return 1; } fprintf(stderr, "iOS 5 kernel detected, replacing IOFlashControlerUserClient::externalMethod\n"); kern_return_t r = task_for_pid(mach_task_self(), 0, &kernel_task); if( r != 0) { fprintf(stderr, "task_for_pid returned %x : missing tfp0 kernel patch (use latest kernel_patcher.py) or wrong entitlements\n", r); return 0; } uint32_t i; pointer_t buf; unsigned int sz; vm_address_t addr = 0x80002000; while( addr < (0x80002000 + 0xA00000)) { vm_read(kernel_task, addr, 2048, &buf, &sz); if( buf == NULL || sz == 0) continue; uint32_t* p = (uint32_t*) buf; for(i=0; i < sz/sizeof(uint32_t); i++) { if (externalMethod_original != NULL) { if (p[i] == (externalMethod_original+1)) { externalMethod_ptr = (void*) (addr + i*4); fprintf(stderr, "Found externalMethod ptr at %x\n", (uint32_t) externalMethod_ptr); write_kernel(kernel_task, externalMethod_ptr, (uint32_t) myIOFlashStorage_externalMethod); signal(SIGINT, signal_handler);//handle ctrl+c return 1; } else if(IOMemoryDescriptor__withAddress == NULL && !memcmp(&p[i], "\x20\x46\x26\xB0\x5D\xF8\x04\x8B\xF0\xBD", 10)) { IOMemoryDescriptor__withAddress = (void*) p[i+5]; fprintf(stderr, "IOMemoryDescriptor__withAddress=%x\n", (uint32_t) IOMemoryDescriptor__withAddress); } } else if(!memcmp(&p[i], "\xF0\xB5\x03\xAF\x4D\xF8\x04\x8D\xA6\xB0\x40\xF2\xC2\x24\x13\x6A", 16)) { externalMethod_original = (void*) (addr + i*4); fprintf(stderr, "Found IOFlashControlerUserClient::externalMethod at %x\n", (uint32_t) externalMethod_original); } } addr += 2048; } fprintf(stderr, "Kernel patching failed\n"); return 0; }