为什么这个内存地址%fs:0x28(fs [0x28])有一个随机值?

Dr.*_*all 25 c gcc x86-64 buffer-overflow disassembly

我写了一段C代码,我已经拆解了它,并阅读了寄存器以了解程序在汇编中的工作原理.

int test(char *this){
    char sum_buf[6];
    strncpy(sum_buf,this,32);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我一直在研究的代码是测试功能.当我反汇编输出我的测试功能时,我得到...

   0x00000000004005c0 <+12>:        mov    %fs:0x28,%rax
=> 0x00000000004005c9 <+21>:        mov    %rax,-0x8(%rbp)
... stuff ..
   0x00000000004005f0 <+60>:        xor    %fs:0x28,%rdx
   0x00000000004005f9 <+69>:        je     0x400600 <test+76>
   0x00000000004005fb <+71>:        callq  0x4004a0 <__stack_chk_fail@plt>
   0x0000000000400600 <+76>:        leaveq 
   0x0000000000400601 <+77>:        retq 
Run Code Online (Sandbox Code Playgroud)

我想知道的mov %fs:0x28,%rax是真正在做什么?

Jas*_*son 49

在x86_64上,不再使用分段寻址,但是FSGS寄存器都可以用作基指针地址,以便访问特殊的操作系统数据结构.所以你所看到的是一个值,该值是在FS寄存器中保存的值的偏移量处加载的,而不是对FS寄存器内容的位操作.

特别是正在发生的事情是,FS:0x28在Linux上存储了一个特殊的标记栈保护值,并且代码正在执行堆栈保护检查.例如,如果您进一步查看代码,您将看到值FS:0x28存储在堆栈中,然后调用堆栈的内容并XOR执行原始值为的处理FS:0x28.如果两个值相等,这意味着零位已经设置,因为XOR两个相同的值导致零值,那么我们跳转到test例程,否则我们跳转到一个特殊函数,指示堆栈以某种方式被破坏,并且存储在堆栈上的sentinel值已更改.

如果使用GCC,可以禁用此功能

-fno-stack-protector
Run Code Online (Sandbox Code Playgroud)

  • @need_to_know_now:这将是内存到内存的移动,而 x86 没有任何此类指令。尝试组装它,你会得到一个错误。如果要将内存移至内存,则必须加载到寄存器中,然后使用第二条指令进行存储。 (6认同)
  • 最简单的方法是在MOV操作后直接查看RAX寄存器的内容. (3认同)
  • _分段寻址不再使用_ — 这不是真的,分段寻址仍然 100% 使用,尽管使用情况_戏剧性地_转变为虚拟空间而不是段。由于我们可以使用 64 位的单个寄存器来寻址整个内存(尽管在大多数情况下只使用 48 位),因此我们不会修改这些寄存器,但它们在与 MMU 和内存保护方案的联系中非常重要。 (3认同)