2008-05-11 18:29:53 +00:00
/*
2009-10-27 11:19:49 +00:00
* Copyright © 2008 Rafaël Carré < rafael . carre @ gmail . com >
*
* 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 program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 , USA
*
*/
# define _ISOC99_SOURCE /* snprintf() */
2008-05-11 18:29:53 +00:00
# include <stdio.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
2009-10-27 11:19:49 +00:00
# include <errno.h>
2008-05-11 18:29:53 +00:00
# include <unistd.h>
2009-10-27 11:19:49 +00:00
# include <stdlib.h>
# include <inttypes.h>
# include <string.h>
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
# if 1 /* ANSI colors */
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
# define color(a) printf("%s",a)
char OFF [ ] = { 0x1b , 0x5b , 0x31 , 0x3b , ' 0 ' , ' 0 ' , 0x6d , ' \0 ' } ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
char GREY [ ] = { 0x1b , 0x5b , 0x31 , 0x3b , ' 3 ' , ' 0 ' , 0x6d , ' \0 ' } ;
char RED [ ] = { 0x1b , 0x5b , 0x31 , 0x3b , ' 3 ' , ' 1 ' , 0x6d , ' \0 ' } ;
char GREEN [ ] = { 0x1b , 0x5b , 0x31 , 0x3b , ' 3 ' , ' 2 ' , 0x6d , ' \0 ' } ;
char YELLOW [ ] = { 0x1b , 0x5b , 0x31 , 0x3b , ' 3 ' , ' 3 ' , 0x6d , ' \0 ' } ;
char BLUE [ ] = { 0x1b , 0x5b , 0x31 , 0x3b , ' 3 ' , ' 4 ' , 0x6d , ' \0 ' } ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
# else
/* disable colors */
# define color(a)
# endif
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
# define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0)
# define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
/* byte swapping */
# define get32le(a) ((uint32_t) \
( buf [ a + 3 ] < < 24 | buf [ a + 2 ] < < 16 | buf [ a + 1 ] < < 8 | buf [ a ] ) )
# define get16le(a) ((uint16_t)( buf[a+1] << 8 | buf[a] ))
/* all blocks are sized as a multiple of 0x1ff */
# define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
/* If you find a firmware that breaks the known format ^^ */
# define assert(a) do { if(!(a)) { fprintf(stderr,"Assertion \"%s\" failed in %s() line %d!\n\nPlease send us your firmware!\n",#a,__func__,__LINE__); exit(1); } } while(0)
/* globals */
size_t sz ; /* file size */
uint8_t * buf ; /* file content */
/* 1st block description */
uint32_t idx , checksum , bs_multiplier , firmware_sz ;
2010-05-24 10:06:52 +00:00
uint32_t unknown_4_1 ; uint16_t unknown_1 , unknown_2 ;
2009-10-27 11:19:49 +00:00
uint32_t unknown_4_2 , unknown_4_3 ;
static void * xmalloc ( size_t s ) /* malloc helper */
2008-05-11 18:29:53 +00:00
{
2009-10-27 11:19:49 +00:00
void * r = malloc ( s ) ;
if ( ! r ) bugp ( " malloc " ) ;
return r ;
2008-05-11 18:29:53 +00:00
}
2009-10-27 11:19:49 +00:00
/* checksums the firmware (the firmware header contains the verification) */
static uint32_t do_checksum ( void )
2008-05-11 18:29:53 +00:00
{
2009-10-27 11:19:49 +00:00
uint32_t c = 0 ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
size_t i = 0x400 / 4 ;
while ( i < ( 0x400 + firmware_sz ) / 4 )
c + = ( ( uint32_t * ) buf ) [ i + + ] ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
return c ;
2008-05-11 18:29:53 +00:00
}
2009-10-27 11:19:49 +00:00
/* verify the firmware header */
static void check ( void )
2008-05-11 18:29:53 +00:00
{
2009-10-27 11:19:49 +00:00
uint32_t checksum2 ;
assert ( sz > = 0x400 & & sz % 0x200 = = 0 ) ;
size_t i ;
checksum2 = 0 ;
for ( i = 0 ; i < sz / 4 - 1 ; i + + )
checksum2 + = ( ( uint32_t * ) buf ) [ i ] ;
uint32_t last_word = get32le ( sz - 4 ) ;
switch ( last_word )
{
case 0 : /* no whole file checksum */
break ;
case 0xefbeadde : /* no whole file checksum */
break ;
default : /* verify whole file checksum */
assert ( last_word = = checksum2 ) ;
}
idx = get32le ( 0 ) ;
unsigned int shift = ( get32le ( 4 ) = = 0x0000f000 ) ? 4 : 0 ;
checksum = get32le ( 4 + shift ) ;
bs_multiplier = get32le ( 8 + shift ) ;
firmware_sz = get32le ( 0xc + shift ) ;
assert ( bs_multiplier < < 9 = = PAD_TO_BOUNDARY ( firmware_sz ) ) ; /* 0x200 * bs_multiplier */
unknown_4_1 = get32le ( 0x10 + shift ) ;
2010-05-24 10:06:52 +00:00
unknown_1 = get16le ( 0x14 + shift ) ;
2009-10-27 11:19:49 +00:00
unknown_2 = get16le ( 0x16 + shift ) ;
unknown_4_2 = get32le ( 0x18 + shift ) ;
unknown_4_3 = get32le ( 0x1c + shift ) ;
color ( GREEN ) ;
printf ( " 4 Index %d \n " , idx ) ;
assert ( idx = = 0 ) ;
color ( GREEN ) ;
printf ( " 4 Firmware Checksum %x " , checksum ) ;
checksum2 = do_checksum ( ) ;
color ( GREEN ) ;
printf ( " (%x) \n " , checksum2 ) ;
assert ( checksum = = checksum2 ) ;
color ( GREEN ) ;
printf ( " 4 Block Size Multiplier %x \n " , bs_multiplier ) ;
color ( GREEN ) ;
printf ( " 4 Firmware block size %x (%d) \n " , firmware_sz , firmware_sz ) ;
color ( GREEN ) ;
printf ( " 4 Unknown (should be 3) %x \n " , unknown_4_1 ) ;
assert ( unknown_4_1 = = 3 ) ;
/* variable */
color ( GREEN ) ;
printf ( " 1 Unknown %x \n " , unknown_1 ) ;
color ( GREEN ) ;
printf ( " 2 Unknown (should be 0) %x \n " , unknown_2 ) ;
assert ( unknown_2 = = 0 ) ;
color ( GREEN ) ;
printf ( " 4 Unknown (should be 40) %x \n " , unknown_4_2 ) ;
assert ( unknown_4_2 = = 0x40 ) ;
color ( GREEN ) ;
printf ( " 4 Unknown (should be 1) %x \n " , unknown_4_3 ) ;
assert ( unknown_4_3 = = 1 ) ;
/* the 2nd block is identical, except that the 1st byte has been incremented */
assert ( buf [ 0x0 ] = = 0 & & buf [ 0x200 ] = = 1 ) ;
assert ( ! memcmp ( & buf [ 1 ] , & buf [ 0x201 ] , 0x1FF - shift ) ) ;
2008-05-11 18:29:53 +00:00
}
2009-10-27 11:19:49 +00:00
typedef enum
2008-05-11 18:29:53 +00:00
{
#if 0
2009-10-27 11:19:49 +00:00
FW_HEADER ,
FW ,
2008-05-11 18:29:53 +00:00
# endif
2009-10-27 11:19:49 +00:00
LIB ,
PAD ,
HEADER ,
UNKNOWN
} type ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
static unsigned int n_libs = 0 , n_pads_ff = 0 , n_pads_deadbeef = 0 , n_unkn = 0 , n_headers = 0 ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
static void show_lib ( size_t off )
{
/* first word: char* */
uint32_t start = get32le ( off + 4 ) ;
uint32_t stop = get32le ( off + 8 ) ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
uint32_t size = get32le ( off + 0xc ) ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
#if 0 /* library block hacking */
/* assert(stop > start); */
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
/* assert(stop - start == size); */
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
if ( stop - start ! = size )
{
color ( RED ) ;
printf ( " STOP - START != SIZE || 0x%.8x - 0x%.8x == 0x%.8x != 0x%.8x \n " ,
stop , start , stop - start , size ) ;
}
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
color ( RED ) ;
printf ( " 0x%.8x -> 0x%.8x SIZE 0x%.6x \n " , start , stop , size ) ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
uint32_t first = get32le ( off + 0x10 ) ; /* ? */
printf ( " ? = 0x%.8x , " , first ) ;
# endif
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
uint32_t funcs = get32le ( off + 0x14 ) ; /* nmbr of functions */
color ( YELLOW ) ;
printf ( " \t %d funcs " , funcs ) ;
unsigned int i ;
for ( i = 0 ; i < funcs ; i + + )
{
uint32_t fptr = get32le ( off + 0x18 + i * 4 ) ;
if ( ! fptr )
{
assert ( funcs = = 1 ) ; /* if 1 function is exported, it's empty */
}
else
{
assert ( fptr - start < 0x0000ffff ) ;
/* printf("0x%.4x ",fptr); */
}
}
color ( BLUE ) ;
printf ( " \t BASE 0x%.8x (code + 0x%x) END 0x%.8x : SIZE 0x%.8x \n " , start , 0x18 + i * 4 , stop , stop - start ) ;
char name [ 12 + 1 ] ;
memcpy ( name , & buf [ off + get32le ( off ) ] , 12 ) ;
name [ 12 ] = ' \0 ' ;
FILE * out = fopen ( name , " w " ) ;
if ( ! out )
bug ( " library block " ) ;
if ( fwrite ( & buf [ off ] , size , 1 , out ) ! = 1 )
bug ( ) ;
fclose ( out ) ;
}
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
static int unknown = 0 ;
static int padding = 0 ;
static void print_block ( size_t off , type t )
{
/* reset counters if needed */
if ( t ! = UNKNOWN & & unknown )
{ /* print only the number of following blocks */
color ( GREY ) ;
printf ( " %d unknown blocks (0x%.6x bytes) \n " , unknown , unknown * 0x200 ) ;
unknown = 0 ;
}
else if ( t ! = PAD & & padding )
{ /* same */
color ( GREY ) ;
printf ( " %d padding blocks (0x%.6x bytes) \n " , padding , padding * 0x200 ) ;
padding = 0 ;
}
if ( t ! = UNKNOWN & & t ! = PAD ) /* for other block types, always print the offset */
{
color ( GREEN ) ;
printf ( " 0x%.6x \t " , ( unsigned int ) off ) ;
color ( OFF ) ;
}
switch ( t )
{
size_t s ;
FILE * f ;
char filename [ 8 + 4 ] ; /* unknown\0 , 10K max */
#if 0
case FW_HEADER :
printf ( " firmware header 0x%x \n " , off ) ;
break ;
case FW :
printf ( " firmware block 0x%x \n " , off ) ;
break ;
# endif
case LIB :
2010-03-14 23:35:01 +00:00
s = get32le ( off + 12 ) ;
2009-10-27 11:19:49 +00:00
color ( RED ) ;
printf ( " library block 0x%.6x \t -> \t 0x%.6x \t \" %s \" \n " ,
( unsigned int ) s , ( unsigned int ) ( off + s ) ,
& buf [ off + get32le ( off ) ] ) ;
show_lib ( off ) ;
n_libs + + ;
break ;
case PAD :
if ( buf [ off ] = = 0xff )
n_pads_ff + + ;
else
n_pads_deadbeef + + ;
padding + + ;
break ;
case UNKNOWN :
unknown + + ;
n_unkn + + ;
#if 0 /* do not dump unknown blocks */
snprintf ( filename , sizeof ( filename ) , " unknown%d " , n_unkn ) ;
f = fopen ( filename , " w " ) ;
if ( f )
{
if ( fwrite ( buf + off , 0x200 , 1 , f ) ! = 1 )
bugp ( " unknown block " ) ;
fclose ( f ) ;
}
else
bugp ( " unknown block " ) ;
# endif
break ;
case HEADER :
color ( YELLOW ) ;
printf ( " header block 0x%.6x \t -> \t 0x%.6x \n " ,
PAD_TO_BOUNDARY ( get32le ( off ) ) ,
( unsigned int ) PAD_TO_BOUNDARY ( off + get32le ( off ) ) ) ;
snprintf ( filename , sizeof ( filename ) , " header%d " , n_headers + + ) ;
f = fopen ( filename , " w " ) ;
if ( ! f )
bug ( " header " ) ;
if ( fwrite ( & buf [ off ] , get32le ( off ) , 1 , f ) ! = 1 )
bug ( ) ;
fclose ( f ) ;
break ;
default :
abort ( ) ;
}
if ( t ! = PAD & & t ! = UNKNOWN )
printf ( " \n " ) ;
}
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
static size_t verify_block ( size_t off )
{
assert ( ! ( off % 0x200 ) ) ;
assert ( off + 0x200 < sz ) ;
size_t s = 0x200 ;
type t = UNKNOWN ;
size_t offset_str = get32le ( off ) ;
if ( get32le ( off ) = = 0xefbeadde )
{
#if 0 /* some blocks begin with 0xdeadbeef but aren't padded with that value */
unsigned int i ;
for ( i = 0 ; i < s ; i + = 4 )
assert ( get32le ( off + i ) = = 0xefbeadde ) ;
# endif
t = PAD ;
}
else if ( * ( uint32_t * ) ( & buf [ off ] ) = = 0xffffffff )
{
unsigned int i ;
for ( i = 0 ; i < s ; i + + )
assert ( buf [ off + i ] = = 0xff ) ;
t = PAD ;
}
else if ( off + offset_str + 12 < sz ) /* XXX: we should check that the address at which
* the string is located is included in this
* library block ' s size , but we only know the
* block ' s size after we confirmed that this is
* a library block ( by looking at the 11 chars
* ASCII string ) . */
{
short int ok = 1 ;
unsigned int i ;
for ( i = 0 ; i < 11 ; i + + )
if ( buf [ off + offset_str + i ] > > 7 | | ! buf [ off + offset_str + i ] )
ok = 0 ;
if ( buf [ off + offset_str + 11 ] )
ok = 0 ;
if ( ok ) /* library block */
{
t = LIB ;
2010-03-14 23:35:01 +00:00
s = get32le ( off + 12 ) ;
2009-10-27 11:19:49 +00:00
}
else
t = UNKNOWN ;
}
else
t = UNKNOWN ;
if ( t = = UNKNOWN )
{
if ( ! strncmp ( ( char * ) buf + off + 8 , " HEADER " , 6 ) )
{
s = PAD_TO_BOUNDARY ( get32le ( off ) ) ; /* first 4 bytes le are the block size */
t = HEADER ;
}
}
print_block ( off , t ) ;
return PAD_TO_BOUNDARY ( s ) ;
}
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
static void extract ( void )
{
FILE * out = fopen ( " firmware " , " w " ) ;
if ( ! out )
bug ( " firmware " ) ;
if ( fwrite ( & buf [ 0x400 ] , firmware_sz , 1 , out ) ! = 1 )
bug ( " firmare writing " ) ;
fclose ( out ) ;
off_t off = PAD_TO_BOUNDARY ( 0x400 + firmware_sz ) ;
unsigned int n = 0 ;
printf ( " \n " ) ;
color ( RED ) ;
printf ( " Extracting \n \n " ) ;
while ( ( unsigned int ) ( off + 0x200 ) < sz )
{
/* look at the next 0x200 bytes if we can recognize a block type */
off + = verify_block ( off ) ; /* then skip its real size */
n + + ; /* and look at the next block ;) */
}
/* statistics */
printf ( " \n " ) ;
color ( RED ) ;
printf ( " TOTAL \t %d \t blocks (%d unknown) \n " , n , n_unkn ) ;
color ( BLUE ) ;
printf ( " \t %d \t libs \n " , n_libs ) ;
color ( GREY ) ;
printf ( " \t %d \t pads ff \n " , n_pads_ff ) ;
color ( GREY ) ;
printf ( " \t %d \t pads deadbeef \n " , n_pads_deadbeef ) ;
color ( GREEN ) ;
printf ( " \t %d \t headers \n " , n_headers ) ;
}
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
int main ( int argc , const char * * argv )
{
int fd ;
struct stat st ;
if ( argc ! = 2 )
bug ( " Usage: %s <firmware> \n " , * argv ) ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
if ( ( fd = open ( argv [ 1 ] , O_RDONLY ) ) = = - 1 )
bugp ( " opening firmware failed " ) ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
if ( fstat ( fd , & st ) = = - 1 )
bugp ( " firmware stat() failed " ) ;
sz = st . st_size ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
buf = xmalloc ( sz ) ;
if ( read ( fd , buf , sz ) ! = ( ssize_t ) sz ) /* load the whole file into memory */
bugp ( " reading firmware " ) ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
close ( fd ) ;
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
check ( ) ; /* verify header and checksums */
extract ( ) ; /* split in blocks */
2008-05-11 18:29:53 +00:00
2009-10-27 11:19:49 +00:00
free ( buf ) ;
return 0 ;
2008-05-11 18:29:53 +00:00
}