FreeCalypso > hg > freecalypso-reveng
comparison mysteryffs/dump2.c @ 23:671db68916c7
MysteryFFS: dump2 started, dumping the initial frag of each file
| author | Michael Spacefalcon <msokolov@ivan.Harhan.ORG> |
|---|---|
| date | Sat, 18 May 2013 19:48:07 +0000 |
| parents | |
| children | 9f3a7b014e63 |
comparison
equal
deleted
inserted
replaced
| 22:00ad22936ca5 | 23:671db68916c7 |
|---|---|
| 1 /* | |
| 2 * This program attempts to traverse the FFS directory tree | |
| 3 * from the root down, following the descendant and sibling | |
| 4 * pointers, and dumps everything it encounters. | |
| 5 * | |
| 6 * The objective is to understand how to extract the precise | |
| 7 * content of data files. | |
| 8 */ | |
| 9 | |
| 10 #include <sys/types.h> | |
| 11 #include <sys/file.h> | |
| 12 #include <sys/stat.h> | |
| 13 #include <endian.h> | |
| 14 #include <ctype.h> | |
| 15 #include <stdio.h> | |
| 16 #include <string.h> | |
| 17 #include <strings.h> | |
| 18 #include <stdlib.h> | |
| 19 #include <unistd.h> | |
| 20 | |
| 21 typedef unsigned char u8; | |
| 22 typedef unsigned short u16; | |
| 23 typedef unsigned int u32; | |
| 24 | |
| 25 u8 mysteryffs_hdr[6] = {'F', 'f', 's', '#', 0x10, 0x02}; | |
| 26 | |
| 27 struct index_entry { | |
| 28 u16 len; | |
| 29 u8 unknown_b1; | |
| 30 u8 type; | |
| 31 u16 descend; | |
| 32 u16 sibling; | |
| 33 u32 dataptr; | |
| 34 u16 unknown_w1; | |
| 35 u16 unknown_w2; | |
| 36 }; | |
| 37 | |
| 38 char *imgfile; | |
| 39 u32 eraseblk_size; | |
| 40 int total_blocks; | |
| 41 u32 total_img_size; | |
| 42 u8 *image, *indexblk; | |
| 43 | |
| 44 char workpath[512]; | |
| 45 | |
| 46 read_img_file() | |
| 47 { | |
| 48 int fd; | |
| 49 struct stat st; | |
| 50 | |
| 51 fd = open(imgfile, O_RDONLY); | |
| 52 if (fd < 0) { | |
| 53 perror(imgfile); | |
| 54 exit(1); | |
| 55 } | |
| 56 fstat(fd, &st); | |
| 57 if (!S_ISREG(st.st_mode)) { | |
| 58 fprintf(stderr, "%s is not a regular file\n", imgfile); | |
| 59 exit(1); | |
| 60 } | |
| 61 if (st.st_size < total_img_size) { | |
| 62 fprintf(stderr, "%s has fewer than 0x%x bytes\n", imgfile, | |
| 63 total_img_size); | |
| 64 exit(1); | |
| 65 } | |
| 66 image = malloc(total_img_size); | |
| 67 if (!image) { | |
| 68 perror("malloc"); | |
| 69 exit(1); | |
| 70 } | |
| 71 read(fd, image, total_img_size); | |
| 72 close(fd); | |
| 73 } | |
| 74 | |
| 75 find_index_block() | |
| 76 { | |
| 77 int i; | |
| 78 u8 *ptr; | |
| 79 | |
| 80 for (ptr = image, i = 0; i < total_blocks; i++, ptr += eraseblk_size) { | |
| 81 if (bcmp(ptr, mysteryffs_hdr, 6)) | |
| 82 continue; | |
| 83 if (ptr[8] != 0xAB) | |
| 84 continue; | |
| 85 printf("Found index in erase block #%d (offset %x)\n", i, | |
| 86 ptr - image); | |
| 87 indexblk = ptr; | |
| 88 return(0); | |
| 89 } | |
| 90 fprintf(stderr, "could not find a MysteryFFS index block in %s\n", | |
| 91 imgfile); | |
| 92 exit(1); | |
| 93 } | |
| 94 | |
| 95 get_index_entry(num, host) | |
| 96 int num; | |
| 97 struct index_entry *host; | |
| 98 { | |
| 99 struct index_entry *le; | |
| 100 | |
| 101 le = (struct index_entry *) indexblk + num; | |
| 102 host->len = le16toh(le->len); | |
| 103 host->unknown_b1 = le->unknown_b1; | |
| 104 host->type = le->type; | |
| 105 host->descend = le16toh(le->descend); | |
| 106 host->sibling = le16toh(le->sibling); | |
| 107 host->dataptr = le32toh(le->dataptr); | |
| 108 host->unknown_w1 = le16toh(le->unknown_w1); | |
| 109 host->unknown_w2 = le16toh(le->unknown_w2); | |
| 110 } | |
| 111 | |
| 112 is_namestr_ok(s) | |
| 113 char *s; | |
| 114 { | |
| 115 int cnt; | |
| 116 | |
| 117 for (cnt = 0; *s; s++, cnt++) { | |
| 118 if (cnt >= 32) | |
| 119 return(0); | |
| 120 if (!isprint(*s)) | |
| 121 return(0); | |
| 122 } | |
| 123 if (cnt) | |
| 124 return(1); | |
| 125 else | |
| 126 return(0); | |
| 127 } | |
| 128 | |
| 129 char * | |
| 130 get_name(dptr) | |
| 131 u32 dptr; | |
| 132 { | |
| 133 u8 *name; | |
| 134 | |
| 135 if (dptr > 0x0FFFFFFF) | |
| 136 return(0); | |
| 137 dptr <<= 4; | |
| 138 if (dptr >= total_img_size - 32) | |
| 139 return(0); | |
| 140 name = image + dptr; | |
| 141 if (is_namestr_ok(name)) | |
| 142 return(name); | |
| 143 else | |
| 144 return(0); | |
| 145 } | |
| 146 | |
| 147 dump_common(idx, rec, path_prefix, typestr, newprefix) | |
| 148 int idx, path_prefix, *newprefix; | |
| 149 struct index_entry *rec; | |
| 150 char *typestr; | |
| 151 { | |
| 152 u8 *name; | |
| 153 | |
| 154 name = get_name(rec->dataptr); | |
| 155 if (!name) { | |
| 156 printf("entry #%x has an invalid name pointer!\n", idx); | |
| 157 return(-1); | |
| 158 } | |
| 159 if (sizeof(workpath) - path_prefix < strlen(name) + 2) { | |
| 160 printf("entry #%x: pathname buffer overflow!\n", idx); | |
| 161 return(-1); | |
| 162 } | |
| 163 path_prefix += sprintf(workpath + path_prefix, "/%s", name); | |
| 164 printf("\n%s (%s)\n", workpath, typestr); | |
| 165 printf("len=%x, unknown fields: %02X %04X %04X\n", rec->len, | |
| 166 rec->unknown_b1, rec->unknown_w1, rec->unknown_w2); | |
| 167 if (newprefix) | |
| 168 *newprefix = path_prefix; | |
| 169 return(0); | |
| 170 } | |
| 171 | |
| 172 dump_data_frag(entryidx, rec) | |
| 173 struct index_entry *rec; | |
| 174 { | |
| 175 u32 dptr, endptr; | |
| 176 int i, c; | |
| 177 | |
| 178 if (rec->len & 0xF || !rec->len) { | |
| 179 printf("entry #%x: don't know how to dump fragment of length %x\n", | |
| 180 entryidx, rec->len); | |
| 181 return(-1); | |
| 182 } | |
| 183 dptr = rec->dataptr; | |
| 184 if (dptr > 0x0FFFFFFF) { | |
| 185 inv: printf("entry #%x: invalid data pointer\n", entryidx); | |
| 186 return(-1); | |
| 187 } | |
| 188 dptr <<= 4; | |
| 189 if (dptr > total_img_size - rec->len) | |
| 190 goto inv; | |
| 191 for (endptr = dptr + rec->len; dptr < endptr; dptr += 0x10) { | |
| 192 printf("%08X: ", dptr); | |
| 193 for (i = 0; i < 16; i++) { | |
| 194 printf("%02X ", image[dptr + i]); | |
| 195 if (i == 7 || i == 15) | |
| 196 putchar(' '); | |
| 197 } | |
| 198 for (i = 0; i < 16; i++) { | |
| 199 c = image[dptr + i]; | |
| 200 if (!isprint(c)) | |
| 201 c = '.'; | |
| 202 putchar(c); | |
| 203 } | |
| 204 putchar('\n'); | |
| 205 } | |
| 206 return(0); | |
| 207 } | |
| 208 | |
| 209 dump_dir(firstent, path_prefix) | |
| 210 { | |
| 211 struct index_entry rec; | |
| 212 int ent; | |
| 213 int subprefix; | |
| 214 | |
| 215 for (ent = firstent; ent != 0xFFFF; ent = rec.sibling) { | |
| 216 get_index_entry(ent, &rec); | |
| 217 switch (rec.type) { | |
| 218 case 0x00: | |
| 219 /* deleted object - skip it */ | |
| 220 continue; | |
| 221 case 0xF2: | |
| 222 /* subdirectory */ | |
| 223 if (dump_common(ent, &rec, path_prefix, "directory", | |
| 224 &subprefix) < 0) | |
| 225 continue; | |
| 226 dump_dir(rec.descend, subprefix); | |
| 227 continue; | |
| 228 case 0xF1: | |
| 229 /* regular file */ | |
| 230 dump_common(ent, &rec, path_prefix, "file", 0); | |
| 231 dump_data_frag(ent, &rec); | |
| 232 continue; | |
| 233 case 0xE1: | |
| 234 /* special .journal file */ | |
| 235 dump_common(ent, &rec, path_prefix, "E1 file", 0); | |
| 236 dump_data_frag(ent, &rec); | |
| 237 continue; | |
| 238 default: | |
| 239 printf("entry #%x: unexpected type %02X\n", ent, | |
| 240 rec.type); | |
| 241 } | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 dump_root() | |
| 246 { | |
| 247 struct index_entry rec; | |
| 248 char *name; | |
| 249 | |
| 250 get_index_entry(1, &rec); | |
| 251 if (rec.type != 0xF2) { | |
| 252 fprintf(stderr, | |
| 253 "error: entry #1 (expected root dir) is not a directory\n"); | |
| 254 exit(1); | |
| 255 } | |
| 256 name = get_name(rec.dataptr); | |
| 257 if (!name) { | |
| 258 fprintf(stderr, "root entry has an invalid name pointer!\n"); | |
| 259 exit(1); | |
| 260 } | |
| 261 printf("Root node name: %s\n", name); | |
| 262 printf("len=%x, unknown fields: %02X %04X %04X\n", rec.len, | |
| 263 rec.unknown_b1, rec.unknown_w1, rec.unknown_w2); | |
| 264 if (rec.sibling != 0xFFFF) | |
| 265 printf("warning: root entry has a non-nil sibling pointer\n"); | |
| 266 dump_dir(rec.descend, 0); | |
| 267 } | |
| 268 | |
| 269 main(argc, argv) | |
| 270 char **argv; | |
| 271 { | |
| 272 if (argc != 4) { | |
| 273 fprintf(stderr, "usage: %s imgfile blksize nblocks\n", argv[0]); | |
| 274 exit(1); | |
| 275 } | |
| 276 imgfile = argv[1]; | |
| 277 eraseblk_size = strtoul(argv[2], 0, 0); | |
| 278 total_blocks = strtoul(argv[3], 0, 0); | |
| 279 total_img_size = eraseblk_size * total_blocks; | |
| 280 read_img_file(); | |
| 281 find_index_block(); | |
| 282 dump_root(); | |
| 283 exit(0); | |
| 284 } |
