summaryrefslogtreecommitdiffstats
path: root/utils/AMS/hacking/amsinfo.c
diff options
context:
space:
mode:
authorRafaël Carré <rafael.carre@gmail.com>2009-10-27 11:19:49 +0000
committerRafaël Carré <rafael.carre@gmail.com>2009-10-27 11:19:49 +0000
commit03f88d53ee3ac69add22d248103a9d468c0b36d2 (patch)
treea7c1133a6eaa5b4ea70ef676f88ecc7086a99d2e /utils/AMS/hacking/amsinfo.c
parentc9667e6d235c94b8c785260f4ce30e43930b1c45 (diff)
downloadrockbox-03f88d53ee3ac69add22d248103a9d468c0b36d2.tar.gz
rockbox-03f88d53ee3ac69add22d248103a9d468c0b36d2.tar.bz2
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.c599
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 @@
/*
-
-amsinfo - a tool for examining AMS firmware files
-
-Copyright (C) Dave Chapman 2007
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
-
-*/
-
+ * Copyright © 2008 Rafaël Carré <rafael.carre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
+ *
+ */
+
+#define _ISOC99_SOURCE /* snprintf() */
#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <errno.h>
#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#if 1 /* ANSI colors */
-/* Win32 compatibility */
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-
-#define PAD_TO_BOUNDARY(x) ((x) + 0x1ff) & ~0x1ff;
-
+# define color(a) printf("%s",a)
+char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
-static off_t filesize(int fd) {
- struct stat buf;
+char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
+char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
+char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
+char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
+char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
- if (fstat(fd,&buf) < 0) {
- perror("[ERR] Checking filesize of input file");
- return -1;
- } else {
- return(buf.st_size);
- }
-}
+#else
+ /* disable colors */
+# define color(a)
+#endif
-static uint32_t get_uint32le(unsigned char* p)
+#define LIB_OFFSET 160 /* FIXME (see below) */
+/* The alignement of library blocks (in number of 0x200 bytes blocks)
+ * alignement - md5sum - filename - model
+ * 120 : fc9dd6116001b3e6a150b898f1b091f0 m200p-4.1.08A.bin M200
+ * 128 : 82e3194310d1514e3bbcd06e84c4add3 m200p.bin Fuze
+ * 160 : c12711342169c66e209540cd1f27cd26 m300f.bin CLIP
+ *
+ * Note : the size of library blocks is variable:
+ *
+ * For m200p-4.1.08A.bin it's always 0x1e000 blocks = 240 * 0x200
+ *
+ * For m200p.bin it can be 0x20000 (256*0x200) or 0x40000 (512*0x200)
+ * (for "acp_decoder" and "sd_reload__" blocks)
+ *
+ * For m300f.bin it can be 0x28000 (320*0x200) or 0x14000 (160 * 0x200)
+ *
+ */
+
+#define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0)
+#define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
+
+/* byte swapping */
+#define get32le(a) ((uint32_t) \
+ ( buf[a+3] << 24 | buf[a+2] << 16 | buf[a+1] << 8 | buf[a] ))
+#define get16le(a) ((uint16_t)( buf[a+1] << 8 | buf[a] ))
+
+/* all blocks are sized as a multiple of 0x1ff */
+#define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
+
+/* If you find a firmware that breaks the known format ^^ */
+#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)
+
+/* globals */
+
+size_t sz; /* file size */
+uint8_t *buf; /* file content */
+
+
+/* 1st block description */
+uint32_t idx,checksum,bs_multiplier,firmware_sz;
+uint32_t unknown_4_1; uint8_t unknown_1,id; uint16_t unknown_2;
+uint32_t unknown_4_2,unknown_4_3;
+
+static void *xmalloc(size_t s) /* malloc helper */
{
- return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+ void * r = malloc(s);
+ if(!r) bugp("malloc");
+ return r;
}
-static uint16_t get_uint16le(unsigned char* p)
+/* known models */
+static const char * model(uint8_t id)
{
- return p[0] | (p[1] << 8);
+ switch(id)
+ {
+ case 0x1E: return "FUZE"; break;
+ case 0x22: return "CLIP"; break;
+ case 0x23: return "C200"; break;
+ case 0x24: return "E200"; break;
+ case 0x25: return "M200"; break;
+ case 0x27: return "CLV2"; break;
+ case 0x70:
+ case 0x6d: return "FUZ2"; break;
+ default:
+ printf("Unknown ID 0x%x\n", id);
+
+ assert(id == 0x1E || (id > 0x21 && id < 0x26));
+ return "UNKNOWN!";
+ }
}
-static int calc_checksum(unsigned char* buf, int n)
+/* checksums the firmware (the firmware header contains the verification) */
+static uint32_t do_checksum(void)
{
- int sum = 0;
- int i;
+ uint32_t c = 0;
- for (i=0;i<n;i+=4)
- sum += get_uint32le(buf + 0x400 + i);
+ size_t i = 0x400/4;
+ while(i<(0x400+firmware_sz)/4)
+ c += ((uint32_t*)buf)[i++];
- return sum;
+ return c;
}
-
-static void dump_header(unsigned char* buf, int i)
+/* verify the firmware header */
+static void check(void)
{
- printf("0x%08x:\n",i);
- printf(" HEADER: 0x%08x\n",i);
- printf(" FirmwareHeaderIndex: 0x%08x\n",get_uint32le(&buf[i]));
- printf(" FirmwareChecksum: 0x%08x\n",get_uint32le(&buf[i+0x04]));
- printf(" CodeBlockSizeMultiplier: 0x%08x\n",get_uint32le(&buf[i+0x08]));
- printf(" FirmwareSize: 0x%08x\n",get_uint32le(&buf[i+0x0c]));
- printf(" Unknown1: 0x%08x\n",get_uint32le(&buf[i+0x10]));
- printf(" ModelID: 0x%04x\n",get_uint16le(&buf[i+0x14]));
- printf(" Unknown2: 0x%04x\n",get_uint16le(&buf[i+0x16]));
+ uint32_t checksum2;
+
+ assert(sz >= 0x400 && sz % 0x200 == 0);
+
+ size_t i;
+ checksum2 = 0;
+ for(i=0;i<sz/4-1;i++)
+ checksum2 += ((uint32_t*)buf)[i];
+
+ uint32_t last_word = get32le(sz - 4);
+
+ switch(last_word)
+ {
+ case 0: /* no whole file checksum */
+ break;
+ case 0xefbeadde: /* no whole file checksum */
+ break;
+ default: /* verify whole file checksum */
+ assert(last_word == checksum2);
+ }
+
+ idx = get32le(0);
+ unsigned int shift = (get32le(4) == 0x0000f000) ? 4 : 0;
+ checksum = get32le(4 + shift);
+ bs_multiplier = get32le(8 + shift);
+ firmware_sz = get32le(0xc + shift);
+ assert(bs_multiplier << 9 == PAD_TO_BOUNDARY(firmware_sz)); /* 0x200 * bs_multiplier */
+
+ unknown_4_1 = get32le(0x10 + shift);
+ unknown_1 = buf[0x14 + shift];
+ id = buf[0x15 + shift];
+ unknown_2 = get16le(0x16 + shift);
+ unknown_4_2 = get32le(0x18 + shift);
+ unknown_4_3 = get32le(0x1c + shift);
+
+ color(GREEN);
+ printf("4 Index %d\n",idx);
+ assert(idx == 0);
+ color(GREEN);
+ printf("4 Firmware Checksum %x",checksum);
+ checksum2=do_checksum();
+ color(GREEN);
+ printf(" (%x)\n",checksum2);
+ assert(checksum == checksum2);
+ color(GREEN);
+ printf("4 Block Size Multiplier %x\n",bs_multiplier);
+ color(GREEN);
+ printf("4 Firmware block size %x (%d)\n",firmware_sz,firmware_sz);
+
+ color(GREEN);
+ printf("4 Unknown (should be 3) %x\n",unknown_4_1);
+ assert(unknown_4_1 == 3);
+
+ /* variable */
+ color(GREEN);
+ printf("1 Unknown %x\n",unknown_1);
+
+ color(GREEN);
+ printf("1 Model ID %x (%s)\n",id,model(id));
+
+ color(GREEN);
+ printf("2 Unknown (should be 0) %x\n",unknown_2);
+ assert(unknown_2 == 0);
+
+ color(GREEN);
+ printf("4 Unknown (should be 40) %x\n",unknown_4_2);
+ assert(unknown_4_2 == 0x40 );
+
+ color(GREEN);
+ printf("4 Unknown (should be 1) %x\n",unknown_4_3);
+ assert(unknown_4_3 == 1);
+
+ /* rest of the block is padded with 0xff */
+ for(i=0x20 + shift;i<0x200 - shift;i++)
+ assert(buf[i]==0xff /* normal case */ ||
+ ((id==0x1e||id==0x24) && ( /* Fuze or E200 */
+ (i>=0x3c && i<=0x3f && get32le(0x3c)==0x00005000)
+ )));
+
+ /* the 2nd block is identical, except that the 1st byte has been incremented */
+ assert(buf[0x0]==0&&buf[0x200]==1);
+ assert(!memcmp(&buf[1],&buf[0x201],0x1FF - shift));
}
-static int dump_lib(unsigned char* buf, int i)
+typedef enum
{
- int export_count;
- int size;
- int unknown1;
- int baseaddr, endaddr;
-
- baseaddr = get_uint32le(&buf[i+0x04]);
- endaddr = get_uint32le(&buf[i+0x08]);
- size = get_uint32le(&buf[i+0x0c]);
- unknown1 = get_uint32le(&buf[i+0x10]);
- export_count = get_uint32le(&buf[i+0x14]);
-
- 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);
-
#if 0
- if (export_count > 1) {
- for (j=0;j<export_count;j++) {
- printf(" Exports[%02d]: 0x%08x\n",j,get_uint32le(&buf[i+0x18+4*j]));
- }
- }
+ FW_HEADER,
+ FW,
#endif
- return PAD_TO_BOUNDARY(size);
-}
+ LIB,
+ PAD,
+ HEADER,
+ UNKNOWN
+} type;
-int main(int argc, char* argv[])
-{
- int fd;
- off_t len;
- int n;
- unsigned char* buf;
- int firmware_size;
- int i;
+static unsigned int n_libs = 0, n_pads_ff = 0, n_pads_deadbeef = 0, n_unkn = 0, n_headers = 0;
- if (argc != 2) {
- fprintf(stderr,"USAGE: amsinfo firmware.bin\n");
- return 1;
- }
-
- fd = open(argv[1],O_RDONLY|O_BINARY);
+static void show_lib(size_t off)
+{
+ /* first word: char* */
+ uint32_t start = get32le(off+4);
+ uint32_t stop = get32le(off+8);
- if ((len = filesize(fd)) < 0)
- return 1;
+ uint32_t size = get32le(off+0xc);
- if ((buf = malloc(len)) == NULL) {
- fprintf(stderr,"[ERR] Could not allocate buffer for input file (%d bytes)\n",(int)len);
- return 1;
- }
+#if 0 /* library block hacking */
+ /* assert(stop > start); */
- n = read(fd, buf, len);
+ /* assert(stop - start == size); */
- if (n != len) {
- fprintf(stderr,"[ERR] Could not read file\n");
- return 1;
- }
+ if(stop - start != size)
+ {
+ color(RED);
+ printf("STOP - START != SIZE || 0x%.8x - 0x%.8x == 0x%.8x != 0x%.8x\n",
+ stop, start, stop - start, size);
+ }
- close(fd);
+ color(RED);
+ printf("0x%.8x -> 0x%.8x SIZE 0x%.6x\n", start, stop, size);
- /* Now we dump the firmware structure */
+ uint32_t first = get32le(off+0x10); /* ? */
+ printf("? = 0x%.8x , ",first);
+#endif
- dump_header(buf,0); /* First copy of header block */
-// dump_header(buf,0x200); /* Second copy of header block */
+ uint32_t funcs = get32le(off+0x14); /* nmbr of functions */
+ color(YELLOW);
+ printf("\t%d funcs",funcs);
+
+ unsigned int i;
+ for(i=0;i<funcs;i++)
+ {
+ uint32_t fptr = get32le(off+0x18+i*4);
+ if(!fptr)
+ {
+ assert(funcs==1); /* if 1 function is exported, it's empty */
+ }
+ else
+ {
+ assert(fptr - start < 0x0000ffff);
+ /* printf("0x%.4x ",fptr); */
+ }
+ }
+
+ color(BLUE);
+ printf("\tBASE 0x%.8x (code + 0x%x) END 0x%.8x : SIZE 0x%.8x\n",start, 0x18 + i*4, stop, stop - start);
+
+ char name[12+1];
+ memcpy(name,&buf[off+get32le(off)],12);
+ name[12] = '\0';
+
+ FILE *out = fopen(name,"w");
+ if(!out)
+ bug("library block");
+
+ if(fwrite(&buf[off],size,1,out)!=1)
+ bug();
+
+ fclose(out);
+}
- firmware_size = get_uint32le(&buf[0x0c]);
+static int unknown = 0;
+static int padding = 0;
+static void print_block(size_t off, type t)
+{
+ /* reset counters if needed */
+ if(t != UNKNOWN && unknown)
+ { /* print only the number of following blocks */
+ color(GREY);
+ printf("%d unknown blocks (0x%.6x bytes)\n",unknown,unknown*0x200);
+ unknown = 0;
+ }
+ else if(t != PAD && padding)
+ { /* same */
+ color(GREY);
+ printf("%d padding blocks (0x%.6x bytes)\n",padding,padding*0x200);
+ padding = 0;
+ }
+
+ if(t != UNKNOWN && t != PAD) /* for other block types, always print the offset */
+ {
+ color(GREEN);
+ printf("0x%.6x\t", (unsigned int)off);
+ color(OFF);
+ }
+
+ switch(t)
+ {
+ size_t s;
+ FILE *f;
+ char filename[8+4]; /* unknown\0 , 10K max */
+#if 0
+ case FW_HEADER:
+ printf("firmware header 0x%x\n",off);
+ break;
+ case FW:
+ printf("firmware block 0x%x\n",off);
+ break;
+#endif
+ case LIB:
+ s = LIB_OFFSET * 0x200;
+ while(s < get32le(off+12))
+ s <<= 1;
+ color(RED);
+ printf("library block 0x%.6x\t->\t0x%.6x\t\"%s\"\n",
+ (unsigned int)s, (unsigned int)(off+s),
+ &buf[off+get32le(off)]);
+ show_lib(off);
+ n_libs++;
+ break;
+ case PAD:
+ if(buf[off] == 0xff)
+ n_pads_ff++;
+ else
+ n_pads_deadbeef++;
+ padding++;
+ break;
+ case UNKNOWN:
+ unknown++;
+ n_unkn++;
+#if 0 /* do not dump unknown blocks */
+ snprintf(filename, sizeof(filename), "unknown%d", n_unkn);
+ f = fopen(filename, "w");
+ if(f)
+ {
+ if( fwrite(buf+off, 0x200, 1, f) != 1 )
+ bugp("unknown block");
+ fclose(f);
+ }
+ else
+ bugp("unknown block");
+#endif
+ break;
+ case HEADER:
+ color(YELLOW);
+ printf("header block 0x%.6x\t->\t0x%.6x\n",
+ PAD_TO_BOUNDARY(get32le(off)),
+ (unsigned int)PAD_TO_BOUNDARY(off+get32le(off)));
+ snprintf(filename, sizeof(filename), "header%d", n_headers++);
+ f = fopen(filename,"w");
+ if(!f)
+ bug("header");
+
+ if(fwrite(&buf[off],get32le(off),1,f)!=1)
+ bug();
+
+ fclose(f);
+
+ break;
+ default:
+ abort();
+ }
+
+ if(t != PAD && t != UNKNOWN)
+ printf("\n");
+}
- printf("Calculated firmware checksum: 0x%08x\n",calc_checksum(buf,firmware_size));
+static size_t verify_block(size_t off)
+{
+ assert(!(off%0x200));
+ assert(off+0x200 < sz);
+
+ size_t s = 0x200;
+ type t = UNKNOWN;
+
+ size_t offset_str = get32le(off);
+ if(get32le(off) == 0xefbeadde )
+ {
+#if 0 /* some blocks begin with 0xdeadbeef but aren't padded with that value */
+ unsigned int i;
+ for(i=0;i<s;i+=4)
+ assert(get32le(off+i) == 0xefbeadde);
+#endif
+ t = PAD;
+ }
+ else if( *(uint32_t*)(&buf[off]) == 0xffffffff)
+ {
+ unsigned int i;
+ for(i=0;i<s;i++)
+ assert(buf[off+i] == 0xff);
+ t = PAD;
+ }
+ else if(off+offset_str+12<sz) /* XXX: we should check that the address at which
+ * the string is located is included in this
+ * library block's size, but we only know the
+ * block's size after we confirmed that this is
+ * a library block (by looking at the 11 chars
+ * ASCII string). */
+ {
+ short int ok = 1;
+ unsigned int i;
+ for(i=0;i<11;i++)
+ if(buf[off+offset_str+i] >> 7 || !buf[off+offset_str+i])
+ ok = 0;
+ if(buf[off+offset_str+11])
+ ok = 0;
+ if(ok) /* library block */
+ {
+ t = LIB;
+ s = LIB_OFFSET * 0x200;
+ while(s < get32le(off+12)) /* of course the minimum is the size
+ * specified in the block header */
+ s <<= 1;
+ }
+ else
+ t = UNKNOWN;
+ }
+ else
+ t = UNKNOWN;
+
+ if(t==UNKNOWN)
+ {
+ if(!strncmp((char*)buf+off+8,"HEADER",6))
+ {
+ s = PAD_TO_BOUNDARY(get32le(off)); /* first 4 bytes le are the block size */
+ t = HEADER;
+ }
+ }
+
+ print_block(off,t);
+
+ return PAD_TO_BOUNDARY(s);
+}
- /* Round size up to next multiple of 0x200 */
+static void extract(void)
+{
+ FILE *out = fopen("firmware","w");
+ if(!out)
+ bug("firmware");
+
+ if(fwrite(&buf[0x400],firmware_sz,1,out)!=1)
+ bug("firmare writing");
+ fclose(out);
+
+ off_t off = PAD_TO_BOUNDARY(0x400 + firmware_sz);
+ unsigned int n = 0;
+
+ printf("\n");
+ color(RED);
+ printf("Extracting\n\n");
+
+ while((unsigned int)(off+0x200)<sz)
+ {
+ /* look at the next 0x200 bytes if we can recognize a block type */
+ off += verify_block(off); /* then skip its real size */
+ n++; /* and look at the next block ;) */
+ }
+
+ /* statistics */
+ printf("\n");
+ color(RED);
+ printf("TOTAL\t%d\tblocks (%d unknown)\n",n,n_unkn);
+ color(BLUE);
+ printf("\t%d\tlibs\n",n_libs);
+ color(GREY);
+ printf("\t%d\tpads ff\n",n_pads_ff);
+ color(GREY);
+ printf("\t%d\tpads deadbeef\n",n_pads_deadbeef);
+ color(GREEN);
+ printf("\t%d\theaders\n",n_headers);
+}
- firmware_size = PAD_TO_BOUNDARY(firmware_size);
+int main(int argc, const char **argv)
+{
+ int fd;
+ struct stat st;
+ if(argc != 2)
+ bug("Usage: %s <firmware>\n",*argv);
- i = firmware_size + 0x400;
+ if( (fd = open(argv[1],O_RDONLY)) == -1 )
+ bugp("opening firmware failed");
- printf("LIBRARY BLOCKS:\n");
- printf("Offset Name BaseAddr EndAddr BlockSize Unknown1 EntryCount\n");
+ if(fstat(fd,&st) == -1)
+ bugp("firmware stat() failed");
+ sz = st.st_size;
- while (get_uint32le(&buf[i]) != 0xffffffff)
- {
- i += dump_lib(buf,i);
+ buf=xmalloc(sz);
+ if(read(fd,buf,sz)!=(ssize_t)sz) /* load the whole file into memory */
+ bugp("reading firmware");
- while (get_uint32le(&buf[i]) == 0xefbeadde)
- i+=4;
- }
+ close(fd);
- printf("0x%08x: PADDING BLOCK\n",i);
-
- return 0;
+ check(); /* verify header and checksums */
+ extract(); /* split in blocks */
+ free(buf);
+ return 0;
}