comparison mysteryffs/extract.c @ 25:ae5337f881e3

MysteryFFS: beginning of the extract utility
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Sat, 18 May 2013 23:08:13 +0000
parents
children d19b4e20ff9f
comparison
equal deleted inserted replaced
24:9f3a7b014e63 25:ae5337f881e3
1 /*
2 * This program is the logical culmination of the MysteryFFS reverse eng
3 * experiments: it walks the FFS directory tree from the root down,
4 * recreates a matching tree in the local Unix file system, and
5 * extracts the full content of all data files.
6 *
7 * All acquired understanding of the MysteryFFS structure is tested
8 * in the process.
9 */
10
11 #include <sys/types.h>
12 #include <sys/file.h>
13 #include <sys/stat.h>
14 #include <endian.h>
15 #include <ctype.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <strings.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21
22 typedef unsigned char u8;
23 typedef unsigned short u16;
24 typedef unsigned int u32;
25
26 u8 mysteryffs_hdr[6] = {'F', 'f', 's', '#', 0x10, 0x02};
27
28 /* actual MysteryFFS on-media structure */
29 struct mysteryffs_index {
30 u16 len;
31 u8 unknown_b1;
32 u8 type;
33 u16 descend;
34 u16 sibling;
35 u32 dataptr;
36 u16 unknown_w1;
37 u16 unknown_w2;
38 };
39
40 /* our own struct for convenience */
41 struct objinfo {
42 u16 entryno;
43 struct mysteryffs_index *idxrec;
44 u8 *dataptr;
45 u32 offset;
46 u16 len;
47 u8 type;
48 u16 descend;
49 u16 sibling;
50 };
51
52 char *imgfile;
53 u32 eraseblk_size;
54 int total_blocks;
55 u32 total_img_size;
56 u8 *image, *indexblk;
57
58 char workpath[512];
59
60 read_img_file()
61 {
62 int fd;
63 struct stat st;
64
65 fd = open(imgfile, O_RDONLY);
66 if (fd < 0) {
67 perror(imgfile);
68 exit(1);
69 }
70 fstat(fd, &st);
71 if (!S_ISREG(st.st_mode)) {
72 fprintf(stderr, "%s is not a regular file\n", imgfile);
73 exit(1);
74 }
75 if (st.st_size < total_img_size) {
76 fprintf(stderr, "%s has fewer than 0x%x bytes\n", imgfile,
77 total_img_size);
78 exit(1);
79 }
80 image = malloc(total_img_size);
81 if (!image) {
82 perror("malloc");
83 exit(1);
84 }
85 read(fd, image, total_img_size);
86 close(fd);
87 }
88
89 find_index_block()
90 {
91 int i;
92 u8 *ptr;
93
94 for (ptr = image, i = 0; i < total_blocks; i++, ptr += eraseblk_size) {
95 if (bcmp(ptr, mysteryffs_hdr, 6))
96 continue;
97 if (ptr[8] != 0xAB)
98 continue;
99 printf("Found index in erase block #%d (offset %x)\n", i,
100 ptr - image);
101 indexblk = ptr;
102 return(0);
103 }
104 fprintf(stderr, "could not find a MysteryFFS index block in %s\n",
105 imgfile);
106 exit(1);
107 }
108
109 get_index_entry(oi)
110 struct objinfo *oi;
111 {
112 struct mysteryffs_index *le;
113
114 if (oi->entryno >= (eraseblk_size >> 4)) {
115 fprintf(stderr,
116 "error: index block pointer %x past the erase block size!\n",
117 oi->entryno);
118 exit(1);
119 }
120 le = (struct mysteryffs_index *) indexblk + oi->entryno;
121 oi->idxrec = le;
122 oi->len = le16toh(le->len);
123 oi->type = le->type;
124 oi->descend = le16toh(le->descend);
125 oi->sibling = le16toh(le->sibling);
126 return(0);
127 }
128
129 validate_chunk(oi)
130 struct objinfo *oi;
131 {
132 u32 dptr;
133
134 if (oi->len & 0xF || !oi->len) {
135 fprintf(stderr, "index entry #%x: invalid chunk length\n",
136 oi->entryno);
137 exit(1);
138 }
139 dptr = le32toh(oi->idxrec->dataptr);
140 if (dptr > 0x0FFFFFFF) {
141 invdptr: fprintf(stderr, "index entry #%x: invalid data pointer\n",
142 oi->entryno);
143 exit(1);
144 }
145 dptr <<= 4;
146 if (dptr >= total_img_size - oi->len)
147 goto invdptr;
148 oi->offset = dptr;
149 oi->dataptr = image + dptr;
150 return(0);
151 }
152
153 validate_obj_name(oi)
154 struct objinfo *oi;
155 {
156 u8 *cp;
157 int cnt;
158
159 for (cp = oi->dataptr, cnt = 0; ; cp++, cnt++) {
160 if (cnt >= oi->len) {
161 fprintf(stderr,
162 "object at index %x: name expected at %x: length overrun\n",
163 oi->entryno, oi->offset);
164 exit(1);
165 }
166 if (!*cp)
167 break;
168 if (*cp < '!' || *cp > '~') {
169 fprintf(stderr,
170 "object at index %x: name expected at %x: bad character\n",
171 oi->entryno, oi->offset);
172 exit(1);
173 }
174 }
175 if (!cnt) {
176 fprintf(stderr,
177 "object at index %x: name expected at %x: null string\n",
178 oi->entryno, oi->offset);
179 exit(1);
180 }
181 return(0);
182 }
183
184 name_safe_for_extract(oi)
185 struct objinfo *oi;
186 {
187 char *s;
188
189 s = (char *)oi->dataptr;
190 if (!isalnum(*s) && *s != '_')
191 return(0);
192 for (s++; *s; s++)
193 if (!isalnum(*s) && *s != '_' && *s != '.')
194 return(0);
195 return(1);
196 }
197
198 dump_dir(firstent, path_prefix)
199 {
200 int ent;
201 struct objinfo obj;
202 int typechar;
203 u32 length;
204
205 for (ent = firstent; ent != 0xFFFF; ent = obj.sibling) {
206 obj.entryno = ent;
207 get_index_entry(&obj);
208 if (!obj.type) /* skip deleted objects w/o further validation */
209 continue;
210 validate_chunk(&obj);
211 validate_obj_name(&obj);
212 if (path_prefix + strlen(obj.dataptr) + 2 > sizeof workpath) {
213 fprintf(stderr,
214 "handling object at index %x, name \"%s\": path buffer overflow\n",
215 obj.entryno, (char *)obj.dataptr);
216 exit(1);
217 }
218 sprintf(workpath + path_prefix, "/%s", (char *)obj.dataptr);
219 switch (obj.type) {
220 case 0xF2:
221 /* directory */
222 printf("dir: %s\n", workpath);
223 /* mkdir will go here */
224 dump_dir(obj.descend, strlen(workpath));
225 continue;
226 case 0xF1:
227 /* regular file */
228 printf("file: %s\n", workpath);
229 /* file extraction will go here */
230 continue;
231 case 0xE1:
232 /* special .journal file */
233 if (path_prefix == 0 &&
234 !strcmp((char *)obj.dataptr, ".journal"))
235 printf("skipping /.journal\n");
236 else
237 printf("skipping unexpected E1 file: %s\n",
238 workpath);
239 continue;
240 default:
241 printf("%s (index entry #%x): unexpected type %02X; skipping\n",
242 workpath, obj.entryno, obj.type);
243 continue;
244 }
245 }
246 }
247
248 dump_root()
249 {
250 struct objinfo root;
251
252 root.entryno = 1;
253 get_index_entry(&root);
254 validate_chunk(&root);
255 validate_obj_name(&root);
256 printf("Root node name: %s\n", (char *)root.dataptr);
257 if (root.type != 0xF2) {
258 fprintf(stderr,
259 "error: index entry #1 (expected root dir) is not a directory\n");
260 exit(1);
261 }
262 if (root.sibling != 0xFFFF)
263 printf("warning: root entry has a non-nil sibling pointer\n");
264 dump_dir(root.descend, 0);
265 }
266
267 main(argc, argv)
268 char **argv;
269 {
270 if (argc != 4) {
271 fprintf(stderr, "usage: %s imgfile blksize nblocks\n", argv[0]);
272 exit(1);
273 }
274 imgfile = argv[1];
275 eraseblk_size = strtoul(argv[2], 0, 0);
276 total_blocks = strtoul(argv[3], 0, 0);
277 total_img_size = eraseblk_size * total_blocks;
278 read_img_file();
279 find_index_block();
280 dump_root();
281 exit(0);
282 }