forked from len0rd/rockbox
sbtools: move sb reading from sbtoelf.c to sb.c with a proper interface, fix sb production bug, fix ugly code, add sb dump code and sbtoelf loopback facility
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30881 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
591ec0349b
commit
e36471df9c
7 changed files with 750 additions and 645 deletions
|
|
@ -24,15 +24,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "dbparser.h"
|
#include "dbparser.h"
|
||||||
|
#include "misc.h"
|
||||||
typedef uint8_t byte;
|
|
||||||
|
|
||||||
extern bool g_debug;
|
|
||||||
extern void *xmalloc(size_t s);
|
|
||||||
extern int convxdigit(char digit, byte *val);
|
|
||||||
|
|
||||||
#define bug(...) do { fprintf(stderr, __VA_ARGS__); exit(1); } while(0)
|
|
||||||
#define bugp(...) do { fprintf(stderr, __VA_ARGS__); perror(" "); exit(1); } while(0)
|
|
||||||
|
|
||||||
enum lexem_type_t
|
enum lexem_type_t
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,8 @@ void elf_add_fill_section(struct elf_params_t *params,
|
||||||
void elf_write_file(struct elf_params_t *params, elf_write_fn_t write,
|
void elf_write_file(struct elf_params_t *params, elf_write_fn_t write,
|
||||||
elf_printf_fn_t printf, void *user)
|
elf_printf_fn_t printf, void *user)
|
||||||
{
|
{
|
||||||
|
(void) printf;
|
||||||
|
|
||||||
Elf32_Ehdr ehdr;
|
Elf32_Ehdr ehdr;
|
||||||
uint32_t phnum = 0;
|
uint32_t phnum = 0;
|
||||||
struct elf_section_t *sec = params->first_section;
|
struct elf_section_t *sec = params->first_section;
|
||||||
|
|
|
||||||
|
|
@ -437,7 +437,13 @@ int main(int argc, char **argv)
|
||||||
if(crypto_iv.method == CRYPTO_KEY)
|
if(crypto_iv.method == CRYPTO_KEY)
|
||||||
sb_file->crypto_iv = &crypto_iv.u.key;
|
sb_file->crypto_iv = &crypto_iv.u.key;
|
||||||
|
|
||||||
sb_produce_file(sb_file, output_filename);
|
/* fill with default parameters since there is no command file support for them */
|
||||||
|
sb_file->drive_tag = 0;
|
||||||
|
sb_file->first_boot_sec_id = sb_file->sections[0].identifier;
|
||||||
|
sb_file->flags = 0;
|
||||||
|
sb_file->minor_version = 1;
|
||||||
|
|
||||||
|
sb_write_file(sb_file, output_filename);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
#define STR(a) _STR(a)
|
#define STR(a) _STR(a)
|
||||||
|
|
||||||
#define bug(...) do { fprintf(stderr,"["__FILE__":"STR(__LINE__)"]ERROR: "__VA_ARGS__); exit(1); } while(0)
|
#define bug(...) do { fprintf(stderr,"["__FILE__":"STR(__LINE__)"]ERROR: "__VA_ARGS__); exit(1); } while(0)
|
||||||
#define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
|
#define bugp(...) do { fprintf(stderr, __VA_ARGS__); perror(" "); exit(1); } while(0)
|
||||||
|
|
||||||
#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round))
|
#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
#include "sb.h"
|
#include "sb.h"
|
||||||
|
|
@ -64,6 +65,7 @@ static void compute_sb_offsets(struct sb_file_t *sb)
|
||||||
alignment /= BLOCK_SIZE; /* alignment in block sizes */
|
alignment /= BLOCK_SIZE; /* alignment in block sizes */
|
||||||
|
|
||||||
struct sb_section_t *sec = &sb->sections[i];
|
struct sb_section_t *sec = &sb->sections[i];
|
||||||
|
sec->sec_size = 0;
|
||||||
|
|
||||||
if(g_debug)
|
if(g_debug)
|
||||||
{
|
{
|
||||||
|
|
@ -203,7 +205,7 @@ static void produce_sb_header(struct sb_file_t *sb, struct sb_header_t *sb_hdr)
|
||||||
sb_hdr->flags = 0;
|
sb_hdr->flags = 0;
|
||||||
sb_hdr->image_size = sb->image_size;
|
sb_hdr->image_size = sb->image_size;
|
||||||
sb_hdr->header_size = sizeof(struct sb_header_t) / BLOCK_SIZE;
|
sb_hdr->header_size = sizeof(struct sb_header_t) / BLOCK_SIZE;
|
||||||
sb_hdr->first_boot_sec_id = sb->sections[0].identifier;
|
sb_hdr->first_boot_sec_id = sb->first_boot_sec_id;
|
||||||
sb_hdr->nr_keys = g_nr_keys;
|
sb_hdr->nr_keys = g_nr_keys;
|
||||||
sb_hdr->nr_sections = sb->nr_sections;
|
sb_hdr->nr_sections = sb->nr_sections;
|
||||||
sb_hdr->sec_hdr_size = sizeof(struct sb_section_header_t) / BLOCK_SIZE;
|
sb_hdr->sec_hdr_size = sizeof(struct sb_section_header_t) / BLOCK_SIZE;
|
||||||
|
|
@ -213,12 +215,17 @@ static void produce_sb_header(struct sb_file_t *sb, struct sb_header_t *sb_hdr)
|
||||||
sizeof(struct sb_key_dictionary_entry_t) * sb_hdr->nr_keys / BLOCK_SIZE;
|
sizeof(struct sb_key_dictionary_entry_t) * sb_hdr->nr_keys / BLOCK_SIZE;
|
||||||
generate_random_data(sb_hdr->rand_pad0, sizeof(sb_hdr->rand_pad0));
|
generate_random_data(sb_hdr->rand_pad0, sizeof(sb_hdr->rand_pad0));
|
||||||
generate_random_data(sb_hdr->rand_pad1, sizeof(sb_hdr->rand_pad1));
|
generate_random_data(sb_hdr->rand_pad1, sizeof(sb_hdr->rand_pad1));
|
||||||
|
/* Version 1.0 has 6 bytes of random padding,
|
||||||
|
* Version 1.1 requires the last 4 bytes to be 'sgtl' */
|
||||||
|
if(sb->minor_version >= 1)
|
||||||
|
memcpy(&sb_hdr->rand_pad0[2], "sgtl", 4);
|
||||||
|
|
||||||
sb_hdr->timestamp = generate_timestamp();
|
sb_hdr->timestamp = generate_timestamp();
|
||||||
sb_hdr->product_ver = sb->product_ver;
|
sb_hdr->product_ver = sb->product_ver;
|
||||||
fix_version(&sb_hdr->product_ver);
|
fix_version(&sb_hdr->product_ver);
|
||||||
sb_hdr->component_ver = sb->component_ver;
|
sb_hdr->component_ver = sb->component_ver;
|
||||||
fix_version(&sb_hdr->component_ver);
|
fix_version(&sb_hdr->component_ver);
|
||||||
sb_hdr->drive_tag = 0;
|
sb_hdr->drive_tag = sb->drive_tag;
|
||||||
|
|
||||||
sha_1_init(&sha_1_params);
|
sha_1_init(&sha_1_params);
|
||||||
sha_1_update(&sha_1_params, &sb_hdr->signature[0],
|
sha_1_update(&sha_1_params, &sb_hdr->signature[0],
|
||||||
|
|
@ -292,7 +299,7 @@ void produce_sb_instruction(struct sb_inst_t *inst,
|
||||||
cmd->hdr.checksum = instruction_checksum(&cmd->hdr);
|
cmd->hdr.checksum = instruction_checksum(&cmd->hdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sb_produce_file(struct sb_file_t *sb, const char *filename)
|
void sb_write_file(struct sb_file_t *sb, const char *filename)
|
||||||
{
|
{
|
||||||
FILE *fd = fopen(filename, "wb");
|
FILE *fd = fopen(filename, "wb");
|
||||||
if(fd == NULL)
|
if(fd == NULL)
|
||||||
|
|
@ -424,3 +431,670 @@ void sb_produce_file(struct sb_file_t *sb, const char *filename)
|
||||||
|
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *memdup(void *p, size_t len)
|
||||||
|
{
|
||||||
|
void *cpy = xmalloc(len);
|
||||||
|
memcpy(cpy, p, len);
|
||||||
|
return cpy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sb_section_t *read_section(bool data_sec, uint32_t id, byte *buf,
|
||||||
|
int size, const char *indent, void *u, sb_color_printf cprintf)
|
||||||
|
{
|
||||||
|
#define printf(c, ...) cprintf(u, false, c, __VA_ARGS__)
|
||||||
|
|
||||||
|
struct sb_section_t *sec = xmalloc(sizeof(struct sb_section_t));
|
||||||
|
memset(sec, 0, sizeof(struct sb_section_t));
|
||||||
|
sec->identifier = id;
|
||||||
|
sec->is_data = data_sec;
|
||||||
|
sec->sec_size = ROUND_UP(size, BLOCK_SIZE) / BLOCK_SIZE;
|
||||||
|
|
||||||
|
if(data_sec)
|
||||||
|
{
|
||||||
|
sec->nr_insts = 1;
|
||||||
|
sec->insts = xmalloc(sizeof(struct sb_inst_t));
|
||||||
|
memset(sec->insts, 0, sizeof(struct sb_inst_t));
|
||||||
|
sec->insts->inst = SB_INST_DATA;
|
||||||
|
sec->insts->size = size;
|
||||||
|
sec->insts->data = memdup(buf, size);
|
||||||
|
return sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pretty print the content */
|
||||||
|
int pos = 0;
|
||||||
|
while(pos < size)
|
||||||
|
{
|
||||||
|
struct sb_inst_t inst;
|
||||||
|
memset(&inst, 0, sizeof(inst));
|
||||||
|
|
||||||
|
struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)&buf[pos];
|
||||||
|
inst.inst = hdr->opcode;
|
||||||
|
|
||||||
|
printf(OFF, "%s", indent);
|
||||||
|
uint8_t checksum = instruction_checksum(hdr);
|
||||||
|
if(checksum != hdr->checksum)
|
||||||
|
printf(GREY, "[Bad checksum]");
|
||||||
|
if(hdr->flags != 0)
|
||||||
|
{
|
||||||
|
printf(GREY, "[");
|
||||||
|
printf(BLUE, "f=%x", hdr->flags);
|
||||||
|
printf(GREY, "] ");
|
||||||
|
}
|
||||||
|
if(hdr->opcode == SB_INST_LOAD)
|
||||||
|
{
|
||||||
|
struct sb_instruction_load_t *load = (struct sb_instruction_load_t *)&buf[pos];
|
||||||
|
inst.size = load->len;
|
||||||
|
inst.addr = load->addr;
|
||||||
|
inst.data = memdup(load + 1, load->len);
|
||||||
|
|
||||||
|
printf(RED, "LOAD");
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(BLUE, "addr=0x%08x", load->addr);
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(GREEN, "len=0x%08x", load->len);
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(YELLOW, "crc=0x%08x", load->crc);
|
||||||
|
/* data is padded to 16-byte boundary with random data and crc'ed with it */
|
||||||
|
uint32_t computed_crc = crc(&buf[pos + sizeof(struct sb_instruction_load_t)],
|
||||||
|
ROUND_UP(load->len, 16));
|
||||||
|
if(load->crc == computed_crc)
|
||||||
|
printf(RED, " Ok\n");
|
||||||
|
else
|
||||||
|
printf(RED, " Failed (crc=0x%08x)\n", computed_crc);
|
||||||
|
|
||||||
|
pos += load->len + sizeof(struct sb_instruction_load_t);
|
||||||
|
}
|
||||||
|
else if(hdr->opcode == SB_INST_FILL)
|
||||||
|
{
|
||||||
|
struct sb_instruction_fill_t *fill = (struct sb_instruction_fill_t *)&buf[pos];
|
||||||
|
inst.pattern = fill->pattern;
|
||||||
|
inst.size = fill->len;
|
||||||
|
inst.addr = fill->addr;
|
||||||
|
|
||||||
|
printf(RED, "FILL");
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(BLUE, "addr=0x%08x", fill->addr);
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(GREEN, "len=0x%08x", fill->len);
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(YELLOW, "pattern=0x%08x\n", fill->pattern);
|
||||||
|
|
||||||
|
pos += sizeof(struct sb_instruction_fill_t);
|
||||||
|
}
|
||||||
|
else if(hdr->opcode == SB_INST_CALL ||
|
||||||
|
hdr->opcode == SB_INST_JUMP)
|
||||||
|
{
|
||||||
|
int is_call = (hdr->opcode == SB_INST_CALL);
|
||||||
|
struct sb_instruction_call_t *call = (struct sb_instruction_call_t *)&buf[pos];
|
||||||
|
inst.addr = call->addr;
|
||||||
|
inst.argument = call->arg;
|
||||||
|
|
||||||
|
if(is_call)
|
||||||
|
printf(RED, "CALL");
|
||||||
|
else
|
||||||
|
printf(RED, "JUMP");
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(BLUE, "addr=0x%08x", call->addr);
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(GREEN, "arg=0x%08x\n", call->arg);
|
||||||
|
|
||||||
|
pos += sizeof(struct sb_instruction_call_t);
|
||||||
|
}
|
||||||
|
else if(hdr->opcode == SB_INST_MODE)
|
||||||
|
{
|
||||||
|
struct sb_instruction_mode_t *mode = (struct sb_instruction_mode_t *)hdr;
|
||||||
|
inst.argument = mode->mode;
|
||||||
|
|
||||||
|
printf(RED, "MODE");
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(BLUE, "mod=0x%08x\n", mode->mode);
|
||||||
|
|
||||||
|
pos += sizeof(struct sb_instruction_mode_t);
|
||||||
|
}
|
||||||
|
else if(hdr->opcode == SB_INST_NOP)
|
||||||
|
{
|
||||||
|
printf(RED, "NOOP\n");
|
||||||
|
pos += sizeof(struct sb_instruction_mode_t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf(RED, "Unknown instruction %d at address 0x%08lx\n", hdr->opcode, (unsigned long)pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sec->insts = augment_array(sec->insts, sizeof(struct sb_inst_t), sec->nr_insts++, &inst, 1);
|
||||||
|
pos = ROUND_UP(pos, BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sec;
|
||||||
|
#undef printf
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fill_section_name(char name[5], uint32_t identifier)
|
||||||
|
{
|
||||||
|
name[0] = (identifier >> 24) & 0xff;
|
||||||
|
name[1] = (identifier >> 16) & 0xff;
|
||||||
|
name[2] = (identifier >> 8) & 0xff;
|
||||||
|
name[3] = identifier & 0xff;
|
||||||
|
for(int i = 0; i < 4; i++)
|
||||||
|
if(!isprint(name[i]))
|
||||||
|
name[i] = '_';
|
||||||
|
name[4] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t guess_alignment(uint32_t off)
|
||||||
|
{
|
||||||
|
/* find greatest power of two which divides the offset */
|
||||||
|
if(off == 0)
|
||||||
|
return 1;
|
||||||
|
uint32_t a = 1;
|
||||||
|
while(off % (2 * a) == 0)
|
||||||
|
a *= 2;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sb_file_t *sb_read_file(const char *filename, bool raw_mode, void *u,
|
||||||
|
sb_color_printf cprintf)
|
||||||
|
{
|
||||||
|
#define printf(c, ...) cprintf(u, false, c, __VA_ARGS__)
|
||||||
|
#define fatal(...) do { cprintf(u, true, GREY, __VA_ARGS__); return NULL; } while(0)
|
||||||
|
#define print_hex(c, p, len, nl) \
|
||||||
|
do { printf(c, ""); print_hex(p, len, nl); } while(0)
|
||||||
|
|
||||||
|
FILE *f = fopen(filename, "rb");
|
||||||
|
if(f == NULL)
|
||||||
|
fatal("Cannot open file for reading\n");
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long filesize = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
uint8_t *buf = xmalloc(filesize);
|
||||||
|
fread(buf, 1, filesize, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
struct sha_1_params_t sha_1_params;
|
||||||
|
struct sb_file_t *sb_file = xmalloc(sizeof(struct sb_file_t));
|
||||||
|
memset(sb_file, 0, sizeof(struct sb_file_t));
|
||||||
|
struct sb_header_t *sb_header = (struct sb_header_t *)buf;
|
||||||
|
|
||||||
|
sb_file->image_size = sb_header->image_size;
|
||||||
|
sb_file->minor_version = sb_header->minor_ver;
|
||||||
|
sb_file->flags = sb_header->flags;
|
||||||
|
sb_file->drive_tag = sb_header->drive_tag;
|
||||||
|
sb_file->first_boot_sec_id = sb_header->first_boot_sec_id;
|
||||||
|
|
||||||
|
if(memcmp(sb_header->signature, "STMP", 4) != 0)
|
||||||
|
fatal("Bad signature\n");
|
||||||
|
if(sb_header->image_size * BLOCK_SIZE > filesize)
|
||||||
|
fatal("File too small mismatch");
|
||||||
|
if(sb_header->header_size * BLOCK_SIZE != sizeof(struct sb_header_t))
|
||||||
|
fatal("Bad header size");
|
||||||
|
if(sb_header->sec_hdr_size * BLOCK_SIZE != sizeof(struct sb_section_header_t))
|
||||||
|
fatal("Bad section header size");
|
||||||
|
|
||||||
|
if(filesize > sb_header->image_size * BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
printf(GREY, "[Restrict file size from %lu to %d bytes]\n", filesize,
|
||||||
|
sb_header->image_size * BLOCK_SIZE);
|
||||||
|
filesize = sb_header->image_size * BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(BLUE, "Basic info:\n");
|
||||||
|
printf(GREEN, " SB version: ");
|
||||||
|
printf(YELLOW, "%d.%d\n", sb_header->major_ver, sb_header->minor_ver);
|
||||||
|
printf(GREEN, " Header SHA-1: ");
|
||||||
|
byte *hdr_sha1 = sb_header->sha1_header;
|
||||||
|
print_hex(YELLOW, hdr_sha1, 20, false);
|
||||||
|
/* Check SHA1 sum */
|
||||||
|
byte computed_sha1[20];
|
||||||
|
sha_1_init(&sha_1_params);
|
||||||
|
sha_1_update(&sha_1_params, &sb_header->signature[0],
|
||||||
|
sizeof(struct sb_header_t) - sizeof(sb_header->sha1_header));
|
||||||
|
sha_1_finish(&sha_1_params);
|
||||||
|
sha_1_output(&sha_1_params, computed_sha1);
|
||||||
|
if(memcmp(hdr_sha1, computed_sha1, 20) == 0)
|
||||||
|
printf(RED, " Ok\n");
|
||||||
|
else
|
||||||
|
printf(RED, " Failed\n");
|
||||||
|
printf(GREEN, " Flags: ");
|
||||||
|
printf(YELLOW, "%x\n", sb_header->flags);
|
||||||
|
printf(GREEN, " Total file size : ");
|
||||||
|
printf(YELLOW, "%ld\n", filesize);
|
||||||
|
|
||||||
|
/* Sizes and offsets */
|
||||||
|
printf(BLUE, "Sizes and offsets:\n");
|
||||||
|
printf(GREEN, " # of encryption keys = ");
|
||||||
|
printf(YELLOW, "%d\n", sb_header->nr_keys);
|
||||||
|
printf(GREEN, " # of sections = ");
|
||||||
|
printf(YELLOW, "%d\n", sb_header->nr_sections);
|
||||||
|
|
||||||
|
/* Versions */
|
||||||
|
printf(BLUE, "Versions\n");
|
||||||
|
|
||||||
|
printf(GREEN, " Random 1: ");
|
||||||
|
print_hex(YELLOW, sb_header->rand_pad0, sizeof(sb_header->rand_pad0), true);
|
||||||
|
printf(GREEN, " Random 2: ");
|
||||||
|
print_hex(YELLOW, sb_header->rand_pad1, sizeof(sb_header->rand_pad1), true);
|
||||||
|
|
||||||
|
uint64_t micros = sb_header->timestamp;
|
||||||
|
time_t seconds = (micros / (uint64_t)1000000L);
|
||||||
|
struct tm tm_base = {0, 0, 0, 1, 0, 100, 0, 0, 1, 0, NULL}; /* 2000/1/1 0:00:00 */
|
||||||
|
seconds += mktime(&tm_base);
|
||||||
|
struct tm *time = gmtime(&seconds);
|
||||||
|
printf(GREEN, " Creation date/time = ");
|
||||||
|
printf(YELLOW, "%s", asctime(time));
|
||||||
|
|
||||||
|
struct sb_version_t product_ver = sb_header->product_ver;
|
||||||
|
fix_version(&product_ver);
|
||||||
|
struct sb_version_t component_ver = sb_header->component_ver;
|
||||||
|
fix_version(&component_ver);
|
||||||
|
|
||||||
|
memcpy(&sb_file->product_ver, &product_ver, sizeof(product_ver));
|
||||||
|
memcpy(&sb_file->component_ver, &component_ver, sizeof(component_ver));
|
||||||
|
|
||||||
|
printf(GREEN, " Product version = ");
|
||||||
|
printf(YELLOW, "%X.%X.%X\n", product_ver.major, product_ver.minor, product_ver.revision);
|
||||||
|
printf(GREEN, " Component version = ");
|
||||||
|
printf(YELLOW, "%X.%X.%X\n", component_ver.major, component_ver.minor, component_ver.revision);
|
||||||
|
|
||||||
|
printf(GREEN, " Drive tag = ");
|
||||||
|
printf(YELLOW, "%x\n", sb_header->drive_tag);
|
||||||
|
printf(GREEN, " First boot tag offset = ");
|
||||||
|
printf(YELLOW, "%x\n", sb_header->first_boot_tag_off);
|
||||||
|
printf(GREEN, " First boot section ID = ");
|
||||||
|
printf(YELLOW, "0x%08x\n", sb_header->first_boot_sec_id);
|
||||||
|
|
||||||
|
/* encryption cbc-mac */
|
||||||
|
byte real_key[16];
|
||||||
|
bool valid_key = false; /* false until a matching key was found */
|
||||||
|
if(sb_header->nr_keys > 0)
|
||||||
|
{
|
||||||
|
if(sb_header->nr_keys > g_nr_keys)
|
||||||
|
{
|
||||||
|
fatal("SB file has %d keys but only %d were specified\n",
|
||||||
|
sb_header->nr_keys, g_nr_keys);
|
||||||
|
}
|
||||||
|
printf(BLUE, "Encryption data\n");
|
||||||
|
for(int i = 0; i < sb_header->nr_keys; i++)
|
||||||
|
{
|
||||||
|
printf(RED, " Key %d: ", i);
|
||||||
|
printf(YELLOW, "");
|
||||||
|
print_key(&g_key_array[i], true);
|
||||||
|
printf(GREEN, " CBC-MAC of headers: ");
|
||||||
|
|
||||||
|
uint32_t ofs = sizeof(struct sb_header_t)
|
||||||
|
+ sizeof(struct sb_section_header_t) * sb_header->nr_sections
|
||||||
|
+ sizeof(struct sb_key_dictionary_entry_t) * i;
|
||||||
|
struct sb_key_dictionary_entry_t *dict_entry =
|
||||||
|
(struct sb_key_dictionary_entry_t *)&buf[ofs];
|
||||||
|
/* cbc mac */
|
||||||
|
print_hex(YELLOW, dict_entry->hdr_cbc_mac, 16, false);
|
||||||
|
/* check it */
|
||||||
|
byte computed_cbc_mac[16];
|
||||||
|
byte zero[16];
|
||||||
|
memset(zero, 0, 16);
|
||||||
|
crypto_cbc(buf, NULL, sb_header->header_size + sb_header->nr_sections,
|
||||||
|
&g_key_array[i], zero, &computed_cbc_mac, 1);
|
||||||
|
|
||||||
|
bool ok = memcmp(dict_entry->hdr_cbc_mac, computed_cbc_mac, 16) == 0;
|
||||||
|
if(ok)
|
||||||
|
{
|
||||||
|
valid_key = true;
|
||||||
|
printf(RED, " Ok\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf(RED, " Failed\n");
|
||||||
|
|
||||||
|
printf(GREEN, " Encrypted key : ");
|
||||||
|
print_hex(YELLOW, dict_entry->key, 16, true);
|
||||||
|
/* decrypt */
|
||||||
|
byte decrypted_key[16];
|
||||||
|
byte iv[16];
|
||||||
|
memcpy(iv, buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */
|
||||||
|
crypto_cbc(dict_entry->key, decrypted_key, 1, &g_key_array[i], iv, NULL, 0);
|
||||||
|
printf(GREEN, " Decrypted key : ");
|
||||||
|
print_hex(YELLOW, decrypted_key, 16, false);
|
||||||
|
/* cross-check or copy */
|
||||||
|
if(valid_key && ok)
|
||||||
|
memcpy(real_key, decrypted_key, 16);
|
||||||
|
else if(valid_key)
|
||||||
|
{
|
||||||
|
if(memcmp(real_key, decrypted_key, 16) == 0)
|
||||||
|
printf(RED, " Cross-Check Ok");
|
||||||
|
else
|
||||||
|
printf(RED, " Cross-Check Failed");
|
||||||
|
}
|
||||||
|
printf(OFF, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getenv("SB_REAL_KEY") != 0)
|
||||||
|
{
|
||||||
|
struct crypto_key_t k;
|
||||||
|
char *env = getenv("SB_REAL_KEY");
|
||||||
|
if(!parse_key(&env, &k) || *env)
|
||||||
|
bug("Invalid SB_REAL_KEY");
|
||||||
|
memcpy(real_key, k.u.key, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(RED, " Summary:\n");
|
||||||
|
printf(GREEN, " Real key: ");
|
||||||
|
print_hex(YELLOW, real_key, 16, true);
|
||||||
|
printf(GREEN, " IV : ");
|
||||||
|
print_hex(YELLOW, buf, 16, true);
|
||||||
|
|
||||||
|
sb_file->real_key = xmalloc(16);
|
||||||
|
memcpy(*sb_file->real_key, real_key, 16);
|
||||||
|
sb_file->crypto_iv = xmalloc(16);
|
||||||
|
memcpy(*sb_file->crypto_iv, buf, 16);
|
||||||
|
|
||||||
|
/* sections */
|
||||||
|
if(!raw_mode)
|
||||||
|
{
|
||||||
|
sb_file->nr_sections = sb_header->nr_sections;
|
||||||
|
sb_file->sections = xmalloc(sb_file->nr_sections * sizeof(struct sb_section_t));
|
||||||
|
memset(sb_file->sections, 0, sb_file->nr_sections * sizeof(struct sb_section_t));
|
||||||
|
printf(BLUE, "Sections\n");
|
||||||
|
for(int i = 0; i < sb_header->nr_sections; i++)
|
||||||
|
{
|
||||||
|
uint32_t ofs = sb_header->header_size * BLOCK_SIZE + i * sizeof(struct sb_section_header_t);
|
||||||
|
struct sb_section_header_t *sec_hdr = (struct sb_section_header_t *)&buf[ofs];
|
||||||
|
|
||||||
|
char name[5];
|
||||||
|
fill_section_name(name, sec_hdr->identifier);
|
||||||
|
int pos = sec_hdr->offset * BLOCK_SIZE;
|
||||||
|
int size = sec_hdr->size * BLOCK_SIZE;
|
||||||
|
int data_sec = !(sec_hdr->flags & SECTION_BOOTABLE);
|
||||||
|
int encrypted = !(sec_hdr->flags & SECTION_CLEARTEXT) && sb_header->nr_keys > 0;
|
||||||
|
|
||||||
|
printf(GREEN, " Section ");
|
||||||
|
printf(YELLOW, "'%s'\n", name);
|
||||||
|
printf(GREEN, " pos = ");
|
||||||
|
printf(YELLOW, "%8x - %8x\n", pos, pos+size);
|
||||||
|
printf(GREEN, " len = ");
|
||||||
|
printf(YELLOW, "%8x\n", size);
|
||||||
|
printf(GREEN, " flags = ");
|
||||||
|
printf(YELLOW, "%8x", sec_hdr->flags);
|
||||||
|
if(data_sec)
|
||||||
|
printf(RED, " Data Section");
|
||||||
|
else
|
||||||
|
printf(RED, " Boot Section");
|
||||||
|
if(encrypted)
|
||||||
|
printf(RED, " (Encrypted)");
|
||||||
|
printf(OFF, "\n");
|
||||||
|
|
||||||
|
/* save it */
|
||||||
|
byte *sec = xmalloc(size);
|
||||||
|
if(encrypted)
|
||||||
|
cbc_mac(buf + pos, sec, size / BLOCK_SIZE, real_key, buf, NULL, 0);
|
||||||
|
else
|
||||||
|
memcpy(sec, buf + pos, size);
|
||||||
|
|
||||||
|
struct sb_section_t *s = read_section(data_sec, sec_hdr->identifier,
|
||||||
|
sec, size, " ", u, cprintf);
|
||||||
|
if(s)
|
||||||
|
{
|
||||||
|
s->is_cleartext = !encrypted;
|
||||||
|
s->alignment = guess_alignment(pos);
|
||||||
|
memcpy(&sb_file->sections[i], s, sizeof(struct sb_section_t));
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* advanced raw mode */
|
||||||
|
printf(BLUE, "Commands\n");
|
||||||
|
uint32_t offset = sb_header->first_boot_tag_off * BLOCK_SIZE;
|
||||||
|
byte iv[16];
|
||||||
|
const char *indent = " ";
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
/* restart with IV */
|
||||||
|
memcpy(iv, buf, 16);
|
||||||
|
byte cmd[BLOCK_SIZE];
|
||||||
|
if(sb_header->nr_keys > 0)
|
||||||
|
cbc_mac(buf + offset, cmd, 1, real_key, iv, &iv, 0);
|
||||||
|
else
|
||||||
|
memcpy(cmd, buf + offset, BLOCK_SIZE);
|
||||||
|
struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)cmd;
|
||||||
|
printf(OFF, "%s", indent);
|
||||||
|
uint8_t checksum = instruction_checksum(hdr);
|
||||||
|
if(checksum != hdr->checksum)
|
||||||
|
printf(GREY, "[Bad checksum']");
|
||||||
|
|
||||||
|
if(hdr->opcode == SB_INST_NOP)
|
||||||
|
{
|
||||||
|
printf(RED, "NOOP\n");
|
||||||
|
offset += BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
else if(hdr->opcode == SB_INST_TAG)
|
||||||
|
{
|
||||||
|
struct sb_instruction_tag_t *tag = (struct sb_instruction_tag_t *)hdr;
|
||||||
|
printf(RED, "BTAG");
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(BLUE, "sec=0x%08x", tag->identifier);
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(GREEN, "cnt=0x%08x", tag->len);
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(YELLOW, "flg=0x%08x", tag->flags);
|
||||||
|
if(tag->hdr.flags & SB_INST_LAST_TAG)
|
||||||
|
{
|
||||||
|
printf(OFF, " | ");
|
||||||
|
printf(RED, " Last section");
|
||||||
|
}
|
||||||
|
printf(OFF, "\n");
|
||||||
|
offset += sizeof(struct sb_instruction_tag_t);
|
||||||
|
|
||||||
|
char name[5];
|
||||||
|
fill_section_name(name, tag->identifier);
|
||||||
|
int pos = offset;
|
||||||
|
int size = tag->len * BLOCK_SIZE;
|
||||||
|
int data_sec = !(tag->flags & SECTION_BOOTABLE);
|
||||||
|
int encrypted = !(tag->flags & SECTION_CLEARTEXT) && sb_header->nr_keys > 0;
|
||||||
|
|
||||||
|
printf(GREEN, "%sSection ", indent);
|
||||||
|
printf(YELLOW, "'%s'\n", name);
|
||||||
|
printf(GREEN, "%s pos = ", indent);
|
||||||
|
printf(YELLOW, "%8x - %8x\n", pos, pos+size);
|
||||||
|
printf(GREEN, "%s len = ", indent);
|
||||||
|
printf(YELLOW, "%8x\n", size);
|
||||||
|
printf(GREEN, "%s flags = ", indent);
|
||||||
|
printf(YELLOW, "%8x", tag->flags);
|
||||||
|
if(data_sec)
|
||||||
|
printf(RED, " Data Section");
|
||||||
|
else
|
||||||
|
printf(RED, " Boot Section");
|
||||||
|
if(encrypted)
|
||||||
|
printf(RED, " (Encrypted)");
|
||||||
|
printf(OFF, "\n");
|
||||||
|
|
||||||
|
/* save it */
|
||||||
|
byte *sec = xmalloc(size);
|
||||||
|
if(encrypted)
|
||||||
|
cbc_mac(buf + pos, sec, size / BLOCK_SIZE, real_key, buf, NULL, 0);
|
||||||
|
else
|
||||||
|
memcpy(sec, buf + pos, size);
|
||||||
|
|
||||||
|
struct sb_section_t *s = read_section(data_sec, tag->identifier,
|
||||||
|
sec, size, " ", u, cprintf);
|
||||||
|
if(s)
|
||||||
|
{
|
||||||
|
s->is_cleartext = !encrypted;
|
||||||
|
s->alignment = guess_alignment(pos);
|
||||||
|
sb_file->sections = augment_array(sb_file->sections,
|
||||||
|
sizeof(struct sb_section_t), sb_file->nr_sections++,
|
||||||
|
s, 1);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
free(sec);
|
||||||
|
|
||||||
|
/* last one ? */
|
||||||
|
if(tag->hdr.flags & SB_INST_LAST_TAG)
|
||||||
|
break;
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf(RED, "Unknown instruction %d at address 0x%08lx\n", hdr->opcode, (long)offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* final signature */
|
||||||
|
printf(BLUE, "Final signature:\n");
|
||||||
|
byte decrypted_block[32];
|
||||||
|
if(sb_header->nr_keys > 0)
|
||||||
|
{
|
||||||
|
printf(GREEN, " Encrypted SHA-1:\n");
|
||||||
|
byte *encrypted_block = &buf[filesize - 32];
|
||||||
|
printf(OFF, " ");
|
||||||
|
print_hex(YELLOW, encrypted_block, 16, true);
|
||||||
|
printf(OFF, " ");
|
||||||
|
print_hex(YELLOW, encrypted_block + 16, 16, true);
|
||||||
|
/* decrypt it */
|
||||||
|
cbc_mac(encrypted_block, decrypted_block, 2, real_key, buf, NULL, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
memcpy(decrypted_block, &buf[filesize - 32], 32);
|
||||||
|
printf(GREEN, " File SHA-1:\n ");
|
||||||
|
print_hex(YELLOW, decrypted_block, 20, false);
|
||||||
|
/* check it */
|
||||||
|
sha_1_init(&sha_1_params);
|
||||||
|
sha_1_update(&sha_1_params, buf, filesize - 32);
|
||||||
|
sha_1_finish(&sha_1_params);
|
||||||
|
sha_1_output(&sha_1_params, computed_sha1);
|
||||||
|
if(memcmp(decrypted_block, computed_sha1, 20) == 0)
|
||||||
|
printf(RED, " Ok\n");
|
||||||
|
else
|
||||||
|
printf(RED, " Failed\n");
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return sb_file;
|
||||||
|
#undef printf
|
||||||
|
#undef fatal
|
||||||
|
#undef print_hex
|
||||||
|
}
|
||||||
|
|
||||||
|
void sb_dump(struct sb_file_t *file, void *u, sb_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, "SB File\n");
|
||||||
|
printf(TREE, "+-");
|
||||||
|
printf(HEADER, "Version: ");
|
||||||
|
printf(TEXT, "1.%d\n", file->minor_version);
|
||||||
|
printf(TREE, "+-");
|
||||||
|
printf(HEADER, "Flags: ");
|
||||||
|
printf(TEXT, "%x\n", file->flags);
|
||||||
|
printf(TREE, "+-");
|
||||||
|
printf(HEADER, "Drive Tag: ");
|
||||||
|
printf(TEXT, "%x\n", file->drive_tag);
|
||||||
|
printf(TREE, "+-");
|
||||||
|
printf(HEADER, "First Boot Section ID: ");
|
||||||
|
char name[5];
|
||||||
|
fill_section_name(name, file->first_boot_sec_id);
|
||||||
|
printf(TEXT, "%08x (%s)\n", file->first_boot_sec_id, name);
|
||||||
|
|
||||||
|
if(file->real_key)
|
||||||
|
{
|
||||||
|
printf(TREE, "+-");
|
||||||
|
printf(HEADER, "Real key: ");
|
||||||
|
print_hex(TEXT, *file->real_key, 16, true);
|
||||||
|
}
|
||||||
|
if(file->crypto_iv)
|
||||||
|
{
|
||||||
|
printf(TREE, "+-");
|
||||||
|
printf(HEADER, "IV : ");
|
||||||
|
print_hex(TEXT, *file->crypto_iv, 16, true);
|
||||||
|
}
|
||||||
|
printf(TREE, "+-");
|
||||||
|
printf(HEADER, "Product Version: ");
|
||||||
|
printf(TEXT, "%X.%X.%X\n", file->product_ver.major, file->product_ver.minor,
|
||||||
|
file->product_ver.revision);
|
||||||
|
printf(TREE, "+-");
|
||||||
|
printf(HEADER, "Component Version: ");
|
||||||
|
printf(TEXT, "%X.%X.%X\n", file->component_ver.major, file->component_ver.minor,
|
||||||
|
file->component_ver.revision);
|
||||||
|
|
||||||
|
for(int i = 0; i < file->nr_sections; i++)
|
||||||
|
{
|
||||||
|
struct sb_section_t *sec = &file->sections[i];
|
||||||
|
printf(TREE, "+-");
|
||||||
|
printf(HEADER, "Section\n");
|
||||||
|
printf(TREE,"| +-");
|
||||||
|
printf(HEADER, "Identifier: ");
|
||||||
|
fill_section_name(name, sec->identifier);
|
||||||
|
printf(TEXT, "%08x (%s)\n", sec->identifier, name);
|
||||||
|
printf(TREE, "| +-");
|
||||||
|
printf(HEADER, "Type: ");
|
||||||
|
printf(TEXT, "%s (%s)\n", sec->is_data ? "Data Section" : "Boot Section",
|
||||||
|
sec->is_cleartext ? "Cleartext" : "Encrypted");
|
||||||
|
printf(TREE, "| +-");
|
||||||
|
printf(HEADER, "Alignment: ");
|
||||||
|
printf(TEXT, "%d (bytes)\n", sec->alignment);
|
||||||
|
printf(TREE, "| +-");
|
||||||
|
printf(HEADER, "Instructions\n");
|
||||||
|
for(int j = 0; j < sec->nr_insts; j++)
|
||||||
|
{
|
||||||
|
struct sb_inst_t *inst = &sec->insts[j];
|
||||||
|
printf(TREE, "| | +-");
|
||||||
|
switch(inst->inst)
|
||||||
|
{
|
||||||
|
case SB_INST_DATA:
|
||||||
|
printf(HEADER, "DATA");
|
||||||
|
printf(SEP, " | ");
|
||||||
|
printf(TEXT, "size=0x%08x\n", inst->size);
|
||||||
|
break;
|
||||||
|
case SB_INST_CALL:
|
||||||
|
case SB_INST_JUMP:
|
||||||
|
printf(HEADER, "%s", inst->inst == SB_INST_CALL ? "CALL" : "JUMP");
|
||||||
|
printf(SEP, " | ");
|
||||||
|
printf(TEXT, "addr=0x%08x", inst->addr);
|
||||||
|
printf(SEP, " | ");
|
||||||
|
printf(TEXT2, "arg=0x%08x\n", inst->argument);
|
||||||
|
break;
|
||||||
|
case SB_INST_LOAD:
|
||||||
|
printf(HEADER, "LOAD");
|
||||||
|
printf(SEP, " | ");
|
||||||
|
printf(TEXT, "addr=0x%08x", inst->addr);
|
||||||
|
printf(SEP, " | ");
|
||||||
|
printf(TEXT2, "len=0x%08x\n", inst->size);
|
||||||
|
break;
|
||||||
|
case SB_INST_FILL:
|
||||||
|
printf(HEADER, "FILL");
|
||||||
|
printf(SEP, " | ");
|
||||||
|
printf(TEXT, "addr=0x%08x", inst->addr);
|
||||||
|
printf(SEP, " | ");
|
||||||
|
printf(TEXT2, "len=0x%08x", inst->size);
|
||||||
|
printf(SEP, " | ");
|
||||||
|
printf(TEXT2, "pattern=0x%08x\n", inst->pattern);
|
||||||
|
break;
|
||||||
|
case SB_INST_MODE:
|
||||||
|
printf(HEADER, "MODE");
|
||||||
|
printf(SEP, " | ");
|
||||||
|
printf(TEXT, "mod=0x%08x\n", inst->addr);
|
||||||
|
break;
|
||||||
|
case SB_INST_NOP:
|
||||||
|
printf(HEADER, "NOOP\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf(GREY, "[Unknown instruction %x]\n", inst->inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef printf
|
||||||
|
#undef print_hex
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
#define BLOCK_SIZE 16
|
#define BLOCK_SIZE 16
|
||||||
|
|
||||||
/* All fields are in big-endian BCD */
|
/* All fields are in big-endian BCD */
|
||||||
|
|
@ -161,12 +163,12 @@ struct sb_inst_t
|
||||||
{
|
{
|
||||||
uint8_t inst; /* SB_INST_* */
|
uint8_t inst; /* SB_INST_* */
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
|
uint32_t addr;
|
||||||
// <union>
|
// <union>
|
||||||
void *data;
|
void *data;
|
||||||
uint32_t pattern;
|
uint32_t pattern;
|
||||||
uint32_t addr;
|
|
||||||
// </union>
|
// </union>
|
||||||
uint32_t argument; // for call and jump
|
uint32_t argument; // for call, jump and mode
|
||||||
/* for production use */
|
/* for production use */
|
||||||
uint32_t padding_size;
|
uint32_t padding_size;
|
||||||
uint8_t *padding;
|
uint8_t *padding;
|
||||||
|
|
@ -194,6 +196,10 @@ struct sb_file_t
|
||||||
uint8_t (*crypto_iv)[16];
|
uint8_t (*crypto_iv)[16];
|
||||||
|
|
||||||
int nr_sections;
|
int nr_sections;
|
||||||
|
uint16_t drive_tag;
|
||||||
|
uint32_t first_boot_sec_id;
|
||||||
|
uint16_t flags;
|
||||||
|
uint8_t minor_version;
|
||||||
struct sb_section_t *sections;
|
struct sb_section_t *sections;
|
||||||
struct sb_version_t product_ver;
|
struct sb_version_t product_ver;
|
||||||
struct sb_version_t component_ver;
|
struct sb_version_t component_ver;
|
||||||
|
|
@ -201,6 +207,12 @@ struct sb_file_t
|
||||||
uint32_t image_size; /* in blocks */
|
uint32_t image_size; /* in blocks */
|
||||||
};
|
};
|
||||||
|
|
||||||
void sb_produce_file(struct sb_file_t *sb, const char *filename);
|
void sb_write_file(struct sb_file_t *sb, const char *filename);
|
||||||
|
|
||||||
|
typedef void (*sb_color_printf)(void *u, bool err, color_t c, const char *f, ...);
|
||||||
|
struct sb_file_t *sb_read_file(const char *filename, bool raw_mode, void *u,
|
||||||
|
sb_color_printf printf);
|
||||||
|
|
||||||
|
void sb_dump(struct sb_file_t *file, void *u, sb_color_printf printf);
|
||||||
|
|
||||||
#endif /* __SB_H__ */
|
#endif /* __SB_H__ */
|
||||||
|
|
|
||||||
|
|
@ -56,19 +56,7 @@
|
||||||
|
|
||||||
/* globals */
|
/* globals */
|
||||||
|
|
||||||
uint8_t *g_buf; /* file content */
|
|
||||||
char *g_out_prefix;
|
char *g_out_prefix;
|
||||||
bool g_debug;
|
|
||||||
bool g_raw_mode;
|
|
||||||
|
|
||||||
static uint8_t instruction_checksum(struct sb_instruction_header_t *hdr)
|
|
||||||
{
|
|
||||||
uint8_t sum = 90;
|
|
||||||
byte *ptr = (byte *)hdr;
|
|
||||||
for(int i = 1; i < 16; i++)
|
|
||||||
sum += ptr[i];
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void elf_printf(void *user, bool error, const char *fmt, ...)
|
static void elf_printf(void *user, bool error, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
|
@ -104,614 +92,34 @@ static void extract_elf_section(struct elf_params_t *elf, int count, const char
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void extract_section(int data_sec, char name[5], byte *buf, int size, const char *indent)
|
static void usage(void)
|
||||||
{
|
|
||||||
char *filename = xmalloc(strlen(g_out_prefix) + strlen(name) + 5);
|
|
||||||
if(g_out_prefix)
|
|
||||||
{
|
|
||||||
sprintf(filename, "%s%s.bin", g_out_prefix, name);
|
|
||||||
FILE *fd = fopen(filename, "wb");
|
|
||||||
if (fd != NULL)
|
|
||||||
{
|
|
||||||
fwrite(buf, size, 1, fd);
|
|
||||||
fclose(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(data_sec)
|
|
||||||
return;
|
|
||||||
|
|
||||||
sprintf(filename, "%s%s", g_out_prefix, name);
|
|
||||||
|
|
||||||
/* elf construction */
|
|
||||||
struct elf_params_t elf;
|
|
||||||
elf_init(&elf);
|
|
||||||
int elf_count = 0;
|
|
||||||
/* Pretty print the content */
|
|
||||||
int pos = 0;
|
|
||||||
while(pos < size)
|
|
||||||
{
|
|
||||||
struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)&buf[pos];
|
|
||||||
printf("%s", indent);
|
|
||||||
uint8_t checksum = instruction_checksum(hdr);
|
|
||||||
if(checksum != hdr->checksum)
|
|
||||||
{
|
|
||||||
color(GREY);
|
|
||||||
printf("[Bad checksum]");
|
|
||||||
}
|
|
||||||
if(hdr->flags != 0)
|
|
||||||
{
|
|
||||||
color(GREY);
|
|
||||||
printf("[");
|
|
||||||
color(BLUE);
|
|
||||||
printf("f=%x", hdr->flags);
|
|
||||||
color(GREY);
|
|
||||||
printf("] ");
|
|
||||||
}
|
|
||||||
if(hdr->opcode == SB_INST_LOAD)
|
|
||||||
{
|
|
||||||
struct sb_instruction_load_t *load = (struct sb_instruction_load_t *)&buf[pos];
|
|
||||||
color(RED);
|
|
||||||
printf("LOAD");
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(BLUE);
|
|
||||||
printf("addr=0x%08x", load->addr);
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(GREEN);
|
|
||||||
printf("len=0x%08x", load->len);
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("crc=0x%08x", load->crc);
|
|
||||||
/* data is padded to 16-byte boundary with random data and crc'ed with it */
|
|
||||||
uint32_t computed_crc = crc(&buf[pos + sizeof(struct sb_instruction_load_t)],
|
|
||||||
ROUND_UP(load->len, 16));
|
|
||||||
color(RED);
|
|
||||||
if(load->crc == computed_crc)
|
|
||||||
printf(" Ok\n");
|
|
||||||
else
|
|
||||||
printf(" Failed (crc=0x%08x)\n", computed_crc);
|
|
||||||
|
|
||||||
/* elf construction */
|
|
||||||
elf_add_load_section(&elf, load->addr, load->len,
|
|
||||||
&buf[pos + sizeof(struct sb_instruction_load_t)]);
|
|
||||||
|
|
||||||
pos += load->len + sizeof(struct sb_instruction_load_t);
|
|
||||||
}
|
|
||||||
else if(hdr->opcode == SB_INST_FILL)
|
|
||||||
{
|
|
||||||
struct sb_instruction_fill_t *fill = (struct sb_instruction_fill_t *)&buf[pos];
|
|
||||||
color(RED);
|
|
||||||
printf("FILL");
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(BLUE);
|
|
||||||
printf("addr=0x%08x", fill->addr);
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(GREEN);
|
|
||||||
printf("len=0x%08x", fill->len);
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("pattern=0x%08x\n", fill->pattern);
|
|
||||||
color(OFF);
|
|
||||||
|
|
||||||
/* elf construction */
|
|
||||||
elf_add_fill_section(&elf, fill->addr, fill->len, fill->pattern);
|
|
||||||
|
|
||||||
pos += sizeof(struct sb_instruction_fill_t);
|
|
||||||
}
|
|
||||||
else if(hdr->opcode == SB_INST_CALL ||
|
|
||||||
hdr->opcode == SB_INST_JUMP)
|
|
||||||
{
|
|
||||||
int is_call = (hdr->opcode == SB_INST_CALL);
|
|
||||||
struct sb_instruction_call_t *call = (struct sb_instruction_call_t *)&buf[pos];
|
|
||||||
color(RED);
|
|
||||||
if(is_call)
|
|
||||||
printf("CALL");
|
|
||||||
else
|
|
||||||
printf("JUMP");
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(BLUE);
|
|
||||||
printf("addr=0x%08x", call->addr);
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(GREEN);
|
|
||||||
printf("arg=0x%08x\n", call->arg);
|
|
||||||
color(OFF);
|
|
||||||
|
|
||||||
/* elf construction */
|
|
||||||
elf_set_start_addr(&elf, call->addr);
|
|
||||||
extract_elf_section(&elf, elf_count++, filename, indent);
|
|
||||||
elf_release(&elf);
|
|
||||||
elf_init(&elf);
|
|
||||||
|
|
||||||
pos += sizeof(struct sb_instruction_call_t);
|
|
||||||
}
|
|
||||||
else if(hdr->opcode == SB_INST_MODE)
|
|
||||||
{
|
|
||||||
struct sb_instruction_mode_t *mode = (struct sb_instruction_mode_t *)hdr;
|
|
||||||
color(RED);
|
|
||||||
printf("MODE");
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(BLUE);
|
|
||||||
printf("mod=0x%08x\n", mode->mode);
|
|
||||||
color(OFF);
|
|
||||||
pos += sizeof(struct sb_instruction_mode_t);
|
|
||||||
}
|
|
||||||
else if(hdr->opcode == SB_INST_NOP)
|
|
||||||
{
|
|
||||||
color(RED);
|
|
||||||
printf("NOOP\n");
|
|
||||||
pos += sizeof(struct sb_instruction_mode_t);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
color(RED);
|
|
||||||
printf("Unknown instruction %d at address 0x%08lx\n", hdr->opcode, (unsigned long)pos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = ROUND_UP(pos, BLOCK_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!elf_is_empty(&elf))
|
|
||||||
extract_elf_section(&elf, elf_count++, filename, indent);
|
|
||||||
elf_release(&elf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fill_section_name(char name[5], uint32_t identifier)
|
|
||||||
{
|
|
||||||
name[0] = (identifier >> 24) & 0xff;
|
|
||||||
name[1] = (identifier >> 16) & 0xff;
|
|
||||||
name[2] = (identifier >> 8) & 0xff;
|
|
||||||
name[3] = identifier & 0xff;
|
|
||||||
for(int i = 0; i < 4; i++)
|
|
||||||
if(!isprint(name[i]))
|
|
||||||
name[i] = '_';
|
|
||||||
name[4] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t swap16(uint16_t t)
|
|
||||||
{
|
|
||||||
return (t << 8) | (t >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fix_version(struct sb_version_t *ver)
|
|
||||||
{
|
|
||||||
ver->major = swap16(ver->major);
|
|
||||||
ver->minor = swap16(ver->minor);
|
|
||||||
ver->revision = swap16(ver->revision);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void extract(unsigned long filesize)
|
|
||||||
{
|
|
||||||
struct sha_1_params_t sha_1_params;
|
|
||||||
/* Basic header info */
|
|
||||||
struct sb_header_t *sb_header = (struct sb_header_t *)g_buf;
|
|
||||||
|
|
||||||
if(memcmp(sb_header->signature, "STMP", 4) != 0)
|
|
||||||
bugp("Bad signature");
|
|
||||||
/*
|
|
||||||
if(sb_header->image_size * BLOCK_SIZE > filesize)
|
|
||||||
bugp("File size mismatch");
|
|
||||||
*/
|
|
||||||
if(sb_header->header_size * BLOCK_SIZE != sizeof(struct sb_header_t))
|
|
||||||
bugp("Bad header size");
|
|
||||||
if(sb_header->sec_hdr_size * BLOCK_SIZE != sizeof(struct sb_section_header_t))
|
|
||||||
bugp("Bad section header size");
|
|
||||||
|
|
||||||
if(filesize > sb_header->image_size * BLOCK_SIZE)
|
|
||||||
{
|
|
||||||
color(GREY);
|
|
||||||
printf("[Restrict file size from %lu to %d bytes]\n", filesize,
|
|
||||||
sb_header->image_size * BLOCK_SIZE);
|
|
||||||
filesize = sb_header->image_size * BLOCK_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
color(BLUE);
|
|
||||||
printf("Basic info:\n");
|
|
||||||
color(GREEN);
|
|
||||||
printf(" SB version: ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%d.%d\n", sb_header->major_ver, sb_header->minor_ver);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Header SHA-1: ");
|
|
||||||
byte *hdr_sha1 = sb_header->sha1_header;
|
|
||||||
color(YELLOW);
|
|
||||||
print_hex(hdr_sha1, 20, false);
|
|
||||||
/* Check SHA1 sum */
|
|
||||||
byte computed_sha1[20];
|
|
||||||
sha_1_init(&sha_1_params);
|
|
||||||
sha_1_update(&sha_1_params, &sb_header->signature[0],
|
|
||||||
sizeof(struct sb_header_t) - sizeof(sb_header->sha1_header));
|
|
||||||
sha_1_finish(&sha_1_params);
|
|
||||||
sha_1_output(&sha_1_params, computed_sha1);
|
|
||||||
color(RED);
|
|
||||||
if(memcmp(hdr_sha1, computed_sha1, 20) == 0)
|
|
||||||
printf(" Ok\n");
|
|
||||||
else
|
|
||||||
printf(" Failed\n");
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Flags: ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%x\n", sb_header->flags);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Total file size : ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%ld\n", filesize);
|
|
||||||
|
|
||||||
/* Sizes and offsets */
|
|
||||||
color(BLUE);
|
|
||||||
printf("Sizes and offsets:\n");
|
|
||||||
color(GREEN);
|
|
||||||
printf(" # of encryption keys = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%d\n", sb_header->nr_keys);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" # of sections = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%d\n", sb_header->nr_sections);
|
|
||||||
|
|
||||||
/* Versions */
|
|
||||||
color(BLUE);
|
|
||||||
printf("Versions\n");
|
|
||||||
color(GREEN);
|
|
||||||
|
|
||||||
printf(" Random 1: ");
|
|
||||||
color(YELLOW);
|
|
||||||
print_hex(sb_header->rand_pad0, sizeof(sb_header->rand_pad0), true);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Random 2: ");
|
|
||||||
color(YELLOW);
|
|
||||||
print_hex(sb_header->rand_pad1, sizeof(sb_header->rand_pad1), true);
|
|
||||||
|
|
||||||
uint64_t micros = sb_header->timestamp;
|
|
||||||
time_t seconds = (micros / (uint64_t)1000000L);
|
|
||||||
struct tm tm_base = {0, 0, 0, 1, 0, 100, 0, 0, 1, 0, NULL}; /* 2000/1/1 0:00:00 */
|
|
||||||
seconds += mktime(&tm_base);
|
|
||||||
struct tm *time = gmtime(&seconds);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Creation date/time = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%s", asctime(time));
|
|
||||||
|
|
||||||
struct sb_version_t product_ver = sb_header->product_ver;
|
|
||||||
fix_version(&product_ver);
|
|
||||||
struct sb_version_t component_ver = sb_header->component_ver;
|
|
||||||
fix_version(&component_ver);
|
|
||||||
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Product version = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%X.%X.%X\n", product_ver.major, product_ver.minor, product_ver.revision);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Component version = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%X.%X.%X\n", component_ver.major, component_ver.minor, component_ver.revision);
|
|
||||||
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Drive tag = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%x\n", sb_header->drive_tag);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" First boot tag offset = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%x\n", sb_header->first_boot_tag_off);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" First boot section ID = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("0x%08x\n", sb_header->first_boot_sec_id);
|
|
||||||
|
|
||||||
/* encryption cbc-mac */
|
|
||||||
byte real_key[16];
|
|
||||||
bool valid_key = false; /* false until a matching key was found */
|
|
||||||
if(sb_header->nr_keys > 0)
|
|
||||||
{
|
|
||||||
if(sb_header->nr_keys > g_nr_keys)
|
|
||||||
{
|
|
||||||
color(GREY);
|
|
||||||
bug("SB file has %d keys but only %d were specified on command line\n",
|
|
||||||
sb_header->nr_keys, g_nr_keys);
|
|
||||||
}
|
|
||||||
color(BLUE);
|
|
||||||
printf("Encryption data\n");
|
|
||||||
for(int i = 0; i < sb_header->nr_keys; i++)
|
|
||||||
{
|
|
||||||
color(RED);
|
|
||||||
printf(" Key %d: ", i);
|
|
||||||
print_key(&g_key_array[i], true);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" CBC-MAC of headers: ");
|
|
||||||
|
|
||||||
uint32_t ofs = sizeof(struct sb_header_t)
|
|
||||||
+ sizeof(struct sb_section_header_t) * sb_header->nr_sections
|
|
||||||
+ sizeof(struct sb_key_dictionary_entry_t) * i;
|
|
||||||
struct sb_key_dictionary_entry_t *dict_entry =
|
|
||||||
(struct sb_key_dictionary_entry_t *)&g_buf[ofs];
|
|
||||||
/* cbc mac */
|
|
||||||
color(YELLOW);
|
|
||||||
print_hex(dict_entry->hdr_cbc_mac, 16, false);
|
|
||||||
/* check it */
|
|
||||||
byte computed_cbc_mac[16];
|
|
||||||
byte zero[16];
|
|
||||||
memset(zero, 0, 16);
|
|
||||||
crypto_cbc(g_buf, NULL, sb_header->header_size + sb_header->nr_sections,
|
|
||||||
&g_key_array[i], zero, &computed_cbc_mac, 1);
|
|
||||||
color(RED);
|
|
||||||
bool ok = memcmp(dict_entry->hdr_cbc_mac, computed_cbc_mac, 16) == 0;
|
|
||||||
if(ok)
|
|
||||||
{
|
|
||||||
valid_key = true;
|
|
||||||
printf(" Ok\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
printf(" Failed\n");
|
|
||||||
color(GREEN);
|
|
||||||
|
|
||||||
printf(" Encrypted key : ");
|
|
||||||
color(YELLOW);
|
|
||||||
print_hex(dict_entry->key, 16, true);
|
|
||||||
color(GREEN);
|
|
||||||
/* decrypt */
|
|
||||||
byte decrypted_key[16];
|
|
||||||
byte iv[16];
|
|
||||||
memcpy(iv, g_buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */
|
|
||||||
crypto_cbc(dict_entry->key, decrypted_key, 1, &g_key_array[i], iv, NULL, 0);
|
|
||||||
printf(" Decrypted key : ");
|
|
||||||
color(YELLOW);
|
|
||||||
print_hex(decrypted_key, 16, false);
|
|
||||||
/* cross-check or copy */
|
|
||||||
if(valid_key && ok)
|
|
||||||
memcpy(real_key, decrypted_key, 16);
|
|
||||||
else if(valid_key)
|
|
||||||
{
|
|
||||||
if(memcmp(real_key, decrypted_key, 16) == 0)
|
|
||||||
{
|
|
||||||
color(RED);
|
|
||||||
printf(" Cross-Check Ok");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
color(RED);
|
|
||||||
printf(" Cross-Check Failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(getenv("SB_REAL_KEY") != 0)
|
|
||||||
{
|
|
||||||
struct crypto_key_t k;
|
|
||||||
char *env = getenv("SB_REAL_KEY");
|
|
||||||
if(!parse_key(&env, &k) || *env)
|
|
||||||
bug("Invalid SB_REAL_KEY");
|
|
||||||
memcpy(real_key, k.u.key, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
color(RED);
|
|
||||||
printf(" Summary:\n");
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Real key: ");
|
|
||||||
color(YELLOW);
|
|
||||||
print_hex(real_key, 16, true);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" IV : ");
|
|
||||||
color(YELLOW);
|
|
||||||
print_hex(g_buf, 16, true);
|
|
||||||
|
|
||||||
/* sections */
|
|
||||||
if(!g_raw_mode)
|
|
||||||
{
|
|
||||||
color(BLUE);
|
|
||||||
printf("Sections\n");
|
|
||||||
for(int i = 0; i < sb_header->nr_sections; i++)
|
|
||||||
{
|
|
||||||
uint32_t ofs = sb_header->header_size * BLOCK_SIZE + i * sizeof(struct sb_section_header_t);
|
|
||||||
struct sb_section_header_t *sec_hdr = (struct sb_section_header_t *)&g_buf[ofs];
|
|
||||||
|
|
||||||
char name[5];
|
|
||||||
fill_section_name(name, sec_hdr->identifier);
|
|
||||||
int pos = sec_hdr->offset * BLOCK_SIZE;
|
|
||||||
int size = sec_hdr->size * BLOCK_SIZE;
|
|
||||||
int data_sec = !(sec_hdr->flags & SECTION_BOOTABLE);
|
|
||||||
int encrypted = !(sec_hdr->flags & SECTION_CLEARTEXT) && sb_header->nr_keys > 0;
|
|
||||||
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Section ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("'%s'\n", name);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" pos = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%8x - %8x\n", pos, pos+size);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" len = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%8x\n", size);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" flags = ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%8x", sec_hdr->flags);
|
|
||||||
color(RED);
|
|
||||||
if(data_sec)
|
|
||||||
printf(" Data Section");
|
|
||||||
else
|
|
||||||
printf(" Boot Section");
|
|
||||||
if(encrypted)
|
|
||||||
printf(" (Encrypted)");
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
/* save it */
|
|
||||||
byte *sec = xmalloc(size);
|
|
||||||
if(encrypted)
|
|
||||||
cbc_mac(g_buf + pos, sec, size / BLOCK_SIZE, real_key, g_buf, NULL, 0);
|
|
||||||
else
|
|
||||||
memcpy(sec, g_buf + pos, size);
|
|
||||||
|
|
||||||
extract_section(data_sec, name, sec, size, " ");
|
|
||||||
free(sec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* advanced raw mode */
|
|
||||||
color(BLUE);
|
|
||||||
printf("Commands\n");
|
|
||||||
uint32_t offset = sb_header->first_boot_tag_off * BLOCK_SIZE;
|
|
||||||
byte iv[16];
|
|
||||||
const char *indent = " ";
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
/* restart with IV */
|
|
||||||
memcpy(iv, g_buf, 16);
|
|
||||||
byte cmd[BLOCK_SIZE];
|
|
||||||
if(sb_header->nr_keys > 0)
|
|
||||||
cbc_mac(g_buf + offset, cmd, 1, real_key, iv, &iv, 0);
|
|
||||||
else
|
|
||||||
memcpy(cmd, g_buf + offset, BLOCK_SIZE);
|
|
||||||
struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)cmd;
|
|
||||||
printf("%s", indent);
|
|
||||||
uint8_t checksum = instruction_checksum(hdr);
|
|
||||||
if(checksum != hdr->checksum)
|
|
||||||
{
|
|
||||||
color(GREY);
|
|
||||||
printf("[Bad checksum']");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(hdr->opcode == SB_INST_NOP)
|
|
||||||
{
|
|
||||||
color(RED);
|
|
||||||
printf("NOOP\n");
|
|
||||||
offset += BLOCK_SIZE;
|
|
||||||
}
|
|
||||||
else if(hdr->opcode == SB_INST_TAG)
|
|
||||||
{
|
|
||||||
struct sb_instruction_tag_t *tag = (struct sb_instruction_tag_t *)hdr;
|
|
||||||
color(RED);
|
|
||||||
printf("BTAG");
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(BLUE);
|
|
||||||
printf("sec=0x%08x", tag->identifier);
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(GREEN);
|
|
||||||
printf("cnt=0x%08x", tag->len);
|
|
||||||
color(OFF);printf(" | ");
|
|
||||||
color(YELLOW);
|
|
||||||
printf("flg=0x%08x", tag->flags);
|
|
||||||
color(OFF);
|
|
||||||
if(tag->hdr.flags & SB_INST_LAST_TAG)
|
|
||||||
{
|
|
||||||
printf(" | ");
|
|
||||||
color(RED);
|
|
||||||
printf(" Last section");
|
|
||||||
color(OFF);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
offset += sizeof(struct sb_instruction_tag_t);
|
|
||||||
|
|
||||||
char name[5];
|
|
||||||
fill_section_name(name, tag->identifier);
|
|
||||||
int pos = offset;
|
|
||||||
int size = tag->len * BLOCK_SIZE;
|
|
||||||
int data_sec = !(tag->flags & SECTION_BOOTABLE);
|
|
||||||
int encrypted = !(tag->flags & SECTION_CLEARTEXT) && sb_header->nr_keys > 0;
|
|
||||||
|
|
||||||
color(GREEN);
|
|
||||||
printf("%sSection ", indent);
|
|
||||||
color(YELLOW);
|
|
||||||
printf("'%s'\n", name);
|
|
||||||
color(GREEN);
|
|
||||||
printf("%s pos = ", indent);
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%8x - %8x\n", pos, pos+size);
|
|
||||||
color(GREEN);
|
|
||||||
printf("%s len = ", indent);
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%8x\n", size);
|
|
||||||
color(GREEN);
|
|
||||||
printf("%s flags = ", indent);
|
|
||||||
color(YELLOW);
|
|
||||||
printf("%8x", tag->flags);
|
|
||||||
color(RED);
|
|
||||||
if(data_sec)
|
|
||||||
printf(" Data Section");
|
|
||||||
else
|
|
||||||
printf(" Boot Section");
|
|
||||||
if(encrypted)
|
|
||||||
printf(" (Encrypted)");
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
/* save it */
|
|
||||||
byte *sec = xmalloc(size);
|
|
||||||
if(encrypted)
|
|
||||||
cbc_mac(g_buf + pos, sec, size / BLOCK_SIZE, real_key, g_buf, NULL, 0);
|
|
||||||
else
|
|
||||||
memcpy(sec, g_buf + pos, size);
|
|
||||||
|
|
||||||
extract_section(data_sec, name, sec, size, " ");
|
|
||||||
free(sec);
|
|
||||||
|
|
||||||
/* last one ? */
|
|
||||||
if(tag->hdr.flags & SB_INST_LAST_TAG)
|
|
||||||
break;
|
|
||||||
offset += size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
color(RED);
|
|
||||||
printf("Unknown instruction %d at address 0x%08lx\n", hdr->opcode, (long)offset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* final signature */
|
|
||||||
color(BLUE);
|
|
||||||
printf("Final signature:\n");
|
|
||||||
byte decrypted_block[32];
|
|
||||||
if(sb_header->nr_keys > 0)
|
|
||||||
{
|
|
||||||
color(GREEN);
|
|
||||||
printf(" Encrypted SHA-1:\n");
|
|
||||||
color(YELLOW);
|
|
||||||
byte *encrypted_block = &g_buf[filesize - 32];
|
|
||||||
printf(" ");
|
|
||||||
print_hex(encrypted_block, 16, true);
|
|
||||||
printf(" ");
|
|
||||||
print_hex(encrypted_block + 16, 16, true);
|
|
||||||
/* decrypt it */
|
|
||||||
cbc_mac(encrypted_block, decrypted_block, 2, real_key, g_buf, NULL, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
memcpy(decrypted_block, &g_buf[filesize - 32], 32);
|
|
||||||
color(GREEN);
|
|
||||||
printf(" File SHA-1:\n ");
|
|
||||||
color(YELLOW);
|
|
||||||
print_hex(decrypted_block, 20, false);
|
|
||||||
/* check it */
|
|
||||||
sha_1_init(&sha_1_params);
|
|
||||||
sha_1_update(&sha_1_params, g_buf, filesize - 32);
|
|
||||||
sha_1_finish(&sha_1_params);
|
|
||||||
sha_1_output(&sha_1_params, computed_sha1);
|
|
||||||
color(RED);
|
|
||||||
if(memcmp(decrypted_block, computed_sha1, 20) == 0)
|
|
||||||
printf(" Ok\n");
|
|
||||||
else
|
|
||||||
printf(" Failed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void usage(void)
|
|
||||||
{
|
{
|
||||||
printf("Usage: sbtoelf [options] sb-file\n");
|
printf("Usage: sbtoelf [options] sb-file\n");
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -?/--help\tDisplay this message\n");
|
printf(" -?/--help\tDisplay this message\n");
|
||||||
printf(" -o <file>\tSet output prefix\n");
|
printf(" -o <file>\tSet output prefix\n");
|
||||||
printf(" -d/--debug\tEnable debug output\n");
|
printf(" -d/--debug\tEnable debug output*\n");
|
||||||
printf(" -k <file>\tAdd key file\n");
|
printf(" -k <file>\tAdd key file\n");
|
||||||
printf(" -z\t\tAdd zero key\n");
|
printf(" -z\t\tAdd zero key\n");
|
||||||
printf(" -r\t\tUse raw command mode\n");
|
printf(" -r\t\tUse raw command mode\n");
|
||||||
printf(" -a/--add-key <key>\tAdd single key (hex or usbotp)\n");
|
printf(" -a/--add-key <key>\tAdd single key (hex or usbotp)\n");
|
||||||
printf(" -n/--no-color\tDisable output colors\n");
|
printf(" -n/--no-color\tDisable output colors\n");
|
||||||
|
printf(" -l/--loopback <file>\tProduce sb file out of extracted description*\n");
|
||||||
|
printf("Options marked with a * are for debug purpose only\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sb_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 =
|
static struct crypto_key_t g_zero_key =
|
||||||
{
|
{
|
||||||
.method = CRYPTO_KEY,
|
.method = CRYPTO_KEY,
|
||||||
|
|
@ -720,6 +128,9 @@ static struct crypto_key_t g_zero_key =
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
bool raw_mode = false;
|
||||||
|
const char *loopback = NULL;
|
||||||
|
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
static struct option long_options[] =
|
static struct option long_options[] =
|
||||||
|
|
@ -728,16 +139,22 @@ int main(int argc, char **argv)
|
||||||
{"debug", no_argument, 0, 'd'},
|
{"debug", no_argument, 0, 'd'},
|
||||||
{"add-key", required_argument, 0, 'a'},
|
{"add-key", required_argument, 0, 'a'},
|
||||||
{"no-color", no_argument, 0, 'n'},
|
{"no-color", no_argument, 0, 'n'},
|
||||||
|
{"loopback", required_argument, 0, 'l'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
int c = getopt_long(argc, argv, "?do:k:zra:n", long_options, NULL);
|
int c = getopt_long(argc, argv, "?do:k:zra:nl:", long_options, NULL);
|
||||||
if(c == -1)
|
if(c == -1)
|
||||||
break;
|
break;
|
||||||
switch(c)
|
switch(c)
|
||||||
{
|
{
|
||||||
case -1:
|
case -1:
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
if(loopback)
|
||||||
|
bug("Only one loopback file can be specified !\n");
|
||||||
|
loopback = optarg;
|
||||||
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
enable_color(false);
|
enable_color(false);
|
||||||
break;
|
break;
|
||||||
|
|
@ -761,7 +178,7 @@ int main(int argc, char **argv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'r':
|
case 'r':
|
||||||
g_raw_mode = true;
|
raw_mode = true;
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
{
|
{
|
||||||
|
|
@ -788,24 +205,26 @@ int main(int argc, char **argv)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *sb_file = argv[optind];
|
const char *sb_filename = argv[optind];
|
||||||
FILE *fd = fopen(sb_file, "rb");
|
|
||||||
if(fd == NULL)
|
|
||||||
bug("Cannot open input file\n");
|
|
||||||
fseek(fd, 0, SEEK_END);
|
|
||||||
size_t size = ftell(fd);
|
|
||||||
fseek(fd, 0, SEEK_SET);
|
|
||||||
|
|
||||||
g_buf = xmalloc(size);
|
struct sb_file_t *file = sb_read_file(sb_filename, raw_mode, NULL, sb_printf);
|
||||||
if(fread(g_buf, 1, size, fd) != size) /* load the whole file into memory */
|
if(g_debug)
|
||||||
bugp("reading firmware");
|
{
|
||||||
|
color(GREY);
|
||||||
|
printf("[Debug output]\n");
|
||||||
|
sb_dump(file, NULL, sb_printf);
|
||||||
|
}
|
||||||
|
if(loopback)
|
||||||
|
{
|
||||||
|
/* sb_read_file will fill real key and IV but we don't want to override
|
||||||
|
* them when looping back otherwise the output will be inconsistent and
|
||||||
|
* garbage */
|
||||||
|
free(file->real_key);
|
||||||
|
file->real_key = NULL;
|
||||||
|
free(file->crypto_iv);
|
||||||
|
file->crypto_iv = NULL;
|
||||||
|
sb_write_file(file, loopback);
|
||||||
|
}
|
||||||
|
|
||||||
fclose(fd);
|
|
||||||
|
|
||||||
extract(size);
|
|
||||||
|
|
||||||
color(OFF);
|
|
||||||
|
|
||||||
free(g_buf);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue