In memory of Ben “bushing” Byer, who passed away on Monday, February 8th, 2016.

User:Magicus/Magicus's Tools/Parse-channel.c

From WiiBrew
< User:Magicus‎ | Magicus's Tools
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
// 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

#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 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 md5_file[16];
	u8 md5_calc[16];

	fread(&header, 1, sizeof header, fp);

	aes_cbc_dec(sd_key, sd_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);
  printf("Size of part B: 0x%x\n", partB_size);

  write_part(&header, sizeof(header), "01_header");
}

static void do_partB_gameinfo(void)
{
	u8 *data;
	size_t rounded_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);
	
  write_part(data, rounded_size, "02_gameinfo");
  
  free(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;
}