⬢ DragonFlyBSD Kernel Audit
DF-2658 / scanbin.c
← back to finding ↓ download raw
/*
 * scanbin.c — binary evidence extraction helper for DF-2658 / DF-2660.
 *
 * Modes:
 *   scanbin find <file> <pattern.bin> [maxhits]   print byte offsets of exact matches
 *   scanbin stat <file> <off> <len>               nonzero-byte count of region
 *   scanbin dump <file> <off> <len> <out.bin>     extract region to a file
 *   scanbin cmp  <file> <off> <len> <ref.bin>     compare region vs ref: equal-prefix
 *                                                  length, first-diff offset, ndiff
 *
 * Build: cc -O2 -o scanbin scanbin.c
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>

#define MAXPAT (1 << 20)

static unsigned char patbuf[MAXPAT];

static void
die(const char *m)
{
	fprintf(stderr, "scanbin: %s\n", m);
	exit(1);
}

static off_t
filesize(int fd)
{
	off_t e = lseek(fd, 0, SEEK_END);
	if (e < 0)
		die("lseek");
	lseek(fd, 0, SEEK_SET);
	return e;
}

static ssize_t
preadfull(int fd, void *buf, size_t n, off_t off)
{
	size_t got = 0;
	while (got < n) {
		ssize_t r = pread(fd, (char *)buf + got, n - got, off + got);
		if (r <= 0)
			break;
		got += r;
	}
	return got;
}

int
main(int argc, char **argv)
{
	int fd, pfd;
	off_t off, fsz;
	size_t len, patlen, maxhits, i, j, hits, chunk;
	unsigned char *win;
	ssize_t r;

	if (argc < 4)
		die("usage (see source header)");

	if (!strcmp(argv[1], "find")) {
		off_t start = 0, end = -1;
		if (argc < 4)
			die("find <file> <pattern.bin> [maxhits [start [end]]]");
		maxhits = (argc > 4) ? (size_t)strtoul(argv[4], NULL, 0) : 16;
		if (argc > 5)
			start = (off_t)strtoull(argv[5], NULL, 0);
		if (argc > 6)
			end = (off_t)strtoull(argv[6], NULL, 0);
		fd = open(argv[2], O_RDONLY);
		if (fd < 0)
			die("open file");
		pfd = open(argv[3], O_RDONLY);
		if (pfd < 0)
			die("open pattern");
		patlen = read(pfd, patbuf, MAXPAT);
		if (patlen <= 0)
			die("pattern read");
		close(pfd);
		if (patlen == 1) {
			/* byte scan */
			unsigned char c = patbuf[0];
			unsigned char buf[1 << 16];
			hits = 0;
			for (off = start, i = 0;
			    (end < 0 || off < end) && off < (fsz = filesize(fd)); ) {
				size_t want = sizeof(buf);
				if (end >= 0 && off + (off_t)want > end)
					want = (size_t)(end - off);
				r = preadfull(fd, buf, want, off);
				if (r <= 0)
					break;
				for (j = 0; j < (size_t)r; ++j) {
					if (buf[j] == c) {
						printf("HIT %lld\n",
						    (long long)(off + j));
						if (++hits >= maxhits)
							return 0;
					}
				}
				off += r;
			}
			return 0;
		}
		/* windowed naive search */
		chunk = 1 << 23;			/* 8 MiB */
		win = malloc(chunk);
		if (!win)
			die("malloc");
		fsz = filesize(fd);
		if (end < 0 || end > fsz)
			end = fsz;
		hits = 0;
		for (off = start; off < end; ) {
			size_t n = (size_t)(end - off);
			if (n > chunk)
				n = chunk;
			r = preadfull(fd, win, n, off);
			if (r <= 0)
				break;
			n = (size_t)r;
			for (i = 0; i + patlen <= n; ++i) {
				if (!memcmp(win + i, patbuf, patlen)) {
					printf("HIT %lld\n",
					    (long long)(off + i));
					if (++hits >= maxhits)
						return 0;
				}
			}
			if (off + n >= (size_t)end)
				break;
			off += n - (patlen - 1);	/* overlap */
		}
		return 0;
	}

	fd = open(argv[2], O_RDONLY);
	if (fd < 0)
		die("open file");
	off = (off_t)strtoull(argv[3], NULL, 0);
	len = (size_t)strtoul(argv[4], NULL, 0);

	if (!strcmp(argv[1], "stat")) {
		unsigned char *b = malloc(len ? len : 1);
		size_t nz = 0;
		if (!b)
			die("malloc");
		r = preadfull(fd, b, len, off);
		if (r < 0 || (size_t)r != len)
			die("short read");
		for (i = 0; i < len; ++i)
			if (b[i])
				++nz;
		printf("REGION off=%lld len=%zu nonzero=%zu zero=%zu\n",
		    (long long)off, len, nz, len - nz);
		return 0;
	}
	if (!strcmp(argv[1], "dump")) {
		int ofd;
		if (argc < 6)
			die("dump <file> <off> <len> <out.bin>");
		unsigned char *b = malloc(len ? len : 1);
		if (!b)
			die("malloc");
		r = preadfull(fd, b, len, off);
		if ((size_t)r != len)
			die("short read");
		ofd = open(argv[5], O_WRONLY | O_CREAT | O_TRUNC, 0644);
		if (ofd < 0)
			die("open out");
		if (write(ofd, b, len) != (ssize_t)len)
			die("write out");
		close(ofd);
		printf("DUMP %lld + %zu -> %s\n", (long long)off, len,
		    argv[5]);
		return 0;
	}
	if (!strcmp(argv[1], "cmp")) {
		unsigned char *b, *ref;
		size_t reflen, eq = 0, dif = 0, firstdif = (size_t)-1;
		if (argc < 6)
			die("cmp <file> <off> <len> <ref.bin>");
		pfd = open(argv[5], O_RDONLY);
		if (pfd < 0)
			die("open ref");
		reflen = filesize(pfd);
		if (reflen < len)
			die("ref shorter than len");
		ref = malloc(len);
		b = malloc(len);
		if (!ref || !b)
			die("malloc");
		if (preadfull(pfd, ref, len, 0) != (ssize_t)len)
			die("ref read");
		close(pfd);
		r = preadfull(fd, b, len, off);
		if ((size_t)r != len)
			die("short read (file region)");
		for (i = 0; i < len; ++i) {
			if (b[i] == ref[i]) {
				if (dif == 0)
					++eq;
			} else {
				++dif;
				if (firstdif == (size_t)-1)
					firstdif = i;
			}
		}
		printf("CMP off=%lld len=%zu equal_prefix=%zu first_diff=%s "
		    "ndiff=%zu\n", (long long)off, len, eq,
		    firstdif == (size_t)-1 ? "NONE" : "set", dif);
		if (firstdif != (size_t)-1) {
			printf("first_diff_at=%zu (region-relative) "
			    "file_byte=%lld\n", firstdif,
			    (long long)(off + firstdif));
			printf("file@diff :");
			for (j = 0; j < 16 && firstdif + j < len; ++j)
				printf(" %02x", b[firstdif + j]);
			printf("\nref@diff  :");
			for (j = 0; j < 16 && firstdif + j < len; ++j)
				printf(" %02x", ref[firstdif + j]);
			printf("\n");
		}
		return 0;
	}
	die("unknown mode");
	return 1;
}