static int get_key_and_sig(bool is_extract, void *buf) { static char keysig[NWZ_KEYSIG_SIZE]; static char kas[NWZ_KAS_SIZE]; /* database lookup */ if(g_model_index != -1) g_kas = g_model_list[g_model_index].kas; /* always prefer KAS because it contains everything */ if(g_kas) { if(strlen(g_kas) != NWZ_KAS_SIZE) { cprintf(GREY, "The KAS has wrong length (must be %d hex digits)\n", NWZ_KAS_SIZE); return 4; } g_key = keysig; g_sig = keysig + NWZ_KEY_SIZE; decrypt_keysig(g_kas, g_key, g_sig); } /* Otherwise require key and signature */ else if(g_key && g_sig) { /* check key and signature size */ if(strlen(g_key) != 8) { cprintf(GREY, "The specified key has wrong length (must be 8 hex digits)\n"); return 4; } if(strlen(g_sig) != 8) { cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n"); return 5; } } /* for extraction, we offer a brute force search method from the MD5 */ else if(is_extract && g_keysig_search != KEYSIG_SEARCH_NONE) { struct upg_md5_t *md5 = (void *)buf; void *encrypted_hdr = (md5 + 1); cprintf(BLUE, "keysig Search\n"); cprintf_field(" Method: ", "%s\n", keysig_search_desc[g_keysig_search].name); bool ok = keysig_search(g_keysig_search, encrypted_hdr, 8, &upg_notify_keysig, keysig, g_nr_threads); cprintf(GREEN, " Result: "); cprintf(ok ? YELLOW : RED, "%s\n", ok ? "Key found" : "No key found"); if(!ok) return 2; } else { cprintf(GREY, "A KAS or a keysig is needed to decrypt the firmware\n"); cprintf(GREY, "You have the following options(see help for more details):\n"); cprintf(GREY, "- select a model with a known KAS\n"); cprintf(GREY, "- specify an explicit KAS or key+sig\n"); if(is_extract) cprintf(GREY, "- let me try to find the keysig by brute force\n"); return 1; } /* If we only have the key and signature, we can create a "fake" KAS * that decrypts to the same key and signature. Since it is not unique, * it will generally not match the "official" one from Sony but will produce * valid files anyway */ if(!g_kas) { /* This is useful to print the KAS for the user when brute-forcing since * the process will produce a key+sig and the database requires a KAS */ g_kas = kas; encrypt_keysig(g_kas, g_key, g_sig); } cprintf(BLUE, "Keys\n"); cprintf_field(" KAS: ", "%."STR(NWZ_KAS_SIZE)"s\n", g_kas); cprintf_field(" Key: ", "%."STR(NWZ_KEY_SIZE)"s\n", g_key); cprintf_field(" Sig: ", "%."STR(NWZ_SIG_SIZE)"s\n", g_sig); return 0; }
int fw_unpack(uint8_t *buf, size_t size, fw_extract_callback_t unpack_cb) { struct fw_hdr_t *hdr = (void *)buf; if(size < sizeof(struct fw_hdr_t)) { cprintf(GREY, "File too small\n"); return 1; } cprintf(BLUE, "Header\n"); cprintf(GREEN, " Signature:"); for(int i = 0; i < FW_SIG_SIZE; i++) cprintf(YELLOW, " %02x", hdr->sig[i]); int variant = 0; if(memcmp(hdr->sig, g_fw_signature_f2, FW_SIG_SIZE) == 0) { variant = 0xf2; cprintf(RED, " Ok (f2 variant)\n"); } else if(memcmp(hdr->sig, g_fw_signature_f0, FW_SIG_SIZE) == 0) { variant = 0xf0; cprintf(RED, " Ok (f0 variant)\n"); } else { cprintf(RED, " Mismatch\n"); return 1; } /* both variants have the same header size, only the fields differ */ if(variant == 0xf2) { cprintf_field(" USB VID: ", "0x%x\n", hdr->usb_vid); cprintf_field(" USB PID: ", "0x%x\n", hdr->usb_pid); cprintf_field(" Date: ", "%x/%x/%02x%02x\n", hdr->day, hdr->month, hdr->year[0], hdr->year[1]); cprintf_field(" Checksum: ", "%x\n", hdr->checksum); cprintf_field(" Productor: ", "%.16s\n", hdr->productor); cprintf_field(" String 2: ", "%.16s\n", hdr->str2); cprintf_field(" String 3: ", "%.32s\n", hdr->str3); cprintf_field(" Device Name: ", "%.32s\n", hdr->dev_name); cprintf(GREEN, " Unknown:\n"); for(int i = 0; i < 8; i++) { cprintf(YELLOW, " "); for(int j = 0; j < 16; j++) cprintf(YELLOW, "%02x ", hdr->res2[i * 16 + j]); cprintf(YELLOW, "\n"); } cprintf_field(" USB Name 1: ", "%.8s\n", hdr->usb_name1); cprintf_field(" USB Name 2: ", "%.8s\n", hdr->usb_name2); cprintf_field(" MTP Name 1: ", "%.32s\n", hdr->mtp_name1); cprintf_field(" MTP Name 2: ", "%.32s\n", hdr->mtp_name2); cprintf_field(" MTP Version: ", "%.32s\n", hdr->mtp_ver); cprintf_field(" MTP VID: ", "0x%x\n", hdr->mtp_vid); cprintf_field(" MTP PID: ", "0x%x\n", hdr->mtp_pid); cprintf_field(" FW Version: ", "%.64s\n", hdr->fw_ver); } else { /* struct fw_hdr_f0_t *hdr_f0 = (void *)hdr; */ cprintf(GREEN, " Header not dumped because format is unclear.\n"); } cprintf(BLUE, "Entries\n"); for(int i = 0; i < FW_ENTRIES; i++) { if(hdr->entry[i].name[0] == 0) continue; struct fw_entry_t *entry = &hdr->entry[i]; char filename[16]; build_filename_fw(filename, entry); cprintf(RED, " %s\n", filename); cprintf_field(" Attr: ", "%02x\n", entry->attr); cprintf_field(" Offset: ", "0x%x\n", entry->block_offset << 9); cprintf_field(" Size: ", "0x%x\n", entry->size); cprintf_field(" Unknown: ", "%x\n", entry->unk); cprintf_field(" Checksum: ", "0x%x ", entry->checksum); uint32_t chk = afi_checksum(buf + (entry->block_offset << 9), entry->size); if(chk != entry->checksum) { cprintf(RED, "Mismatch\n"); return 1; } else cprintf(RED, "Ok\n"); int ret = unpack_cb(filename, buf + (entry->block_offset << 9), entry->size); if(ret != 0) return ret; } return 0; }
static int create_upg(int argc, char **argv) { if(argc == 0) { printf("You must specify a firmware filename\n"); usage(); } int ret = get_key_and_sig(false, NULL); if(ret != 0) return ret; FILE *fout = fopen(argv[0], "wb"); if(fout == NULL) { printf("Cannot open output firmware file: %m\n"); return 1; } int nr_files = argc - 1; FILE **files = malloc(nr_files * sizeof(FILE *)); for(int i = 0; i < nr_files; i++) { files[i] = fopen(argv[1 + i], "rb"); if(files[i] == NULL) { printf("Cannot open input file '%s': %m\n", argv[i + 1]); return 1; } } struct upg_md5_t md5; memset(&md5, 0, sizeof(md5)); MD5_CTX c; MD5_Init(&c); // output a dummy md5 sum fwrite(&md5, 1, sizeof(md5), fout); // output the encrypted signature struct upg_header_t hdr; memcpy(hdr.sig, g_sig, 8); hdr.nr_files = nr_files; hdr.pad = 0; ret = fwp_write(&hdr, sizeof(hdr), &hdr, (void *)g_key); if(ret) return ret; MD5_Update(&c, &hdr, sizeof(hdr)); fwrite(&hdr, 1, sizeof(hdr), fout); // output file headers long offset = sizeof(md5) + sizeof(hdr) + nr_files * sizeof(struct upg_entry_t); for(int i = 0; i < nr_files; i++) { struct upg_entry_t entry; entry.offset = offset; entry.size = filesize(files[i]); offset += ROUND_UP(entry.size, 8); // do it before encryption !! ret = fwp_write(&entry, sizeof(entry), &entry, (void *)g_key); if(ret) return ret; MD5_Update(&c, &entry, sizeof(entry)); fwrite(&entry, 1, sizeof(entry), fout); } cprintf(BLUE, "Files\n"); for(int i = 0; i < nr_files; i++) { long size = filesize(files[i]); long r_size = ROUND_UP(size, 8); cprintf(GREY, " File"); cprintf(RED, " %d\n", i); cprintf_field(" Offset: ", "0x%lx\n", ftell(fout)); cprintf_field(" Size: ", "0x%lx\n", size); void *buf = malloc(r_size); memset(buf, 0, r_size); fread(buf, 1, size, files[i]); fclose(files[i]); ret = fwp_write(buf, r_size, buf, (void *)g_key); if(ret) return ret; MD5_Update(&c, buf, r_size); fwrite(buf, 1, r_size, fout); free(buf); } fseek(fout, 0, SEEK_SET); MD5_Final(md5.md5, &c); fwrite(&md5, 1, sizeof(md5), fout); fclose(fout); return 0; }
static int do_upg(void *buf, long size) { struct upg_md5_t *md5 = buf; cprintf(BLUE, "Preliminary\n"); cprintf(GREEN, " MD5: "); for(int i = 0; i < 16; i++) cprintf(YELLOW, "%02x", md5->md5[i]); printf(" "); uint8_t actual_md5[MD5_DIGEST_LENGTH]; { MD5_CTX c; MD5_Init(&c); MD5_Update(&c, md5 + 1, size - sizeof(struct upg_header_t)); MD5_Final(actual_md5, &c); } check_field(memcmp(actual_md5, md5->md5, 16), 0, "Ok\n", "Mismatch\n"); int ret = get_key_and_sig(true, md5 + 1); if(ret != 0) return ret; struct upg_header_t *hdr = (void *)(md5 + 1); ret = fwp_read(hdr, sizeof(struct upg_header_t), hdr, (void *)g_key); if(ret) return ret; cprintf(BLUE, "Header\n"); cprintf_field(" Signature:", " "); for(int i = 0; i < 8; i++) cprintf(YELLOW, "%c", isprint(hdr->sig[i]) ? hdr->sig[i] : '.'); if(g_sig) { check_field(memcmp(hdr->sig, g_sig, 8), 0, " OK\n", " Mismatch\n"); } else cprintf(RED, " Can't check\n"); cprintf_field(" Files: ", "%d\n", hdr->nr_files); cprintf_field(" Pad: ", "0x%x\n", hdr->pad); cprintf(BLUE, "Files\n"); struct upg_entry_t *entry = (void *)(hdr + 1); for(unsigned i = 0; i < hdr->nr_files; i++, entry++) { int ret = fwp_read(entry, sizeof(struct upg_entry_t), entry, (void *)g_key); if(ret) return ret; cprintf(GREY, " File"); cprintf(RED, " %d\n", i); cprintf_field(" Offset: ", "0x%x\n", entry->offset); cprintf_field(" Size: ", "0x%x\n", entry->size); if(g_out_prefix) { char *str = malloc(strlen(g_out_prefix) + 32); sprintf(str, "%s%d.bin", g_out_prefix, i); FILE *f = fopen(str, "wb"); if(f) { // round up size, there is some padding done with random data int crypt_size = ROUND_UP(entry->size, 8); int ret = fwp_read(buf + entry->offset, crypt_size, buf + entry->offset, (void *)g_key); if(ret) return ret; // but write the *good* amount of data fwrite(buf + entry->offset, 1, entry->size, f); fclose(f); } else cprintf(GREY, "Cannot open '%s' for writing\n", str); } } return 0; }
static int get_key_and_sig(bool is_extract, void *encrypted_hdr) { static char keysig[NWZ_KEYSIG_SIZE]; static char kas[NWZ_KAS_SIZE]; /* database lookup */ if(g_model_index != -1) { if(g_model_list[g_model_index].flags & HAS_KAS) g_kas = g_model_list[g_model_index].kas; if(g_model_list[g_model_index].flags & HAS_KEY) g_key = g_model_list[g_model_index].key; if(g_model_list[g_model_index].flags & HAS_SIG) g_sig = g_model_list[g_model_index].sig; } /* always prefer KAS because it contains everything */ if(g_kas) { if(strlen(g_kas) != NWZ_KAS_SIZE) { cprintf(GREY, "The KAS has wrong length (must be %d hex digits)\n", NWZ_KAS_SIZE); return 4; } g_key = keysig; g_sig = keysig + NWZ_KEY_SIZE; decrypt_keysig(g_kas, g_key, g_sig); } /* fall back to key and signature otherwise. The signature is not required * when extracting but prevents from checking decryption */ else if(g_key && (is_extract || g_sig)) { if(strlen(g_key) != 8) { cprintf(GREY, "The specified key has wrong length (must be 8 hex digits)\n"); return 4; } /* if there is a signature, it must have the correct size */ if(g_sig) { if(strlen(g_sig) != 8) { cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n"); return 5; } } else { cprintf(GREY, "Warning: you have specified a key but no sig, I won't be able to do any checks\n"); } } /* for extraction, we offer a brute force search method from the MD5 */ else if(is_extract && g_keysig_search != KEYSIG_SEARCH_NONE) { cprintf(BLUE, "keysig Search\n"); cprintf_field(" Method: ", "%s\n", keysig_search_desc[g_keysig_search].name); bool ok = keysig_search(g_keysig_search, encrypted_hdr, 8, &upg_notify_keysig, keysig, g_nr_threads); cprintf(GREEN, " Result: "); cprintf(ok ? YELLOW : RED, "%s\n", ok ? "Key found" : "No key found"); if(!ok) return 2; } else { cprintf(GREY, "A KAS or a keysig is needed to decrypt the firmware\n"); cprintf(GREY, "You have the following options(see help for more details):\n"); cprintf(GREY, "- select a model with a known KAS\n"); cprintf(GREY, "- specify an explicit KAS or key+sig\n"); if(is_extract) cprintf(GREY, "- let me try to find the keysig(slow !)\n"); return 1; } /* If we only have the key and signature, we can create a "fake" KAS * that decrypts to the same key and signature. Since it is not unique, * it will generally not match the "official" one from Sony but will produce * valid files anyway */ if(!g_kas) { if(!g_sig) { /* if we extract and don't have a signature, just use a random * one, we cannot check it anyway */ g_sig = keysig; memset(g_sig, '?', NWZ_SIG_SIZE); } g_kas = kas; encrypt_keysig(g_kas, g_key, g_sig); } cprintf(BLUE, "Keys\n"); cprintf_field(" KAS: ", "%."STR(NWZ_KAS_SIZE)"s\n", g_kas); cprintf_field(" Key: ", "%."STR(NWZ_KEY_SIZE)"s\n", g_key); if(g_sig) cprintf_field(" Sig: ", "%."STR(NWZ_SIG_SIZE)"s\n", g_sig); return 0; }
int afi_unpack(uint8_t *buf, size_t size, afi_extract_callback_t unpack_cb) { struct afi_t *afi = (void *)buf; if(size < sizeof(struct afi_t)) { cprintf(GREY, "File too small\n"); return 1; } cprintf(BLUE, "Header\n"); cprintf(GREEN, " Signature:"); for(int i = 0; i < AFI_SIG_SIZE; i++) cprintf(YELLOW, " %02x", afi->hdr.sig[i]); if(memcmp(afi->hdr.sig, g_afi_signature, AFI_SIG_SIZE) == 0) cprintf(RED, " Ok\n"); else { cprintf(RED, " Mismatch\n"); return 1; } cprintf_field(" Vendor ID: ", "0x%x\n", afi->hdr.vendor_id); cprintf_field(" Product ID: ", "0x%x\n", afi->hdr.product_id); cprintf_field(" Version: ", "%x.%x\n", afi->hdr.ver_id[0], afi->hdr.ver_id[1]); cprintf_field(" Ext Version: ", "%x.%x\n", afi->hdr.ext_ver_id[0], afi->hdr.ext_ver_id[1]); cprintf_field(" Date: ", "%02x/%02x/%02x%02x\n", afi->hdr.day, afi->hdr.month, afi->hdr.year[0], afi->hdr.year[1]); cprintf_field(" AFI size: ", "%d ", afi->hdr.afi_size); if(afi->hdr.afi_size == size) cprintf(RED, " Ok\n"); else if(afi->hdr.afi_size < size) cprintf(RED, " Ok (file greater than archive)\n"); else { cprintf(RED, " Error (file too small)\n"); return 1; } cprintf_field(" Reserved: ", "%x %x %x\n", afi->hdr.res[0], afi->hdr.res[1], afi->hdr.res[2]); cprintf(BLUE, "Entries\n"); for(int i = 0; i < AFI_ENTRIES; i++) { if(afi->entry[i].name[0] == 0) continue; struct afi_entry_t *entry = &afi->entry[i]; char filename[16]; build_filename(filename, entry); cprintf(RED, " %s\n", filename); cprintf_field(" Type: ", "%02x", entry->type); if(isprint(entry->type)) cprintf(RED, " %c", entry->type); printf("\n"); cprintf_field(" Addr: ", "0x%x\n", entry->addr); cprintf_field(" Offset: ", "0x%x\n", entry->offset); cprintf_field(" Size: ", "0x%x\n", entry->size); cprintf_field(" Desc: ", "%.4s\n", entry->desc); cprintf_field(" Checksum: ", "0x%x ", entry->checksum); uint32_t chk = afi_checksum(buf + entry->offset, entry->size); if(chk != entry->checksum) { cprintf(RED, "Mismatch\n"); return 1; } else cprintf(RED, "Ok\n"); int ret = unpack_cb(filename, buf + entry->offset, entry->size); if(ret != 0) return ret; } cprintf(BLUE, "Post Header\n"); cprintf_field(" Checksum: ", "%x ", afi->post.checksum); uint32_t chk = afi_checksum(buf, sizeof(struct afi_t) - 4); if(chk != afi->post.checksum) { cprintf(RED, "Mismatch\n"); return 1; } else cprintf(RED, "Ok\n"); return 0; }