โฌข DragonFlyBSD Kernel Audit
DF-5755 / dump5755.c
โ† back to finding โ†“ download raw
/* DF-5755 โ€” scan the raw FAT image for the UTF-16LE (LFN) encodings that the
 * kernel wrote for our overlong-UTF-8 filenames.  Proves the synthesized
 * characters landed in on-disk directory metadata.
 */
#include <sys/types.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct pat {
	const char *label;
	const unsigned char bytes[32];
	size_t len;
};

/* UTF-16LE encodings, as written by unix2winfn() (low byte first).
 * LFN entries hold 5 units in wePart1, 6 in wePart2, 2 in wePart3 โ€”
 * long names are split, so search per-part as well as whole. */
static struct pat pats[] = {
	{ "OV/S3 LFN part1 (O V / S 3) [units 0-4, wePart1]",
	  { 0x4f,0x00,0x56,0x00,0x2f,0x00,0x53,0x00,0x33,0x00 }, 10 },
	{ "OV/SLASH LFN part1 (O V / S L) [units 0-4, wePart1]",
	  { 0x4f,0x00,0x56,0x00,0x2f,0x00,0x53,0x00,0x4c,0x00 }, 10 },
	{ "OV/SLASH LFN part2 (A S H NUL) [units 5-8, wePart2]",
	  { 0x41,0x00,0x53,0x00,0x48,0x00,0x00,0x00 }, 8 },
	{ "OV.DOT LFN part1 (O V . D O) [units 0-4, wePart1]",
	  { 0x4f,0x00,0x56,0x00,0x2e,0x00,0x44,0x00,0x4f,0x00 }, 10 },
	{ "OV.DOT LFN part2 (T NUL) [unit 5, wePart2]",
	  { 0x54,0x00,0x00,0x00 }, 4 },
	{ "8.3 alias 'OV/SLASH   ' (raw '/' in short entry)",
	  { 'O','V','/','S','L','A','S','H',' ',' ',' ' }, 11 },
	{ "8.3 alias 'OV/S3   ' (raw '/' in short entry)",
	  { 'O','V','/','S','3',' ',' ',' ',' ',' ',' ' }, 11 },
	{ NULL, { 0 }, 0 }
};

int
main(int argc, char **argv)
{
	unsigned char *buf;
	ssize_t n;
	size_t sz, i;
	int fd, p, found_any = 0;

	if (argc != 2)
		errx(1, "usage: %s <image>", argv[0]);
	fd = open(argv[1], O_RDONLY);
	if (fd < 0)
		err(1, "open %s", argv[1]);
	sz = lseek(fd, 0, SEEK_END);
	lseek(fd, 0, SEEK_SET);
	buf = malloc(sz);
	if (!buf)
		err(1, "malloc");
	n = read(fd, buf, sz);
	if (n < 0 || (size_t)n != sz)
		err(1, "read");
	close(fd);

	for (p = 0; pats[p].label; p++) {
		int hits = 0;
		for (i = 0; i + pats[p].len <= sz; i++) {
			if (memcmp(buf + i, pats[p].bytes, pats[p].len) == 0) {
				size_t j, st = i >= 16 ? i - 16 : 0;
				size_t en = i + pats[p].len + 16;
				printf("[FOUND] %s at file offset 0x%zx:\n",
				    pats[p].label, i);
				printf("        context (from 0x%zx): ", st);
				for (j = st; j < en && j < sz; j++)
					printf("%02x ", buf[j]);
				printf("\n");
				/* LFN entry header: seq at i-1 (wePart starts
				 * at entry+1), attr 0x0F at entry+11 */
				if (i >= 1) {
					unsigned char seq = buf[i - 1];
					printf("        LFN seq=0x%02x ", seq);
					if (i + 10 <= sz)
						printf("attr@+11=0x%02x\n",
						    buf[i + 10]);
					else
						printf("\n");
				}
				hits++;
				found_any = 1;
				/* skip ahead to avoid duplicate overlaps */
				i += pats[p].len - 1;
			}
		}
		if (!hits)
			printf("[absent] %s โ€” pattern NOT in image\n",
			    pats[p].label);
	}
	free(buf);
	return found_any ? 0 : 2;
}