Welcome to the seventh installment in our Shellcode Signature Series—a collection of blog posts where we disassemble and talk about various shellcode and obfuscation techniques, and write signatures to detect them.
In this post, we will dive into methods of obtaining EIP/RIP. These techniques have been around for a long time and while nothing here is new information, we hope for it to be a starting point for someone who is researching shellcode to gain understanding of the methods in use today.
Our topic in this case is one of the core properties of shellcode. Obtaining the value of the instruction pointer is essential to any stub of shellcode. It’s useful for a variety of reasons including:
- Calculating the offset of a payload (or encoded buffer)
- Address everything relative to the running code
While it’s technically possible to run shellcode without obtaining the value of EIP/RIP, in most cases shellcode will find and use this value to perform its tasks.
A Note About EIP
In x86 assembly, it’s not possible to directly pull the value of the EIP register. However, shellcode authors figured out other ways to obtain the value of EIP in compact code without much overhead.
Call $ + ?
When performing a call operation in x86/x64 the address of the instruction following the call (the return location) is pushed onto the stack. The function will perform its task placing the return value in EAX, then a RET instruction at the end of the function will pop the return value off the stack and continue execution to it. With this technique, the call typically leads directly to a pop instruction which immediately pops the return address of the stack. This return address is the address of the POP instruction itself (EIP).
This return address (EIP) is then the anchor point that other structures (typically an encoded buffer) are addressed relative to.
Probably the simplest method of obtaining the value in EIP is Call $+5, which is a CALL to the next instruction. The value of EIP is then pulled off the stack and placed into a register.
A simple form of this can be observed within the Bloxor shellcode encoder. Note: This is not Bloxor’s primary method—it uses many others as well.