summaryrefslogtreecommitdiff
path: root/utils/AMS/hacking
diff options
context:
space:
mode:
Diffstat (limited to 'utils/AMS/hacking')
-rw-r--r--utils/AMS/hacking/Makefile2
-rw-r--r--utils/AMS/hacking/amsinfo.c599
2 files changed, 473 insertions, 128 deletions
diff --git a/utils/AMS/hacking/Makefile b/utils/AMS/hacking/Makefile
index 1a297bc3ab..bd062cfc5c 100644
--- a/utils/AMS/hacking/Makefile
+++ b/utils/AMS/hacking/Makefile
@@ -1,7 +1,7 @@
1all: amsinfo 1all: amsinfo
2 2
3amsinfo: amsinfo.c 3amsinfo: amsinfo.c
4 gcc -o amsinfo -W -Wall amsinfo.c 4 $(CC) -ansi -o amsinfo -W -Wall amsinfo.c
5 5
6clean: 6clean:
7 rm -fr amsinfo 7 rm -fr amsinfo
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>
3amsinfo - a tool for examining AMS firmware files 3 *
4 4 * This program is free software; you can redistribute it and/or modify
5Copyright (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
7This program is free software; you can redistribute it and/or modify 7 * (at your option) any later version.
8it under the terms of the GNU General Public License as published by 8 *
9the 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
12This program is distributed in the hope that it will be useful, 12 * GNU General Public License for more details.
13but WITHOUT ANY WARRANTY; without even the implied warranty of 13 *
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * You should have received a copy of the GNU General Public License
15GNU 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
17You should have received a copy of the GNU General Public License 17 *
18along with this program; if not, write to the Free Software 18 */
19Foundation, 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 34char 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
41static off_t filesize(int fd) { 36char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
42 struct stat buf; 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' };
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
52static 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
81size_t sz; /* file size */
82uint8_t *buf; /* file content */
83
84
85/* 1st block description */
86uint32_t idx,checksum,bs_multiplier,firmware_sz;
87uint32_t unknown_4_1; uint8_t unknown_1,id; uint16_t unknown_2;
88uint32_t unknown_4_2,unknown_4_3;
89
90static 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
57static uint16_t get_uint16le(unsigned char* p) 97/* known models */
98static 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
62static int calc_checksum(unsigned char* buf, int n) 118/* checksums the firmware (the firmware header contains the verification) */
119static 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 */
74static void dump_header(unsigned char* buf, int i) 131static 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
87static int dump_lib(unsigned char* buf, int i) 217typedef 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
112int main(int argc, char* argv[]) 229static 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) { 231static 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]); 294static int unknown = 0;
295static int padding = 0;
296static 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)); 390static 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 */ 457static 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); 495int 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}