3 minutes
Practical Reverse Engineering - Exercise 1, Page 17
Table of Contents
Question
Given what you learned about CALL
and RET
, explain how you would read
the value of EIP
? Why can’t you just do MOV EAX, EIP
?
Answer
mov eax, eip
is seen as an invalid instruction(not encodable) by any assembler since EIP
is not a General Purpose Register(GPR)
; it is a special purpose register that is used as a pointer to the next instruction to execute(hence known as, extended instruction pointer).
However, there are a couple of tricks to get EIP
register value by reading it from the stack following the execution of the call
instruction.
Consider the following assembly subroutine(I’m using MASM as the assembler):
; Obtain instruction pointer(EIP) register value
OPTION LANGUAGE: SYSCALL
@get_eip@0 PROC PUBLIC
; Function prologue - save the non-volatile registers onto the stack and perform explicit stack frame linkage
push ebp ; ESP = ESP - 0x4 and [ESP] = EBP, preserve the current base frame pointer on the stack
mov ebp, esp ; EBP = ESP, create a new local stack frame within the callee by setting the base frame pointer to point to the current top of the stack
; Get the address of the instruction in the calling function immediately after the call instruction that will be executed after control returns to the caller
mov eax, dword ptr [ebp + 4h] ; EAX = [EBP + 0x4], obtain the return address from the stack and store it into EAX
; Function epilogue - perform cleanup and return EAX register value to the calling procedure
;leave
;mov esp, ebp ; ESP = EBP, restore the stack by releasing the local stack frame
pop ebp ; EBP = [ESP] and ESP = ESP + 0x4, restore the caller's base frame pointer
ret ; EIP = [ESP] and ESP = ESP + 0x4, return from procedure
@get_eip@0 ENDP
OPTION LANGUAGE: C
And the corresponding function declaration in a header file to be able to call it from C/C++
code:
extern "C" __declspec(noinline) DWORD __fastcall get_eip(
void
);
Note that the same function could also be written in C
instead of assembly like so:
DWORD __fastcall get_eip(void) {
return (DWORD)_ReturnAddress();
}
To get EIP
register value, we call the above subroutine:
After the call
instruction is executed, retaddr
is pushed onto the stack and EIP
value is changed to the call
target.
Note that the top of the stack now contains the return address. Also, note that WinDbg
stack view displays lower memory addresses higher and higher memory addresses lower and that’s why the stack appears to grow in the upward direction here.
We can read the retaddr
from the stack by reading memory at address (base frame pointer + 0x4) and storing it into EAX
.
Finally, when the ret
instruction is executed, the retaddr
is popped from the stack into EIP
.
Note that after returning from the subroutine, EAX
now contains the same value as EIP
.
Another important thing to note is that using the call $+5; pop eax
technique should be avoided altogether to prevent messing up the Return Address Stack(RAS)
and causing branch mispredictions on older Pentium Pro
processors.
522 Words
2022-07-16 02:00