changeset 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 9f3a7b014e63
children d19b4e20ff9f
files .hgignore mysteryffs/Makefile mysteryffs/extract.c
diffstat 3 files changed, 285 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Sat May 18 21:09:33 2013 +0000
+++ b/.hgignore	Sat May 18 23:08:13 2013 +0000
@@ -1,3 +1,4 @@
 re:^mokosrec2bin$
 re:^mysteryffs/dump[12]$
+re:^mysteryffs/extract$
 re:^mysteryffs/scan1$
--- a/mysteryffs/Makefile	Sat May 18 21:09:33 2013 +0000
+++ b/mysteryffs/Makefile	Sat May 18 23:08:13 2013 +0000
@@ -1,6 +1,6 @@
 CC=	gcc
 CFLAGS=	-O2
-PROGS=	dump1 dump2 scan1
+PROGS=	dump1 dump2 extract scan1
 
 all:	${PROGS}
 
@@ -9,6 +9,7 @@
 
 dump1:	dump1.c
 dump2:	dump2.c
+extract: extract.c
 scan1:	scan1.c
 
 clean:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mysteryffs/extract.c	Sat May 18 23:08:13 2013 +0000
@@ -0,0 +1,282 @@
+/*
+ * This program is the logical culmination of the MysteryFFS reverse eng
+ * experiments: it walks the FFS directory tree from the root down,
+ * recreates a matching tree in the local Unix file system, and
+ * extracts the full content of all data files.
+ *
+ * All acquired understanding of the MysteryFFS structure is tested
+ * in the process.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <endian.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+u8 mysteryffs_hdr[6] = {'F', 'f', 's', '#', 0x10, 0x02};
+
+/* actual MysteryFFS on-media structure */
+struct mysteryffs_index {
+	u16	len;
+	u8	unknown_b1;
+	u8	type;
+	u16	descend;
+	u16	sibling;
+	u32	dataptr;
+	u16	unknown_w1;
+	u16	unknown_w2;
+};
+
+/* our own struct for convenience */
+struct objinfo {
+	u16	entryno;
+	struct	mysteryffs_index *idxrec;
+	u8	*dataptr;
+	u32	offset;
+	u16	len;
+	u8	type;
+	u16	descend;
+	u16	sibling;
+};
+
+char *imgfile;
+u32 eraseblk_size;
+int total_blocks;
+u32 total_img_size;
+u8 *image, *indexblk;
+
+char workpath[512];
+
+read_img_file()
+{
+	int fd;
+	struct stat st;
+
+	fd = open(imgfile, O_RDONLY);
+	if (fd < 0) {
+		perror(imgfile);
+		exit(1);
+	}
+	fstat(fd, &st);
+	if (!S_ISREG(st.st_mode)) {
+		fprintf(stderr, "%s is not a regular file\n", imgfile);
+		exit(1);
+	}
+	if (st.st_size < total_img_size) {
+		fprintf(stderr, "%s has fewer than 0x%x bytes\n", imgfile,
+			total_img_size);
+		exit(1);
+	}
+	image = malloc(total_img_size);
+	if (!image) {
+		perror("malloc");
+		exit(1);
+	}
+	read(fd, image, total_img_size);
+	close(fd);
+}
+
+find_index_block()
+{
+	int i;
+	u8 *ptr;
+
+	for (ptr = image, i = 0; i < total_blocks; i++, ptr += eraseblk_size) {
+		if (bcmp(ptr, mysteryffs_hdr, 6))
+			continue;
+		if (ptr[8] != 0xAB)
+			continue;
+		printf("Found index in erase block #%d (offset %x)\n", i,
+			ptr - image);
+		indexblk = ptr;
+		return(0);
+	}
+	fprintf(stderr, "could not find a MysteryFFS index block in %s\n",
+		imgfile);
+	exit(1);
+}
+
+get_index_entry(oi)
+	struct objinfo *oi;
+{
+	struct mysteryffs_index *le;
+
+	if (oi->entryno >= (eraseblk_size >> 4)) {
+		fprintf(stderr,
+		"error: index block pointer %x past the erase block size!\n",
+			oi->entryno);
+		exit(1);
+	}
+	le = (struct mysteryffs_index *) indexblk + oi->entryno;
+	oi->idxrec = le;
+	oi->len = le16toh(le->len);
+	oi->type = le->type;
+	oi->descend = le16toh(le->descend);
+	oi->sibling = le16toh(le->sibling);
+	return(0);
+}
+
+validate_chunk(oi)
+	struct objinfo *oi;
+{
+	u32 dptr;
+
+	if (oi->len & 0xF || !oi->len) {
+		fprintf(stderr, "index entry #%x: invalid chunk length\n",
+			oi->entryno);
+		exit(1);
+	}
+	dptr = le32toh(oi->idxrec->dataptr);
+	if (dptr > 0x0FFFFFFF) {
+invdptr:	fprintf(stderr, "index entry #%x: invalid data pointer\n",
+			oi->entryno);
+		exit(1);
+	}
+	dptr <<= 4;
+	if (dptr >= total_img_size - oi->len)
+		goto invdptr;
+	oi->offset = dptr;
+	oi->dataptr = image + dptr;
+	return(0);
+}
+
+validate_obj_name(oi)
+	struct objinfo *oi;
+{
+	u8 *cp;
+	int cnt;
+
+	for (cp = oi->dataptr, cnt = 0; ; cp++, cnt++) {
+		if (cnt >= oi->len) {
+			fprintf(stderr,
+		"object at index %x: name expected at %x: length overrun\n",
+				oi->entryno, oi->offset);
+			exit(1);
+		}
+		if (!*cp)
+			break;
+		if (*cp < '!' || *cp > '~') {
+			fprintf(stderr,
+		"object at index %x: name expected at %x: bad character\n",
+				oi->entryno, oi->offset);
+			exit(1);
+		}
+	}
+	if (!cnt) {
+		fprintf(stderr,
+		"object at index %x: name expected at %x: null string\n",
+			oi->entryno, oi->offset);
+		exit(1);
+	}
+	return(0);
+}
+
+name_safe_for_extract(oi)
+	struct objinfo *oi;
+{
+	char *s;
+
+	s = (char *)oi->dataptr;
+	if (!isalnum(*s) && *s != '_')
+		return(0);
+	for (s++; *s; s++)
+		if (!isalnum(*s) && *s != '_' && *s != '.')
+			return(0);
+	return(1);
+}
+
+dump_dir(firstent, path_prefix)
+{
+	int ent;
+	struct objinfo obj;
+	int typechar;
+	u32 length;
+
+	for (ent = firstent; ent != 0xFFFF; ent = obj.sibling) {
+		obj.entryno = ent;
+		get_index_entry(&obj);
+		if (!obj.type) /* skip deleted objects w/o further validation */
+			continue;
+		validate_chunk(&obj);
+		validate_obj_name(&obj);
+		if (path_prefix + strlen(obj.dataptr) + 2 > sizeof workpath) {
+			fprintf(stderr,
+	"handling object at index %x, name \"%s\": path buffer overflow\n",
+				obj.entryno, (char *)obj.dataptr);
+			exit(1);
+		}
+		sprintf(workpath + path_prefix, "/%s", (char *)obj.dataptr);
+		switch (obj.type) {
+		case 0xF2:
+			/* directory */
+			printf("dir: %s\n", workpath);
+			/* mkdir will go here */
+			dump_dir(obj.descend, strlen(workpath));
+			continue;
+		case 0xF1:
+			/* regular file */
+			printf("file: %s\n", workpath);
+			/* file extraction will go here */
+			continue;
+		case 0xE1:
+			/* special .journal file */
+			if (path_prefix == 0 &&
+			    !strcmp((char *)obj.dataptr, ".journal"))
+				printf("skipping /.journal\n");
+			else
+				printf("skipping unexpected E1 file: %s\n",
+					workpath);
+			continue;
+		default:
+		printf("%s (index entry #%x): unexpected type %02X; skipping\n",
+				workpath, obj.entryno, obj.type);
+			continue;
+		}
+	}
+}
+
+dump_root()
+{
+	struct objinfo root;
+
+	root.entryno = 1;
+	get_index_entry(&root);
+	validate_chunk(&root);
+	validate_obj_name(&root);
+	printf("Root node name: %s\n", (char *)root.dataptr);
+	if (root.type != 0xF2) {
+		fprintf(stderr,
+	"error: index entry #1 (expected root dir) is not a directory\n");
+		exit(1);
+	}
+	if (root.sibling != 0xFFFF)
+		printf("warning: root entry has a non-nil sibling pointer\n");
+	dump_dir(root.descend, 0);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc != 4) {
+		fprintf(stderr, "usage: %s imgfile blksize nblocks\n", argv[0]);
+		exit(1);
+	}
+	imgfile = argv[1];
+	eraseblk_size = strtoul(argv[2], 0, 0);
+	total_blocks = strtoul(argv[3], 0, 0);
+	total_img_size = eraseblk_size * total_blocks;
+	read_img_file();
+	find_index_block();
+	dump_root();
+	exit(0);
+}