⬢ DragonFlyBSD Kernel Audit
DF-5766 / csprng_dump.c
← back to finding ↓ download raw
/*
 * csprng_dump.c — live introspection of the kernel Fortuna states (DF-5766 PoC)
 *
 * Reads csprng_pcpu[] out of /dev/kmem (symbols from the installed kernel's
 * symtab; struct offsets from kernel.debug DWARF, see VERDICT.md) and dumps
 * each per-CPU csprng_state: reseed_cnt, pool[i].bytes, nrandevents,
 * last_reseed and the current ChaCha key.  "poll" mode watches reseed_cnt.
 *
 * Run as root.  Not a KLD: static kernel symbols cannot be linked by a module,
 * but are readable via /dev/kmem.
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <sys/time.h>

#define PCPU_SYM   0xffffffff81199140UL	/* nm /boot/kernel/kernel: csprng_pcpu */
#define OFF_KEY     0
#define OFF_RCNT    32
#define OFF_POOL    104
#define POOL_STRIDE 112
#define OFF_NEV     4240
#define OFF_LAST    4256

static int kd;

static int
rd(uint64_t addr, void *buf, size_t len)
{
	if (pread(kd, buf, len, (off_t)addr) != (ssize_t)len) {
		perror("pread kmem");
		return -1;
	}
	return 0;
}

static void
dump_one(uint64_t addr, int idx)
{
	uint8_t st[6656];
	uint64_t rc, pb[32], nev, tv[2];
	uint8_t key[32];
	int i;

	if (rd(addr, st, sizeof(st)) < 0)
		exit(1);
	memcpy(key, st + OFF_KEY, 32);
	memcpy(&rc, st + OFF_RCNT, 8);
	for (i = 0; i < 32; i++)
		memcpy(&pb[i], st + OFF_POOL + POOL_STRIDE * i, 8);
	memcpy(&nev, st + OFF_NEV, 8);
	memcpy(tv, st + OFF_LAST, 16);
	printf("state[%d] @%#llx: reseed_cnt=%llu pool0=%llu pool1=%llu "
	       "pool2=%llu pool3=%llu nrandevents=%lld last_reseed=%lld.%06lld\n",
	       idx, (unsigned long long)addr,
	       (unsigned long long)rc,
	       (unsigned long long)pb[0], (unsigned long long)pb[1],
	       (unsigned long long)pb[2], (unsigned long long)pb[3],
	       (long long)nev, (long long)tv[0], (long long)tv[1]);
	printf("    key=");
	for (i = 0; i < 32; i++)
		printf("%02x", key[i]);
	printf("\n    pools:");
	for (i = 0; i < 32; i++)
		printf(" %llu", (unsigned long long)pb[i]);
	printf("\n");
}

int
main(int argc, char **argv)
{
	uint64_t base;
	int ncpu = 6, i;

	kd = open("/dev/kmem", O_RDONLY);
	if (kd < 0) { perror("open /dev/kmem"); return 1; }
	if (argc > 1 && strcmp(argv[1], "poll") == 0) {
		double secs = argc > 2 ? atof(argv[2]) : 30.0;
		uint64_t prev[64], now;
		struct timeval t0, t;
		int events = 0;

		if (rd(PCPU_SYM, &base, 8) < 0) return 1;
		for (i = 0; i < ncpu; i++) {
			prev[i] = 0;
			rd(base + 6656ULL * i + OFF_RCNT, &prev[i], 8);
		}
		gettimeofday(&t0, NULL);
		printf("poll %.1fs: initial reseed_cnt:", secs);
		for (i = 0; i < ncpu; i++) printf(" %llu", (unsigned long long)prev[i]);
		printf("\n");
		for (;;) {
			usleep(50000);
			gettimeofday(&t, NULL);
			double el = (t.tv_sec - t0.tv_sec) + (t.tv_usec - t0.tv_usec) / 1e6;
			if (el > secs) break;
			for (i = 0; i < ncpu; i++) {
				rd(base + 6656ULL * i + OFF_RCNT, &now, 8);
				if (now != prev[i]) {
					prev[i] = now;
					events++;
					printf("t=%7.3f cpu%d reseed_cnt -> %llu\n",
					       el, i, (unsigned long long)now);
				}
			}
		}
		printf("reseed transitions observed in window: %d\n", events);
		return 0;
	}
	if (rd(PCPU_SYM, &base, 8) < 0) return 1;
	printf("csprng_pcpu = %#llx (ncpu=%d)\n", (unsigned long long)base, ncpu);
	for (i = 0; i < ncpu; i++)
		dump_one(base + 6656ULL * i, i);
	return 0;
}