I want to get the current value of the EIP register with assembly language. Is that possible?
Asked
Active
Viewed 4.2k times
3 Answers
60
Assuming 32-bit x86, use the following function:
get_eip: mov eax, [esp]
ret
Then, to get the value of EIP in EAX, simply:
call get_eip

user200783
- 13,722
- 12
- 69
- 135
-
1i wish there was a way to do this without labels or byte counting – Dmytro Jan 21 '17 at 23:25
-
1There is a way to do this without labels. `call $+0 / ret / mov eax, [esp-4]` – Gregory Morse Jan 18 '21 at 21:16
-
@GregoryMorse: `call $+0` is an infinite loop. (Until you fill up the stack and segfault when it tries to push another return address). Even `call $+5` / `ret` wouldn't work; the first `ret` would return to itself, then it would run again and pop the next thing on the stack into EIP. Also, it's not safe to read from below ESP; it could have been clobbered asynchronously by a signal handler or debugger. (Or on the kernel stack, by an interrupt.) – Peter Cordes Aug 25 '21 at 12:32
-
Yes my comment is wrong for both of those reasons. `call $+5 / pop eax` is without a doubt the best sequence. However $+5 is not guaranteed to be correct it assumes a `E9 CD` where CD is a rel32 or 32-bit displacement. In 16-bit mode it would be `E9 CW` where CW is a rel16 or 16-bit displacement. However you are wrong that reading below the stack pointer is unsafe. We are talking about hardware, not operating system shenanigans. Yes practically speaking the operating system when doing task switching will not preserve it, though there might be some tricks to guarantee it will. – Gregory Morse Aug 26 '21 at 17:44
-
@GregoryMorse: Didn't see your reply since you didn't \@ notify. Unless you're using an ABI with a specific guarantee of a red-zone (like user-space x86-64 System V), memory below the stack pointer might not read back what you just wrote. If you're talking about bare HW, in kernel mode ESP is often the same stack used to handle interrupts. Unless interrupts are disabled, "just wrote" could actually be before/after an interrupt pushed cs:eip on the stack and then then the interrupt returned. For user-space, see [Is it valid to write below ESP?](https://stackoverflow.com/q/52258402) – Peter Cordes Dec 12 '21 at 06:14
-
@GregoryMorse: It's not user-space context-switching that makes unreserved stack memory volatile, it's the potential for async use of the stack, e.g. by a debugger, signal handler, or whatever. (But not interrupts in user-space; kernels will use a separate stack.) If you don't have any signal handlers installed, in practice it's safe to use space below ESP, but a debugger could still step on it if you do `print foo()` - most will execute that function in the context of the current thread.) All that makes it unsafe as a general answer to an SO question, but fortunately call/pop makes it moot – Peter Cordes Dec 12 '21 at 06:17
-
@Peter Cordes Yes hardware interrupt and signal handlers for sure are problematic. Modern debuggers are in separate threads and processes usually buy breakpoints specifically are often implemented by replacing bytes in memory with a single byte int3 interupt call to handle it where the original byte is swapped back. So again this is more of an interest issue not really a debugger issue. Regardless, it could save some tricky headaches to just simply follow the ABI convention. So unless on an architecture which specifically allows it, just don't do this – Gregory Morse Dec 13 '21 at 10:09
-
Like I said, only stuff like `print foo()` in GDB is a problem. That gets GDB to call a function in your program and print the return value. It does that by setting registers (including EIP/RIP) and actually calling the function in the context of the target thread. You're right that normal use of a debugger is non-intrusive, using OS APIs like Linux `ptrace` to access target memory and registers, but there is this corner case / special feature even for modern debuggers which makes it potentially unsafe / surprising to use space below the stack pointer even in a process without signal handlers – Peter Cordes Dec 13 '21 at 10:14
-
(When the target ABI includes a red zone, GDB of course respects that when doing this, but only up to the ABI-specified size. If you assume that even more space is safe to use, then you're in the same boat as 32-bit x86. And BTW, it's not (just) the architecture that makes it safe to do this, it's also the ABI/calling convention. On x86-64, Windows x64 doesn't have a red-zone. On PowerPC, there might only be one mainstream calling convention, and it *does* have one, so in that case it's somewhat justified to say "architecture", but still maybe not safe in kernel code, IDK.) – Peter Cordes Dec 13 '21 at 10:16
30
On x86-64 (as opposed to 32 bit x86), there's RIP
-relative addressing (RIP
is the 64-bit analogue of EIP
). So in 64-bit code, you can just do
lea rax, [rip]
to move the current contents of RIP
to RAX
(you can use lea
but not mov
for this).

Daniel S.
- 6,458
- 4
- 35
- 78

Fabian Giesen
- 3,231
- 16
- 16
-
Excellent comment. I've deleted my incorrect answer. Comments should not be posted as answers. – Mike Gonta Nov 01 '10 at 10:21
-
Hmmm, this doesn't work in a VC++ 2015 `__asm{}` block. :( Get `error C2415: improper operand type` error. – Adrian Sep 27 '17 at 14:56
-
@Adrian: MSVC doesn't support inline asm when compiling for x86-64 (64-bit mode), so you're stuck with obsolete 32-bit code if you want to use its clunky inline asm support. Of course if you're writing in C, you can just take the address of a function and the compiler + linker will do whatever is necessary to give you the address. RIP-relative addressing was new in x86-64, making position-independent code significantly more efficient. – Peter Cordes Aug 25 '21 at 12:37
11
call foo
foo:
pop eax ; address of foo

Abyx
- 12,345
- 5
- 44
- 76
-
8This approach has a subtle issue. Modern processors try to predict return addresses - a call which is not paired with a return messes up the prediction. See http://blogs.msdn.com/b/oldnewthing/archive/2004/12/16/317157.aspx – user200783 Oct 31 '10 at 08:54
-
@Paul Baker usually it isn't a critical issue. For example `call @f / db '123',0 / @@:` is a common practice – Abyx Oct 31 '10 at 09:01
-
-
1@Jens Björnhager it's infinite loop. `call foo / foo: pop eax / add eax, bar-foo / push eax / ret / bar:` – Abyx Oct 31 '10 at 14:46
-
4@Abyx Then at least you know at which EIP your program is stuck! :) – Jens Björnhager Oct 31 '10 at 20:53
-
1If foo isn't inline, then you won't get stuck and will get the EIP of **the next* instruction. – Jens Björnhager Oct 31 '10 at 20:54
-
@Abyx: How about `call foo / sjmp ahead / foo: pop eax / push eax / ret / ahead:`? That would make the return go to its "proper" spot. – supercat Jul 05 '14 at 15:59
-
It is not an infinite loop. Certainly the instruction after call will be there but it is wasteful. `call geteip / geteip: ret / mov eax, [esp-4]` would be the best solution though. Now branch prediction is working and its guaranteed that the address would still be on the "unused" part of the stack. – Gregory Morse Jan 18 '21 at 21:11
-
-
@GregoryMorse, `$+5`. Also `[esp-4]` could be overwritten by anything that suspends your thread. – Abyx Jan 19 '21 at 00:08
-
There are at least 2 other bugs in @GregoryMorse's suggestion: `call $+0` (infinite loop) / `ret` (to itself if changed to `call $+5`), see [my comment about the same code in another comment thread](https://stackoverflow.com/questions/4062403/how-to-check-the-eip-value-with-assembly-language#comment121807195_4062434). – Peter Cordes Aug 25 '21 at 12:41
-
2@user200783: Surprisingly, `call next_insn` is special-cased to *not* break the return address predictor stack on most CPUs: http://blog.stuffedcow.net/2018/04/ras-microbenchmarks/#call0 - it's only a problem on crusty old PPro (presumably up to PIII). So this answer is fine. [Reading program counter directly](https://stackoverflow.com/q/599968) – Peter Cordes Aug 25 '21 at 12:44
-
`call $+5 / pop eax` with the `E9 CD` idiom is better, indeed both the displacement and return instruction there are problematic. It would actually return to some data on the stack which likely would cause a crash. – Gregory Morse Aug 26 '21 at 17:47