Calling conventions
x86
Let's take another simple example.
main.c
int add(int a, int b)
{
return a + b;
}
int main()
{
return add(1, 2);
}
When we call a function in C, the arguments are pushed on the stack.
This can be verified by disassembling the binary obtained from compilating the code.
# compile
gcc main.c -m32 -o main
# disassemble directly in bash
objdump -d -M intel main
Which gives :
00001194 <main>:
1194: 55 push ebp
1195: 89 e5 mov ebp,esp
1197: e8 13 00 00 00 call 11af <__x86.get_pc_thunk.ax>
119c: 05 58 2e 00 00 add eax,0x2e58
11a1: 6a 02 push 0x2 # second argument
11a3: 6a 01 push 0x1 # first argument
11a5: e8 d3 ff ff ff call 117d <add> # call add()
11aa: 83 c4 08 add esp,0x8
11ad: c9 leave
11ae: c3 ret
So on the x86 architecture, arguments in a function call are always pushed on the stack, in reverse order.
So that when you actually get inside the function, the stack layout will look like :
pwndbg> stack 20
00:0000│ ebp esp 0xffffd238 —▸ 0xffffd248 ◂— 0x0
01:0004│ 0xffffd23c —▸ 0x565561aa (main+22) ◂— add esp, 8
02:0008│ 0xffffd240 ◂— 0x1
03:000c│ 0xffffd244 ◂— 0x2
04:0010│ 0xffffd248 ◂— 0x0
Rule
Generally, with a function defined like :
int func(int arg1, int arg2, int arg3)
{
int local_var1;
int local_var2;
int local_var3;
return arg1 + arg2 + arg3;
}
Your stack layout will look like this when the function starts :