2010-11-28 10:53:01 +00:00
/***************************************************************************
* __________ __ ___ .
* 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 <errno.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include <time.h>
2011-10-29 14:22:17 +00:00
# include <stdarg.h>
2011-06-09 09:39:21 +00:00
# include <strings.h>
2011-10-29 14:22:17 +00:00
# include <getopt.h>
2010-12-01 16:17:11 +00:00
2010-11-29 14:15:06 +00:00
# include "crypto.h"
2010-12-01 16:17:11 +00:00
# include "elf.h"
2011-04-16 18:22:37 +00:00
# include "sb.h"
2012-11-26 22:54:44 +00:00
# include "sb1.h"
2011-10-29 14:22:17 +00:00
# include "misc.h"
2010-11-28 10:53:01 +00:00
/* 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)
2011-10-29 14:22:17 +00:00
# define crypto_cbc(...) \
do { int ret = crypto_cbc ( __VA_ARGS__ ) ; \
if ( ret ! = CRYPTO_ERROR_SUCCESS ) \
bug ( " crypto_cbc error: %d \n " , ret ) ; \
} while ( 0 )
2010-11-28 10:53:01 +00:00
/* globals */
2012-11-27 23:17:15 +00:00
static char * g_out_prefix ;
static bool g_elf_simplify = true ;
2010-11-28 20:56:09 +00:00
2011-10-29 22:27:53 +00:00
static void elf_printf ( void * user , bool error , const char * fmt , . . . )
{
if ( ! g_debug & & ! error )
return ;
( void ) user ;
va_list args ;
va_start ( args , fmt ) ;
vprintf ( fmt , args ) ;
va_end ( args ) ;
}
2010-12-01 16:17:11 +00:00
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 ) ;
}
2011-11-01 11:26:16 +00:00
static void extract_elf_section ( struct elf_params_t * elf , int count , uint32_t id )
2010-12-01 16:17:11 +00:00
{
2011-11-01 11:26:16 +00:00
char name [ 5 ] ;
char * filename = xmalloc ( strlen ( g_out_prefix ) + 32 ) ;
sb_fill_section_name ( name , id ) ;
sprintf ( filename , " %s%s.%d.elf " , g_out_prefix , name , count ) ;
if ( g_debug )
printf ( " Write boot section %s to %s \n " , name , filename ) ;
2013-06-15 20:19:52 +00:00
2010-12-01 16:17:11 +00:00
FILE * fd = fopen ( filename , " wb " ) ;
free ( filename ) ;
2013-06-15 20:19:52 +00:00
2010-12-01 16:17:11 +00:00
if ( fd = = NULL )
2012-11-27 23:17:15 +00:00
return ;
if ( g_elf_simplify )
elf_simplify ( elf ) ;
2011-10-29 22:27:53 +00:00
elf_write_file ( elf , elf_write , elf_printf , fd ) ;
2010-12-01 16:17:11 +00:00
fclose ( fd ) ;
}
2011-11-01 11:26:16 +00:00
static void extract_sb_section ( struct sb_section_t * sec )
{
if ( sec - > is_data )
{
char sec_name [ 5 ] ;
char * filename = xmalloc ( strlen ( g_out_prefix ) + 32 ) ;
sb_fill_section_name ( sec_name , sec - > identifier ) ;
sprintf ( filename , " %s%s.bin " , g_out_prefix , sec_name ) ;
FILE * fd = fopen ( filename , " wb " ) ;
if ( fd = = NULL )
bugp ( " Cannot open %s for writing \n " , filename ) ;
if ( g_debug )
printf ( " Write data section %s to %s \n " , sec_name , filename ) ;
free ( filename ) ;
2013-06-15 20:19:52 +00:00
2011-11-01 11:26:16 +00:00
for ( int j = 0 ; j < sec - > nr_insts ; j + + )
{
assert ( sec - > insts [ j ] . inst = = SB_INST_DATA ) ;
fwrite ( sec - > insts [ j ] . data , sec - > insts [ j ] . size , 1 , fd ) ;
}
fclose ( fd ) ;
}
int elf_count = 0 ;
struct elf_params_t elf ;
elf_init ( & elf ) ;
for ( int i = 0 ; i < sec - > nr_insts ; i + + )
{
struct sb_inst_t * inst = & sec - > insts [ i ] ;
switch ( inst - > inst )
{
case SB_INST_LOAD :
elf_add_load_section ( & elf , inst - > addr , inst - > size , inst - > data ) ;
break ;
case SB_INST_FILL :
elf_add_fill_section ( & elf , inst - > addr , inst - > size , inst - > pattern ) ;
break ;
case SB_INST_CALL :
case SB_INST_JUMP :
elf_set_start_addr ( & elf , inst - > addr ) ;
extract_elf_section ( & elf , elf_count + + , sec - > identifier ) ;
elf_release ( & elf ) ;
elf_init ( & elf ) ;
break ;
default :
/* ignore mode and nop */
break ;
}
}
if ( ! elf_is_empty ( & elf ) )
extract_elf_section ( & elf , elf_count , sec - > identifier ) ;
elf_release ( & elf ) ;
}
static void extract_sb_file ( struct sb_file_t * file )
{
for ( int i = 0 ; i < file - > nr_sections ; i + + )
extract_sb_section ( & file - > sections [ i ] ) ;
}
2012-11-27 21:38:48 +00:00
static void extract_elf ( struct elf_params_t * elf , int count )
{
char * filename = xmalloc ( strlen ( g_out_prefix ) + 32 ) ;
sprintf ( filename , " %s.%d.elf " , g_out_prefix , count ) ;
if ( g_debug )
printf ( " Write boot content to %s \n " , filename ) ;
FILE * fd = fopen ( filename , " wb " ) ;
free ( filename ) ;
if ( fd = = NULL )
2012-11-27 23:17:15 +00:00
return ;
if ( g_elf_simplify )
elf_simplify ( elf ) ;
2012-11-27 21:38:48 +00:00
elf_write_file ( elf , elf_write , elf_printf , fd ) ;
fclose ( fd ) ;
}
2012-11-26 22:54:44 +00:00
static void extract_sb1_file ( struct sb1_file_t * file )
{
2012-11-27 21:38:48 +00:00
int elf_count = 0 ;
struct elf_params_t elf ;
elf_init ( & elf ) ;
for ( int i = 0 ; i < file - > nr_insts ; i + + )
{
struct sb1_inst_t * inst = & file - > insts [ i ] ;
switch ( inst - > cmd )
{
case SB1_INST_LOAD :
elf_add_load_section ( & elf , inst - > addr , inst - > size , inst - > data ) ;
break ;
case SB1_INST_FILL :
elf_add_fill_section ( & elf , inst - > addr , inst - > size , inst - > pattern ) ;
break ;
case SB1_INST_CALL :
case SB1_INST_JUMP :
elf_set_start_addr ( & elf , inst - > addr ) ;
extract_elf ( & elf , elf_count + + ) ;
elf_release ( & elf ) ;
elf_init ( & elf ) ;
break ;
default :
/* ignore mode and nop */
break ;
}
}
if ( ! elf_is_empty ( & elf ) )
extract_elf ( & elf , elf_count ) ;
elf_release ( & elf ) ;
2012-11-26 22:54:44 +00:00
}
2011-11-01 11:23:43 +00:00
static void usage ( void )
2010-11-28 10:53:01 +00:00
{
2011-10-29 14:22:17 +00:00
printf ( " Usage: sbtoelf [options] sb-file \n " ) ;
printf ( " Options: \n " ) ;
printf ( " -?/--help \t Display this message \n " ) ;
2011-11-01 11:26:16 +00:00
printf ( " -o <prefix> \t Enable output and set prefix \n " ) ;
2011-11-01 11:23:43 +00:00
printf ( " -d/--debug \t Enable debug output* \n " ) ;
2011-10-29 14:22:17 +00:00
printf ( " -k <file> \t Add key file \n " ) ;
printf ( " -z \t \t Add zero key \n " ) ;
printf ( " -r \t \t Use raw command mode \n " ) ;
2011-11-01 11:23:24 +00:00
printf ( " -a/--add-key <key> \t Add single key (hex or usbotp) \n " ) ;
printf ( " -n/--no-color \t Disable output colors \n " ) ;
2011-11-01 11:23:43 +00:00
printf ( " -l/--loopback <file> \t Produce sb file out of extracted description* \n " ) ;
2012-06-27 12:50:39 +00:00
printf ( " -f/--force \t Force reading even without a key* \n " ) ;
2012-11-26 22:54:44 +00:00
printf ( " -1/--v1 \t Force to read file as a version 1 file \n " ) ;
printf ( " -2/--v2 \t Force to read file as a version 2 file \n " ) ;
2012-11-27 23:17:15 +00:00
printf ( " -s/--no-simpl \t Prevent elf files from being simplified* \n " ) ;
printf ( " -x \t \t Use default sb1 key \n " ) ;
2013-02-16 19:47:07 +00:00
printf ( " -b \t Brute force key \n " ) ;
2011-11-01 11:23:43 +00:00
printf ( " Options marked with a * are for debug purpose only \n " ) ;
2011-10-29 14:22:17 +00:00
exit ( 1 ) ;
}
2010-11-28 20:56:09 +00:00
2011-11-01 11:23:43 +00:00
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 ) ;
}
2011-10-29 14:22:17 +00:00
static struct crypto_key_t g_zero_key =
{
. method = CRYPTO_KEY ,
. u . key = { 0 }
} ;
2010-11-28 20:56:09 +00:00
2012-12-16 00:53:20 +00:00
2012-11-26 22:54:44 +00:00
enum sb_version_guess_t
{
SB_VERSION_1 ,
SB_VERSION_2 ,
SB_VERSION_UNK ,
} ;
enum sb_version_guess_t guess_sb_version ( const char * filename )
{
2012-11-27 21:16:56 +00:00
# define ret(x) do { fclose(f); return x; } while(0)
2012-11-26 22:54:44 +00:00
FILE * f = fopen ( filename , " rb " ) ;
if ( f = = NULL )
bugp ( " Cannot open file for reading \n " ) ;
// check signature
uint8_t sig [ 4 ] ;
if ( fseek ( f , 20 , SEEK_SET ) )
2012-11-27 21:16:56 +00:00
ret ( SB_VERSION_UNK ) ;
2012-11-26 22:54:44 +00:00
if ( fread ( sig , 4 , 1 , f ) ! = 1 )
2012-11-27 21:16:56 +00:00
ret ( SB_VERSION_UNK ) ;
2012-11-26 22:54:44 +00:00
if ( memcmp ( sig , " STMP " , 4 ) ! = 0 )
2012-11-27 21:16:56 +00:00
ret ( SB_VERSION_UNK ) ;
2012-11-26 22:54:44 +00:00
// check header size (v1)
uint32_t hdr_size ;
if ( fseek ( f , 8 , SEEK_SET ) )
2012-11-27 21:16:56 +00:00
ret ( SB_VERSION_UNK ) ;
2012-11-26 22:54:44 +00:00
if ( fread ( & hdr_size , 4 , 1 , f ) ! = 1 )
2012-11-27 21:16:56 +00:00
ret ( SB_VERSION_UNK ) ;
2012-11-26 22:54:44 +00:00
if ( hdr_size = = 0x34 )
2012-11-27 21:16:56 +00:00
ret ( SB_VERSION_1 ) ;
2013-01-29 20:46:28 +00:00
// check header params relationship
struct
{
uint16_t nr_keys ; /* Number of encryption keys */
uint16_t key_dict_off ; /* Offset to key dictionary (in blocks) */
uint16_t header_size ; /* In blocks */
uint16_t nr_sections ; /* Number of sections */
uint16_t sec_hdr_size ; /* Section header size (in blocks) */
} __attribute__ ( ( packed ) ) u ;
if ( fseek ( f , 0x28 , SEEK_SET ) )
2012-11-27 21:16:56 +00:00
ret ( SB_VERSION_UNK ) ;
2013-01-29 20:46:28 +00:00
if ( fread ( & u , sizeof ( u ) , 1 , f ) ! = 1 )
2012-11-27 21:16:56 +00:00
ret ( SB_VERSION_UNK ) ;
2013-01-29 20:46:28 +00:00
if ( u . sec_hdr_size = = 1 & & u . header_size = = 6 & & u . key_dict_off = = u . header_size + u . nr_sections )
2012-11-27 21:16:56 +00:00
ret ( SB_VERSION_2 ) ;
ret ( SB_VERSION_UNK ) ;
# undef ret
2012-11-26 22:54:44 +00:00
}
2011-10-29 14:22:17 +00:00
int main ( int argc , char * * argv )
{
2011-11-01 11:23:43 +00:00
bool raw_mode = false ;
const char * loopback = NULL ;
2012-11-26 22:54:44 +00:00
bool force_sb1 = false ;
bool force_sb2 = false ;
2013-02-16 19:47:07 +00:00
bool brute_force = false ;
2012-11-26 22:54:44 +00:00
2011-10-29 14:22:17 +00:00
while ( 1 )
{
static struct option long_options [ ] =
{
{ " help " , no_argument , 0 , ' ? ' } ,
{ " debug " , no_argument , 0 , ' d ' } ,
2011-10-29 17:04:20 +00:00
{ " add-key " , required_argument , 0 , ' a ' } ,
2011-11-01 11:23:24 +00:00
{ " no-color " , no_argument , 0 , ' n ' } ,
2011-11-01 11:23:43 +00:00
{ " loopback " , required_argument , 0 , ' l ' } ,
2012-11-26 22:54:44 +00:00
{ " force " , no_argument , 0 , ' f ' } ,
{ " v1 " , no_argument , 0 , ' 1 ' } ,
{ " v2 " , no_argument , 0 , ' 2 ' } ,
2012-11-27 23:17:15 +00:00
{ " no-simpl " , no_argument , 0 , ' s ' } ,
2011-10-29 14:22:17 +00:00
{ 0 , 0 , 0 , 0 }
} ;
2013-02-16 19:47:07 +00:00
int c = getopt_long ( argc , argv , " ?do:k:zra:nl:f12xsb " , long_options , NULL ) ;
2011-10-29 14:22:17 +00:00
if ( c = = - 1 )
break ;
switch ( c )
{
case - 1 :
break ;
2011-11-01 11:23:43 +00:00
case ' l ' :
if ( loopback )
bug ( " Only one loopback file can be specified ! \n " ) ;
loopback = optarg ;
break ;
2011-11-01 11:23:24 +00:00
case ' n ' :
enable_color ( false ) ;
break ;
2011-10-29 14:22:17 +00:00
case ' d ' :
g_debug = true ;
break ;
case ' ? ' :
usage ( ) ;
break ;
case ' o ' :
g_out_prefix = optarg ;
break ;
2012-06-27 12:50:39 +00:00
case ' f ' :
g_force = true ;
break ;
2011-10-29 14:22:17 +00:00
case ' k ' :
{
2012-02-19 17:36:57 +00:00
if ( ! add_keys_from_file ( optarg ) )
bug ( " Cannot add keys from %s \n " , optarg ) ;
2011-10-29 14:22:17 +00:00
break ;
}
case ' z ' :
add_keys ( & g_zero_key , 1 ) ;
break ;
2012-11-26 22:54:44 +00:00
case ' x ' :
2012-12-16 00:53:20 +00:00
{
struct crypto_key_t key ;
sb1_get_default_key ( & key ) ;
add_keys ( & key , 1 ) ;
2012-11-26 22:54:44 +00:00
break ;
2012-12-16 00:53:20 +00:00
}
2011-10-29 22:27:53 +00:00
case ' r ' :
2011-11-01 11:23:43 +00:00
raw_mode = true ;
2011-10-29 22:27:53 +00:00
break ;
2011-10-29 17:04:20 +00:00
case ' a ' :
2011-10-29 14:22:17 +00:00
{
struct crypto_key_t key ;
2011-10-29 17:04:20 +00:00
char * s = optarg ;
if ( ! parse_key ( & s , & key ) )
2012-06-27 12:50:39 +00:00
bug ( " Invalid key specified as argument \n " ) ;
2011-10-29 17:04:20 +00:00
if ( * s ! = 0 )
2012-06-27 12:50:39 +00:00
bug ( " Trailing characters after key specified as argument \n " ) ;
2011-10-29 14:22:17 +00:00
add_keys ( & key , 1 ) ;
break ;
}
2012-11-26 22:54:44 +00:00
case ' 1 ' :
force_sb1 = true ;
break ;
case ' 2 ' :
force_sb2 = true ;
break ;
2012-11-27 23:17:15 +00:00
case ' s ' :
g_elf_simplify = false ;
break ;
2013-02-16 19:47:07 +00:00
case ' b ' :
brute_force = true ;
break ;
2011-10-29 14:22:17 +00:00
default :
abort ( ) ;
}
}
2010-11-28 20:56:09 +00:00
2012-11-26 22:54:44 +00:00
if ( force_sb1 & & force_sb2 )
bug ( " You cannot force both version 1 and 2 \n " ) ;
2011-10-29 14:22:17 +00:00
if ( argc - optind ! = 1 )
2011-10-29 17:04:20 +00:00
{
usage ( ) ;
return 1 ;
}
2010-11-28 10:53:01 +00:00
2011-11-01 11:23:43 +00:00
const char * sb_filename = argv [ optind ] ;
2010-11-28 10:53:01 +00:00
2012-11-26 22:54:44 +00:00
enum sb_version_guess_t ver = guess_sb_version ( sb_filename ) ;
if ( force_sb2 | | ver = = SB_VERSION_2 )
2011-11-06 01:49:13 +00:00
{
2012-11-26 22:54:44 +00:00
enum sb_error_t err ;
struct sb_file_t * file = sb_read_file ( sb_filename , raw_mode , NULL , sb_printf , & err ) ;
if ( file = = NULL )
{
color ( OFF ) ;
printf ( " SB read failed: %d \n " , err ) ;
return 1 ;
}
2011-11-06 01:49:13 +00:00
color ( OFF ) ;
2012-11-26 22:54:44 +00:00
if ( g_out_prefix )
extract_sb_file ( file ) ;
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 */
file - > override_real_key = false ;
file - > override_crypto_iv = false ;
sb_write_file ( file , loopback ) ;
}
sb_free ( file ) ;
2011-11-06 01:49:13 +00:00
}
2012-11-26 22:54:44 +00:00
else if ( force_sb1 | | ver = = SB_VERSION_1 )
2011-11-01 11:23:43 +00:00
{
2013-02-16 19:47:07 +00:00
if ( brute_force )
{
struct crypto_key_t key ;
enum sb1_error_t err ;
if ( ! sb1_brute_force ( sb_filename , NULL , sb_printf , & err , & key ) )
{
color ( OFF ) ;
printf ( " Brute force failed: %d \n " , err ) ;
return 1 ;
}
color ( RED ) ;
printf ( " Key found: " ) ;
color ( YELLOW ) ;
for ( int i = 0 ; i < 32 ; i + + )
printf ( " %08x " , key . u . xor_key [ i / 16 ] . k [ i % 16 ] ) ;
color ( OFF ) ;
printf ( " \n " ) ;
color ( RED ) ;
printf ( " Key: " ) ;
color ( YELLOW ) ;
for ( int i = 0 ; i < 128 ; i + + )
printf ( " %02x " , key . u . xor_key [ i / 64 ] . key [ i % 64 ] ) ;
color ( OFF ) ;
printf ( " \n " ) ;
add_keys ( & key , 1 ) ;
}
2012-11-26 22:54:44 +00:00
enum sb1_error_t err ;
struct sb1_file_t * file = sb1_read_file ( sb_filename , NULL , sb_printf , & err ) ;
if ( file = = NULL )
{
color ( OFF ) ;
printf ( " SB read failed: %d \n " , err ) ;
return 1 ;
}
color ( OFF ) ;
if ( g_out_prefix )
extract_sb1_file ( file ) ;
if ( g_debug )
{
color ( GREY ) ;
printf ( " [Debug output] \n " ) ;
sb1_dump ( file , NULL , sb_printf ) ;
}
if ( loopback )
sb1_write_file ( file , loopback ) ;
2013-06-15 20:19:52 +00:00
2012-11-26 22:54:44 +00:00
sb1_free ( file ) ;
2011-11-01 11:23:43 +00:00
}
2012-11-26 22:54:44 +00:00
else
2011-11-01 11:23:43 +00:00
{
2012-11-26 22:54:44 +00:00
color ( OFF ) ;
printf ( " Cannot guess file type, are you sure it's a valid image ? \n " ) ;
return 1 ;
2011-11-01 11:23:43 +00:00
}
2011-11-06 01:49:13 +00:00
clear_keys ( ) ;
2013-06-15 20:19:52 +00:00
2010-11-28 20:56:09 +00:00
return 0 ;
2010-11-28 10:53:01 +00:00
}