diff options
author | Rafaël Carré <rafael.carre@gmail.com> | 2009-10-27 11:19:49 +0000 |
---|---|---|
committer | Rafaël Carré <rafael.carre@gmail.com> | 2009-10-27 11:19:49 +0000 |
commit | 03f88d53ee3ac69add22d248103a9d468c0b36d2 (patch) | |
tree | a7c1133a6eaa5b4ea70ef676f88ecc7086a99d2e /utils/AMS/hacking/amsinfo.c | |
parent | c9667e6d235c94b8c785260f4ce30e43930b1c45 (diff) | |
download | rockbox-03f88d53ee3ac69add22d248103a9d468c0b36d2.tar.gz rockbox-03f88d53ee3ac69add22d248103a9d468c0b36d2.zip |
Replace amsinfo by another version which:
- supports Clipv2/Fuzev2 firmwares
- dumps firmware and library blocks in current directory
- adds assertions and boundaries checks about the firmware format
- has nice colors !
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23366 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'utils/AMS/hacking/amsinfo.c')
-rw-r--r-- | utils/AMS/hacking/amsinfo.c | 599 |
1 files changed, 472 insertions, 127 deletions
diff --git a/utils/AMS/hacking/amsinfo.c b/utils/AMS/hacking/amsinfo.c index 1aed9db075..631345f2f9 100644 --- a/utils/AMS/hacking/amsinfo.c +++ b/utils/AMS/hacking/amsinfo.c | |||
@@ -1,175 +1,520 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Copyright © 2008 Rafaël Carré <rafael.carre@gmail.com> | |
3 | amsinfo - a tool for examining AMS firmware files | 3 | * |
4 | 4 | * This program is free software; you can redistribute it and/or modify | |
5 | Copyright (C) Dave Chapman 2007 | 5 | * it under the terms of the GNU General Public License as published by |
6 | 6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | This program is free software; you can redistribute it and/or modify | 7 | * (at your option) any later version. |
8 | it under the terms of the GNU General Public License as published by | 8 | * |
9 | the Free Software Foundation; either version 2 of the License, or | 9 | * This program is distributed in the hope that it will be useful, |
10 | (at your option) any later version. | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | This program is distributed in the hope that it will be useful, | 12 | * GNU General Public License for more details. |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | * You should have received a copy of the GNU General Public License |
15 | GNU General Public License for more details. | 15 | * along with this program; if not, write to the Free Software |
16 | 16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA | |
17 | You should have received a copy of the GNU General Public License | 17 | * |
18 | along with this program; if not, write to the Free Software | 18 | */ |
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA | 19 | |
20 | 20 | #define _ISOC99_SOURCE /* snprintf() */ | |
21 | */ | ||
22 | |||
23 | #include <stdio.h> | 21 | #include <stdio.h> |
24 | #include <stdlib.h> | ||
25 | #include <stdint.h> | ||
26 | #include <sys/types.h> | 22 | #include <sys/types.h> |
27 | #include <sys/stat.h> | 23 | #include <sys/stat.h> |
28 | #include <fcntl.h> | 24 | #include <fcntl.h> |
25 | #include <errno.h> | ||
29 | #include <unistd.h> | 26 | #include <unistd.h> |
27 | #include <stdlib.h> | ||
28 | #include <inttypes.h> | ||
29 | #include <string.h> | ||
30 | 30 | ||
31 | #if 1 /* ANSI colors */ | ||
31 | 32 | ||
32 | /* Win32 compatibility */ | 33 | # define color(a) printf("%s",a) |
33 | #ifndef O_BINARY | 34 | char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' }; |
34 | #define O_BINARY 0 | ||
35 | #endif | ||
36 | |||
37 | |||
38 | #define PAD_TO_BOUNDARY(x) ((x) + 0x1ff) & ~0x1ff; | ||
39 | |||
40 | 35 | ||
41 | static off_t filesize(int fd) { | 36 | char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' }; |
42 | struct stat buf; | 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' }; | ||
43 | 41 | ||
44 | if (fstat(fd,&buf) < 0) { | 42 | #else |
45 | perror("[ERR] Checking filesize of input file"); | 43 | /* disable colors */ |
46 | return -1; | 44 | # define color(a) |
47 | } else { | 45 | #endif |
48 | return(buf.st_size); | ||
49 | } | ||
50 | } | ||
51 | 46 | ||
52 | static uint32_t get_uint32le(unsigned char* p) | 47 | #define LIB_OFFSET 160 /* FIXME (see below) */ |
48 | /* The alignement of library blocks (in number of 0x200 bytes blocks) | ||
49 | * alignement - md5sum - filename - model | ||
50 | * 120 : fc9dd6116001b3e6a150b898f1b091f0 m200p-4.1.08A.bin M200 | ||
51 | * 128 : 82e3194310d1514e3bbcd06e84c4add3 m200p.bin Fuze | ||
52 | * 160 : c12711342169c66e209540cd1f27cd26 m300f.bin CLIP | ||
53 | * | ||
54 | * Note : the size of library blocks is variable: | ||
55 | * | ||
56 | * For m200p-4.1.08A.bin it's always 0x1e000 blocks = 240 * 0x200 | ||
57 | * | ||
58 | * For m200p.bin it can be 0x20000 (256*0x200) or 0x40000 (512*0x200) | ||
59 | * (for "acp_decoder" and "sd_reload__" blocks) | ||
60 | * | ||
61 | * For m300f.bin it can be 0x28000 (320*0x200) or 0x14000 (160 * 0x200) | ||
62 | * | ||
63 | */ | ||
64 | |||
65 | #define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0) | ||
66 | #define bugp(a) do { perror("ERROR: "a); exit(1); } while(0) | ||
67 | |||
68 | /* byte swapping */ | ||
69 | #define get32le(a) ((uint32_t) \ | ||
70 | ( buf[a+3] << 24 | buf[a+2] << 16 | buf[a+1] << 8 | buf[a] )) | ||
71 | #define get16le(a) ((uint16_t)( buf[a+1] << 8 | buf[a] )) | ||
72 | |||
73 | /* all blocks are sized as a multiple of 0x1ff */ | ||
74 | #define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff) | ||
75 | |||
76 | /* If you find a firmware that breaks the known format ^^ */ | ||
77 | #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) | ||
78 | |||
79 | /* globals */ | ||
80 | |||
81 | size_t sz; /* file size */ | ||
82 | uint8_t *buf; /* file content */ | ||
83 | |||
84 | |||
85 | /* 1st block description */ | ||
86 | uint32_t idx,checksum,bs_multiplier,firmware_sz; | ||
87 | uint32_t unknown_4_1; uint8_t unknown_1,id; uint16_t unknown_2; | ||
88 | uint32_t unknown_4_2,unknown_4_3; | ||
89 | |||
90 | static void *xmalloc(size_t s) /* malloc helper */ | ||
53 | { | 91 | { |
54 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | 92 | void * r = malloc(s); |
93 | if(!r) bugp("malloc"); | ||
94 | return r; | ||
55 | } | 95 | } |
56 | 96 | ||
57 | static uint16_t get_uint16le(unsigned char* p) | 97 | /* known models */ |
98 | static const char * model(uint8_t id) | ||
58 | { | 99 | { |
59 | return p[0] | (p[1] << 8); | 100 | switch(id) |
101 | { | ||
102 | case 0x1E: return "FUZE"; break; | ||
103 | case 0x22: return "CLIP"; break; | ||
104 | case 0x23: return "C200"; break; | ||
105 | case 0x24: return "E200"; break; | ||
106 | case 0x25: return "M200"; break; | ||
107 | case 0x27: return "CLV2"; break; | ||
108 | case 0x70: | ||
109 | case 0x6d: return "FUZ2"; break; | ||
110 | default: | ||
111 | printf("Unknown ID 0x%x\n", id); | ||
112 | |||
113 | assert(id == 0x1E || (id > 0x21 && id < 0x26)); | ||
114 | return "UNKNOWN!"; | ||
115 | } | ||
60 | } | 116 | } |
61 | 117 | ||
62 | static int calc_checksum(unsigned char* buf, int n) | 118 | /* checksums the firmware (the firmware header contains the verification) */ |
119 | static uint32_t do_checksum(void) | ||
63 | { | 120 | { |
64 | int sum = 0; | 121 | uint32_t c = 0; |
65 | int i; | ||
66 | 122 | ||
67 | for (i=0;i<n;i+=4) | 123 | size_t i = 0x400/4; |
68 | sum += get_uint32le(buf + 0x400 + i); | 124 | while(i<(0x400+firmware_sz)/4) |
125 | c += ((uint32_t*)buf)[i++]; | ||
69 | 126 | ||
70 | return sum; | 127 | return c; |
71 | } | 128 | } |
72 | 129 | ||
73 | 130 | /* verify the firmware header */ | |
74 | static void dump_header(unsigned char* buf, int i) | 131 | static void check(void) |
75 | { | 132 | { |
76 | printf("0x%08x:\n",i); | 133 | uint32_t checksum2; |
77 | printf(" HEADER: 0x%08x\n",i); | 134 | |
78 | printf(" FirmwareHeaderIndex: 0x%08x\n",get_uint32le(&buf[i])); | 135 | assert(sz >= 0x400 && sz % 0x200 == 0); |
79 | printf(" FirmwareChecksum: 0x%08x\n",get_uint32le(&buf[i+0x04])); | 136 | |
80 | printf(" CodeBlockSizeMultiplier: 0x%08x\n",get_uint32le(&buf[i+0x08])); | 137 | size_t i; |
81 | printf(" FirmwareSize: 0x%08x\n",get_uint32le(&buf[i+0x0c])); | 138 | checksum2 = 0; |
82 | printf(" Unknown1: 0x%08x\n",get_uint32le(&buf[i+0x10])); | 139 | for(i=0;i<sz/4-1;i++) |
83 | printf(" ModelID: 0x%04x\n",get_uint16le(&buf[i+0x14])); | 140 | checksum2 += ((uint32_t*)buf)[i]; |
84 | printf(" Unknown2: 0x%04x\n",get_uint16le(&buf[i+0x16])); | 141 | |
142 | uint32_t last_word = get32le(sz - 4); | ||
143 | |||
144 | switch(last_word) | ||
145 | { | ||
146 | case 0: /* no whole file checksum */ | ||
147 | break; | ||
148 | case 0xefbeadde: /* no whole file checksum */ | ||
149 | break; | ||
150 | default: /* verify whole file checksum */ | ||
151 | assert(last_word == checksum2); | ||
152 | } | ||
153 | |||
154 | idx = get32le(0); | ||
155 | unsigned int shift = (get32le(4) == 0x0000f000) ? 4 : 0; | ||
156 | checksum = get32le(4 + shift); | ||
157 | bs_multiplier = get32le(8 + shift); | ||
158 | firmware_sz = get32le(0xc + shift); | ||
159 | assert(bs_multiplier << 9 == PAD_TO_BOUNDARY(firmware_sz)); /* 0x200 * bs_multiplier */ | ||
160 | |||
161 | unknown_4_1 = get32le(0x10 + shift); | ||
162 | unknown_1 = buf[0x14 + shift]; | ||
163 | id = buf[0x15 + shift]; | ||
164 | unknown_2 = get16le(0x16 + shift); | ||
165 | unknown_4_2 = get32le(0x18 + shift); | ||
166 | unknown_4_3 = get32le(0x1c + shift); | ||
167 | |||
168 | color(GREEN); | ||
169 | printf("4 Index %d\n",idx); | ||
170 | assert(idx == 0); | ||
171 | color(GREEN); | ||
172 | printf("4 Firmware Checksum %x",checksum); | ||
173 | checksum2=do_checksum(); | ||
174 | color(GREEN); | ||
175 | printf(" (%x)\n",checksum2); | ||
176 | assert(checksum == checksum2); | ||
177 | color(GREEN); | ||
178 | printf("4 Block Size Multiplier %x\n",bs_multiplier); | ||
179 | color(GREEN); | ||
180 | printf("4 Firmware block size %x (%d)\n",firmware_sz,firmware_sz); | ||
181 | |||
182 | color(GREEN); | ||
183 | printf("4 Unknown (should be 3) %x\n",unknown_4_1); | ||
184 | assert(unknown_4_1 == 3); | ||
185 | |||
186 | /* variable */ | ||
187 | color(GREEN); | ||
188 | printf("1 Unknown %x\n",unknown_1); | ||
189 | |||
190 | color(GREEN); | ||
191 | printf("1 Model ID %x (%s)\n",id,model(id)); | ||
192 | |||
193 | color(GREEN); | ||
194 | printf("2 Unknown (should be 0) %x\n",unknown_2); | ||
195 | assert(unknown_2 == 0); | ||
196 | |||
197 | color(GREEN); | ||
198 | printf("4 Unknown (should be 40) %x\n",unknown_4_2); | ||
199 | assert(unknown_4_2 == 0x40 ); | ||
200 | |||
201 | color(GREEN); | ||
202 | printf("4 Unknown (should be 1) %x\n",unknown_4_3); | ||
203 | assert(unknown_4_3 == 1); | ||
204 | |||
205 | /* rest of the block is padded with 0xff */ | ||
206 | for(i=0x20 + shift;i<0x200 - shift;i++) | ||
207 | assert(buf[i]==0xff /* normal case */ || | ||
208 | ((id==0x1e||id==0x24) && ( /* Fuze or E200 */ | ||
209 | (i>=0x3c && i<=0x3f && get32le(0x3c)==0x00005000) | ||
210 | ))); | ||
211 | |||
212 | /* the 2nd block is identical, except that the 1st byte has been incremented */ | ||
213 | assert(buf[0x0]==0&&buf[0x200]==1); | ||
214 | assert(!memcmp(&buf[1],&buf[0x201],0x1FF - shift)); | ||
85 | } | 215 | } |
86 | 216 | ||
87 | static int dump_lib(unsigned char* buf, int i) | 217 | typedef enum |
88 | { | 218 | { |
89 | int export_count; | ||
90 | int size; | ||
91 | int unknown1; | ||
92 | int baseaddr, endaddr; | ||
93 | |||
94 | baseaddr = get_uint32le(&buf[i+0x04]); | ||
95 | endaddr = get_uint32le(&buf[i+0x08]); | ||
96 | size = get_uint32le(&buf[i+0x0c]); | ||
97 | unknown1 = get_uint32le(&buf[i+0x10]); | ||
98 | export_count = get_uint32le(&buf[i+0x14]); | ||
99 | |||
100 | printf("0x%08x: \"%s\" 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i, buf + i + get_uint32le(&buf[i]),baseaddr,endaddr,size,unknown1,export_count); | ||
101 | |||
102 | #if 0 | 219 | #if 0 |
103 | if (export_count > 1) { | 220 | FW_HEADER, |
104 | for (j=0;j<export_count;j++) { | 221 | FW, |
105 | printf(" Exports[%02d]: 0x%08x\n",j,get_uint32le(&buf[i+0x18+4*j])); | ||
106 | } | ||
107 | } | ||
108 | #endif | 222 | #endif |
109 | return PAD_TO_BOUNDARY(size); | 223 | LIB, |
110 | } | 224 | PAD, |
225 | HEADER, | ||
226 | UNKNOWN | ||
227 | } type; | ||
111 | 228 | ||
112 | int main(int argc, char* argv[]) | 229 | static unsigned int n_libs = 0, n_pads_ff = 0, n_pads_deadbeef = 0, n_unkn = 0, n_headers = 0; |
113 | { | ||
114 | int fd; | ||
115 | off_t len; | ||
116 | int n; | ||
117 | unsigned char* buf; | ||
118 | int firmware_size; | ||
119 | int i; | ||
120 | 230 | ||
121 | if (argc != 2) { | 231 | static void show_lib(size_t off) |
122 | fprintf(stderr,"USAGE: amsinfo firmware.bin\n"); | 232 | { |
123 | return 1; | 233 | /* first word: char* */ |
124 | } | 234 | uint32_t start = get32le(off+4); |
125 | 235 | uint32_t stop = get32le(off+8); | |
126 | fd = open(argv[1],O_RDONLY|O_BINARY); | ||
127 | 236 | ||
128 | if ((len = filesize(fd)) < 0) | 237 | uint32_t size = get32le(off+0xc); |
129 | return 1; | ||
130 | 238 | ||
131 | if ((buf = malloc(len)) == NULL) { | 239 | #if 0 /* library block hacking */ |
132 | fprintf(stderr,"[ERR] Could not allocate buffer for input file (%d bytes)\n",(int)len); | 240 | /* assert(stop > start); */ |
133 | return 1; | ||
134 | } | ||
135 | 241 | ||
136 | n = read(fd, buf, len); | 242 | /* assert(stop - start == size); */ |
137 | 243 | ||
138 | if (n != len) { | 244 | if(stop - start != size) |
139 | fprintf(stderr,"[ERR] Could not read file\n"); | 245 | { |
140 | return 1; | 246 | color(RED); |
141 | } | 247 | printf("STOP - START != SIZE || 0x%.8x - 0x%.8x == 0x%.8x != 0x%.8x\n", |
248 | stop, start, stop - start, size); | ||
249 | } | ||
142 | 250 | ||
143 | close(fd); | 251 | color(RED); |
252 | printf("0x%.8x -> 0x%.8x SIZE 0x%.6x\n", start, stop, size); | ||
144 | 253 | ||
145 | /* Now we dump the firmware structure */ | 254 | uint32_t first = get32le(off+0x10); /* ? */ |
255 | printf("? = 0x%.8x , ",first); | ||
256 | #endif | ||
146 | 257 | ||
147 | dump_header(buf,0); /* First copy of header block */ | 258 | uint32_t funcs = get32le(off+0x14); /* nmbr of functions */ |
148 | // dump_header(buf,0x200); /* Second copy of header block */ | 259 | color(YELLOW); |
260 | printf("\t%d funcs",funcs); | ||
261 | |||
262 | unsigned int i; | ||
263 | for(i=0;i<funcs;i++) | ||
264 | { | ||
265 | uint32_t fptr = get32le(off+0x18+i*4); | ||
266 | if(!fptr) | ||
267 | { | ||
268 | assert(funcs==1); /* if 1 function is exported, it's empty */ | ||
269 | } | ||
270 | else | ||
271 | { | ||
272 | assert(fptr - start < 0x0000ffff); | ||
273 | /* printf("0x%.4x ",fptr); */ | ||
274 | } | ||
275 | } | ||
276 | |||
277 | color(BLUE); | ||
278 | printf("\tBASE 0x%.8x (code + 0x%x) END 0x%.8x : SIZE 0x%.8x\n",start, 0x18 + i*4, stop, stop - start); | ||
279 | |||
280 | char name[12+1]; | ||
281 | memcpy(name,&buf[off+get32le(off)],12); | ||
282 | name[12] = '\0'; | ||
283 | |||
284 | FILE *out = fopen(name,"w"); | ||
285 | if(!out) | ||
286 | bug("library block"); | ||
287 | |||
288 | if(fwrite(&buf[off],size,1,out)!=1) | ||
289 | bug(); | ||
290 | |||
291 | fclose(out); | ||
292 | } | ||
149 | 293 | ||
150 | firmware_size = get_uint32le(&buf[0x0c]); | 294 | static int unknown = 0; |
295 | static int padding = 0; | ||
296 | static void print_block(size_t off, type t) | ||
297 | { | ||
298 | /* reset counters if needed */ | ||
299 | if(t != UNKNOWN && unknown) | ||
300 | { /* print only the number of following blocks */ | ||
301 | color(GREY); | ||
302 | printf("%d unknown blocks (0x%.6x bytes)\n",unknown,unknown*0x200); | ||
303 | unknown = 0; | ||
304 | } | ||
305 | else if(t != PAD && padding) | ||
306 | { /* same */ | ||
307 | color(GREY); | ||
308 | printf("%d padding blocks (0x%.6x bytes)\n",padding,padding*0x200); | ||
309 | padding = 0; | ||
310 | } | ||
311 | |||
312 | if(t != UNKNOWN && t != PAD) /* for other block types, always print the offset */ | ||
313 | { | ||
314 | color(GREEN); | ||
315 | printf("0x%.6x\t", (unsigned int)off); | ||
316 | color(OFF); | ||
317 | } | ||
318 | |||
319 | switch(t) | ||
320 | { | ||
321 | size_t s; | ||
322 | FILE *f; | ||
323 | char filename[8+4]; /* unknown\0 , 10K max */ | ||
324 | #if 0 | ||
325 | case FW_HEADER: | ||
326 | printf("firmware header 0x%x\n",off); | ||
327 | break; | ||
328 | case FW: | ||
329 | printf("firmware block 0x%x\n",off); | ||
330 | break; | ||
331 | #endif | ||
332 | case LIB: | ||
333 | s = LIB_OFFSET * 0x200; | ||
334 | while(s < get32le(off+12)) | ||
335 | s <<= 1; | ||
336 | color(RED); | ||
337 | printf("library block 0x%.6x\t->\t0x%.6x\t\"%s\"\n", | ||
338 | (unsigned int)s, (unsigned int)(off+s), | ||
339 | &buf[off+get32le(off)]); | ||
340 | show_lib(off); | ||
341 | n_libs++; | ||
342 | break; | ||
343 | case PAD: | ||
344 | if(buf[off] == 0xff) | ||
345 | n_pads_ff++; | ||
346 | else | ||
347 | n_pads_deadbeef++; | ||
348 | padding++; | ||
349 | break; | ||
350 | case UNKNOWN: | ||
351 | unknown++; | ||
352 | n_unkn++; | ||
353 | #if 0 /* do not dump unknown blocks */ | ||
354 | snprintf(filename, sizeof(filename), "unknown%d", n_unkn); | ||
355 | f = fopen(filename, "w"); | ||
356 | if(f) | ||
357 | { | ||
358 | if( fwrite(buf+off, 0x200, 1, f) != 1 ) | ||
359 | bugp("unknown block"); | ||
360 | fclose(f); | ||
361 | } | ||
362 | else | ||
363 | bugp("unknown block"); | ||
364 | #endif | ||
365 | break; | ||
366 | case HEADER: | ||
367 | color(YELLOW); | ||
368 | printf("header block 0x%.6x\t->\t0x%.6x\n", | ||
369 | PAD_TO_BOUNDARY(get32le(off)), | ||
370 | (unsigned int)PAD_TO_BOUNDARY(off+get32le(off))); | ||
371 | snprintf(filename, sizeof(filename), "header%d", n_headers++); | ||
372 | f = fopen(filename,"w"); | ||
373 | if(!f) | ||
374 | bug("header"); | ||
375 | |||
376 | if(fwrite(&buf[off],get32le(off),1,f)!=1) | ||
377 | bug(); | ||
378 | |||
379 | fclose(f); | ||
380 | |||
381 | break; | ||
382 | default: | ||
383 | abort(); | ||
384 | } | ||
385 | |||
386 | if(t != PAD && t != UNKNOWN) | ||
387 | printf("\n"); | ||
388 | } | ||
151 | 389 | ||
152 | printf("Calculated firmware checksum: 0x%08x\n",calc_checksum(buf,firmware_size)); | 390 | static size_t verify_block(size_t off) |
391 | { | ||
392 | assert(!(off%0x200)); | ||
393 | assert(off+0x200 < sz); | ||
394 | |||
395 | size_t s = 0x200; | ||
396 | type t = UNKNOWN; | ||
397 | |||
398 | size_t offset_str = get32le(off); | ||
399 | if(get32le(off) == 0xefbeadde ) | ||
400 | { | ||
401 | #if 0 /* some blocks begin with 0xdeadbeef but aren't padded with that value */ | ||
402 | unsigned int i; | ||
403 | for(i=0;i<s;i+=4) | ||
404 | assert(get32le(off+i) == 0xefbeadde); | ||
405 | #endif | ||
406 | t = PAD; | ||
407 | } | ||
408 | else if( *(uint32_t*)(&buf[off]) == 0xffffffff) | ||
409 | { | ||
410 | unsigned int i; | ||
411 | for(i=0;i<s;i++) | ||
412 | assert(buf[off+i] == 0xff); | ||
413 | t = PAD; | ||
414 | } | ||
415 | else if(off+offset_str+12<sz) /* XXX: we should check that the address at which | ||
416 | * the string is located is included in this | ||
417 | * library block's size, but we only know the | ||
418 | * block's size after we confirmed that this is | ||
419 | * a library block (by looking at the 11 chars | ||
420 | * ASCII string). */ | ||
421 | { | ||
422 | short int ok = 1; | ||
423 | unsigned int i; | ||
424 | for(i=0;i<11;i++) | ||
425 | if(buf[off+offset_str+i] >> 7 || !buf[off+offset_str+i]) | ||
426 | ok = 0; | ||
427 | if(buf[off+offset_str+11]) | ||
428 | ok = 0; | ||
429 | if(ok) /* library block */ | ||
430 | { | ||
431 | t = LIB; | ||
432 | s = LIB_OFFSET * 0x200; | ||
433 | while(s < get32le(off+12)) /* of course the minimum is the size | ||
434 | * specified in the block header */ | ||
435 | s <<= 1; | ||
436 | } | ||
437 | else | ||
438 | t = UNKNOWN; | ||
439 | } | ||
440 | else | ||
441 | t = UNKNOWN; | ||
442 | |||
443 | if(t==UNKNOWN) | ||
444 | { | ||
445 | if(!strncmp((char*)buf+off+8,"HEADER",6)) | ||
446 | { | ||
447 | s = PAD_TO_BOUNDARY(get32le(off)); /* first 4 bytes le are the block size */ | ||
448 | t = HEADER; | ||
449 | } | ||
450 | } | ||
451 | |||
452 | print_block(off,t); | ||
453 | |||
454 | return PAD_TO_BOUNDARY(s); | ||
455 | } | ||
153 | 456 | ||
154 | /* Round size up to next multiple of 0x200 */ | 457 | static void extract(void) |
458 | { | ||
459 | FILE *out = fopen("firmware","w"); | ||
460 | if(!out) | ||
461 | bug("firmware"); | ||
462 | |||
463 | if(fwrite(&buf[0x400],firmware_sz,1,out)!=1) | ||
464 | bug("firmare writing"); | ||
465 | fclose(out); | ||
466 | |||
467 | off_t off = PAD_TO_BOUNDARY(0x400 + firmware_sz); | ||
468 | unsigned int n = 0; | ||
469 | |||
470 | printf("\n"); | ||
471 | color(RED); | ||
472 | printf("Extracting\n\n"); | ||
473 | |||
474 | while((unsigned int)(off+0x200)<sz) | ||
475 | { | ||
476 | /* look at the next 0x200 bytes if we can recognize a block type */ | ||
477 | off += verify_block(off); /* then skip its real size */ | ||
478 | n++; /* and look at the next block ;) */ | ||
479 | } | ||
480 | |||
481 | /* statistics */ | ||
482 | printf("\n"); | ||
483 | color(RED); | ||
484 | printf("TOTAL\t%d\tblocks (%d unknown)\n",n,n_unkn); | ||
485 | color(BLUE); | ||
486 | printf("\t%d\tlibs\n",n_libs); | ||
487 | color(GREY); | ||
488 | printf("\t%d\tpads ff\n",n_pads_ff); | ||
489 | color(GREY); | ||
490 | printf("\t%d\tpads deadbeef\n",n_pads_deadbeef); | ||
491 | color(GREEN); | ||
492 | printf("\t%d\theaders\n",n_headers); | ||
493 | } | ||
155 | 494 | ||
156 | firmware_size = PAD_TO_BOUNDARY(firmware_size); | 495 | int main(int argc, const char **argv) |
496 | { | ||
497 | int fd; | ||
498 | struct stat st; | ||
499 | if(argc != 2) | ||
500 | bug("Usage: %s <firmware>\n",*argv); | ||
157 | 501 | ||
158 | i = firmware_size + 0x400; | 502 | if( (fd = open(argv[1],O_RDONLY)) == -1 ) |
503 | bugp("opening firmware failed"); | ||
159 | 504 | ||
160 | printf("LIBRARY BLOCKS:\n"); | 505 | if(fstat(fd,&st) == -1) |
161 | printf("Offset Name BaseAddr EndAddr BlockSize Unknown1 EntryCount\n"); | 506 | bugp("firmware stat() failed"); |
507 | sz = st.st_size; | ||
162 | 508 | ||
163 | while (get_uint32le(&buf[i]) != 0xffffffff) | 509 | buf=xmalloc(sz); |
164 | { | 510 | if(read(fd,buf,sz)!=(ssize_t)sz) /* load the whole file into memory */ |
165 | i += dump_lib(buf,i); | 511 | bugp("reading firmware"); |
166 | 512 | ||
167 | while (get_uint32le(&buf[i]) == 0xefbeadde) | 513 | close(fd); |
168 | i+=4; | ||
169 | } | ||
170 | 514 | ||
171 | printf("0x%08x: PADDING BLOCK\n",i); | 515 | check(); /* verify header and checksums */ |
172 | 516 | extract(); /* split in blocks */ | |
173 | return 0; | ||
174 | 517 | ||
518 | free(buf); | ||
519 | return 0; | ||
175 | } | 520 | } |