I wanted to understand a litte more about assembly and wrote a little example:
#include <stdio.h>
#include <math.h>
void f() {
unsigned char i[4];
i[0] = 5;
i[1] = 6;
i[2] = 7;
i[3] = 8;
int j = 0;
for(j=0; j < 20; j++)
printf("%02X\n", i[j]);
}
int main() {
int i[5];
i[0] = 3;
i[1] = 3;
i[2] = 3;
i[3] = 3;
i[4] = 3;
f();
return 0;
}
My goal was to see the actual return address for the instruction pointer, laid down by the call to
callq in main(), when it started f().
I used gdb to disassemble main() and got the following
Dump of assembler code for function main:
0x0000000100000eb0 <main+0>: push %rbp
0x0000000100000eb1 <main+1>: mov %rsp,%rbp
0x0000000100000eb4 <main+4>: sub $0x20,%rsp
0x0000000100000eb8 <main+8>: movl $0x3,-0x1c(%rbp)
0x0000000100000ebf <main+15>: movl $0x3,-0x18(%rbp)
0x0000000100000ec6 <main+22>: movl $0x3,-0x14(%rbp)
0x0000000100000ecd <main+29>: movl $0x3,-0x10(%rbp)
0x0000000100000ed4 <main+36>: movl $0x3,-0xc(%rbp)
0x0000000100000edb <main+43>: callq 0x100000e40 <f>
0x0000000100000ee0 <main+48>: movl $0x0,-0x8(%rbp)
0x0000000100000ee7 <main+55>: mov -0x8(%rbp),%eax
0x0000000100000eea <main+58>: mov %eax,-0x4(%rbp)
0x0000000100000eed <main+61>: mov -0x4(%rbp),%eax
0x0000000100000ef0 <main+64>: add $0x20,%rsp
0x0000000100000ef4 <main+68>: pop %rbp
0x0000000100000ef5 <main+69>: retq
so i was expecting to find the laid down instruction pointer return address to be 0x0000000100000ee0, as this is the next instruction after callq. When I run my program I get ( I grouped these in groups of 4 so you can read them better):
05
06
07
08
40
1B
08
56
FF
7F
00
00
E0
EE
B7
09
01
00
00
00
00
00
00
00
03
00
00
00
03
00
00
00
03
00
00
00
03
00
00
00
Ok, so I can see my 5,6,7,8 that I wrote into my local variable in f() and I can see the local variables of main() those 4-byte integers, which have been set to 3. After 5,6,7,8 (this is a 64 bit system) I would have expected the next 8 bytes to encode the previous value of the %rbp register, and THEN the
next 8 bytes to contain the return address for the instruction pointer. So the return address should be
E0
EE
B7
09
01
00
00
00
Now when I compare this to the 0x0000000100000ee0 that I am expecting from gdb, I can see the 00000001 in the last 4 bytes and I can see the e0 from 00000ee0 in the very first byte. But why am I not getting exactly what I am expecting? I thought about byte-ordering (Mac OS X is little endian I believe), but that would not explain what I see here, from what I understood.
Any input is welcome,
Thank you guys,
Christoph
Try this program and run it multiple times.
I’m pretty sure that you’ll get different addresses every time. MacOS has position independent binaries and the stack changes positions all the time too. This is a security feature.
If you run your program in gdb, you’ll probably get what you expect since gdb disables the randomization to make debugging easier.