diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/sbtools/dbparser.c | 10 | ||||
-rw-r--r-- | utils/sbtools/elf.c | 2 | ||||
-rw-r--r-- | utils/sbtools/elftosb.c | 10 | ||||
-rw-r--r-- | utils/sbtools/misc.h | 2 | ||||
-rw-r--r-- | utils/sbtools/sb.c | 680 | ||||
-rw-r--r-- | utils/sbtools/sb.h | 18 | ||||
-rw-r--r-- | utils/sbtools/sbtoelf.c | 673 |
7 files changed, 750 insertions, 645 deletions
diff --git a/utils/sbtools/dbparser.c b/utils/sbtools/dbparser.c index 2dcc21eeba..3cd0652d49 100644 --- a/utils/sbtools/dbparser.c +++ b/utils/sbtools/dbparser.c @@ -24,15 +24,7 @@ #include <stdint.h> #include <string.h> #include "dbparser.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) +#include "misc.h" enum lexem_type_t { diff --git a/utils/sbtools/elf.c b/utils/sbtools/elf.c index 718cb58b05..91b5d74848 100644 --- a/utils/sbtools/elf.c +++ b/utils/sbtools/elf.c @@ -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, elf_printf_fn_t printf, void *user) { + (void) printf; + Elf32_Ehdr ehdr; uint32_t phnum = 0; struct elf_section_t *sec = params->first_section; diff --git a/utils/sbtools/elftosb.c b/utils/sbtools/elftosb.c index c1a86ea23b..b8d68b82e7 100644 --- a/utils/sbtools/elftosb.c +++ b/utils/sbtools/elftosb.c @@ -436,8 +436,14 @@ int main(int argc, char **argv) sb_file->real_key = &real_key.u.key; if(crypto_iv.method == CRYPTO_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; } diff --git a/utils/sbtools/misc.h b/utils/sbtools/misc.h index a685816047..9f14497680 100644 --- a/utils/sbtools/misc.h +++ b/utils/sbtools/misc.h @@ -28,7 +28,7 @@ #define STR(a) _STR(a) #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)) diff --git a/utils/sbtools/sb.c b/utils/sbtools/sb.c index 3921710a2d..99f953600d 100644 --- a/utils/sbtools/sb.c +++ b/utils/sbtools/sb.c @@ -21,6 +21,7 @@ #include <stdio.h> #include <time.h> #include <stdlib.h> +#include <ctype.h> #include "misc.h" #include "crypto.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 */ struct sb_section_t *sec = &sb->sections[i]; + sec->sec_size = 0; 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->image_size = sb->image_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_sections = sb->nr_sections; 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; generate_random_data(sb_hdr->rand_pad0, sizeof(sb_hdr->rand_pad0)); 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->product_ver = sb->product_ver; fix_version(&sb_hdr->product_ver); sb_hdr->component_ver = sb->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_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); } -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"); if(fd == NULL) @@ -424,3 +431,670 @@ void sb_produce_file(struct sb_file_t *sb, const char *filename) 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 +} diff --git a/utils/sbtools/sb.h b/utils/sbtools/sb.h index 27f5668d2e..548b3ef55a 100644 --- a/utils/sbtools/sb.h +++ b/utils/sbtools/sb.h @@ -24,6 +24,8 @@ #include <stdint.h> #include <stdbool.h> +#include "misc.h" + #define BLOCK_SIZE 16 /* All fields are in big-endian BCD */ @@ -161,12 +163,12 @@ struct sb_inst_t { uint8_t inst; /* SB_INST_* */ uint32_t size; + uint32_t addr; // <union> void *data; uint32_t pattern; - uint32_t addr; // </union> - uint32_t argument; // for call and jump + uint32_t argument; // for call, jump and mode /* for production use */ uint32_t padding_size; uint8_t *padding; @@ -194,6 +196,10 @@ struct sb_file_t uint8_t (*crypto_iv)[16]; 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_version_t product_ver; struct sb_version_t component_ver; @@ -201,6 +207,12 @@ struct sb_file_t 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__ */ diff --git a/utils/sbtools/sbtoelf.c b/utils/sbtools/sbtoelf.c index 24417dc88e..0199ffb4f5 100644 --- a/utils/sbtools/sbtoelf.c +++ b/utils/sbtools/sbtoelf.c @@ -56,19 +56,7 @@ /* globals */ -uint8_t *g_buf; /* file content */ 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, ...) { @@ -104,614 +92,34 @@ static void extract_elf_section(struct elf_params_t *elf, int count, const char fclose(fd); } -static void extract_section(int data_sec, char name[5], byte *buf, int size, const char *indent) -{ - 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) +static void usage(void) { printf("Usage: sbtoelf [options] sb-file\n"); printf("Options:\n"); printf(" -?/--help\tDisplay this message\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(" -z\t\tAdd zero key\n"); printf(" -r\t\tUse raw command mode\n"); printf(" -a/--add-key <key>\tAdd single key (hex or usbotp)\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); } +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 = { .method = CRYPTO_KEY, @@ -720,6 +128,9 @@ static struct crypto_key_t g_zero_key = int main(int argc, char **argv) { + bool raw_mode = false; + const char *loopback = NULL; + while(1) { static struct option long_options[] = @@ -728,16 +139,22 @@ int main(int argc, char **argv) {"debug", no_argument, 0, 'd'}, {"add-key", required_argument, 0, 'a'}, {"no-color", no_argument, 0, 'n'}, + {"loopback", required_argument, 0, 'l'}, {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) 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; @@ -761,7 +178,7 @@ int main(int argc, char **argv) break; } case 'r': - g_raw_mode = true; + raw_mode = true; break; case 'a': { @@ -788,24 +205,26 @@ int main(int argc, char **argv) return 1; } - const char *sb_file = 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); - if(fread(g_buf, 1, size, fd) != size) /* load the whole file into memory */ - bugp("reading firmware"); - - fclose(fd); - - extract(size); + const char *sb_filename = argv[optind]; - color(OFF); - - free(g_buf); + struct sb_file_t *file = sb_read_file(sb_filename, raw_mode, NULL, sb_printf); + if(g_debug) + { + 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); + } + return 0; } |