在内核模式或其他模式下,能否直接(即无需“技巧”)读取Intel CPU上的程序计数器?
不,EIP / IP 不能直接访问,但在位置相关的代码中,它是一个链接时间常量,因此您可以使用附近(或远离)的符号作为立即数。
mov eax, nearby_label ; in position-dependent code
nearby_label:
要在位置无关的32位代码中获取EIP或IP:
call _here
_here: pop eax
; eax now holds the PC.
ret
指令进行分支错误预测。(除非您不打算返回,或者很少返回才不重要)。但是,返回地址预测器堆栈将恢复正常。get_retaddr_ppro:
mov eax, [esp]
ret ; keeps the return-address predictor stack balanced
; even on CPUs where call +0 isn't a no-op.
lea
指令直接读取RIP。default rel ; NASM directive: use RIP-relative by default
lea rax, [_here] ; RIP + 0
_here:
MASM或GNU .intel_syntax
语法: lea rax, [rip]
AT&T语法: lea 0(%rip), %rax
如果您需要特定指令的地址,通常可以使用类似以下代码:
thisone:
mov (e)ax,thisone
(注:在某些汇编器上,这可能会做错事情并从 [thisone] 读取一个字,但通常有一些语法可以让汇编器做正确的事情。)
如果您的代码是静态加载到特定地址的,则汇编器已经知道(如果您告诉它正确的起始地址)所有指令的绝对地址。动态加载的代码,例如作为任何现代操作系统上应用程序的一部分,将通过动态链接器执行地址重定位而获得正确的地址(前提是汇编器足够聪明以生成重定位表,它们通常会这样做)。
lea rax,[thisone]
之外,没有其他解决方法),要么在Linux上制作共享对象,因此这也无法工作。 (mov
-immediate需要链接时常量地址,因此它仅适用于位置相关代码)。但是,如果您正在制作Linux可执行文件,则您的编译器可能默认制作位置无关的可执行文件,并且-no-pie -fno-pie
可以制作位置相关的可执行文件,您可以使用绝对寻址。 - Peter Cordes在x86-64上,你可以这样做:
lea rax,[rip] (48 8d 05 00 00 00 00)
lea rax, [rip]
无法工作。看起来RIP只能与rel
一起间接使用,例如lea rax, [rel _start]
? - Ciro Santilli OurBigBook.com在x86上没有直接读取指令指针(EIP)的指令。您可以通过一些内联汇编获取正在汇编的当前指令的地址:
// GCC inline assembler; for MSVC, syntax is different
uint32_t eip;
__asm__ __volatile__("movl $., %0", : "=r"(eip));
.
汇编指令会被汇编器替换为当前指令的地址。需要注意的是,如果你将上面的代码段放在函数调用中,每次都会得到相同的地址(在该函数内部)。如果你想要一个更可用的C函数,可以使用一些非内联汇编:
// In a C header file:
uint32_t get_eip(void);
// In a separate assembly (.S) file:
.globl _get_eip
_get_eip:
mov 0(%esp), %eax
ret
uintptr_t func_addr = (uintptr_t)&function_name;
。在x86 C/C++实现中,函数指针只是代码地址。这将组装为所需的任何内容。 - Peter Cordes有一种跟架构无关(但依赖于gcc)的方式可以通过使用标签作为值来访问正在执行的地址:
http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
void foo()
{
void *current_address = $$current_address_label;
current_address_label:
....
}
&¤t_address_label
,而不是 $$
。 - phuclvasm("movl $., %0", : "=r"(eip))
答案一样好(如果不是更好,因为这个答案也适用于PIC代码。在这种情况下,编译器将不得不使用RIP相对LEA或32位PIC方法来获取静态地址,但当执行通过该标签时,它将与EIP匹配,除非您以某种方式复制了代码,破坏了静态寻址。) - Peter Cordes你也可以从 /proc/stat 中读取这个信息。请查看 proc 的 man 手册。
/proc/self/stat
,引用手册也很酷。 - Ciro Santilli OurBigBook.com/proc/self/stat
根据 man proc(5)
,有一个字段叫做 kstkeip %lu
:当前的 EIP(指令指针)。 - Paul Praetread()
函数中syscall
指令的地址吗?这似乎对于找出自己的EIP/RIP值没有什么用处。 - Peter Cordes有一种简单的方法可以改变程序计数器(eip)。
当你用“call”调用一个函数时,eip被推入堆栈中,然后当你用“ret”返回时,eip就从堆栈中弹出。因此,你需要做的就是将你想要的值压入堆栈,然后使用“ret”。
例如:
mov eax, 0x100
push eax`
ret
这就完成了。
jmp
。我的回答解释了为什么x86机器码被设计成不能像在ARM上一样编码一个mov
到eip
,因为PC是通用整数寄存器之一。 - Peter Cordes