C 如何获取动态生成的X86_64以返回相对于RIP/RBP的值
我正在尝试读取内存中与X86_64上的C 如何获取动态生成的X86_64以返回相对于RIP/RBP的值,c,assembly,code-generation,x86-64,C,Assembly,Code Generation,X86 64,我正在尝试读取内存中与X86_64上的%rip相关的值。在我的第一个示例中,我只想阅读 如果我用C编写下面的代码,我可以调用它并得到正确的结果(\x….C3C9): 生成的代码如下所示: 0000000000400624 <test>: 400624: 55 push %rbp 400625: 48 89 e5 mov %rsp,%rbp 400628: 48 8b 05 00
%rip
相关的值。在我的第一个示例中,我只想阅读
如果我用C编写下面的代码,我可以调用它并得到正确的结果(\x….C3C9
):
生成的代码如下所示:
0000000000400624 <test>:
400624: 55 push %rbp
400625: 48 89 e5 mov %rsp,%rbp
400628: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 40062f <test+0xb>
40062f: c9 leaveq
400630: c3 retq
我做错了什么
编辑
答案是,我不应该使用malloc
而是mmap
来分配页面对齐的内存区域:
(unsigned char*)mmap(NULL, 1024, PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
当然,我应该检查调用
mprotect
的返回值。它返回-1标记它已失败。在mprotect()调用期间,很可能会收到SIGSEGV。如果操作系统不允许执行malloc()返回的内存的代码(很可能是因为您没有使用某个古老的内核),那么mprotect()只会执行segfaults。这不是一个bug,而是一个特性。ret指令基本上是堆栈中的一个pop值;转到值
当您goto
,ret
最终被执行时,问题是堆栈上有垃圾(可能ret
指令试图goto 9
,因为codesize是堆栈顶部的变量,谁知道呢…)
基本上,它不起作用,因为您错误地使用了asm代码段
请问你想做什么?我可以帮忙:)
获取rip的测试程序:
static inline unsigned long get_rip(void)
{
unsigned long val;
asm volatile(
"call 1f\n"
"1: popq %0\n"
: "=r"(val));
return val;
}
int main()
{
printf("rip = %p\n", (void *)get_rip());
return 0;
}
m保护
将不会出现故障。实际上,它会成功,但如果不能成功,它将返回-1并设置errno
。实际上,如果参数没有对齐页面,它将返回错误,而不是segfault。随后的代码出现了故障。是的,就我所知,这就是问题所在。我只是习惯于使用静态字符串而不是malloc
ing,这似乎很管用。我猜静态字符串是页面对齐的,但malloced字符串不是。这个程序刚用gdb启动,在返回mprotect()后它会出现故障,即使我在mprotect()后填充了一些无害的代码。无论如何,mprotect()不应该与malloc()一起使用,而应该与mmap()一起使用。在这里,在执行m保护后,它从未出现故障。我确实放了printf
s。但是你是对的,我应该使用mmap
:)我收到了一条评论,有人再次删除了他的评论,但是他把我引向了正确的方向。他说的mprotect
失败是对的(愚蠢的我,我应该检查一下)。我猜malloc
(显然)不会返回与页面边界对齐的内存。通过使用(unsigned char*)mmap(NULL,1024,PROT_WRITE,PROT_READ,MAP_ANONYMOUS,MAP_SHARED,-1,0)代码>它确实有效。如果您将PROT_-EXEC
添加到mmap
调用中,您就不需要mprotect
。我没有这样做,因为我在某个地方读到一些内核不允许同时使用PROT_-WRITE
和PROT_-EXEC
。我误会了吗?嗯,也许是吧,虽然我会认为这是一个bug…为什么会有垃圾在堆栈上?当我转到
时,我还没有接触堆栈,所以我只是在运行它,就好像它直接在主
函数中一样。我正在开发Smalltalk->ASM编译器(用Smalltalk编写),因为我想完全摆脱虚拟机。X86_64似乎是一个很好的起点。要学习的东西很多,但也很有趣:)发布了一个代码片段,向您展示如何获取rip值:)在retq
之前的leaveq
指令应该让堆栈指针指向main()
的返回地址,假设main()
使用了正常的函数序言。对,因为我的代码应该在代码中运行,所以我自己生成的序言将完全符合我的预期。例如,如果您开始在GCC中进行优化,这显然是行不通的。好提示;)
(unsigned char*)mmap(NULL, 1024, PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
static inline unsigned long get_rip(void)
{
unsigned long val;
asm volatile(
"call 1f\n"
"1: popq %0\n"
: "=r"(val));
return val;
}
int main()
{
printf("rip = %p\n", (void *)get_rip());
return 0;
}