Arc*_*tek 2 c assembly gcc ld virtual-memory
我不确定我问的问题是否正确,但我想知道 C 或 ASM 程序是否可以在虚拟地址 0x0 处写入?
我知道内核不允许在虚拟 0x0 处写入/读取,但我想知道是否有一些奇怪的方法可以做到这一点。可能使用我不知道的 ld 或 gcc 标志?
设置如下:
为此需要三个步骤:
配置系统以允许将内存映射到地址零。这是通过将vm.mmap_min_addr
sysctl 设置为 0 来完成的:
sysctl vm.mmap_min_addr=0
Run Code Online (Sandbox Code Playgroud)
如果您在系统上使用 Wine,则该 sysctl 可能已经配置,因为 Wine 需要它才能执行 16 位代码。
另一种方法vm.mmap_min_addr=0
是使用 运行进程CAP_SYS_RAWIO
,例如以 root 身份运行sudo strace ./a.out
。(strace
让您看到它进行的系统调用,这样您就可以看到mmap
返回成功0
而不是(void*)-1
)
在地址0处建立映射。为了让内核让你向地址0写入数据,你需要在那里映射一些内存。这可以通过调用mmap
映射一些内存或安排二进制文件加载段来轻松完成。
有关示例mmap
,请参阅在 Linux 上使用 mmap 分配地址零失败
取消引用指向地址 0 的指针。这可以使用 C 代码(例如*(int *)0 = 42
.
这恰好可以使用 GCC 或禁用优化的 clang 编译为我们想要的 asm 1。
脚注 1:
0
是 C 中的空指针常量,取消引用它是未定义的行为。禁用优化后,GCC 和 clang 仅编译为存储到该地址 ( Godbolt ) 的 asm,但 clang 警告非易失性空指针的间接将被删除,而不是陷阱,请考虑使用__builtin_trap()
或限定指针与 '易失性'。这确实是启用优化后会发生的情况。GCC-O2
或更高版本仍然会发出存储指令,但在 x86-64 上会出现ud2
非法指令陷阱。
volatile int *volatile zero_ptr = 0;
*zero_ptr = 42;
在 GCC 和 clang 中工作,以向编译器隐藏 UB:Godbolt。(使指针对象本身volatile
隐藏了它是空指针的事实。使其成为一个volatile int
围绕 GCC bug(?)的指针,它优化了实际存储,也许假设它不能指向任何有生命周期的东西比这个函数更持久。)