2 * Copyright © 2008 Rafaël Carré <rafael.carre@gmail.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
20 #define _ISOC99_SOURCE /* snprintf() */
22 #include <sys/types.h>
31 #if 1 /* ANSI colors */
33 # define color(a) printf("%s",a)
34 char OFF
[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
36 char GREY
[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
37 char RED
[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
38 char GREEN
[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
39 char YELLOW
[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
40 char BLUE
[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
47 #define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0)
48 #define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
51 #define get32le(a) ((uint32_t) \
52 ( buf[a+3] << 24 | buf[a+2] << 16 | buf[a+1] << 8 | buf[a] ))
53 #define get16le(a) ((uint16_t)( buf[a+1] << 8 | buf[a] ))
55 /* all blocks are sized as a multiple of 0x1ff */
56 #define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
58 /* If you find a firmware that breaks the known format ^^ */
59 #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)
63 size_t sz
; /* file size */
64 uint8_t *buf
; /* file content */
67 /* 1st block description */
68 uint32_t idx
,checksum
,bs_multiplier
,firmware_sz
;
69 uint32_t unknown_4_1
; uint16_t unknown_1
, unknown_2
;
70 uint32_t unknown_4_2
,unknown_4_3
;
72 static void *xmalloc(size_t s
) /* malloc helper */
75 if(!r
) bugp("malloc");
79 /* checksums the firmware (the firmware header contains the verification) */
80 static uint32_t do_checksum(void)
85 while(i
<(0x400+firmware_sz
)/4)
86 c
+= ((uint32_t*)buf
)[i
++];
91 /* verify the firmware header */
92 static void check(void)
96 assert(sz
>= 0x400 && sz
% 0x200 == 0);
100 for(i
=0;i
<sz
/4-1;i
++)
101 checksum2
+= ((uint32_t*)buf
)[i
];
103 uint32_t last_word
= get32le(sz
- 4);
107 case 0: /* no whole file checksum */
109 case 0xefbeadde: /* no whole file checksum */
111 default: /* verify whole file checksum */
112 assert(last_word
== checksum2
);
116 unsigned int shift
= (get32le(4) == 0x0000f000) ? 4 : 0;
117 checksum
= get32le(4 + shift
);
118 bs_multiplier
= get32le(8 + shift
);
119 firmware_sz
= get32le(0xc + shift
);
120 assert(bs_multiplier
<< 9 == PAD_TO_BOUNDARY(firmware_sz
)); /* 0x200 * bs_multiplier */
122 unknown_4_1
= get32le(0x10 + shift
);
123 unknown_1
= get16le(0x14 + shift
);
124 unknown_2
= get16le(0x16 + shift
);
125 unknown_4_2
= get32le(0x18 + shift
);
126 unknown_4_3
= get32le(0x1c + shift
);
129 printf("4 Index %d\n",idx
);
132 printf("4 Firmware Checksum %x",checksum
);
133 checksum2
=do_checksum();
135 printf(" (%x)\n",checksum2
);
136 assert(checksum
== checksum2
);
138 printf("4 Block Size Multiplier %x\n",bs_multiplier
);
140 printf("4 Firmware block size %x (%d)\n",firmware_sz
,firmware_sz
);
143 printf("4 Unknown (should be 3) %x\n",unknown_4_1
);
144 assert(unknown_4_1
== 3);
148 printf("1 Unknown %x\n",unknown_1
);
151 printf("2 Unknown (should be 0) %x\n",unknown_2
);
152 assert(unknown_2
== 0);
155 printf("4 Unknown (should be 40) %x\n",unknown_4_2
);
156 assert(unknown_4_2
== 0x40 );
159 printf("4 Unknown (should be 1) %x\n",unknown_4_3
);
160 assert(unknown_4_3
== 1);
162 /* the 2nd block is identical, except that the 1st byte has been incremented */
163 assert(buf
[0x0]==0&&buf
[0x200]==1);
164 assert(!memcmp(&buf
[1],&buf
[0x201],0x1FF - shift
));
179 static unsigned int n_libs
= 0, n_pads_ff
= 0, n_pads_deadbeef
= 0, n_unkn
= 0, n_headers
= 0;
181 static void show_lib(size_t off
)
183 /* first word: char* */
184 uint32_t start
= get32le(off
+4);
185 uint32_t stop
= get32le(off
+8);
187 uint32_t size
= get32le(off
+0xc);
189 #if 0 /* library block hacking */
190 /* assert(stop > start); */
192 /* assert(stop - start == size); */
194 if(stop
- start
!= size
)
197 printf("STOP - START != SIZE || 0x%.8x - 0x%.8x == 0x%.8x != 0x%.8x\n",
198 stop
, start
, stop
- start
, size
);
202 printf("0x%.8x -> 0x%.8x SIZE 0x%.6x\n", start
, stop
, size
);
204 uint32_t first
= get32le(off
+0x10); /* ? */
205 printf("? = 0x%.8x , ",first
);
208 uint32_t funcs
= get32le(off
+0x14); /* nmbr of functions */
210 printf("\t%d funcs",funcs
);
215 uint32_t fptr
= get32le(off
+0x18+i
*4);
218 assert(funcs
==1); /* if 1 function is exported, it's empty */
222 assert(fptr
- start
< 0x0000ffff);
223 /* printf("0x%.4x ",fptr); */
228 printf("\tBASE 0x%.8x (code + 0x%x) END 0x%.8x : SIZE 0x%.8x\n",start
, 0x18 + i
*4, stop
, stop
- start
);
231 memcpy(name
,&buf
[off
+get32le(off
)],12);
234 FILE *out
= fopen(name
,"w");
236 bug("library block");
238 if(fwrite(&buf
[off
],size
,1,out
)!=1)
244 static int unknown
= 0;
245 static int padding
= 0;
246 static void print_block(size_t off
, type t
)
248 /* reset counters if needed */
249 if(t
!= UNKNOWN
&& unknown
)
250 { /* print only the number of following blocks */
252 printf("%d unknown blocks (0x%.6x bytes)\n",unknown
,unknown
*0x200);
255 else if(t
!= PAD
&& padding
)
258 printf("%d padding blocks (0x%.6x bytes)\n",padding
,padding
*0x200);
262 if(t
!= UNKNOWN
&& t
!= PAD
) /* for other block types, always print the offset */
265 printf("0x%.6x\t", (unsigned int)off
);
273 char filename
[8+4]; /* unknown\0 , 10K max */
276 printf("firmware header 0x%x\n",off
);
279 printf("firmware block 0x%x\n",off
);
285 printf("library block 0x%.6x\t->\t0x%.6x\t\"%s\"\n",
286 (unsigned int)s
, (unsigned int)(off
+s
),
287 &buf
[off
+get32le(off
)]);
301 #if 0 /* do not dump unknown blocks */
302 snprintf(filename
, sizeof(filename
), "unknown%d", n_unkn
);
303 f
= fopen(filename
, "w");
306 if( fwrite(buf
+off
, 0x200, 1, f
) != 1 )
307 bugp("unknown block");
311 bugp("unknown block");
316 printf("header block 0x%.6x\t->\t0x%.6x\n",
317 PAD_TO_BOUNDARY(get32le(off
)),
318 (unsigned int)PAD_TO_BOUNDARY(off
+get32le(off
)));
319 snprintf(filename
, sizeof(filename
), "header%d", n_headers
++);
320 f
= fopen(filename
,"w");
324 if(fwrite(&buf
[off
],get32le(off
),1,f
)!=1)
334 if(t
!= PAD
&& t
!= UNKNOWN
)
338 static size_t verify_block(size_t off
)
340 assert(!(off
%0x200));
341 assert(off
+0x200 < sz
);
346 size_t offset_str
= get32le(off
);
347 if(get32le(off
) == 0xefbeadde )
349 #if 0 /* some blocks begin with 0xdeadbeef but aren't padded with that value */
352 assert(get32le(off
+i
) == 0xefbeadde);
356 else if( *(uint32_t*)(&buf
[off
]) == 0xffffffff)
360 assert(buf
[off
+i
] == 0xff);
363 else if(off
+offset_str
+12<sz
) /* XXX: we should check that the address at which
364 * the string is located is included in this
365 * library block's size, but we only know the
366 * block's size after we confirmed that this is
367 * a library block (by looking at the 11 chars
373 if(buf
[off
+offset_str
+i
] >> 7 || !buf
[off
+offset_str
+i
])
375 if(buf
[off
+offset_str
+11])
377 if(ok
) /* library block */
390 if(!strncmp((char*)buf
+off
+8,"HEADER",6))
392 s
= PAD_TO_BOUNDARY(get32le(off
)); /* first 4 bytes le are the block size */
399 return PAD_TO_BOUNDARY(s
);
402 static void extract(void)
404 FILE *out
= fopen("firmware","w");
408 if(fwrite(&buf
[0x400],firmware_sz
,1,out
)!=1)
409 bug("firmare writing");
412 off_t off
= PAD_TO_BOUNDARY(0x400 + firmware_sz
);
417 printf("Extracting\n\n");
419 while((unsigned int)(off
+0x200)<sz
)
421 /* look at the next 0x200 bytes if we can recognize a block type */
422 off
+= verify_block(off
); /* then skip its real size */
423 n
++; /* and look at the next block ;) */
429 printf("TOTAL\t%d\tblocks (%d unknown)\n",n
,n_unkn
);
431 printf("\t%d\tlibs\n",n_libs
);
433 printf("\t%d\tpads ff\n",n_pads_ff
);
435 printf("\t%d\tpads deadbeef\n",n_pads_deadbeef
);
437 printf("\t%d\theaders\n",n_headers
);
440 int main(int argc
, const char **argv
)
445 bug("Usage: %s <firmware>\n",*argv
);
447 if( (fd
= open(argv
[1],O_RDONLY
)) == -1 )
448 bugp("opening firmware failed");
450 if(fstat(fd
,&st
) == -1)
451 bugp("firmware stat() failed");
455 if(read(fd
,buf
,sz
)!=(ssize_t
)sz
) /* load the whole file into memory */
456 bugp("reading firmware");
460 check(); /* verify header and checksums */
461 extract(); /* split in blocks */