I\'m studying some security related things and right now I\'m playing around with my own stack. What I\'m doing should be very trivial, I\'m not even trying to execute the s
The answers given by "kch" and "import os.boom.headshot" are not quite correct.
What is actually happening is that the value on the stack (0x4141414141414141) which is to be popped into RIP by the RET instruction contains an address which is in the 'non-canonical' address range of the processor. This causes the CPU to generate a general protection fault (GPF) interrupt rather than a fault generated by a kernel pre-check. The GPF in turn triggers the kernel to report a segmentation fault before RIP is actually updated and that is what you're seeing in GDB.
Most modern CPUs only provide a 48-bit address range which is split between a higher and lower half which occupy the address ranges 0x0000000000000000 to 0x00007FFFFFFFFFFF and 0xFFFF800000000 to 0xFFFFFFFFFFFFFFFF respectively. See this wikipedia link for further information.
If the address had been outside the non-canonical range (0x00008FFFFFFFFFFF to 0xFFFF7FFFFFFFFFFF) then RIP would have been updated as expected. Of course, a subsequent fault may have been generated by the kernel if the new address was invalid for any other reason (i.e. outside the process's address range).
Those two instructions are doing exactly what you expect them to do. You have overwritten the previous stack frame with 0x41
's so when you hit the leaveq
, you are doing this:
mov rsp, rbp
pop rpb
Now rsp
points to where rbp
did before. However, you have overwritten that region of memory, so when you do the pop rbp
, the hardware is essentially doing this
mov rbp, [rsp]
add rsp,1
But [rsp]
now has 0x41
's. So this is why you're seeing rbp
get filled with that value.
As for why rip
isn't getting set like you expect, it's because ret
is setting the rip
to 0x41
and then generating an exception (page fault) on the instruction fetch. I wouldn't rely on GDB to show the right thing in this case. You should try overwriting the return value with a valid address within the program's text segment and you likely won't see this weird behavior.
The reason you get a crash of EIP 0×41414141 on x32 is because when the program pops the previously saved EIP value off the stack and back into EIP the CPU then tries to execute the instruction at memory address 0×41414141 which causes a segfault. (it must fetch the page prior to execution of course)
Now, during x64 execution when the program pops the previously saved RIP value back into the RIP register the kernel then tries to execute the instructions at memory address 0×4141414141414141. Firstly, due to canonical form addressing, bits 48 through 63 of any virtual address must be copies of bit 47 (in a manner akin to sign extension), or the processor will raise an exception. If that was not an issue- the kernel does additional checks before invoking the page fault handler since the max user space address is 0x00007FFFFFFFFFF.
To recap, in x32 architecture the address is passed without any “validation” to the page fault handler which attempts to load the page which triggers the kernel to send the program segfault but x64 does not get this far.
Test it, overwrite RIP with 0×0000414141414141 and you will see the expected value is placed in RIP since the prechecks by the kernel pass and then the the page fault handler is invoked like the x32 case (which of course, then causes the program to crash).