In order to alter the return address within function()
to skip over the x = 1
in main()
, you need two pieces of information.
1. The location of the return address in the stack frame.
I used gdb to determine this value. I set a breakpoint at function()
(break function
), execute the code up to the breakpoint (run
), retrieve the location in memory of the current stack frame (p $rbp
or info reg
), and then retrieve the location in memory of buffer
(p &buffer
). Using the retrieved values, the location of the return address can be determined.
(compiled w/ GCC -g
flag to include debug symbols and executed in a 64-bit environment)
(gdb) break function
...
(gdb) run
...
(gdb) p $rbp
$1 = (void *) 0x7fffffffe270
(gdb) p &buffer
$2 = (char (*)[5]) 0x7fffffffe260
(gdb) quit
(frame pointer address + size of word) - buffer address = number of bytes from local buffer variable to return address
(0x7fffffffe270
+ 8) - 0x7fffffffe260
= 24
If you are having difficulties understanding how the call stack works, reading the call stack and function prologue Wikipedia articles may help. This shows the difficulty in making "buffer overflow" examples in C. The offset of 24 from buffer
assumes a certain padding style and compile options. GCC will happily insert stack canaries nowadays unless you tell it not to.
2. The number of bytes to add to the return address to skip over x = 1
.
In your case the saved instruction pointer will point to 0x00000000004002f4
(<main+35>
), the first instruction after function returns. To skip the assignment you need to make the saved instruction pointer point to 0x00000000004002fb
(<main+42>
).
Your calculation that this is 7 bytes is correct (0x4002fb
- 0x4002fb
= 7).
I used gdb to disassemble the application (disas main
) and verified the calculation for my case as well. This value is best resolved manually by inspecting the disassembly.
Note that I used a Ubuntu 10.10 64-bit environment to test the following code.
#include <stdio.h>
void function(int a, int b, int c)
{
char buffer[5];
int *ret;
ret = (int *)(buffer + 24);
(*ret) += 7;
}
int main()
{
int x = 0;
function(1, 2, 3);
x = 1;
printf("x = %i \n", x);
return 0;
}
output
x = 0
This is really just altering the return address of function()
rather than an actual buffer overflow. In an actual buffer overflow, you would be overflowing buffer[5]
to overwrite the return address. However, most modern implementations use techniques such as stack canaries to protect against this.