da44fd6f99
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30544 a1c6a512-1295-4272-9138-f99709370657
786 lines
24 KiB
C
786 lines
24 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2010 Bertrik Sikken
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* .sb file parser and chunk extractor
|
|
*
|
|
* Based on amsinfo, which is
|
|
* Copyright © 2008 Rafaël Carré <rafael.carre@gmail.com>
|
|
*/
|
|
|
|
#define _ISOC99_SOURCE /* snprintf() */
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <strings.h>
|
|
|
|
#include "crypto.h"
|
|
#include "elf.h"
|
|
#include "sb.h"
|
|
|
|
#if 1 /* ANSI colors */
|
|
|
|
# define color(a) printf("%s",a)
|
|
char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
|
|
|
|
char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
|
|
char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
|
|
char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
|
|
char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
|
|
char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
|
|
|
|
#else
|
|
/* disable colors */
|
|
# define color(a)
|
|
#endif
|
|
|
|
#define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0)
|
|
#define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
|
|
|
|
/* 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)
|
|
|
|
/* globals */
|
|
|
|
uint8_t *g_buf; /* file content */
|
|
#define PREFIX_SIZE 128
|
|
char out_prefix[PREFIX_SIZE];
|
|
const char *key_file;
|
|
|
|
char *s_getenv(const char *name)
|
|
{
|
|
char *s = getenv(name);
|
|
return s ? s : "";
|
|
}
|
|
|
|
void *xmalloc(size_t s) /* malloc helper, used in elf.c */
|
|
{
|
|
void * r = malloc(s);
|
|
if(!r) bugp("malloc");
|
|
return r;
|
|
}
|
|
|
|
static void print_hex(byte *data, int len, bool newline)
|
|
{
|
|
for(int i = 0; i < len; i++)
|
|
printf("%02X ", data[i]);
|
|
if(newline)
|
|
printf("\n");
|
|
}
|
|
|
|
static int convxdigit(char digit, byte *val)
|
|
{
|
|
if(digit >= '0' && digit <= '9')
|
|
{
|
|
*val = digit - '0';
|
|
return 0;
|
|
}
|
|
else if(digit >= 'A' && digit <= 'F')
|
|
{
|
|
*val = digit - 'A' + 10;
|
|
return 0;
|
|
}
|
|
else if(digit >= 'a' && digit <= 'f')
|
|
{
|
|
*val = digit - 'a' + 10;
|
|
return 0;
|
|
}
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
typedef byte (*key_array_t)[16];
|
|
|
|
static key_array_t read_keys(int num_keys)
|
|
{
|
|
int size;
|
|
struct stat st;
|
|
int fd = open(key_file,O_RDONLY);
|
|
if(fd == -1)
|
|
bugp("opening key file failed");
|
|
if(fstat(fd,&st) == -1)
|
|
bugp("key file stat() failed");
|
|
size = st.st_size;
|
|
char *buf = xmalloc(size);
|
|
if(read(fd, buf, size) != (ssize_t)size)
|
|
bugp("reading key file");
|
|
close(fd);
|
|
|
|
key_array_t keys = xmalloc(sizeof(byte[16]) * num_keys);
|
|
int pos = 0;
|
|
for(int i = 0; i < num_keys; i++)
|
|
{
|
|
/* skip ws */
|
|
while(pos < size && isspace(buf[pos]))
|
|
pos++;
|
|
/* enough space ? */
|
|
if((pos + 32) > size)
|
|
bugp("invalid key file (not enough keys)");
|
|
for(int j = 0; j < 16; j++)
|
|
{
|
|
byte a, b;
|
|
if(convxdigit(buf[pos + 2 * j], &a) || convxdigit(buf[pos + 2 * j + 1], &b))
|
|
bugp(" invalid key, it should be a 128-bit key written in hexadecimal\n");
|
|
keys[i][j] = (a << 4) | b;
|
|
}
|
|
pos += 32;
|
|
}
|
|
free(buf);
|
|
|
|
return keys;
|
|
}
|
|
|
|
#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round))
|
|
|
|
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_write(void *user, uint32_t addr, const void *buf, size_t count)
|
|
{
|
|
FILE *f = user;
|
|
fseek(f, addr, SEEK_SET);
|
|
fwrite(buf, count, 1, f);
|
|
}
|
|
|
|
static void extract_elf_section(struct elf_params_t *elf, int count, const char *prefix,
|
|
const char *indent)
|
|
{
|
|
char *filename = xmalloc(strlen(prefix) + 32);
|
|
sprintf(filename, "%s.%d.elf", prefix, count);
|
|
printf("%swrite %s\n", indent, filename);
|
|
|
|
FILE *fd = fopen(filename, "wb");
|
|
free(filename);
|
|
|
|
if(fd == NULL)
|
|
return ;
|
|
elf_write_file(elf, elf_write, fd);
|
|
fclose(fd);
|
|
}
|
|
|
|
static void extract_section(int data_sec, char name[5], byte *buf, int size, const char *indent)
|
|
{
|
|
char filename[PREFIX_SIZE + 32];
|
|
snprintf(filename, sizeof filename, "%s%s.bin", out_prefix, name);
|
|
FILE *fd = fopen(filename, "wb");
|
|
if (fd != NULL) {
|
|
fwrite(buf, size, 1, fd);
|
|
fclose(fd);
|
|
}
|
|
if(data_sec)
|
|
return;
|
|
|
|
snprintf(filename, sizeof filename, "%s%s", 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);
|
|
// unsure about rounding
|
|
pos = ROUND_UP(pos, 16);
|
|
}
|
|
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);
|
|
// fixme: useless as pos is a multiple of 16 and fill struct is 4-bytes wide ?
|
|
pos = ROUND_UP(pos, 16);
|
|
}
|
|
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);
|
|
// fixme: useless as pos is a multiple of 16 and call struct is 4-bytes wide ?
|
|
pos = ROUND_UP(pos, 16);
|
|
}
|
|
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
|
|
{
|
|
color(RED);
|
|
printf("Unknown instruction %d at address 0x%08lx\n", hdr->opcode, (unsigned long)pos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 */
|
|
key_array_t keys = NULL; /* array of 16-bytes keys */
|
|
byte real_key[16];
|
|
if(sb_header->nr_keys > 0)
|
|
{
|
|
keys = read_keys(sb_header->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_hex(keys[i], 16, 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);
|
|
cbc_mac(g_buf, NULL, sb_header->header_size + sb_header->nr_sections,
|
|
keys[i], zero, &computed_cbc_mac, 1);
|
|
color(RED);
|
|
if(memcmp(dict_entry->hdr_cbc_mac, computed_cbc_mac, 16) == 0)
|
|
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 */
|
|
cbc_mac(dict_entry->key, decrypted_key, 1, keys[i], iv, NULL, 0);
|
|
printf(" Decrypted key : ");
|
|
color(YELLOW);
|
|
print_hex(decrypted_key, 16, false);
|
|
/* cross-check or copy */
|
|
if(i == 0)
|
|
memcpy(real_key, decrypted_key, 16);
|
|
else if(memcmp(real_key, decrypted_key, 16) == 0)
|
|
{
|
|
color(RED);
|
|
printf(" Cross-Check Ok");
|
|
}
|
|
else
|
|
{
|
|
color(RED);
|
|
printf(" Cross-Check Failed");
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
/* sections */
|
|
if(strcasecmp(s_getenv("SB_RAW_CMD"), "YES") != 0)
|
|
{
|
|
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];
|
|
memcpy(iv, g_buf, 16);
|
|
const char *indent = " ";
|
|
while(true)
|
|
{
|
|
byte cmd[16];
|
|
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;
|
|
/* restart with IV */
|
|
memcpy(iv, g_buf, 16);
|
|
}
|
|
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");
|
|
}
|
|
|
|
int main(int argc, const char **argv)
|
|
{
|
|
int fd;
|
|
struct stat st;
|
|
if(argc != 3 && argc != 4)
|
|
{
|
|
printf("Usage: %s <firmware> <key file> [<out prefix>]\n",*argv);
|
|
printf("To use raw command mode, set environment variable SB_RAW_CMD to YES\n");
|
|
return 1;
|
|
}
|
|
|
|
if(argc == 4)
|
|
snprintf(out_prefix, PREFIX_SIZE, "%s", argv[3]);
|
|
else
|
|
strcpy(out_prefix, "");
|
|
|
|
if( (fd = open(argv[1], O_RDONLY)) == -1 )
|
|
bugp("opening firmware failed");
|
|
|
|
key_file = argv[2];
|
|
|
|
if(fstat(fd, &st) == -1)
|
|
bugp("firmware stat() failed");
|
|
|
|
g_buf = xmalloc(st.st_size);
|
|
if(read(fd, g_buf, st.st_size) != (ssize_t)st.st_size) /* load the whole file into memory */
|
|
bugp("reading firmware");
|
|
|
|
close(fd);
|
|
|
|
extract(st.st_size);
|
|
|
|
color(OFF);
|
|
|
|
free(g_buf);
|
|
return 0;
|
|
}
|