Exploring No-RIP, and Partial-RIP attacks with Saved Frame Pointer Abuse
From 1999 with modern exploit mitigations
No-RIP, Partial-RIP via Saved Frame Pointer Attacks
Going back to exploit development, there are several other vectors to hijack execution besides controlling the Return Instruction Pointer (RIP). If you can control and overwrite the Base Pointer (RBP) or the RAX register (which holds the return value), you can hijack a binary to execute memory corruption attacks. This is a more advanced method, as most introductory courses emphasize only RIP.
In 32-bit systems, you can also control offsets from the Stack Pointer (ESP) to modify arguments in memory. Alternatively, you can control the Base Pointer (EBP) or the return value—assuming it returns a pointer or a function—to be called from a higher level.
Most people never teach these methods. They may resort to partial RIP overwrites, where you control the instruction pointer in the most obvious way, or they may control the Structured Exception Handler (SEH) chain, known as an SEH overwrite. However, few teach RAX or RBP overwrites, or argument overwrites via stack offsets.
According to the Application Binary Interface (ABI), manipulating these values can still lead to a compromise. This is often the bypass when a full RIP overwrite is impossible due to null-byte constraints. While you cannot use null bytes in many string-based overflows, a partial overwrite allows you to change the lower bytes of the pointer, effectively hijacking the control flow within the same memory page.
Understanding the attack in 64-bit
SFP Overwrites have been explored in 1999
http://www.theamazingking.com/tut4.php
So, I’ve been reading the evaluation, and it brought up something interesting. Yes, you need to trigger a LEAVE and then a RET instruction after overwriting the Base Pointer (RBP) to hijack execution. To clarify: when I say you don’t have RIP control, I mean you don’t control the stored instruction pointer directly, but by overwriting the Base Pointer, you wait for the function to finish executing, hit the LEAVE, and then the RET.
However, there is another trick. If you are stuck with a partial RIP overwrite, you can overwrite the lower bytes of the instruction pointer to jump to a location that isn’t very far—at most a few hundred bytes, since we are targeting a location within the same compiled function. By jumping to a LEAVE; RET sequence (even if it’s the one at the end of the current function), you can force the stack to pivot to wherever your corrupted Base Pointer is pointing. And you can jump over the stack canary without touching it.
In the Windows x64 Calling Convention (the ‘Shadow Space’ or 4-argument convention), the arguments are passed via RCX, RDX, R8, and R9. This helps you set up gadgets to do something like calling VirtualAlloc. You can achieve this by overwriting the RBP and using a LEAVE; RET sequence to pivot the stack into a ROP chain that populates these registers. Whether you have partial or full RIP control, you are essentially aiming for a gadget that sets up the parameters for VirtualAlloc to flip memory permissions to Read-Write-Execute (RWX).
Shellcode-less ROP-ping, constraints with partial RIP
So, moving back to Shellcode ropping. Shellcode ropping originally involved controlling RIP, right? If you only have partial control of RIP, you are effectively limited to building ROP chains within the memory range covered by your partial overwrite.
If you don’t have full control—say you only have a 3-byte overwrite—you can only look up gadgets within that specific partial range. In 64-bit computing, where addresses are 8 bytes long, a 3-byte overwrite restricts you significantly. You are usually restricted to gadgets within the same memory page or a nearby user-defined function.
“In other words, you have X amount of megabytes of usable gadgets from the randomized base address, instead of a monster amount of dumped convenient gadgets, forcing you to populate the arguments with dummy values.”
But—and people often miss this—you don’t always have to build your ROP chains on the stack. ROP on the stack is a series of function pointers fed and executed in sequence. That is a fundamental taught in 32-bit exploitation, but it requires significant stack space. With Shellcode ropping and partial RIP overwrites, you have a specific, searchable range for gadgets. This allows for a more surgical approach where you don’t need a massive stack overflow to gain control.

