diff --git a/utils/imxtools/sbtools/Makefile b/utils/imxtools/sbtools/Makefile index 7d00e4b8c1..bc7180d866 100644 --- a/utils/imxtools/sbtools/Makefile +++ b/utils/imxtools/sbtools/Makefile @@ -3,7 +3,7 @@ CC=gcc LD=gcc CFLAGS=-g -std=c99 -W -Wall `pkg-config --cflags libusb-1.0` $(DEFINES) LDFLAGS=`pkg-config --libs libusb-1.0` -BINS=elftosb sbtoelf sbloader +BINS=elftosb sbtoelf sbloader rsrctool all: $(BINS) @@ -19,6 +19,9 @@ elftosb: elftosb.o crc.o crypto.o aes128.o sha1.o elf.o dbparser.o misc.o sb.o sbloader: sbloader.o $(LD) -o $@ $^ $(LDFLAGS) +rsrctool: rsrctool.o rsrc.o misc.o + $(LD) -o $@ $^ $(LDFLAGS) + clean: rm -fr *.o diff --git a/utils/imxtools/sbtools/rsrc.c b/utils/imxtools/sbtools/rsrc.c new file mode 100644 index 0000000000..da3c418d2c --- /dev/null +++ b/utils/imxtools/sbtools/rsrc.c @@ -0,0 +1,227 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include +#include +#include +#include +#include "misc.h" +#include "crypto.h" +#include "rsrc.h" + +const char crypto_key[16] = "SanDiskSlotRadi"; + +static void rsrc_crypt(void *buf, int size) +{ + if(size % 16) + printf("Size must be a multiple of 16 !\n"); + uint8_t *p = buf; + for(int i = 0; i < size; i+= 16, p += 16) + { + for(int j = 0; j < 16; j++) + p[j] ^= crypto_key[j]; + } +} + +enum rsrc_error_t rsrc_write_file(struct rsrc_file_t *rsrc, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + if(f == NULL) + return RSRC_OPEN_ERROR; + fwrite(rsrc->data, 1, rsrc->size, f); + fclose(f); + return RSRC_SUCCESS; +} + +struct rsrc_file_t *rsrc_read_file(const char *filename, void *u, + rsrc_color_printf cprintf, enum rsrc_error_t *err) +{ + return rsrc_read_file_ex(filename, 0, -1, u, cprintf, err); +} + +struct rsrc_file_t *rsrc_read_file_ex(const char *filename, size_t offset, size_t size, void *u, + rsrc_color_printf cprintf, enum rsrc_error_t *err) +{ + #define fatal(e, ...) \ + do { if(err) *err = e; \ + cprintf(u, true, GREY, __VA_ARGS__); \ + free(buf); \ + return NULL; } while(0) + + FILE *f = fopen(filename, "rb"); + void *buf = NULL; + if(f == NULL) + fatal(RSRC_OPEN_ERROR, "Cannot open file for reading\n"); + fseek(f, 0, SEEK_END); + size_t read_size = ftell(f); + fseek(f, offset, SEEK_SET); + if(size != (size_t)-1) + read_size = size; + buf = xmalloc(read_size); + if(fread(buf, read_size, 1, f) != 1) + { + fclose(f); + fatal(RSRC_READ_ERROR, "Cannot read file\n"); + } + fclose(f); + + struct rsrc_file_t *ret = rsrc_read_memory(buf, read_size, u, cprintf, err); + free(buf); + return ret; + + #undef fatal +} + +static const char *rsrc_table_entry_type_str(int type) +{ + switch(type) + { + case RSRC_TYPE_NONE: return "empty"; + case RSRC_TYPE_NESTED: return "nested"; + case RSRC_TYPE_IMAGE: return "image"; + case RSRC_TYPE_VALUE: return "value"; + case RSRC_TYPE_AUDIO: return "audio"; + case RSRC_TYPE_DATA: return "data"; + default: return "unknown"; + } +} + +static bool read_entries(void *buf, int filesize, void *u, + rsrc_color_printf cprintf, enum rsrc_error_t *err, + int offset, uint32_t base_index, int level, char *prefix) +{ + #define printf(c, ...) cprintf(u, false, c, __VA_ARGS__) + #define fatal(e, ...) \ + do { if(err) *err = e; \ + cprintf(u, true, GREY, __VA_ARGS__); \ + return e; } while(0) + + if(offset >= filesize) + fatal(RSRC_FORMAT_ERROR, "Out of bounds at off=%x base=%x level=%d\n ouch\n"); + if(level < 0) + fatal(RSRC_FORMAT_ERROR, "Out of levels at off=%x base=%x level=%d\n aie\n"); + for(int i = 0; i < 256; i++) + { + uint32_t te = *(uint32_t *)(buf + offset + 4 * i); + if(RSRC_TABLE_ENTRY_TYPE(te) == RSRC_TYPE_NONE) + continue; + uint32_t sz = 0; + if(RSRC_TABLE_ENTRY_TYPE(te) == RSRC_TYPE_VALUE) + sz = 2; + else if(RSRC_TABLE_ENTRY_TYPE(te) == RSRC_TYPE_NESTED) + sz = 4 * 256; + else + sz = *(uint32_t *)(buf + RSRC_TABLE_ENTRY_OFFSET(te)); + + uint32_t index = base_index | i << (level * 8); + printf(OFF, "%s+-%s%#08x %s[%s]%s[size=%#x]\n", prefix, YELLOW, index, BLUE, + rsrc_table_entry_type_str(RSRC_TABLE_ENTRY_TYPE(te)), + GREEN, sz); + + if(RSRC_TABLE_ENTRY_TYPE(te) == RSRC_TYPE_NESTED) + { + char *p = prefix + strlen(prefix); + sprintf(p, "%s| ", RED); + + bool ok = read_entries(buf, filesize, u, cprintf, err, + RSRC_TABLE_ENTRY_OFFSET(te), index, + level - 1, prefix); + if(!ok) + return false; + *p = 0; + } + } + + return true; + #undef printf + #undef fatal +} + +struct rsrc_file_t *rsrc_read_memory(void *_buf, size_t filesize, void *u, + rsrc_color_printf cprintf, enum rsrc_error_t *err) +{ + struct rsrc_file_t *rsrc_file = NULL; + uint8_t *buf = _buf; + + #define printf(c, ...) cprintf(u, false, c, __VA_ARGS__) + #define fatal(e, ...) \ + do { if(err) *err = e; \ + cprintf(u, true, GREY, __VA_ARGS__); \ + rsrc_free(rsrc_file); \ + return NULL; } while(0) + #define print_hex(c, p, len, nl) \ + do { printf(c, ""); print_hex(p, len, nl); } while(0) + + rsrc_file = xmalloc(sizeof(struct rsrc_file_t)); + memset(rsrc_file, 0, sizeof(struct rsrc_file_t)); + + /* There is a padding sector at the beginning of the file with a RSRC string. + * It is unclear if this is a signature since no code I've disassembled + * actually checks it. Allow use of -force to bypass. */ + if(memcmp(buf + 20, "RSRC", 4) != 0) + { + if(g_force) + printf(GREY, "Missing RSRC signature\n"); + else + fatal(RSRC_FORMAT_ERROR, "Missing RSRC signature\n"); + } + + printf(BLUE, "Entries\n"); + char prefix[1024]; + sprintf(prefix, "%s", RED); + bool ok = read_entries(buf, filesize, u, cprintf, err, + RSRC_SECTOR_SIZE, 0, 3, prefix); + if(!ok) + fatal(*err, "Error while parsing rsrc table\n"); + + rsrc_file->data = malloc(filesize); + memcpy(rsrc_file->data, _buf, filesize); + rsrc_file->size = filesize; + + return rsrc_file; + #undef printf + #undef fatal + #undef print_hex +} + +void rsrc_free(struct rsrc_file_t *file) +{ + if(!file) return; + free(file); +} + +void rsrc_dump(struct rsrc_file_t *file, void *u, rsrc_color_printf cprintf) +{ + #define printf(c, ...) cprintf(u, false, c, __VA_ARGS__) + #define print_hex(c, p, len, nl) \ + do { printf(c, ""); print_hex(p, len, nl); } while(0) + + #define TREE RED + #define HEADER GREEN + #define TEXT YELLOW + #define TEXT2 BLUE + #define SEP OFF + + printf(HEADER, "RSRC File\n"); + + #undef printf + #undef print_hex +} + diff --git a/utils/imxtools/sbtools/rsrc.h b/utils/imxtools/sbtools/rsrc.h new file mode 100644 index 0000000000..fc310e348d --- /dev/null +++ b/utils/imxtools/sbtools/rsrc.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __RSRC_H__ +#define __RSRC_H__ + +#include +#include + +#include "misc.h" + +#define RSRC_SECTOR_SIZE 2048 + +#define RSRC_TABLE_ENTRY_TYPE(e) ((e) >> 28) +#define RSRC_TABLE_ENTRY_OFFSET(e) ((e) & 0xfffffff) + +#define RSRC_TYPE_NONE 0 /* empty entry */ +#define RSRC_TYPE_NESTED 1 /* nested entry: points to a sub-table */ +#define RSRC_TYPE_IMAGE 2 /* image entry */ +#define RSRC_TYPE_VALUE 3 /* value stored on 28-bits */ +#define RSRC_TYPE_AUDIO 4 /* audio entry */ +#define RSRC_TYPE_DATA 5 /* data entry */ + +struct rsrc_file_t +{ + void *data; + int size; +}; + +enum rsrc_error_t +{ + RSRC_SUCCESS = 0, + RSRC_ERROR = -1, + RSRC_OPEN_ERROR = -2, + RSRC_READ_ERROR = -3, + RSRC_WRITE_ERROR = -4, + RSRC_FORMAT_ERROR = -5, + RSRC_CHECKSUM_ERROR = -6, + RSRC_NO_VALID_KEY = -7, + RSRC_FIRST_CRYPTO_ERROR = -8, + RSRC_LAST_CRYPTO_ERROR = RSRC_FIRST_CRYPTO_ERROR - CRYPTO_NUM_ERRORS, +}; + +enum rsrc_error_t rsrc_write_file(struct rsrc_file_t *rsrc, const char *filename); + +typedef void (*rsrc_color_printf)(void *u, bool err, color_t c, const char *f, ...); +struct rsrc_file_t *rsrc_read_file(const char *filename, void *u, + rsrc_color_printf printf, enum rsrc_error_t *err); +/* use size_t(-1) to use maximum size */ +struct rsrc_file_t *rsrc_read_file_ex(const char *filename, size_t offset, size_t size, void *u, + rsrc_color_printf printf, enum rsrc_error_t *err); +struct rsrc_file_t *rsrc_read_memory(void *buffer, size_t size, void *u, + rsrc_color_printf printf, enum rsrc_error_t *err); + +void rsrc_dump(struct rsrc_file_t *file, void *u, rsrc_color_printf printf); +void rsrc_free(struct rsrc_file_t *file); + +#endif /* __RSRC_H__ */ + diff --git a/utils/imxtools/sbtools/rsrctool.c b/utils/imxtools/sbtools/rsrctool.c new file mode 100644 index 0000000000..cb0582245a --- /dev/null +++ b/utils/imxtools/sbtools/rsrctool.c @@ -0,0 +1,198 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2012 Amaury Pouly + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#define _ISOC99_SOURCE /* snprintf() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crypto.h" +#include "rsrc.h" +#include "misc.h" + +/* all blocks are sized as a multiple of 0x1ff */ +#define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff) + +/* If you find a firmware that breaks the known format ^^ */ +#define assert(a) do { if(!(a)) { fprintf(stderr,"Assertion \"%s\" failed in %s() line %d!\n\nPlease send us your firmware!\n",#a,__func__,__LINE__); exit(1); } } while(0) + +#define crypto_cbc(...) \ + do { int ret = crypto_cbc(__VA_ARGS__); \ + if(ret != CRYPTO_ERROR_SUCCESS) \ + bug("crypto_cbc error: %d\n", ret); \ + }while(0) + +/* globals */ + +char *g_out_prefix; + +static void extract_rsrc_file(struct rsrc_file_t *file) +{ + +} + +static void usage(void) +{ + printf("Usage: rsrctool [options] rsrc-file\n"); + printf("Options:\n"); + printf(" -?/--help\tDisplay this message\n"); + printf(" -o \tEnable output and set prefix\n"); + printf(" -d/--debug\tEnable debug output*\n"); + printf(" -k \tAdd key file\n"); + printf(" -z\t\tAdd zero key\n"); + printf(" -a/--add-key \tAdd single key (hex or usbotp)\n"); + printf(" -n/--no-color\tDisable output colors\n"); + printf(" -l/--loopback \tProduce rsrc file out of extracted description*\n"); + printf(" -f/--force\tForce reading even without a key*\n"); + printf("Options marked with a * are for debug purpose only\n"); + exit(1); +} + +static void rsrc_printf(void *user, bool error, color_t c, const char *fmt, ...) +{ + (void) user; + (void) error; + va_list args; + va_start(args, fmt); + color(c); + vprintf(fmt, args); + va_end(args); +} + +static struct crypto_key_t g_zero_key = +{ + .method = CRYPTO_KEY, + .u.key = {0} +}; + +int main(int argc, char **argv) +{ + const char *loopback = NULL; + + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, '?'}, + {"debug", no_argument, 0, 'd'}, + {"add-key", required_argument, 0, 'a'}, + {"no-color", no_argument, 0, 'n'}, + {"loopback", required_argument, 0, 'l'}, + {"force", no_argument, 0, 'f' }, + {0, 0, 0, 0} + }; + + int c = getopt_long(argc, argv, "?do:k:za:nl:f", long_options, NULL); + if(c == -1) + break; + switch(c) + { + case -1: + break; + case 'l': + if(loopback) + bug("Only one loopback file can be specified !\n"); + loopback = optarg; + break; + case 'n': + enable_color(false); + break; + case 'd': + g_debug = true; + break; + case '?': + usage(); + break; + case 'o': + g_out_prefix = optarg; + break; + case 'f': + g_force = true; + break; + case 'k': + { + if(!add_keys_from_file(optarg)) + bug("Cannot add keys from %s\n", optarg); + break; + } + case 'z': + { + add_keys(&g_zero_key, 1); + break; + } + case 'a': + { + struct crypto_key_t key; + char *s = optarg; + if(!parse_key(&s, &key)) + bug("Invalid key specified as argument\n"); + if(*s != 0) + bug("Trailing characters after key specified as argument\n"); + add_keys(&key, 1); + break; + } + default: + abort(); + } + } + + if(argc - optind != 1) + { + usage(); + return 1; + } + + const char *rsrc_filename = argv[optind]; + + enum rsrc_error_t err; + struct rsrc_file_t *file = rsrc_read_file(rsrc_filename, NULL, rsrc_printf, &err); + if(file == NULL) + { + color(OFF); + printf("RSRC read failed: %d\n", err); + return 1; + } + + color(OFF); + if(g_out_prefix) + extract_rsrc_file(file); + if(g_debug) + { + color(GREY); + printf("[Debug output]\n"); + rsrc_dump(file, NULL, rsrc_printf); + } + if(loopback) + { + rsrc_write_file(file, loopback); + } + rsrc_free(file); + clear_keys(); + + return 0; +} +