I am trying to modify the following C program so that the main function will skip the printf(“x is 1”) line and only print “x is 0”.
void func(char *str) { char buffer[24]; int *ret; ret = buffer + 28; // Supposed to set ret to the return address of func (*ret) += 32; // Add the offset needed so that func will skip over printf("x is 1") strcpy(buffer, str); } int main(int argc, char **argv) { int x; x = 0; func(argv[1]); x = 1; printf("x is 1"); printf("x is 0"); getchar(); }
As the comments imply, the ret pointer needs to first be set to the return address of the function. I then need to add on an offset that will push it over the line I want to skip. I am running this code on a Linux system with 2 x Intel(R) Xeon(TM) CPU 3.20GHz. I am using gcc version 4.7.2 (Debian 4.7.2-5) to compile. I’m also trying to use example3.c from this (http://insecure.org/stf/smashstack.html) link for reference. Here is a disassembly of the main function using gdb:
Dump of assembler code for function main: 0x0000000000400641 <+0>: push %rbp 0x0000000000400642 <+1>: mov %rsp,%rbp 0x0000000000400645 <+4>: sub $0x20,%rsp 0x0000000000400649 <+8>: mov %edi,-0x14(%rbp) 0x000000000040064c <+11>: mov %rsi,-0x20(%rbp) 0x0000000000400650 <+15>: movl $0x0,-0x4(%rbp) 0x0000000000400657 <+22>: mov -0x20(%rbp),%rax 0x000000000040065b <+26>: add $0x8,%rax 0x000000000040065f <+30>: mov (%rax),%rax 0x0000000000400662 <+33>: mov %rax,%rdi 0x0000000000400665 <+36>: callq 0x4005ac <func> 0x000000000040066a <+41>: movl $0x1,-0x4(%rbp) 0x0000000000400671 <+48>: mov $0x40075b,%edi 0x0000000000400676 <+53>: mov $0x0,%eax 0x000000000040067b <+58>: callq 0x400470 <printf@plt> 0x0000000000400680 <+63>: mov $0x400762,%edi 0x0000000000400685 <+68>: mov $0x0,%eax 0x000000000040068a <+73>: callq 0x400470 <printf@plt> 0x000000000040068f <+78>: callq 0x400490 <getchar@plt> 0x0000000000400694 <+83>: leaveq 0x0000000000400695 <+84>: retq End of assembler dump.
Using what I’ve read from the example, my buffer is 24 bytes long and I should add an extra 4 bytes for the SFP size. This would mean I add 28 bytes to get to the return address of <+41>. It then looks like I want to jump to the last printf call at <+73>. This should be an offset of 32. However, when I execute the code, “x is 1” is still printed. I can’t seem to find out why. Is there something wrong with my math or assumptions?
Advertisement
Answer
You should disassemble the function func() too in order to have a better idea how things are going on. Moreover, I did not understood the role of your call to strcpy(), just a cause of segmentation error for me I commented it out to make your code work.
Do not forget that the sizes you can see in the code are printed in hexadecimal, while you input the buffer shifts in your code in decimal. So, when you read something as:
mov %rdi,-0x28(%rbp)
You must think of 40 bytes (0x28 hexa = 40 decimal), and NOT 28 bytes.
The code above is actually extracted from the func() function disassembly. As @cybermike mentioned, do not forget that while Alpeh1’s text remains a reference on the subject, it is getting quite old now: architecture and protection systems have widely evolved until now.
As mentioned here, on x64 architectures the compiler will now try to align the stack addresses with 16 bytes boundaries, so to allocate the size for your 24 chars array it will actually reserve 32 bytes, ie. the nearest boundary.
Add to that the 8 bytes allocated for your “rest” pointer, then you know that your return address is located exactly 40 bytes away.
Then, looking at main() disassembly, the normal return address is:
0x00000000004005fe <+41>: movl $0x1,-0x4(%rbp)
And we want it to be:
0x0000000000400614 <+63>: mov $0x4006bb,%edi
So we have to increase the return by 63 – 41 = 22.
So, to summarize, I have your exercise working as expected with the following func() function:
void func(char *str) { char buffer[24]; int *ret; ret = buffer + 40; // Supposed to set ret to the return address of func (*ret) += 22; // Add the offset needed so that func will skip over printf("x is 1") //strcpy(buffer, str);
}
Execution result:
$ ./se x is 0