I've been reading this article about "Return oriented programming" and I have a question about the part with "Calling arguments"
What I understand is that we need to have a stack in correct format. Arguments and then return address we don't care about. They set up the stack this way:
| 0x8048580 <not_used> |
| 0x43434343 <fake return address> |
| 0x8048360 <address of system> |
| 0x42424242 <fake old %ebp> |
| 0x41414141 ... |
| ... (0x6c bytes of 'A's) |
| ... 0x41414141 |
Where address 0x8048360
is address we'll jump to after the corrupted function finishes and address 0x8048580
is an argument.
What I don't understand is how can we have a fake EBP address. My understanding was that the EBP address on stack is popped before returning from the function and then the EBP is used to access arguments and local variables. If we put fake address on the stack, wouldn't the system
function use this fake EBP address to access it's argument without success?
wouldn't the "system" function use this fake EBP address to access it's argument without success? If we put fake address on the stack, wouldn't the "system" function use this fake EBP address to access it's argument without success?
No. That would happen if you overwrite EBP and then really return from the function to the caller. Since you are also overwriting the return address, you never actually return to the caller, and you enter system
instead. What will happen when returning into system
will be:
0x0804847a <+22>: ... ; Here %ebp is still valid.
0x0804847f <+27>: leave ; Same as: mov %ebp, %esp; pop %ebp
0x08048480 <+28>: ret ; %ebp becomes invalid, but %esp is still valid.
|
v
0x08048360 <+0>: push %ebp ; Push the invalid value.
0x08048361 <+1>: mov %esp,%ebp ; Move %esp (valid) into %ebp ==> %ebp is now valid again.
That's why the article you reference says "fake EBP", because you don't really care about the value. As long as your function does not really return to the caller, you're fine and your ROP chain will continue without a problem because every function you jump into will have a prologue just like the one above.
You can test this by starting your program under GDB like this:
$ gdb ./program
(gdb) break *0x08048480 # address of the 'ret' instruction
(gdb) run <<< "$(python -c 'print "A"*0x6c + "BBBB" + "\x60\x83\x04\x08" + "CCCC" + "\x80\x85\x04\x08"')"
Then, when you hit the breakpoint, look where you are with the disassemble $eip
command and then proceed with si
(step single instruction) and use info registers
to look at the values of the registers after each instruction.