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
; uint8_t unknown_1
,id
; uint16_t 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");
80 static const char * model(uint8_t id
)
84 case 0x1E: return "FUZE"; break;
85 case 0x22: return "CLIP"; break;
86 case 0x23: return "C200"; break;
87 case 0x24: return "E200"; break;
88 case 0x25: return "M200"; break;
89 case 0x27: return "CLV2"; break;
90 case 0x28: return "CLI+"; break;
92 case 0x6d: return "FUZ2"; break;
94 printf("Unknown ID 0x%x\n", id
);
96 assert(id
== 0x1E || (id
>= 0x22 && id
<= 0x28));
101 /* checksums the firmware (the firmware header contains the verification) */
102 static uint32_t do_checksum(void)
107 while(i
<(0x400+firmware_sz
)/4)
108 c
+= ((uint32_t*)buf
)[i
++];
113 /* verify the firmware header */
114 static void check(void)
118 assert(sz
>= 0x400 && sz
% 0x200 == 0);
122 for(i
=0;i
<sz
/4-1;i
++)
123 checksum2
+= ((uint32_t*)buf
)[i
];
125 uint32_t last_word
= get32le(sz
- 4);
129 case 0: /* no whole file checksum */
131 case 0xefbeadde: /* no whole file checksum */
133 default: /* verify whole file checksum */
134 assert(last_word
== checksum2
);
138 unsigned int shift
= (get32le(4) == 0x0000f000) ? 4 : 0;
139 checksum
= get32le(4 + shift
);
140 bs_multiplier
= get32le(8 + shift
);
141 firmware_sz
= get32le(0xc + shift
);
142 assert(bs_multiplier
<< 9 == PAD_TO_BOUNDARY(firmware_sz
)); /* 0x200 * bs_multiplier */
144 unknown_4_1
= get32le(0x10 + shift
);
145 unknown_1
= buf
[0x14 + shift
];
146 id
= buf
[0x15 + shift
];
147 unknown_2
= get16le(0x16 + shift
);
148 unknown_4_2
= get32le(0x18 + shift
);
149 unknown_4_3
= get32le(0x1c + shift
);
152 printf("4 Index %d\n",idx
);
155 printf("4 Firmware Checksum %x",checksum
);
156 checksum2
=do_checksum();
158 printf(" (%x)\n",checksum2
);
159 assert(checksum
== checksum2
);
161 printf("4 Block Size Multiplier %x\n",bs_multiplier
);
163 printf("4 Firmware block size %x (%d)\n",firmware_sz
,firmware_sz
);
166 printf("4 Unknown (should be 3) %x\n",unknown_4_1
);
167 assert(unknown_4_1
== 3);
171 printf("1 Unknown %x\n",unknown_1
);
174 printf("1 Model ID %x (%s)\n",id
,model(id
));
177 printf("2 Unknown (should be 0) %x\n",unknown_2
);
178 assert(unknown_2
== 0);
181 printf("4 Unknown (should be 40) %x\n",unknown_4_2
);
182 assert(unknown_4_2
== 0x40 );
185 printf("4 Unknown (should be 1) %x\n",unknown_4_3
);
186 assert(unknown_4_3
== 1);
188 /* rest of the block is padded with 0xff */
189 for(i
=0x20 + shift
;i
<0x200 - shift
;i
++)
190 assert(buf
[i
]==0xff /* normal case */ ||
191 ((id
==0x1e||id
==0x24) && ( /* Fuze or E200 */
192 (i
>=0x3c && i
<=0x3f && get32le(0x3c)==0x00005000)
195 /* the 2nd block is identical, except that the 1st byte has been incremented */
196 assert(buf
[0x0]==0&&buf
[0x200]==1);
197 assert(!memcmp(&buf
[1],&buf
[0x201],0x1FF - shift
));
212 static unsigned int n_libs
= 0, n_pads_ff
= 0, n_pads_deadbeef
= 0, n_unkn
= 0, n_headers
= 0;
214 static void show_lib(size_t off
)
216 /* first word: char* */
217 uint32_t start
= get32le(off
+4);
218 uint32_t stop
= get32le(off
+8);
220 uint32_t size
= get32le(off
+0xc);
222 #if 0 /* library block hacking */
223 /* assert(stop > start); */
225 /* assert(stop - start == size); */
227 if(stop
- start
!= size
)
230 printf("STOP - START != SIZE || 0x%.8x - 0x%.8x == 0x%.8x != 0x%.8x\n",
231 stop
, start
, stop
- start
, size
);
235 printf("0x%.8x -> 0x%.8x SIZE 0x%.6x\n", start
, stop
, size
);
237 uint32_t first
= get32le(off
+0x10); /* ? */
238 printf("? = 0x%.8x , ",first
);
241 uint32_t funcs
= get32le(off
+0x14); /* nmbr of functions */
243 printf("\t%d funcs",funcs
);
248 uint32_t fptr
= get32le(off
+0x18+i
*4);
251 assert(funcs
==1); /* if 1 function is exported, it's empty */
255 assert(fptr
- start
< 0x0000ffff);
256 /* printf("0x%.4x ",fptr); */
261 printf("\tBASE 0x%.8x (code + 0x%x) END 0x%.8x : SIZE 0x%.8x\n",start
, 0x18 + i
*4, stop
, stop
- start
);
264 memcpy(name
,&buf
[off
+get32le(off
)],12);
267 FILE *out
= fopen(name
,"w");
269 bug("library block");
271 if(fwrite(&buf
[off
],size
,1,out
)!=1)
277 static int unknown
= 0;
278 static int padding
= 0;
279 static void print_block(size_t off
, type t
)
281 /* reset counters if needed */
282 if(t
!= UNKNOWN
&& unknown
)
283 { /* print only the number of following blocks */
285 printf("%d unknown blocks (0x%.6x bytes)\n",unknown
,unknown
*0x200);
288 else if(t
!= PAD
&& padding
)
291 printf("%d padding blocks (0x%.6x bytes)\n",padding
,padding
*0x200);
295 if(t
!= UNKNOWN
&& t
!= PAD
) /* for other block types, always print the offset */
298 printf("0x%.6x\t", (unsigned int)off
);
306 char filename
[8+4]; /* unknown\0 , 10K max */
309 printf("firmware header 0x%x\n",off
);
312 printf("firmware block 0x%x\n",off
);
318 printf("library block 0x%.6x\t->\t0x%.6x\t\"%s\"\n",
319 (unsigned int)s
, (unsigned int)(off
+s
),
320 &buf
[off
+get32le(off
)]);
334 #if 0 /* do not dump unknown blocks */
335 snprintf(filename
, sizeof(filename
), "unknown%d", n_unkn
);
336 f
= fopen(filename
, "w");
339 if( fwrite(buf
+off
, 0x200, 1, f
) != 1 )
340 bugp("unknown block");
344 bugp("unknown block");
349 printf("header block 0x%.6x\t->\t0x%.6x\n",
350 PAD_TO_BOUNDARY(get32le(off
)),
351 (unsigned int)PAD_TO_BOUNDARY(off
+get32le(off
)));
352 snprintf(filename
, sizeof(filename
), "header%d", n_headers
++);
353 f
= fopen(filename
,"w");
357 if(fwrite(&buf
[off
],get32le(off
),1,f
)!=1)
367 if(t
!= PAD
&& t
!= UNKNOWN
)
371 static size_t verify_block(size_t off
)
373 assert(!(off
%0x200));
374 assert(off
+0x200 < sz
);
379 size_t offset_str
= get32le(off
);
380 if(get32le(off
) == 0xefbeadde )
382 #if 0 /* some blocks begin with 0xdeadbeef but aren't padded with that value */
385 assert(get32le(off
+i
) == 0xefbeadde);
389 else if( *(uint32_t*)(&buf
[off
]) == 0xffffffff)
393 assert(buf
[off
+i
] == 0xff);
396 else if(off
+offset_str
+12<sz
) /* XXX: we should check that the address at which
397 * the string is located is included in this
398 * library block's size, but we only know the
399 * block's size after we confirmed that this is
400 * a library block (by looking at the 11 chars
406 if(buf
[off
+offset_str
+i
] >> 7 || !buf
[off
+offset_str
+i
])
408 if(buf
[off
+offset_str
+11])
410 if(ok
) /* library block */
423 if(!strncmp((char*)buf
+off
+8,"HEADER",6))
425 s
= PAD_TO_BOUNDARY(get32le(off
)); /* first 4 bytes le are the block size */
432 return PAD_TO_BOUNDARY(s
);
435 static void extract(void)
437 FILE *out
= fopen("firmware","w");
441 if(fwrite(&buf
[0x400],firmware_sz
,1,out
)!=1)
442 bug("firmare writing");
445 off_t off
= PAD_TO_BOUNDARY(0x400 + firmware_sz
);
450 printf("Extracting\n\n");
452 while((unsigned int)(off
+0x200)<sz
)
454 /* look at the next 0x200 bytes if we can recognize a block type */
455 off
+= verify_block(off
); /* then skip its real size */
456 n
++; /* and look at the next block ;) */
462 printf("TOTAL\t%d\tblocks (%d unknown)\n",n
,n_unkn
);
464 printf("\t%d\tlibs\n",n_libs
);
466 printf("\t%d\tpads ff\n",n_pads_ff
);
468 printf("\t%d\tpads deadbeef\n",n_pads_deadbeef
);
470 printf("\t%d\theaders\n",n_headers
);
473 int main(int argc
, const char **argv
)
478 bug("Usage: %s <firmware>\n",*argv
);
480 if( (fd
= open(argv
[1],O_RDONLY
)) == -1 )
481 bugp("opening firmware failed");
483 if(fstat(fd
,&st
) == -1)
484 bugp("firmware stat() failed");
488 if(read(fd
,buf
,sz
)!=(ssize_t
)sz
) /* load the whole file into memory */
489 bugp("reading firmware");
493 check(); /* verify header and checksums */
494 extract(); /* split in blocks */