view mysteryffs/dump1.c @ 22:00ad22936ca5

MysteryFFS dump1 tool written
author Michael Spacefalcon <msokolov@ivan.Harhan.ORG>
date Sat, 18 May 2013 19:21:09 +0000
parents
children
line wrap: on
line source

/*
 * This program attempts to traverse the FFS directory tree
 * from the root down, following the descendant and sibling
 * pointers, and dumps everything it encounters.
 *
 * The objective is to understand how to extract the precise
 * content of data files.
 */

#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};

struct index_entry {
	u16	len;
	u8	unknown_b1;
	u8	type;
	u16	descend;
	u16	sibling;
	u32	dataptr;
	u16	unknown_w1;
	u16	unknown_w2;
};

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(num, host)
	int num;
	struct index_entry *host;
{
	struct index_entry *le;

	le = (struct index_entry *) indexblk + num;
	host->len = le16toh(le->len);
	host->unknown_b1 = le->unknown_b1;
	host->type = le->type;
	host->descend = le16toh(le->descend);
	host->sibling = le16toh(le->sibling);
	host->dataptr = le32toh(le->dataptr);
	host->unknown_w1 = le16toh(le->unknown_w1);
	host->unknown_w2 = le16toh(le->unknown_w2);
}

is_namestr_ok(s)
	char *s;
{
	int cnt;

	for (cnt = 0; *s; s++, cnt++) {
		if (cnt >= 32)
			return(0);
		if (!isprint(*s))
			return(0);
	}
	if (cnt)
		return(1);
	else
		return(0);
}

char *
get_name(dptr)
	u32 dptr;
{
	u8 *name;

	if (dptr > 0x0FFFFFFF)
		return(0);
	dptr <<= 4;
	if (dptr >= total_img_size - 32)
		return(0);
	name = image + dptr;
	if (is_namestr_ok(name))
		return(name);
	else
		return(0);
}

dump_common(idx, rec, path_prefix, typestr, newprefix)
	int idx, path_prefix, *newprefix;
	struct index_entry *rec;
	char *typestr;
{
	u8 *name;

	name = get_name(rec->dataptr);
	if (!name) {
		printf("entry #%x has an invalid name pointer!\n", idx);
		return(-1);
	}
	if (sizeof(workpath) - path_prefix < strlen(name) + 2) {
		printf("entry #%x: pathname buffer overflow!\n", idx);
		return(-1);
	}
	path_prefix += sprintf(workpath + path_prefix, "/%s", name);
	printf("\n%s (%s)\n", workpath, typestr);
	printf("len=%x, unknown fields: %02X %04X %04X\n", rec->len,
		rec->unknown_b1, rec->unknown_w1, rec->unknown_w2);
	if (newprefix)
		*newprefix = path_prefix;
	return(0);
}

dump_dir(firstent, path_prefix)
{
	struct index_entry rec;
	int ent;
	int subprefix;

	for (ent = firstent; ent != 0xFFFF; ent = rec.sibling) {
		get_index_entry(ent, &rec);
		switch (rec.type) {
		case 0x00:
			/* deleted object - skip it */
			continue;
		case 0xF2:
			/* subdirectory */
			if (dump_common(ent, &rec, path_prefix, "directory",
					&subprefix) < 0)
				continue;
			dump_dir(rec.descend, subprefix);
			continue;
		case 0xF1:
			/* regular file */
			dump_common(ent, &rec, path_prefix, "file", 0);
			continue;
		case 0xE1:
			/* special .journal file */
			dump_common(ent, &rec, path_prefix, "E1 file", 0);
			continue;
		default:
			printf("entry #%x: unexpected type %02X\n", ent,
				rec.type);
		}
	}
}

dump_root()
{
	struct index_entry rec;
	char *name;

	get_index_entry(1, &rec);
	if (rec.type != 0xF2) {
		fprintf(stderr,
		"error: entry #1 (expected root dir) is not a directory\n");
		exit(1);
	}
	name = get_name(rec.dataptr);
	if (!name) {
		fprintf(stderr, "root entry has an invalid name pointer!\n");
		exit(1);
	}
	printf("Root node name: %s\n", name);
	printf("len=%x, unknown fields: %02X %04X %04X\n", rec.len,
		rec.unknown_b1, rec.unknown_w1, rec.unknown_w2);
	if (rec.sibling != 0xFFFF)
		printf("warning: root entry has a non-nil sibling pointer\n");
	dump_dir(rec.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);
}