summaryrefslogtreecommitdiff
path: root/utils/sbtools/sbtoelf.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/sbtools/sbtoelf.c')
-rw-r--r--utils/sbtools/sbtoelf.c596
1 files changed, 596 insertions, 0 deletions
diff --git a/utils/sbtools/sbtoelf.c b/utils/sbtools/sbtoelf.c
new file mode 100644
index 0000000000..632c4b3eb9
--- /dev/null
+++ b/utils/sbtools/sbtoelf.c
@@ -0,0 +1,596 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Bertrik Sikken
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/*
23 * .sb file parser and chunk extractor
24 *
25 * Based on amsinfo, which is
26 * Copyright © 2008 Rafaël Carré <rafael.carre@gmail.com>
27 */
28
29#define _ISOC99_SOURCE /* snprintf() */
30#include <stdio.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <errno.h>
35#include <unistd.h>
36#include <stdlib.h>
37#include <inttypes.h>
38#include <string.h>
39#include <ctype.h>
40#include <time.h>
41
42#include "crypto.h"
43#include "elf.h"
44#include "sb.h"
45
46#if 1 /* ANSI colors */
47
48# define color(a) printf("%s",a)
49char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
50
51char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
52char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
53char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
54char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
55char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
56
57#else
58 /* disable colors */
59# define color(a)
60#endif
61
62#define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0)
63#define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
64
65/* all blocks are sized as a multiple of 0x1ff */
66#define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
67
68/* If you find a firmware that breaks the known format ^^ */
69#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)
70
71/* globals */
72
73size_t g_sz; /* file size */
74uint8_t *g_buf; /* file content */
75#define PREFIX_SIZE 128
76char out_prefix[PREFIX_SIZE];
77const char *key_file;
78
79void *xmalloc(size_t s) /* malloc helper, used in elf.c */
80{
81 void * r = malloc(s);
82 if(!r) bugp("malloc");
83 return r;
84}
85
86static void print_hex(byte *data, int len, bool newline)
87{
88 for(int i = 0; i < len; i++)
89 printf("%02X ", data[i]);
90 if(newline)
91 printf("\n");
92}
93
94int convxdigit(char digit, byte *val)
95{
96 if(digit >= '0' && digit <= '9')
97 {
98 *val = digit - '0';
99 return 0;
100 }
101 else if(digit >= 'A' && digit <= 'F')
102 {
103 *val = digit - 'A' + 10;
104 return 0;
105 }
106 else if(digit >= 'a' && digit <= 'f')
107 {
108 *val = digit - 'a' + 10;
109 return 0;
110 }
111 else
112 return 1;
113}
114
115typedef byte (*key_array_t)[16];
116
117static key_array_t read_keys(int num_keys)
118{
119 int size;
120 struct stat st;
121 int fd = open(key_file,O_RDONLY);
122 if(fd == -1)
123 bugp("opening key file failed");
124 if(fstat(fd,&st) == -1)
125 bugp("key file stat() failed");
126 size = st.st_size;
127 char *buf = xmalloc(size);
128 if(read(fd, buf, size) != (ssize_t)size)
129 bugp("reading key file");
130 close(fd);
131
132 key_array_t keys = xmalloc(sizeof(byte[16]) * num_keys);
133 int pos = 0;
134 for(int i = 0; i < num_keys; i++)
135 {
136 /* skip ws */
137 while(pos < size && isspace(buf[pos]))
138 pos++;
139 /* enough space ? */
140 if((pos + 32) > size)
141 bugp("invalid key file (not enough keys)");
142 for(int j = 0; j < 16; j++)
143 {
144 byte a, b;
145 if(convxdigit(buf[pos + 2 * j], &a) || convxdigit(buf[pos + 2 * j + 1], &b))
146 bugp(" invalid key, it should be a 128-bit key written in hexadecimal\n");
147 keys[i][j] = (a << 4) | b;
148 }
149 pos += 32;
150 }
151 free(buf);
152
153 return keys;
154}
155
156#define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round))
157
158static uint8_t instruction_checksum(struct sb_instruction_header_t *hdr)
159{
160 uint8_t sum = 90;
161 byte *ptr = (byte *)hdr;
162 for(int i = 1; i < 16; i++)
163 sum += ptr[i];
164 return sum;
165}
166
167static void elf_write(void *user, uint32_t addr, const void *buf, size_t count)
168{
169 FILE *f = user;
170 fseek(f, addr, SEEK_SET);
171 fwrite(buf, count, 1, f);
172}
173
174static void extract_elf_section(struct elf_params_t *elf, int count, const char *prefix,
175 const char *indent)
176{
177 char *filename = xmalloc(strlen(prefix) + 32);
178 sprintf(filename, "%s.%d.elf", prefix, count);
179 printf("%swrite %s\n", indent, filename);
180
181 FILE *fd = fopen(filename, "wb");
182 free(filename);
183
184 if(fd == NULL)
185 return ;
186 elf_output(elf, elf_write, fd);
187 fclose(fd);
188}
189
190static void extract_section(int data_sec, char name[5], byte *buf, int size, const char *indent)
191{
192 char filename[PREFIX_SIZE + 32];
193 snprintf(filename, sizeof filename, "%s%s.bin", out_prefix, name);
194 FILE *fd = fopen(filename, "wb");
195 if (fd != NULL) {
196 fwrite(buf, size, 1, fd);
197 fclose(fd);
198 }
199 if(data_sec)
200 return;
201
202 snprintf(filename, sizeof filename, "%s%s", out_prefix, name);
203
204 /* elf construction */
205 struct elf_params_t elf;
206 elf_init(&elf);
207 int elf_count = 0;
208 /* Pretty print the content */
209 int pos = 0;
210 while(pos < size)
211 {
212 struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)&buf[pos];
213 printf("%s", indent);
214 uint8_t checksum = instruction_checksum(hdr);
215 if(checksum != hdr->checksum)
216 {
217 color(GREY);
218 printf("[Bad checksum]");
219 }
220
221 if(hdr->opcode == SB_INST_LOAD)
222 {
223 struct sb_instruction_load_t *load = (struct sb_instruction_load_t *)&buf[pos];
224 color(RED);
225 printf("LOAD");
226 color(OFF);printf(" | ");
227 color(BLUE);
228 printf("addr=0x%08x", load->addr);
229 color(OFF);printf(" | ");
230 color(GREEN);
231 printf("len=0x%08x", load->len);
232 color(OFF);printf(" | ");
233 color(YELLOW);
234 printf("crc=0x%08x", load->crc);
235 /* data is padded to 16-byte boundary with random data and crc'ed with it */
236 uint32_t computed_crc = crc(&buf[pos + sizeof(struct sb_instruction_load_t)],
237 ROUND_UP(load->len, 16));
238 color(RED);
239 if(load->crc == computed_crc)
240 printf(" Ok\n");
241 else
242 printf(" Failed (crc=0x%08x)\n", computed_crc);
243
244 /* elf construction */
245 elf_add_load_section(&elf, load->addr, load->len,
246 &buf[pos + sizeof(struct sb_instruction_load_t)]);
247
248 pos += load->len + sizeof(struct sb_instruction_load_t);
249 // unsure about rounding
250 pos = ROUND_UP(pos, 16);
251 }
252 else if(hdr->opcode == SB_INST_FILL)
253 {
254 struct sb_instruction_fill_t *fill = (struct sb_instruction_fill_t *)&buf[pos];
255 color(RED);
256 printf("FILL");
257 color(OFF);printf(" | ");
258 color(BLUE);
259 printf("addr=0x%08x", fill->addr);
260 color(OFF);printf(" | ");
261 color(GREEN);
262 printf("len=0x%08x", fill->len);
263 color(OFF);printf(" | ");
264 color(YELLOW);
265 printf("pattern=0x%08x\n", fill->pattern);
266 color(OFF);
267
268 /* elf construction */
269 elf_add_fill_section(&elf, fill->addr, fill->len, fill->pattern);
270
271 pos += sizeof(struct sb_instruction_fill_t);
272 // fixme: useless as pos is a multiple of 16 and fill struct is 4-bytes wide ?
273 pos = ROUND_UP(pos, 16);
274 }
275 else if(hdr->opcode == SB_INST_CALL ||
276 hdr->opcode == SB_INST_JUMP)
277 {
278 int is_call = (hdr->opcode == SB_INST_CALL);
279 struct sb_instruction_call_t *call = (struct sb_instruction_call_t *)&buf[pos];
280 color(RED);
281 if(is_call)
282 printf("CALL");
283 else
284 printf("JUMP");
285 color(OFF);printf(" | ");
286 color(BLUE);
287 printf("addr=0x%08x", call->addr);
288 color(OFF);printf(" | ");
289 color(GREEN);
290 printf("arg=0x%08x\n", call->arg);
291 color(OFF);
292
293 /* elf construction */
294 elf_set_start_addr(&elf, call->addr);
295 extract_elf_section(&elf, elf_count++, filename, indent);
296 elf_release(&elf);
297 elf_init(&elf);
298
299 pos += sizeof(struct sb_instruction_call_t);
300 // fixme: useless as pos is a multiple of 16 and call struct is 4-bytes wide ?
301 pos = ROUND_UP(pos, 16);
302 }
303 else
304 {
305 color(RED);
306 printf("Unknown instruction %d at address 0x%08lx\n", hdr->opcode, (unsigned long)pos);
307 break;
308 }
309 }
310
311 if(!elf_is_empty(&elf))
312 extract_elf_section(&elf, elf_count++, filename, indent);
313 elf_release(&elf);
314}
315
316static void extract(unsigned long filesize)
317{
318 struct sha_1_params_t sha_1_params;
319 /* Basic header info */
320 struct sb_header_t *sb_header = (struct sb_header_t *)g_buf;
321
322 if(memcmp(sb_header->signature, "STMP", 4) != 0)
323 bugp("Bad signature");
324 if(sb_header->image_size * BLOCK_SIZE != filesize)
325 bugp("File size mismatch");
326 if(sb_header->header_size * BLOCK_SIZE != sizeof(struct sb_header_t))
327 bugp("Bad header size");
328 if(sb_header->major_ver != IMAGE_MAJOR_VERSION ||
329 sb_header->minor_ver != IMAGE_MINOR_VERSION)
330 bugp("Bad file format version");
331 if(sb_header->sec_hdr_size * BLOCK_SIZE != sizeof(struct sb_section_header_t))
332 bugp("Bad section header size");
333
334 color(BLUE);
335 printf("Basic info:\n");
336 color(GREEN);
337 printf(" Header SHA-1: ");
338 byte *hdr_sha1 = sb_header->sha1_header;
339 color(YELLOW);
340 print_hex(hdr_sha1, 20, false);
341 /* Check SHA1 sum */
342 byte computed_sha1[20];
343 sha_1_init(&sha_1_params);
344 sha_1_update(&sha_1_params, &sb_header->signature[0],
345 sizeof(struct sb_header_t) - sizeof(sb_header->sha1_header));
346 sha_1_finish(&sha_1_params);
347 sha_1_output(&sha_1_params, computed_sha1);
348 color(RED);
349 if(memcmp(hdr_sha1, computed_sha1, 20) == 0)
350 printf(" Ok\n");
351 else
352 printf(" Failed\n");
353 color(GREEN);
354 printf(" Flags: ");
355 color(YELLOW);
356 printf("%x\n", sb_header->flags);
357 color(GREEN);
358 printf(" Total file size : ");
359 color(YELLOW);
360 printf("%ld\n", filesize);
361
362 /* Sizes and offsets */
363 color(BLUE);
364 printf("Sizes and offsets:\n");
365 color(GREEN);
366 printf(" # of encryption keys = ");
367 color(YELLOW);
368 printf("%d\n", sb_header->nr_keys);
369 color(GREEN);
370 printf(" # of sections = ");
371 color(YELLOW);
372 printf("%d\n", sb_header->nr_sections);
373
374 /* Versions */
375 color(BLUE);
376 printf("Versions\n");
377 color(GREEN);
378
379 printf(" Random 1: ");
380 color(YELLOW);
381 print_hex(sb_header->rand_pad0, sizeof(sb_header->rand_pad0), true);
382 color(GREEN);
383 printf(" Random 2: ");
384 color(YELLOW);
385 print_hex(sb_header->rand_pad1, sizeof(sb_header->rand_pad1), true);
386
387 uint64_t micros = sb_header->timestamp;
388 time_t seconds = (micros / (uint64_t)1000000L);
389 struct tm tm_base = {0, 0, 0, 1, 0, 100, 0, 0, 1, 0, NULL}; /* 2000/1/1 0:00:00 */
390 seconds += mktime(&tm_base);
391 struct tm *time = gmtime(&seconds);
392 color(GREEN);
393 printf(" Creation date/time = ");
394 color(YELLOW);
395 printf("%s", asctime(time));
396
397 color(GREEN);
398 printf(" Product version = ");
399 color(YELLOW);
400 printf("%X.%X.%X\n", sb_header->product_ver.major,
401 sb_header->product_ver.minor, sb_header->product_ver.revision);
402 color(GREEN);
403 printf(" Component version = ");
404 color(YELLOW);
405 printf("%X.%X.%X\n", sb_header->component_ver.major,
406 sb_header->component_ver.minor, sb_header->component_ver.revision);
407
408 /* encryption cbc-mac */
409 key_array_t keys = NULL; /* array of 16-bytes keys */
410 byte real_key[16];
411 if(sb_header->nr_keys > 0)
412 {
413 keys = read_keys(sb_header->nr_keys);
414 color(BLUE);
415 printf("Encryption data\n");
416 for(int i = 0; i < sb_header->nr_keys; i++)
417 {
418 color(RED);
419 printf(" Key %d: ", i);
420 print_hex(keys[i], 16, true);
421 color(GREEN);
422 printf(" CBC-MAC of headers: ");
423
424 uint32_t ofs = sizeof(struct sb_header_t)
425 + sizeof(struct sb_section_header_t) * sb_header->nr_sections
426 + sizeof(struct sb_key_dictionary_entry_t) * i;
427 struct sb_key_dictionary_entry_t *dict_entry =
428 (struct sb_key_dictionary_entry_t *)&g_buf[ofs];
429 /* cbc mac */
430 color(YELLOW);
431 print_hex(dict_entry->hdr_cbc_mac, 16, false);
432 /* check it */
433 byte computed_cbc_mac[16];
434 byte zero[16];
435 memset(zero, 0, 16);
436 cbc_mac(g_buf, NULL, sb_header->header_size + sb_header->nr_sections,
437 keys[i], zero, &computed_cbc_mac, 1);
438 color(RED);
439 if(memcmp(dict_entry->hdr_cbc_mac, computed_cbc_mac, 16) == 0)
440 printf(" Ok\n");
441 else
442 printf(" Failed\n");
443 color(GREEN);
444
445 printf(" Encrypted key : ");
446 color(YELLOW);
447 print_hex(dict_entry->key, 16, true);
448 color(GREEN);
449 /* decrypt */
450 byte decrypted_key[16];
451 byte iv[16];
452 memcpy(iv, g_buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */
453 cbc_mac(dict_entry->key, decrypted_key, 1, keys[i], iv, NULL, 0);
454 printf(" Decrypted key : ");
455 color(YELLOW);
456 print_hex(decrypted_key, 16, false);
457 /* cross-check or copy */
458 if(i == 0)
459 memcpy(real_key, decrypted_key, 16);
460 else if(memcmp(real_key, decrypted_key, 16) == 0)
461 {
462 color(RED);
463 printf(" Cross-Check Ok");
464 }
465 else
466 {
467 color(RED);
468 printf(" Cross-Check Failed");
469 }
470 printf("\n");
471 }
472 }
473
474 /* sections */
475 color(BLUE);
476 printf("Sections\n");
477
478 for(int i = 0; i < sb_header->nr_sections; i++)
479 {
480 uint32_t ofs = sb_header->header_size * BLOCK_SIZE + i * sizeof(struct sb_section_header_t);
481 struct sb_section_header_t *sec_hdr = (struct sb_section_header_t *)&g_buf[ofs];
482
483 char name[5];
484 name[0] = (sec_hdr->identifier >> 24) & 0xff;
485 name[1] = (sec_hdr->identifier >> 16) & 0xff;
486 name[2] = (sec_hdr->identifier >> 8) & 0xff;
487 name[3] = sec_hdr->identifier & 0xff;
488 for(int i = 0; i < 4; i++)
489 if(!isprint(name[i]))
490 name[i] = '_';
491 name[4] = 0;
492 int pos = sec_hdr->offset * BLOCK_SIZE;
493 int size = sec_hdr->size * BLOCK_SIZE;
494 int data_sec = !(sec_hdr->flags & SECTION_BOOTABLE);
495 int encrypted = !(sec_hdr->flags & SECTION_CLEARTEXT);
496
497 color(GREEN);
498 printf(" Chunk ");
499 color(YELLOW);
500 printf("'%s'\n", name);
501 color(GREEN);
502 printf(" pos = ");
503 color(YELLOW);
504 printf("%8x - %8x\n", pos, pos+size);
505 color(GREEN);
506 printf(" len = ");
507 color(YELLOW);
508 printf("%8x\n", size);
509 color(GREEN);
510 printf(" flags = ");
511 color(YELLOW);
512 printf("%8x", sec_hdr->flags);
513 color(RED);
514 if(data_sec)
515 printf(" Data Section");
516 else
517 printf(" Boot Section");
518 if(encrypted)
519 printf(" (Encrypted)");
520 printf("\n");
521
522 /* save it */
523 byte *sec = xmalloc(size);
524 if(encrypted)
525 cbc_mac(g_buf + pos, sec, size / BLOCK_SIZE, real_key, g_buf, NULL, 0);
526 else
527 memcpy(sec, g_buf + pos, size);
528
529 extract_section(data_sec, name, sec, size, " ");
530 free(sec);
531 }
532
533 /* final signature */
534 color(BLUE);
535 printf("Final signature:\n");
536 color(GREEN);
537 printf(" Encrypted signature:\n");
538 color(YELLOW);
539 byte *encrypted_block = &g_buf[filesize - 32];
540 printf(" ");
541 print_hex(encrypted_block, 16, true);
542 printf(" ");
543 print_hex(encrypted_block + 16, 16, true);
544 /* decrypt it */
545 byte decrypted_block[32];
546 cbc_mac(encrypted_block, decrypted_block, 2, real_key, g_buf, NULL, 0);
547 color(GREEN);
548 printf(" Decrypted SHA-1:\n ");
549 color(YELLOW);
550 print_hex(decrypted_block, 20, false);
551 /* check it */
552 sha_1_init(&sha_1_params);
553 sha_1_update(&sha_1_params, g_buf, filesize - 32);
554 sha_1_finish(&sha_1_params);
555 sha_1_output(&sha_1_params, computed_sha1);
556 color(RED);
557 if(memcmp(decrypted_block, computed_sha1, 20) == 0)
558 printf(" Ok\n");
559 else
560 printf(" Failed\n");
561}
562
563int main(int argc, const char **argv)
564{
565 int fd;
566 struct stat st;
567 if(argc != 3 && argc != 4)
568 bug("Usage: %s <firmware> <key file> [<out prefix>]\n",*argv);
569
570 if(argc == 4)
571 snprintf(out_prefix, PREFIX_SIZE, "%s", argv[3]);
572 else
573 strcpy(out_prefix, "");
574
575 if( (fd = open(argv[1], O_RDONLY)) == -1 )
576 bugp("opening firmware failed");
577
578 key_file = argv[2];
579
580 if(fstat(fd, &st) == -1)
581 bugp("firmware stat() failed");
582 g_sz = st.st_size;
583
584 g_buf = xmalloc(g_sz);
585 if(read(fd, g_buf, g_sz) != (ssize_t)g_sz) /* load the whole file into memory */
586 bugp("reading firmware");
587
588 close(fd);
589
590 extract(st.st_size);
591
592 color(OFF);
593
594 free(g_buf);
595 return 0;
596}