Don't need that honking huge header, so out it goes.
[kugel-rb.git] / utils / sbinfo / sbinfo.c
blobd065fc9eed225da8173c418cadb8a88d1f331ecc
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2010 Bertrik Sikken
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 * .sb file parser and chunk extractor
25 * Based on amsinfo, which is
26 * Copyright © 2008 Rafaël Carré <rafael.carre@gmail.com>
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>
42 #include "crypto.h"
43 #include "elf.h"
45 #if 1 /* ANSI colors */
47 # define color(a) printf("%s",a)
48 char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
50 char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
51 char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
52 char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
53 char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
54 char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
56 #else
57 /* disable colors */
58 # define color(a)
59 #endif
61 #define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0)
62 #define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
64 /* byte swapping */
65 #define get32le(a) ((uint32_t) \
66 ( buf[a+3] << 24 | buf[a+2] << 16 | buf[a+1] << 8 | buf[a] ))
67 #define get16le(a) ((uint16_t)( buf[a+1] << 8 | buf[a] ))
69 /* all blocks are sized as a multiple of 0x1ff */
70 #define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
72 /* If you find a firmware that breaks the known format ^^ */
73 #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)
75 /* globals */
77 size_t sz; /* file size */
78 uint8_t *buf; /* file content */
79 #define PREFIX_SIZE 128
80 char out_prefix[PREFIX_SIZE];
81 const char *key_file;
83 #define SB_INST_NOP 0x0
84 #define SB_INST_TAG 0x1
85 #define SB_INST_LOAD 0x2
86 #define SB_INST_FILL 0x3
87 #define SB_INST_JUMP 0x4
88 #define SB_INST_CALL 0x5
89 #define SB_INST_MODE 0x6
91 struct sb_instruction_header_t
93 uint8_t checksum;
94 uint8_t opcode;
95 uint16_t zero_except_for_tag;
96 } __attribute__((packed));
98 struct sb_instruction_load_t
100 struct sb_instruction_header_t hdr;
101 uint32_t addr;
102 uint32_t len;
103 uint32_t crc;
104 } __attribute__((packed));
106 struct sb_instruction_fill_t
108 struct sb_instruction_header_t hdr;
109 uint32_t addr;
110 uint32_t len;
111 uint32_t pattern;
112 } __attribute__((packed));
114 struct sb_instruction_call_t
116 struct sb_instruction_header_t hdr;
117 uint32_t addr;
118 uint32_t zero;
119 uint32_t arg;
120 } __attribute__((packed));
122 void *xmalloc(size_t s) /* malloc helper, used in elf.c */
124 void * r = malloc(s);
125 if(!r) bugp("malloc");
126 return r;
129 static char getchr(int offset)
131 char c;
132 c = buf[offset];
133 return isprint(c) ? c : '_';
136 static void getstrle(char string[], int offset)
138 int i;
139 for (i = 0; i < 4; i++) {
140 string[i] = getchr(offset + 3 - i);
142 string[4] = 0;
145 static void getstrbe(char string[], int offset)
147 int i;
148 for (i = 0; i < 4; i++) {
149 string[i] = getchr(offset + i);
151 string[4] = 0;
154 static void printhex(int offset, int len)
156 int i;
158 for (i = 0; i < len; i++) {
159 printf("%02X ", buf[offset + i]);
161 printf("\n");
164 static void print_key(byte key[16])
166 for(int i = 0; i < 16; i++)
167 printf("%02X ", key[i]);
170 static void print_sha1(byte sha[20])
172 for(int i = 0; i < 20; i++)
173 printf("%02X ", sha[i]);
176 /* verify the firmware header */
177 static void check(unsigned long filesize)
179 /* check STMP marker */
180 char stmp[5];
181 getstrbe(stmp, 0x14);
182 assert(strcmp(stmp, "STMP") == 0);
183 color(GREEN);
185 /* get total size */
186 unsigned long totalsize = 16 * get32le(0x1C);
187 color(GREEN);
188 assert(filesize == totalsize);
191 int convxdigit(char digit, byte *val)
193 if(digit >= '0' && digit <= '9')
195 *val = digit - '0';
196 return 0;
198 else if(digit >= 'A' && digit <= 'F')
200 *val = digit - 'A' + 10;
201 return 0;
203 else if(digit >= 'a' && digit <= 'f')
205 *val = digit - 'a' + 10;
206 return 0;
208 else
209 return 1;
212 typedef byte (*key_array_t)[16];
214 static key_array_t read_keys(int num_keys)
216 int size;
217 struct stat st;
218 int fd = open(key_file,O_RDONLY);
219 if(fd == -1)
220 bugp("opening key file failed");
221 if(fstat(fd,&st) == -1)
222 bugp("key file stat() failed");
223 size = st.st_size;
224 char *buf = xmalloc(size);
225 if(read(fd,buf,sz)!=(ssize_t)size)
226 bugp("reading key file");
227 close(fd);
229 key_array_t keys = xmalloc(sizeof(byte[16]) * num_keys);
230 int pos = 0;
231 for(int i = 0; i < num_keys; i++)
233 /* skip ws */
234 while(pos < size && isspace(buf[pos]))
235 pos++;
236 /* enough space ? */
237 if((pos + 32) > size)
238 bugp("invalid key file (not enough keys)");
239 for(int j = 0; j < 16; j++)
241 byte a, b;
242 if(convxdigit(buf[pos + 2 * j], &a) || convxdigit(buf[pos + 2 * j + 1], &b))
243 bugp(" invalid key, it should be a 128-bit key written in hexadecimal\n");
244 keys[i][j] = (a << 4) | b;
246 pos += 32;
248 free(buf);
250 return keys;
253 #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round))
255 static uint8_t instruction_checksum(struct sb_instruction_header_t *hdr)
257 uint8_t sum = 90;
258 byte *ptr = (byte *)hdr;
259 for(int i = 1; i < 16; i++)
260 sum += ptr[i];
261 return sum;
264 static void elf_write(void *user, uint32_t addr, const void *buf, size_t count)
266 FILE *f = user;
267 fseek(f, addr, SEEK_SET);
268 fwrite(buf, count, 1, f);
271 static void extract_elf_section(struct elf_params_t *elf, int count, const char *prefix)
273 char *filename = xmalloc(strlen(prefix) + 32);
274 sprintf(filename, "%s.%d.elf", prefix, count);
275 printf("write %s\n", filename);
277 FILE *fd = fopen(filename, "wb");
278 free(filename);
280 if(fd == NULL)
281 return ;
282 elf_output(elf, elf_write, fd);
283 fclose(fd);
286 static void extract_section(int data_sec, char name[5], byte *buf, int size, const char *indent)
288 char filename[PREFIX_SIZE + 32];
289 snprintf(filename, sizeof filename, "%s%s.bin", out_prefix, name);
290 FILE *fd = fopen(filename, "wb");
291 if (fd != NULL) {
292 fwrite(buf, size, 1, fd);
293 fclose(fd);
295 if(data_sec)
296 return;
298 snprintf(filename, sizeof filename, "%s%s", out_prefix, name);
300 /* elf construction */
301 struct elf_params_t elf;
302 elf_init(&elf);
303 int elf_count = 0;
304 /* Pretty print the content */
305 int pos = 0;
306 while(pos < size)
308 struct sb_instruction_header_t *hdr = (struct sb_instruction_header_t *)&buf[pos];
309 printf("%s", indent);
310 uint8_t checksum = instruction_checksum(hdr);
311 if(checksum != hdr->checksum)
313 color(GREY);
314 printf("[Bad checksum]");
317 if(hdr->opcode == SB_INST_LOAD)
319 struct sb_instruction_load_t *load = (struct sb_instruction_load_t *)&buf[pos];
320 color(RED);
321 printf("LOAD");
322 color(OFF);printf(" | ");
323 color(BLUE);
324 printf("addr=0x%08x", load->addr);
325 color(OFF);printf(" | ");
326 color(GREEN);
327 printf("len=0x%08x", load->len);
328 color(OFF);printf(" | ");
329 color(YELLOW);
330 printf("crc=0x%08x", load->crc);
331 /* data is padded to 16-byte boundary with random data and crc'ed with it */
332 uint32_t computed_crc = crc(&buf[pos + sizeof(struct sb_instruction_load_t)],
333 ROUND_UP(load->len, 16));
334 color(RED);
335 if(load->crc == computed_crc)
336 printf(" Ok\n");
337 else
338 printf(" Failed (crc=0x%08x)\n", computed_crc);
340 /* elf construction */
341 elf_add_load_section(&elf, load->addr, load->len,
342 &buf[pos + sizeof(struct sb_instruction_load_t)]);
344 pos += load->len + sizeof(struct sb_instruction_load_t);
345 // unsure about rounding
346 pos = ROUND_UP(pos, 16);
348 else if(hdr->opcode == SB_INST_FILL)
350 struct sb_instruction_fill_t *fill = (struct sb_instruction_fill_t *)&buf[pos];
351 color(RED);
352 printf("FILL");
353 color(OFF);printf(" | ");
354 color(BLUE);
355 printf("addr=0x%08x", fill->addr);
356 color(OFF);printf(" | ");
357 color(GREEN);
358 printf("len=0x%08x", fill->len);
359 color(OFF);printf(" | ");
360 color(YELLOW);
361 printf("pattern=0x%08x\n", fill->pattern);
362 color(OFF);
364 /* elf construction */
365 elf_add_fill_section(&elf, fill->addr, fill->len, fill->pattern);
367 pos += sizeof(struct sb_instruction_fill_t);
368 // fixme: useless as pos is a multiple of 16 and fill struct is 4-bytes wide ?
369 pos = ROUND_UP(pos, 16);
371 else if(hdr->opcode == SB_INST_CALL ||
372 hdr->opcode == SB_INST_JUMP)
374 int is_call = (hdr->opcode == SB_INST_CALL);
375 struct sb_instruction_call_t *call = (struct sb_instruction_call_t *)&buf[pos];
376 color(RED);
377 if(is_call)
378 printf("CALL");
379 else
380 printf("JUMP");
381 color(OFF);printf(" | ");
382 color(BLUE);
383 printf("addr=0x%08x", call->addr);
384 color(OFF);printf(" | ");
385 color(GREEN);
386 printf("arg=0x%08x\n", call->arg);
387 color(OFF);
389 /* elf construction */
390 elf_set_start_addr(&elf, call->addr);
391 extract_elf_section(&elf, elf_count++, filename);
392 elf_release(&elf);
393 elf_init(&elf);
395 pos += sizeof(struct sb_instruction_call_t);
396 // fixme: useless as pos is a multiple of 16 and call struct is 4-bytes wide ?
397 pos = ROUND_UP(pos, 16);
399 else
401 color(RED);
402 printf("Unknown instruction %d at address 0x%08lx\n", hdr->opcode, (unsigned long)pos);
403 break;
407 if(!elf_is_empty(&elf))
408 extract_elf_section(&elf, elf_count++, filename);
409 elf_release(&elf);
412 static void extract(unsigned long filesize)
414 struct sha_1_params_t sha_1_params;
415 /* Basic header info */
416 color(BLUE);
417 printf("Basic info:\n");
418 color(GREEN);
419 printf("\tHeader SHA-1: ");
420 byte *hdr_sha1 = &buf[0];
421 color(YELLOW);
422 print_sha1(hdr_sha1);
423 /* Check SHA1 sum */
424 byte computed_sha1[20];
425 sha_1_init(&sha_1_params);
426 sha_1_update(&sha_1_params, &buf[0x14], 0x4C);
427 sha_1_finish(&sha_1_params);
428 sha_1_output(&sha_1_params, computed_sha1);
429 color(RED);
430 if(memcmp(hdr_sha1, computed_sha1, 20) == 0)
431 printf(" Ok\n");
432 else
433 printf(" Failed\n");
434 color(GREEN);
435 printf("\tFlags: ");
436 printhex(0x18, 4);
437 printf("\tTotal file size : %ld\n", filesize);
439 /* Sizes and offsets */
440 color(BLUE);
441 printf("Sizes and offsets:\n");
442 color(GREEN);
443 int num_enc = get16le(0x28);
444 printf("\t# of encryption keys = %d\n", num_enc);
445 int num_chunks = get16le(0x2E);
446 printf("\t# of chunk headers = %d\n", num_chunks);
448 /* Versions */
449 color(BLUE);
450 printf("Versions\n");
451 color(GREEN);
453 printf("\tRandom 1: ");
454 printhex(0x32, 6);
455 printf("\tRandom 2: ");
456 printhex(0x5A, 6);
458 uint64_t micros_l = get32le(0x38);
459 uint64_t micros_h = get32le(0x3c);
460 uint64_t micros = ((uint64_t)micros_h << 32) | micros_l;
461 time_t seconds = (micros / (uint64_t)1000000L);
462 seconds += 946684800; /* 2000/1/1 0:00:00 */
463 struct tm *time = gmtime(&seconds);
464 color(GREEN);
465 printf("\tCreation date/time = %s", asctime(time));
467 int p_maj = get32le(0x40);
468 int p_min = get32le(0x44);
469 int p_sub = get32le(0x48);
470 int c_maj = get32le(0x4C);
471 int c_min = get32le(0x50);
472 int c_sub = get32le(0x54);
473 color(GREEN);
474 printf("\tProduct version = %X.%X.%X\n", p_maj, p_min, p_sub);
475 printf("\tComponent version = %X.%X.%X\n", c_maj, c_min, c_sub);
477 /* encryption cbc-mac */
478 key_array_t keys = NULL; /* array of 16-bytes keys */
479 byte real_key[16];
480 if(num_enc > 0)
482 keys = read_keys(num_enc);
483 color(BLUE),
484 printf("Encryption data\n");
485 for(int i = 0; i < num_enc; i++)
487 color(RED);
488 printf("\tKey %d: ", i);
489 print_key(keys[i]);
490 printf("\n");
491 color(GREEN);
492 printf("\t\tCBC-MAC of headers: ");
493 /* copy the cbc mac */
494 byte hdr_cbc_mac[16];
495 memcpy(hdr_cbc_mac, &buf[0x60 + 16 * num_chunks + 32 * i], 16);
496 color(YELLOW);
497 print_key(hdr_cbc_mac);
498 /* check it */
499 byte computed_cbc_mac[16];
500 byte zero[16];
501 memset(zero, 0, 16);
502 cbc_mac(buf, NULL, 6 + num_chunks, keys[i], zero, &computed_cbc_mac, 1);
503 color(RED);
504 if(memcmp(hdr_cbc_mac, computed_cbc_mac, 16) == 0)
505 printf(" Ok\n");
506 else
507 printf(" Failed\n");
508 color(GREEN);
510 printf("\t\tEncrypted key : ");
511 byte (*encrypted_key)[16];
512 encrypted_key = (key_array_t)&buf[0x60 + 16 * num_chunks + 32 * i + 16];
513 color(YELLOW);
514 print_key(*encrypted_key);
515 printf("\n");
516 color(GREEN);
517 /* decrypt */
518 byte decrypted_key[16];
519 byte iv[16];
520 memcpy(iv, buf, 16); /* uses the first 16-bytes of SHA-1 sig as IV */
521 cbc_mac(*encrypted_key, decrypted_key, 1, keys[i], iv, NULL, 0);
522 printf("\t\tDecrypted key : ");
523 color(YELLOW);
524 print_key(decrypted_key);
525 /* cross-check or copy */
526 if(i == 0)
527 memcpy(real_key, decrypted_key, 16);
528 else if(memcmp(real_key, decrypted_key, 16) == 0)
530 color(RED);
531 printf(" Cross-Check Ok");
533 else
535 color(RED);
536 printf(" Cross-Check Failed");
538 printf("\n");
542 /* chunks */
543 color(BLUE);
544 printf("Chunks\n");
546 for (int i = 0; i < num_chunks; i++) {
547 uint32_t ofs = 0x60 + (i * 16);
549 char name[5];
550 getstrle(name, ofs + 0);
551 int pos = 16 * get32le(ofs + 4);
552 int size = 16 * get32le(ofs + 8);
553 int flags = get32le(ofs + 12);
554 int data_sec = (flags == 2);
555 int encrypted = !data_sec && (num_enc > 0);
557 color(GREEN);
558 printf("\tChunk '%s'\n", name);
559 printf("\t\tpos = %8x - %8x\n", pos, pos+size);
560 printf("\t\tlen = %8x\n", size);
561 printf("\t\tflags = %8x", flags);
562 color(RED);
563 if(data_sec)
564 printf(" Data Section");
565 else
566 printf(" Boot Section");
567 if(encrypted)
568 printf(" (Encrypted)");
569 printf("\n");
571 /* save it */
572 byte *sec = xmalloc(size);
573 if(encrypted)
574 cbc_mac(buf + pos, sec, size / 16, real_key, buf, NULL, 0);
575 else
576 memcpy(sec, buf + pos, size);
578 extract_section(data_sec, name, sec, size, "\t\t\t");
579 free(sec);
582 /* final signature */
583 color(BLUE);
584 printf("Final signature:\n");
585 color(GREEN);
586 printf("\tEncrypted signature:\n");
587 color(YELLOW);
588 printf("\t\t");
589 printhex(filesize - 32, 16);
590 printf("\t\t");
591 printhex(filesize - 16, 16);
592 /* decrypt it */
593 byte *encrypted_block = &buf[filesize - 32];
594 byte decrypted_block[32];
595 cbc_mac(encrypted_block, decrypted_block, 2, real_key, buf, NULL, 0);
596 color(GREEN);
597 printf("\tDecrypted SHA-1:\n\t\t");
598 color(YELLOW);
599 print_sha1(decrypted_block);
600 /* check it */
601 sha_1_init(&sha_1_params);
602 sha_1_update(&sha_1_params, buf, filesize - 32);
603 sha_1_finish(&sha_1_params);
604 sha_1_output(&sha_1_params, computed_sha1);
605 color(RED);
606 if(memcmp(decrypted_block, computed_sha1, 20) == 0)
607 printf(" Ok\n");
608 else
609 printf(" Failed\n");
612 int main(int argc, const char **argv)
614 int fd;
615 struct stat st;
616 if(argc != 3 && argc != 4)
617 bug("Usage: %s <firmware> <key file> [<out prefix>]\n",*argv);
619 if(argc == 4)
620 snprintf(out_prefix, PREFIX_SIZE, "%s", argv[3]);
621 else
622 strcpy(out_prefix, "");
624 if( (fd = open(argv[1],O_RDONLY)) == -1 )
625 bugp("opening firmware failed");
627 key_file = argv[2];
629 if(fstat(fd,&st) == -1)
630 bugp("firmware stat() failed");
631 sz = st.st_size;
633 buf=xmalloc(sz);
634 if(read(fd,buf,sz)!=(ssize_t)sz) /* load the whole file into memory */
635 bugp("reading firmware");
637 close(fd);
639 check(st.st_size); /* verify header and checksums */
640 extract(st.st_size); /* split in blocks */
642 color(OFF);
644 free(buf);
645 return 0;