#!/usr/bin/python import plistlib import zipfile import struct import sys from optparse import OptionParser from Crypto.Cipher import AES from util.lzss import decompress_lzss devices = {"n82ap": "iPhone1,2", "n88ap": "iPhone2,1", "n90ap": "iPhone3,1", "n90bap": "iPhone3,2", "n92ap": "iPhone3,3", "n18ap": "iPod3,1", "n81ap": "iPod4,1", "k48ap": "iPad1,1", "n72ap": "iPod2,1", } h=lambda x:x.replace(" ","").decode("hex") #thx to 0x56 patchs_ios6 = { "IOAESAccelerator enable UID" : (h("B0 F5 FA 6F 00 F0 92 80"), h("B0 F5 FA 6F 00 20 00 20")), "_PE_i_can_has_debugger" : (h("80 B1 43 F2 BE 01 C0 F2"), h("01 20 70 47 BE 01 C0 F2")), } #https://github.com/comex/datautils0/blob/master/make_kernel_patchfile.c patchs_ios5 = { "CSED" : (h("df f8 88 33 1d ee 90 0f a2 6a 1b 68"), h("df f8 88 33 1d ee 90 0f a2 6a 01 23")), "AMFI" : (h("D0 47 01 21 40 B1 13 35"), h("00 20 01 21 40 B1 13 35")), "_PE_i_can_has_debugger" : (h("38 B1 05 49 09 68 00 29"), h("01 20 70 47 09 68 00 29")), "task_for_pid_0" : (h("00 21 02 91 ba f1 00 0f 01 91 06 d1 02 a8"), h("00 21 02 91 ba f1 00 0f 01 91 06 e0 02 a8")), "IOAESAccelerator enable UID" : (h("67 D0 40 F6"), h("00 20 40 F6")), #not stritcly required, useful for testing "getxattr system": ("com.apple.system.\x00", "com.apple.aaaaaa.\x00"), "IOAES gid": (h("40 46 D4 F8 54 43 A0 47"), h("40 46 D4 F8 43 A0 00 20")), #HAX to fit into the 40 char boot-args (redsn0w 0.9.10) "nand-disable-driver": ("nand-disable-driver\x00", "nand-disable\x00\x00\x00\x00\x00\x00\x00\x00") } patchs_ios4 = { "NAND_epoch" : ("\x90\x47\x83\x45", "\x90\x47\x00\x20"), "CSED" : ("\x00\x00\x00\x00\x01\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00", "\x01\x00\x00\x00\x01\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00"), "AMFI" : ("\x01\xD1\x01\x30\x04\xE0\x02\xDB", "\x00\x20\x01\x30\x04\xE0\x02\xDB"), "_PE_i_can_has_debugger" : (h("48 B1 06 4A 13 68 13 B9"), h("01 20 70 47 13 68 13 B9")), "IOAESAccelerator enable UID" : ("\x56\xD0\x40\xF6", "\x00\x00\x40\xF6"), "getxattr system": ("com.apple.system.\x00", "com.apple.aaaaaa.\x00"), } patchs_armv6 = { "NAND_epoch" : (h("00 00 5B E1 0E 00 00 0A"), h("00 00 5B E1 0E 00 00 EA")), "CSED" : (h("00 00 00 00 01 00 00 00 80 00 00 00 00 00 00 00"), h("01 00 00 00 01 00 00 00 80 00 00 00 00 00 00 00")), "AMFI" : (h("00 00 00 0A 00 40 A0 E3 04 00 A0 E1 90 80 BD E8"), h("00 00 00 0A 00 40 A0 E3 01 00 A0 E3 90 80 BD E8")), "_PE_i_can_has_debugger" : (h("00 28 0B D0 07 4A 13 68 00 2B 02 D1 03 60 10 68"), h("01 20 70 47 07 4A 13 68 00 2B 02 D1 03 60 10 68")), "IOAESAccelerator enable UID" : (h("5D D0 36 4B 9A 42"), h("00 20 36 4B 9A 42")), "IOAES gid": (h("FA 23 9B 00 9A 42 05 D1"), h("00 20 00 20 9A 42 05 D1")), "nand-disable-driver": ("nand-disable-driver\x00", "nand-disable\x00\x00\x00\x00\x00\x00\x00\x00"), } patchs_ios4_fixnand = { "Please reboot => jump to prepare signature": (h("B0 47 DF F8 E8 04 F3 E1"), h("B0 47 DF F8 E8 04 1D E0")), "prepare signature => jump to write signature": (h("10 43 18 60 DF F8 AC 04"), h("10 43 18 60 05 E1 00 20")), "check write ok => infinite loop" : (h("A3 48 B0 47 01 24"), h("A3 48 B0 47 FE E7")) } #grab keys from redsn0w Keys.plist class IPSWkeys(object): def __init__(self, manifest): self.keys = {} buildi = manifest["BuildIdentities"][0] dc = buildi["Info"]["DeviceClass"] build = "%s_%s_%s" % (devices.get(dc,dc), manifest["ProductVersion"], manifest["ProductBuildVersion"]) try: rs = plistlib.readPlist("Keys.plist") except: raise Exception("Get Keys.plist from redsn0w and place it in the current directory") for k in rs["Keys"]: if k["Build"] == build: self.keys = k break def getKeyIV(self, filename): if not self.keys.has_key(filename): return None, None k = self.keys[filename] return k.get("Key",""), k.get("IV","") def decryptImg3(blob, key, iv): assert blob[:4] == "3gmI", "Img3 magic tag" data = "" for i in xrange(20, len(blob)): tag = blob[i:i+4] size, real_size = struct.unpack("= real_size, "Img3 length check" data = blob[i+12:i+size] break i += size return AES.new(key, AES.MODE_CBC, iv).decrypt(data)[:real_size] def main(ipswname, options): ipsw = zipfile.ZipFile(ipswname) manifest = plistlib.readPlistFromString(ipsw.read("BuildManifest.plist")) kernelname = manifest["BuildIdentities"][0]["Manifest"]["KernelCache"]["Info"]["Path"] devclass = manifest["BuildIdentities"][0]["Info"]["DeviceClass"] kernel = ipsw.read(kernelname) keys = IPSWkeys(manifest) key,iv = keys.getKeyIV(kernelname) if key == None: print "No keys found for kernel" return print "Decrypting %s" % kernelname kernel = decryptImg3(kernel, key.decode("hex"), iv.decode("hex")) assert kernel.startswith("complzss"), "Decrypted kernelcache does not start with \"complzss\" => bad key/iv ?" print "Unpacking ..." kernel = decompress_lzss(kernel) assert kernel.startswith("\xCE\xFA\xED\xFE"), "Decompressed kernelcache does not start with 0xFEEDFACE" patchs = patchs_ios5 if devclass in ["n82ap", "n72ap"]: print "Using ARMv6 kernel patches" patchs = patchs_armv6 elif manifest["ProductVersion"].startswith("4."): print "Using iOS 4 kernel patches" patchs = patchs_ios4 elif manifest["ProductVersion"].startswith("6."): print "Using iOS 6 kernel patches" patchs = patchs_ios6 if options.fixnand: if patchs != patchs_ios4: print "FAIL : use --fixnand with iOS 4.x IPSW" return patchs.update(patchs_ios4_fixnand) kernelname = "fix_nand_" + kernelname print "WARNING : only use this kernel to fix NAND epoch brick" for p in patchs: print "Doing %s patch" % p s, r = patchs[p] c = kernel.count(s) if c != 1: print "=> FAIL, count=%d, do not boot that kernel it wont work" % c else: kernel = kernel.replace(s,r) outkernel = "%s.patched" % kernelname open(outkernel, "wb").write(kernel) print "Patched kernel written to %s" % outkernel ramdiskname = manifest["BuildIdentities"][0]["Manifest"]["RestoreRamDisk"]["Info"]["Path"] key,iv = keys.getKeyIV("Ramdisk") ramdisk = ipsw.read(ramdiskname) print "Decrypting %s" % ramdiskname ramdisk = decryptImg3(ramdisk, key.decode("hex"), iv.decode("hex")) assert ramdisk[0x400:0x402] == "H+", "H+ magic not found in decrypted ramdisk => bad key/iv ?" customramdisk = "myramdisk_%s.dmg" % devclass f = open(customramdisk, "wb") f.write(ramdisk) f.close() if manifest["ProductVersion"].startswith("6."): print "Run ./build_ramdisk_ios6.sh %s" % customramdisk print "Then redsn0w -i %s -r %s -k %s -a \"-v rd=md0 amfi=0xff cs_enforcement_disable=1\"" % (ipswname, customramdisk, outkernel) return build_cmd = "./build_ramdisk.sh %s %s %s %s %s" % (ipswname, ramdiskname, key, iv, customramdisk) rs_cmd = "redsn0w -i %s -r %s -k %s" % (ipswname, customramdisk, outkernel) rdisk_script="""#!/bin/sh for VER in 4.2 4.3 5.0 5.1 6.0 do if [ -f "/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$VER.sdk/System/Library/Frameworks/IOKit.framework/IOKit" ]; then SDKVER=$VER echo "Found iOS SDK $SDKVER" break fi done if [ "$SDKVER" == "" ]; then echo "iOS SDK not found" exit fi SDKVER=$SDKVER make -C ramdisk_tools %s if [ "$?" == "0" ] then echo "You can boot the ramdisk using the following command (fix paths)" echo "%s" echo "Add -a \\"-v rd=md0 nand-disable=1\\" for nand dump/read only access" fi """ % (build_cmd, rs_cmd) scriptname="make_ramdisk_%s.sh" % devclass f=open(scriptname, "wb") f.write(rdisk_script) f.close() print "Created script %s, you can use it to (re)build the ramdisk"% scriptname if __name__ == "__main__": parser = OptionParser(usage="%prog [options] IPSW") parser.add_option("-f", "--fixnand", action="store_true", dest="fixnand", default=False, help="Apply NAND epoch fix kernel patches") (options, args) = parser.parse_args() if len(args) < 1: parser.print_help() else: main(args[0], options)