User:Magicus/Magicus's Tools/Parse-channel.c
< User:Magicus | Magicus's Tools
Jump to navigation
Jump to search
This is an old revision of this page, as edited by Arcnor (talk | contribs) at 02:10, 12 March 2008. It may differ significantly from the current revision. |
Put this file in the same directory as a compiled version of Segher's Wii.git tools, since it depends on them.
// parse-channel.c // Compile with: // gcc -g -DLARGE_FILES -D_FILE_OFFSET_BITS=64 -Wall -W -O2 -c -o parse-channel.o parse-channel.c // gcc -g -lcrypto parse-channel.o tools.o bn.o ec.o -o parse-channel // The other files are from segher's git repository, created by his Makefile. // Copyright 2008 Magicus <magicus@gmail.com> // This file is based on tachtig.c, which is // Copyright 2007,2008 Segher Boessenkool <segher@kernel.crashing.org> // // Licensed under the terms of the GNU GPL, version 2 // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt // Version 1.0 Initial release // Version 1.1 Fixing IV-bug for part B. Adding support for decompressing part B; // thanks Arcnor <arcnorj@yahoo.es> for the basic LZ77 code! #include <string.h> #include <stdlib.h> #include <stdio.h> #include "tools.h" #define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0) // FIXME: this should really move to tools.c u16 be16(u8 *p) { return (p[0] << 8) | p[1]; } static u8 sd_key[16]; static u8 sd_iv[16]; static u8 md5_blanker[16]; static FILE *fp; static char gamename[5]; static size_t partB_size; static u16 num_contents; static size_t* content_sizes; typedef struct { u32 title_id_01_code; u32 title_id_02_name; u32 partB_size; u8 md5[0x10]; u8 data[0x624]; } partA_header_t; typedef struct { u32 imd5_tag; // 0x494D4435 "IMD5"; u32 size; // size of the rest of part B, starting from next field. u8 zeroes[8]; u8 crypto[16]; u32 lz77_tag; // 0x4C5A3737 "LZ77" u32 unknown; // padding to 0x40? } partB_header_t; typedef struct { u32 sig_type; u8 sig[256]; u8 fill1[60]; u8 issuer[64]; // Root-CA%08x-CP%08x u8 version; u8 ca_crl_version; u8 signer_crl_version; u8 fill2; u64 sys_version; u64 title_id; u32 title_type; u16 group_id; // publisher u8 reserved[62]; u32 access_rights; u16 title_version; u16 num_contents; u16 boot_index; u16 fill3; } tmd_t; typedef struct { u32 cid; // content id u16 index; // # number of the file u16 type; u64 size; u8 hash [20]; // SHA1 hash content } content_record_t; static void write_part(void* data, size_t size, char* name) { FILE *out; char filename[128]; snprintf(filename, sizeof(filename), "%s_%s.bin", gamename, name); filename[128] = '\0'; out = fopen(filename, "wb"); fwrite(data, 1, size, out); fclose(out); } static void do_partA_header(void) { partA_header_t header; u8 partA_iv[16]; u8 md5_file[16]; u8 md5_calc[16]; fread(&header, 1, sizeof header, fp); // Use a private copy of the sd_iv since we need it again // and it will be overwritten otherwise. memcpy(partA_iv, sd_iv, 16); aes_cbc_dec(sd_key, partA_iv, (u8*) &header, sizeof header, (u8*) &header); memcpy(md5_file, header.md5, 16); memcpy(header.md5, md5_blanker, 16); md5((u8*) &header, sizeof header, md5_calc); if (memcmp(md5_file, md5_calc, 0x10)) { ERROR("MD5 mismatch"); } // Get the four-letter code of the game, for file naming purposes. strncpy(gamename, (char*) &header.title_id_02_name, 4); gamename[5] = '\0'; printf("Game code is: %s\n", gamename); partB_size = be32((u8*) &header.partB_size); write_part(&header, sizeof(header), "01_header"); } static u8* decompress_lz77(u8 *data, size_t data_size, size_t* decompressed_size) { u8 *data_end; u8 *decompressed_data; size_t unpacked_size; u8 *in_ptr; u8 *out_ptr; u8 *out_end; in_ptr = data; data_end = data + data_size; // Assume this for now and grow when needed unpacked_size = data_size; decompressed_data = malloc(unpacked_size); out_end = decompressed_data + unpacked_size; out_ptr = decompressed_data; while (in_ptr < data_end) { int bit; u8 bitmask = *in_ptr; in_ptr++; for (bit = 0x80; bit != 0; bit >>= 1) { if (bitmask & bit) { // Next section is compressed u8 rep_length; u16 rep_offset; rep_length = (*in_ptr >> 4) + 3; rep_offset = *in_ptr & 0x0f; in_ptr++; rep_offset = *in_ptr | (rep_offset << 8); in_ptr++; if (out_ptr-decompressed_data < rep_offset) { ERROR("Inconsistency in LZ77 encoding"); } for ( ; rep_length > 0; rep_length--) { *out_ptr = out_ptr[-rep_offset-1]; out_ptr++; if (out_ptr >= out_end) { // Need to grow buffer decompressed_data = realloc(decompressed_data, unpacked_size*2); out_ptr = decompressed_data + unpacked_size; unpacked_size *= 2; out_end = decompressed_data + unpacked_size; } } } else { // Just copy byte *out_ptr = *in_ptr; out_ptr++; if (out_ptr >= out_end) { // Need to grow buffer decompressed_data = realloc(decompressed_data, unpacked_size*2); out_ptr = decompressed_data + unpacked_size; unpacked_size *= 2; out_end = decompressed_data + unpacked_size; } in_ptr++; } } } *decompressed_size = (out_ptr - decompressed_data); return decompressed_data; } static void do_partB_gameinfo(void) { u8 *data; u8 *decompressed_data; size_t rounded_size; size_t decompressed_size; partB_header_t* header; u32 tag; u32 unknown; u32 size; rounded_size = (partB_size + 63) & ~63; data = malloc(rounded_size); fread(data, 1, rounded_size, fp); aes_cbc_dec(sd_key, sd_iv, data, rounded_size, data); header = (partB_header_t*) data; tag = be32((u8*) &header->imd5_tag); if (tag != 0x494D4435) { ERROR("No IMD5 tag"); } size = be32((u8*) &header->size); if (size != partB_size - 32) { ERROR("Size mismatch"); } tag = be32((u8*) &header->lz77_tag); if (tag != 0x4C5A3737) { ERROR("No LZ77 tag"); } unknown = be32((u8*) &header->unknown); printf("Part B unknown field: %x\n", unknown); write_part(data, sizeof(partB_header_t), "02a_banner_header"); decompressed_data = decompress_lz77(data + sizeof(partB_header_t), partB_size - sizeof(partB_header_t), &decompressed_size); write_part(decompressed_data, decompressed_size, "02b_banner_decompressed"); free(data); // free(decompressed_data); } static void do_partC_Bk_header(void) { u8 header[0x80]; fread(header, 1, sizeof header, fp); if (be32(header + 4) != 0x426b0001) ERROR("no Bk header"); if (be32(header) != 0x70) ERROR("wrong Bk header size"); fprintf(stderr, "NG id: %08x\n", be32(header + 8)); write_part(header, sizeof(header), "03_bk_header"); } static void do_partD_tmd(void) { tmd_t tmd; u8* data; size_t tmd_size; int i; content_record_t* rec; fread(&tmd, 1, sizeof tmd, fp); num_contents = be16((u8*) &tmd.num_contents); printf("Number of content files: %d\n", num_contents); // Now we can read the rest of the tmd. tmd_size = sizeof(tmd) + num_contents*sizeof(content_record_t); tmd_size = (tmd_size + 63) & ~63; data = malloc(tmd_size); memcpy(data, &tmd, sizeof(tmd)); fread(&data[sizeof(tmd)], 1, tmd_size-sizeof(tmd), fp); write_part(data, tmd_size, "04_tmd"); content_sizes = calloc(1, sizeof (size_t) * num_contents); rec = (content_record_t*) &data[sizeof(tmd)]; for (i = 0; i < num_contents; i++, rec++) { u16 type = be16((u8*) &rec->type); if (!(type & 0x8000)) { content_sizes[i] = (size_t)be64((u8*) &rec->size); } } } static void do_partE_contents(void) { int i; for (i=0; i < num_contents; i++) { if (content_sizes[i] != 0) { char name[128]; u8 *data; size_t rounded_size = (content_sizes[i] + 63) & ~63; data = malloc(rounded_size); fread(data, 1, rounded_size, fp); snprintf(name, 128, "05_content_%02d", i); printf("Writing included content index %d of size: %x\n", i, content_sizes[i]); write_part(data, rounded_size, name); free (data); } } } static void do_partF_cert(void) { u8 cert[0x340]; fread(cert, 1, sizeof cert, fp); write_part(cert, sizeof(cert), "06_cert"); } int main(int argc, char **argv) { if (argc != 2) { ERROR("Usage: parse-channel <file>"); } get_key("sd-key", sd_key, 16); get_key("sd-iv", sd_iv, 16); get_key("md5-blanker", md5_blanker, 16); fp = fopen(argv[1], "rb"); do_partA_header(); do_partB_gameinfo(); do_partC_Bk_header(); do_partD_tmd(); do_partE_contents(); do_partF_cert(); fclose(fp); return 0; }