blob: 433333ecca9f8d97f18b59d449ef88157ae2357d [file] [log] [blame]
Dave Chapman1aa6cde2008-05-11 18:29:53 +00001/*
Rafaël Carré03f88d52009-10-27 11:19:49 +00002 * Copyright © 2008 Rafaël Carré <rafael.carre@gmail.com>
3 *
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.
8 *
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.
13 *
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
17 *
18 */
Dave Chapman1aa6cde2008-05-11 18:29:53 +000019
Rafaël Carré03f88d52009-10-27 11:19:49 +000020#define _ISOC99_SOURCE /* snprintf() */
Dave Chapman1aa6cde2008-05-11 18:29:53 +000021#include <stdio.h>
Dave Chapman1aa6cde2008-05-11 18:29:53 +000022#include <sys/types.h>
23#include <sys/stat.h>
24#include <fcntl.h>
Rafaël Carré03f88d52009-10-27 11:19:49 +000025#include <errno.h>
Dave Chapman1aa6cde2008-05-11 18:29:53 +000026#include <unistd.h>
Rafaël Carré03f88d52009-10-27 11:19:49 +000027#include <stdlib.h>
28#include <inttypes.h>
29#include <string.h>
Dave Chapman1aa6cde2008-05-11 18:29:53 +000030
Rafaël Carré03f88d52009-10-27 11:19:49 +000031#if 1 /* ANSI colors */
Dave Chapman1aa6cde2008-05-11 18:29:53 +000032
Rafaël Carré03f88d52009-10-27 11:19:49 +000033# define color(a) printf("%s",a)
34char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
35
36char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
37char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
38char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
39char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
40char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
41
42#else
43 /* disable colors */
44# define color(a)
Dave Chapman1aa6cde2008-05-11 18:29:53 +000045#endif
46
Rafaël Carré03f88d52009-10-27 11:19:49 +000047#define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0)
48#define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
49
50/* byte swapping */
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] ))
54
55/* all blocks are sized as a multiple of 0x1ff */
56#define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
57
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)
60
61/* globals */
62
63size_t sz; /* file size */
64uint8_t *buf; /* file content */
Dave Chapman1aa6cde2008-05-11 18:29:53 +000065
66
Rafaël Carré03f88d52009-10-27 11:19:49 +000067/* 1st block description */
68uint32_t idx,checksum,bs_multiplier,firmware_sz;
Rafaël Carréff6b0422010-05-24 10:06:52 +000069uint32_t unknown_4_1; uint16_t unknown_1, unknown_2;
Rafaël Carré03f88d52009-10-27 11:19:49 +000070uint32_t unknown_4_2,unknown_4_3;
Dave Chapman1aa6cde2008-05-11 18:29:53 +000071
Rafaël Carré03f88d52009-10-27 11:19:49 +000072static void *xmalloc(size_t s) /* malloc helper */
73{
74 void * r = malloc(s);
75 if(!r) bugp("malloc");
76 return r;
Dave Chapman1aa6cde2008-05-11 18:29:53 +000077}
78
Rafaël Carré03f88d52009-10-27 11:19:49 +000079/* checksums the firmware (the firmware header contains the verification) */
80static uint32_t do_checksum(void)
Dave Chapman1aa6cde2008-05-11 18:29:53 +000081{
Rafaël Carré03f88d52009-10-27 11:19:49 +000082 uint32_t c = 0;
83
84 size_t i = 0x400/4;
85 while(i<(0x400+firmware_sz)/4)
86 c += ((uint32_t*)buf)[i++];
87
88 return c;
Dave Chapman1aa6cde2008-05-11 18:29:53 +000089}
90
Rafaël Carré03f88d52009-10-27 11:19:49 +000091/* verify the firmware header */
92static void check(void)
Dave Chapman1aa6cde2008-05-11 18:29:53 +000093{
Rafaël Carré03f88d52009-10-27 11:19:49 +000094 uint32_t checksum2;
Dave Chapman1aa6cde2008-05-11 18:29:53 +000095
Rafaël Carré03f88d52009-10-27 11:19:49 +000096 assert(sz >= 0x400 && sz % 0x200 == 0);
Dave Chapman1aa6cde2008-05-11 18:29:53 +000097
Rafaël Carré03f88d52009-10-27 11:19:49 +000098 size_t i;
99 checksum2 = 0;
100 for(i=0;i<sz/4-1;i++)
101 checksum2 += ((uint32_t*)buf)[i];
102
103 uint32_t last_word = get32le(sz - 4);
104
105 switch(last_word)
106 {
107 case 0: /* no whole file checksum */
108 break;
109 case 0xefbeadde: /* no whole file checksum */
110 break;
111 default: /* verify whole file checksum */
112 assert(last_word == checksum2);
113 }
114
115 idx = get32le(0);
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 */
121
122 unknown_4_1 = get32le(0x10 + shift);
Rafaël Carréff6b0422010-05-24 10:06:52 +0000123 unknown_1 = get16le(0x14 + shift);
Rafaël Carré03f88d52009-10-27 11:19:49 +0000124 unknown_2 = get16le(0x16 + shift);
125 unknown_4_2 = get32le(0x18 + shift);
126 unknown_4_3 = get32le(0x1c + shift);
127
128 color(GREEN);
129 printf("4 Index %d\n",idx);
130 assert(idx == 0);
131 color(GREEN);
132 printf("4 Firmware Checksum %x",checksum);
133 checksum2=do_checksum();
134 color(GREEN);
135 printf(" (%x)\n",checksum2);
136 assert(checksum == checksum2);
137 color(GREEN);
138 printf("4 Block Size Multiplier %x\n",bs_multiplier);
139 color(GREEN);
140 printf("4 Firmware block size %x (%d)\n",firmware_sz,firmware_sz);
141
142 color(GREEN);
143 printf("4 Unknown (should be 3) %x\n",unknown_4_1);
144 assert(unknown_4_1 == 3);
145
146 /* variable */
147 color(GREEN);
148 printf("1 Unknown %x\n",unknown_1);
149
150 color(GREEN);
Rafaël Carré03f88d52009-10-27 11:19:49 +0000151 printf("2 Unknown (should be 0) %x\n",unknown_2);
152 assert(unknown_2 == 0);
153
154 color(GREEN);
155 printf("4 Unknown (should be 40) %x\n",unknown_4_2);
156 assert(unknown_4_2 == 0x40 );
157
158 color(GREEN);
159 printf("4 Unknown (should be 1) %x\n",unknown_4_3);
160 assert(unknown_4_3 == 1);
161
Rafaël Carré03f88d52009-10-27 11:19:49 +0000162 /* 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));
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000165}
166
Rafaël Carré03f88d52009-10-27 11:19:49 +0000167typedef enum
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000168{
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000169#if 0
Rafaël Carré03f88d52009-10-27 11:19:49 +0000170 FW_HEADER,
171 FW,
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000172#endif
Rafaël Carré03f88d52009-10-27 11:19:49 +0000173 LIB,
174 PAD,
175 HEADER,
176 UNKNOWN
177} type;
178
179static unsigned int n_libs = 0, n_pads_ff = 0, n_pads_deadbeef = 0, n_unkn = 0, n_headers = 0;
180
181static void show_lib(size_t off)
182{
183 /* first word: char* */
184 uint32_t start = get32le(off+4);
185 uint32_t stop = get32le(off+8);
186
187 uint32_t size = get32le(off+0xc);
188
189#if 0 /* library block hacking */
190 /* assert(stop > start); */
191
192 /* assert(stop - start == size); */
193
194 if(stop - start != size)
195 {
196 color(RED);
197 printf("STOP - START != SIZE || 0x%.8x - 0x%.8x == 0x%.8x != 0x%.8x\n",
198 stop, start, stop - start, size);
199 }
200
201 color(RED);
202 printf("0x%.8x -> 0x%.8x SIZE 0x%.6x\n", start, stop, size);
203
204 uint32_t first = get32le(off+0x10); /* ? */
205 printf("? = 0x%.8x , ",first);
206#endif
207
208 uint32_t funcs = get32le(off+0x14); /* nmbr of functions */
209 color(YELLOW);
210 printf("\t%d funcs",funcs);
211
212 unsigned int i;
213 for(i=0;i<funcs;i++)
214 {
215 uint32_t fptr = get32le(off+0x18+i*4);
216 if(!fptr)
217 {
218 assert(funcs==1); /* if 1 function is exported, it's empty */
219 }
220 else
221 {
222 assert(fptr - start < 0x0000ffff);
223 /* printf("0x%.4x ",fptr); */
224 }
225 }
226
227 color(BLUE);
228 printf("\tBASE 0x%.8x (code + 0x%x) END 0x%.8x : SIZE 0x%.8x\n",start, 0x18 + i*4, stop, stop - start);
229
230 char name[12+1];
231 memcpy(name,&buf[off+get32le(off)],12);
232 name[12] = '\0';
233
234 FILE *out = fopen(name,"w");
235 if(!out)
236 bug("library block");
237
238 if(fwrite(&buf[off],size,1,out)!=1)
239 bug();
240
241 fclose(out);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000242}
243
Rafaël Carré03f88d52009-10-27 11:19:49 +0000244static int unknown = 0;
245static int padding = 0;
246static void print_block(size_t off, type t)
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000247{
Rafaël Carré03f88d52009-10-27 11:19:49 +0000248 /* reset counters if needed */
249 if(t != UNKNOWN && unknown)
250 { /* print only the number of following blocks */
251 color(GREY);
252 printf("%d unknown blocks (0x%.6x bytes)\n",unknown,unknown*0x200);
253 unknown = 0;
254 }
255 else if(t != PAD && padding)
256 { /* same */
257 color(GREY);
258 printf("%d padding blocks (0x%.6x bytes)\n",padding,padding*0x200);
259 padding = 0;
260 }
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000261
Rafaël Carré03f88d52009-10-27 11:19:49 +0000262 if(t != UNKNOWN && t != PAD) /* for other block types, always print the offset */
263 {
264 color(GREEN);
265 printf("0x%.6x\t", (unsigned int)off);
266 color(OFF);
267 }
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000268
Rafaël Carré03f88d52009-10-27 11:19:49 +0000269 switch(t)
270 {
271 size_t s;
272 FILE *f;
273 char filename[8+4]; /* unknown\0 , 10K max */
274#if 0
275 case FW_HEADER:
276 printf("firmware header 0x%x\n",off);
277 break;
278 case FW:
279 printf("firmware block 0x%x\n",off);
280 break;
281#endif
282 case LIB:
Rafaël Carrée32f28b2010-03-14 23:35:01 +0000283 s = get32le(off+12);
Rafaël Carré03f88d52009-10-27 11:19:49 +0000284 color(RED);
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)]);
288 show_lib(off);
289 n_libs++;
290 break;
291 case PAD:
292 if(buf[off] == 0xff)
293 n_pads_ff++;
294 else
295 n_pads_deadbeef++;
296 padding++;
297 break;
298 case UNKNOWN:
299 unknown++;
300 n_unkn++;
301#if 0 /* do not dump unknown blocks */
302 snprintf(filename, sizeof(filename), "unknown%d", n_unkn);
303 f = fopen(filename, "w");
304 if(f)
305 {
306 if( fwrite(buf+off, 0x200, 1, f) != 1 )
307 bugp("unknown block");
308 fclose(f);
309 }
310 else
311 bugp("unknown block");
312#endif
313 break;
314 case HEADER:
315 color(YELLOW);
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");
321 if(!f)
322 bug("header");
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000323
Rafaël Carré03f88d52009-10-27 11:19:49 +0000324 if(fwrite(&buf[off],get32le(off),1,f)!=1)
325 bug();
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000326
Rafaël Carré03f88d52009-10-27 11:19:49 +0000327 fclose(f);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000328
Rafaël Carré03f88d52009-10-27 11:19:49 +0000329 break;
330 default:
331 abort();
332 }
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000333
Rafaël Carré03f88d52009-10-27 11:19:49 +0000334 if(t != PAD && t != UNKNOWN)
335 printf("\n");
336}
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000337
Rafaël Carré03f88d52009-10-27 11:19:49 +0000338static size_t verify_block(size_t off)
339{
340 assert(!(off%0x200));
341 assert(off+0x200 < sz);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000342
Rafaël Carré03f88d52009-10-27 11:19:49 +0000343 size_t s = 0x200;
344 type t = UNKNOWN;
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000345
Rafaël Carré03f88d52009-10-27 11:19:49 +0000346 size_t offset_str = get32le(off);
347 if(get32le(off) == 0xefbeadde )
348 {
349#if 0 /* some blocks begin with 0xdeadbeef but aren't padded with that value */
350 unsigned int i;
351 for(i=0;i<s;i+=4)
352 assert(get32le(off+i) == 0xefbeadde);
353#endif
354 t = PAD;
355 }
356 else if( *(uint32_t*)(&buf[off]) == 0xffffffff)
357 {
358 unsigned int i;
359 for(i=0;i<s;i++)
360 assert(buf[off+i] == 0xff);
361 t = PAD;
362 }
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
368 * ASCII string). */
369 {
370 short int ok = 1;
371 unsigned int i;
372 for(i=0;i<11;i++)
373 if(buf[off+offset_str+i] >> 7 || !buf[off+offset_str+i])
374 ok = 0;
375 if(buf[off+offset_str+11])
376 ok = 0;
377 if(ok) /* library block */
378 {
379 t = LIB;
Rafaël Carrée32f28b2010-03-14 23:35:01 +0000380 s = get32le(off+12);
Rafaël Carré03f88d52009-10-27 11:19:49 +0000381 }
382 else
383 t = UNKNOWN;
384 }
385 else
386 t = UNKNOWN;
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000387
Rafaël Carré03f88d52009-10-27 11:19:49 +0000388 if(t==UNKNOWN)
389 {
390 if(!strncmp((char*)buf+off+8,"HEADER",6))
391 {
392 s = PAD_TO_BOUNDARY(get32le(off)); /* first 4 bytes le are the block size */
393 t = HEADER;
394 }
395 }
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000396
Rafaël Carré03f88d52009-10-27 11:19:49 +0000397 print_block(off,t);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000398
Rafaël Carré03f88d52009-10-27 11:19:49 +0000399 return PAD_TO_BOUNDARY(s);
400}
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000401
Rafaël Carré03f88d52009-10-27 11:19:49 +0000402static void extract(void)
403{
404 FILE *out = fopen("firmware","w");
405 if(!out)
406 bug("firmware");
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000407
Rafaël Carré03f88d52009-10-27 11:19:49 +0000408 if(fwrite(&buf[0x400],firmware_sz,1,out)!=1)
409 bug("firmare writing");
410 fclose(out);
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000411
Rafaël Carré03f88d52009-10-27 11:19:49 +0000412 off_t off = PAD_TO_BOUNDARY(0x400 + firmware_sz);
413 unsigned int n = 0;
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000414
Rafaël Carré03f88d52009-10-27 11:19:49 +0000415 printf("\n");
416 color(RED);
417 printf("Extracting\n\n");
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000418
Rafaël Carré03f88d52009-10-27 11:19:49 +0000419 while((unsigned int)(off+0x200)<sz)
420 {
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 ;) */
424 }
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000425
Rafaël Carré03f88d52009-10-27 11:19:49 +0000426 /* statistics */
427 printf("\n");
428 color(RED);
429 printf("TOTAL\t%d\tblocks (%d unknown)\n",n,n_unkn);
430 color(BLUE);
431 printf("\t%d\tlibs\n",n_libs);
432 color(GREY);
433 printf("\t%d\tpads ff\n",n_pads_ff);
434 color(GREY);
435 printf("\t%d\tpads deadbeef\n",n_pads_deadbeef);
436 color(GREEN);
437 printf("\t%d\theaders\n",n_headers);
438}
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000439
Rafaël Carré03f88d52009-10-27 11:19:49 +0000440int main(int argc, const char **argv)
441{
442 int fd;
443 struct stat st;
444 if(argc != 2)
445 bug("Usage: %s <firmware>\n",*argv);
446
447 if( (fd = open(argv[1],O_RDONLY)) == -1 )
448 bugp("opening firmware failed");
449
450 if(fstat(fd,&st) == -1)
451 bugp("firmware stat() failed");
452 sz = st.st_size;
453
454 buf=xmalloc(sz);
455 if(read(fd,buf,sz)!=(ssize_t)sz) /* load the whole file into memory */
456 bugp("reading firmware");
457
458 close(fd);
459
460 check(); /* verify header and checksums */
461 extract(); /* split in blocks */
462
463 free(buf);
464 return 0;
Dave Chapman1aa6cde2008-05-11 18:29:53 +0000465}