Beispiel #1
0
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;
}
Beispiel #2
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;
}
Beispiel #3
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;
}
Beispiel #4
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;
}
Beispiel #5
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;
}
Beispiel #6
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;
}