├── .gitmodules ├── Makefile ├── README.md └── rasengan.c /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lzfse"] 2 | path = lzfse 3 | url = https://github.com/lzfse/lzfse 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: liblzfse.a 2 | clang -c rasengan.c -o rasengan.o 3 | clang rasengan.o liblzfse.a -o rasengan 4 | rm -rf lzfse 5 | rm *.o 6 | 7 | liblzfse.a: lzfse/src 8 | cd lzfse; make; cp build/bin/liblzfse.a ../; cd .. 9 | lzfse/src: 10 | git submodule init 11 | git submodule update 12 | 13 | clean: 14 | rm *.o rasengan *.a Apple* 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rasengan - extract various firmware blobs from iBoot 2 | 3 | Since A7 Apple have been shipping iBoots with some co-processor firmwares embedded in them. They have been embedded in different formats (sometimes as a raw firmware or as an lzfse compressed blob in iBoot) which vary accross device and iOS version combinations. So far I've added support for extracting ANS1 (Csi + RTBuddy), ANS2, SMC and PMU firmwares. Here's some summed up info about these firmwares: 4 | 5 | 6 | | Fimrwares | SoC | iOS | Aarch | Format | 7 | |:---------------------:|:-------------:|:---------:|:------------------:|:------------:| 8 | | ANS1 (Csi) | A7-A8 | 7-9 | 32bit | Raw | 9 | | ANS1 (RTBuddy) | A7-A8 | 10-12 | 32bit | Raw | 10 | | ANS2 | A11-A13 | 11-14 | 64bit | LZFSE | 11 | | SMC (Raw) | A11 | 11 | 32bit | Raw | 12 | | SMC | A11 | 12-14 | 32bit | LZFSE | 13 | | SMC | A12-A13 | 12-14 | 64bit | LZFSE | 14 | | PMU | A13 | 13-14 | 32bit | Raw | 15 | 16 | From what I've looked into, the extracted PMU firmware will not have the TEXT segment at the very top but a little later and rasengan should print that offset right. It must be noted that there's no documentation on extraction of these firmwares so my method might be wrong but it works(TM). Also with A14 ANS2 is now a seperate IMG4 Mach-O file thus the reduced iBoot size. 17 | 18 | ## Usage 19 | 20 | ``` 21 | ./rasengan -e -i iBoot.n104.RESEARCH_RELEASE.dec 22 | =================================================== 23 | iBoot-6723.0.48 24 | =================================================== 25 | AppleStorageProcessorANS2-1161.1.6~46, @ 0x16D9BC(0x19C19D9BC), 64bit, Size: 0xA4BA1, Decompressed Size: 0x15B160 26 | AppleSMCFirmware-2317.0.32.n104.REL, @ 0x217D9F(0x19C247D9F), 64bit, Size: 0x1FE43, Decompressed Size: 0x437E0 27 | ApplePMUFirmware-26.40.1.Zc0b.debug, @ 0x213E10 (0x19C243E10) Size: 0x2250 TEXT@ 0x213F10 28 | ``` -------------------------------------------------------------------------------- /rasengan.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "lzfse/src/lzfse.h" 6 | 7 | typedef struct 8 | { 9 | void *adr; 10 | uint32_t sz; 11 | char *name; 12 | uint64_t badr; 13 | int extraction_status; 14 | int analysis; 15 | }afile; 16 | 17 | afile iboot; 18 | 19 | typedef struct{ 20 | uint64_t pmu_fw; 21 | uint32_t pmu_sz; 22 | uint32_t pmu_uuid_offset; 23 | }pmu_13; 24 | 25 | typedef struct { 26 | uint64_t pmu_fw; 27 | uint64_t pmu_fw_; 28 | uint64_t pmu_fw_end; 29 | uint64_t unk; 30 | uint32_t pmu_sz; 31 | uint32_t pmu_uuid_offset; 32 | }pmu_14; 33 | 34 | #define OFF(a) (a - iboot.adr) 35 | #define BASE(a) (OFF(a) + iboot.badr) 36 | 37 | afile blobfopen(afile input){ 38 | FILE *fp = fopen(input.name, "r"); 39 | if (!fp){ 40 | printf("[!] Can't open file %s\n", input.name); 41 | exit(1); 42 | } 43 | fseek(fp, 0, SEEK_END); 44 | uint32_t sz = ftell(fp); 45 | void *mem = malloc(sz); 46 | if(!mem){ 47 | printf("Can't alloc memory\n"); 48 | exit(1); 49 | } 50 | fseek(fp, 0, SEEK_SET); 51 | fread(mem, sz, 1, fp); 52 | fclose(fp); 53 | input.adr = mem; 54 | input.sz = sz; 55 | input.badr = 0; 56 | return input; 57 | } 58 | 59 | char *blobfwrite(uint32_t sz, void *ptr, char *name, char **aarch){ 60 | uint64_t ANS = 0x6F7453656C707041; 61 | uint64_t SMC = 0x434D53656C707041; 62 | uint64_t PMU = 0x554D50656C707041; 63 | if(!name){ 64 | name = memmem(ptr, sz, &ANS, sizeof(ANS)); 65 | if(!name){ 66 | name = memmem(ptr, sz, &SMC, sizeof(SMC)); 67 | if(!name){ 68 | name = memmem(ptr, sz, &PMU, sizeof(PMU)); 69 | if(!name) 70 | name = "unknown image"; 71 | } 72 | } 73 | } 74 | if(iboot.extraction_status){ 75 | FILE *fp = fopen(name, "w"); 76 | fwrite(ptr, sz, 1, fp); 77 | if(fclose(fp)){ 78 | printf("Failed to save %s\n", name); 79 | } 80 | } 81 | if(aarch){ 82 | if(*(uint32_t*)ptr == 0xEA000006) 83 | *aarch = "32bit"; 84 | else if(*(uint32_t*)ptr == 0x14000081) 85 | *aarch = "64bit"; 86 | else 87 | *aarch = "Unknown aarch"; 88 | } 89 | return name; 90 | } 91 | 92 | void print_help(){ 93 | printf("\nUsage: rasengan [-a/-e] -i iBoot\n"); 94 | printf("\n\t-a\tPrint info about embedded images\n"); 95 | printf("\t-e\tExtract embedded firmwares if any\n"); 96 | } 97 | 98 | void base_addr(uint32_t major){ 99 | uint32_t base_addr_offset = 0x318; 100 | if(major > 5540) 101 | base_addr_offset = 0x300; 102 | iboot.badr = *(uint64_t*)(iboot.adr + base_addr_offset); 103 | } 104 | 105 | void print_banner(){ 106 | if(*(uint32_t*)(iboot.adr + 0x200) != 0x6F6F4269){ 107 | printf("Input file doesn't seems to be an iBoot image, continuing though\n"); 108 | return; 109 | } 110 | char *version = iboot.adr + 0x286; 111 | base_addr(atoi(version)); 112 | printf("\033[0;32m===================================================\n"); 113 | printf("\t\tiBoot-%s\n", version); 114 | printf("===================================================\033[0m\n"); 115 | } 116 | 117 | void lzfse_fw(){ 118 | uint32_t bvxE = 0x24787662, bvx2 = 0x32787662; 119 | void *compressed_blob_start = NULL, *compressed_blob_end = NULL; 120 | void *image_start = iboot.adr; 121 | void *newbase = iboot.adr; 122 | void *image_end = iboot.adr + iboot.sz; 123 | uint32_t last_offset = 0; 124 | while(newbase < image_end){ 125 | compressed_blob_start = memmem(newbase, iboot.sz - last_offset, &bvx2, sizeof(bvx2)); 126 | if(compressed_blob_start){ 127 | uint32_t compressed_blob_start_offset = OFF(compressed_blob_start); 128 | compressed_blob_end = memmem(newbase , iboot.sz - last_offset, &bvxE, sizeof(bvxE)); 129 | if(compressed_blob_end){ 130 | uint32_t compressed_blob_end_offset = OFF(compressed_blob_end) + 0x4; 131 | uint32_t compressed_size = compressed_blob_end_offset - compressed_blob_start_offset; 132 | uint32_t uncompressed_size = compressed_size * 4; 133 | void *decompressed_blob = malloc(uncompressed_size); 134 | uint32_t decompressed_size = lzfse_decode_buffer(decompressed_blob, uncompressed_size, compressed_blob_start, compressed_size, NULL); 135 | if(!decompressed_size){ 136 | printf("Failed to decompress lzfse blob\n"); 137 | free(decompressed_blob); 138 | return; 139 | } 140 | char *aarch; 141 | char *name = blobfwrite(decompressed_size, decompressed_blob, NULL, &aarch); 142 | printf("\033[0;32m%s\033[0m, @ 0x%X(0x%llX), %s, Size: 0x%X, Decompressed Size: 0x%X\n", name, compressed_blob_start_offset, compressed_blob_start_offset + iboot.badr, aarch, compressed_size, decompressed_size); 143 | newbase = image_start + compressed_blob_end_offset; 144 | last_offset = compressed_blob_end_offset; 145 | free(decompressed_blob); 146 | } 147 | } 148 | else 149 | return; 150 | } 151 | } 152 | 153 | void pmu_fw(){ 154 | uint64_t pmu_magic = 0x6800481200000000; 155 | uint32_t top_of_fw = 0; 156 | void *ptr_to_pmu_text = memmem(iboot.adr, iboot.sz, &pmu_magic, sizeof(pmu_magic)); 157 | if(ptr_to_pmu_text){ 158 | ptr_to_pmu_text += 4; 159 | void *ptr_to_pmu = ptr_to_pmu_text; 160 | while(top_of_fw != 0x20000101){ 161 | top_of_fw = *(uint32_t*)ptr_to_pmu; 162 | ptr_to_pmu -= 0x4; 163 | } 164 | uint64_t iboot_addr_pmu_fw = BASE(ptr_to_pmu); 165 | void *xref_to_pmu_fw = memmem(iboot.adr, iboot.sz, &iboot_addr_pmu_fw, sizeof(iboot_addr_pmu_fw)); 166 | if(xref_to_pmu_fw){ 167 | uint32_t pmu_fw_sz = 0; 168 | if(*(uint64_t*)xref_to_pmu_fw == *(uint64_t*)(xref_to_pmu_fw + 8)) 169 | pmu_fw_sz = ((pmu_14*)xref_to_pmu_fw)->pmu_sz; 170 | else 171 | pmu_fw_sz = ((pmu_13*)xref_to_pmu_fw)->pmu_sz; 172 | char *name = blobfwrite(pmu_fw_sz, ptr_to_pmu, NULL, NULL); 173 | printf("\033[0;32m%s\033[0m, @ 0x%lX (0x%llX) Size: 0x%X TEXT@ 0x%lX\n", name, OFF(ptr_to_pmu), BASE(ptr_to_pmu), pmu_fw_sz, OFF(ptr_to_pmu_text)); 174 | } 175 | } 176 | } 177 | 178 | void raw_fw(){ 179 | uint32_t rv_insn = 0xEAFFFFFE; 180 | uint32_t size = iboot.sz, offset = 0, good_name = 1; 181 | void *newbase = iboot.adr, *iboot_end = newbase + size, *rawfw_rv = NULL, *rawfw_start = NULL; 182 | rawfw_rv = memmem(newbase, size - offset, &rv_insn, sizeof(rv_insn)); 183 | if(rawfw_rv){ 184 | int pc = 0; 185 | offset = rawfw_rv - newbase; 186 | while(pc < 0x100){ 187 | if(*(uint32_t *)(rawfw_rv - pc) == 0){ 188 | rawfw_start = rawfw_rv - pc + 4; 189 | uint32_t off = OFF(rawfw_start); 190 | uint32_t rfw_sz = size - off; 191 | char *fw_name = blobfwrite(rfw_sz, rawfw_start, NULL, NULL); 192 | printf("\033[0;32m%s\033[0m (Raw Image), @ 0x%X(0x%llX), 32-bit, Size: 0x%X\n", fw_name, off, off + iboot.badr, rfw_sz); 193 | return; 194 | } 195 | pc += 0x4; 196 | } 197 | } 198 | } 199 | 200 | void find_fw(){ 201 | lzfse_fw(); 202 | pmu_fw(); 203 | raw_fw(); 204 | } 205 | 206 | int main(int argc, char **argv){ 207 | 208 | int temp_arg = argc - 1; 209 | char *input_file, *output_file; 210 | 211 | while(temp_arg){ 212 | if(*argv[temp_arg] == '-'){ 213 | char arg = *(argv[temp_arg]+ 1); 214 | switch(arg){ 215 | case('i'): 216 | iboot.name = argv[temp_arg + 1]; 217 | iboot = blobfopen(iboot); 218 | break; 219 | 220 | case('e'): 221 | iboot.extraction_status = 1; 222 | break; 223 | 224 | case('a'): 225 | iboot.analysis = 1; 226 | break; 227 | 228 | case('h'): 229 | print_help(); 230 | return 1; 231 | } 232 | } 233 | temp_arg--; 234 | } 235 | 236 | if(!iboot.adr){ 237 | print_help(); 238 | return 1; 239 | } 240 | print_banner(); 241 | find_fw(); 242 | free(iboot.adr); 243 | } --------------------------------------------------------------------------------