18
无效返回输入(无效)
{
   字符数组[30];

   获取(数组);
   printf("%s\n", 数组);
}

在 gcc 中编译后,该函数转换为以下汇编代码:

推%ebp
mov %esp,%ebp
低于 $0x28,%esp
移动 %gs:0x14,%eax
mov %eax,-0x4(%ebp)
xor %eax,%eax
lea -0x22(%ebp),%eax
mov %eax,(%esp)
拨打 0x8048374
lea -0x22(%ebp),%eax
mov %eax,(%esp)
呼叫 0x80483a4
mov -0x4(%ebp),%eax
异或 %gs:0x14,%eax
0x80484ac
拨打 0x8048394
离开  
ret  

我不明白两行:

移动 %gs:0x14,%eax
异或 %gs:0x14,%eax

什么是%gs,这两行到底是做什么的?

这是编译命令:

cc -c -mpreferred-stack-boundary=2 -ggdb file.c
4

3 回答 3

24

GS是一个段寄存器,它在linux中的使用可以在这里阅读(它基本上用于每个线程数据)。

mov    %gs:0x14,%eax
xor    %gs:0x14,%eax

此代码用于验证堆栈没有爆炸或损坏,使用存储在 GS+0x14 的金丝雀值,请参阅this

gcc -fstack-protector=strong在许多现代发行版中默认开启;您可以使用gcc -fno-stack-protector不添加这些检查。(在 x86 上,线程本地存储很便宜,因此 GCC 将随机金丝雀值保留在那里,使其更难泄漏。)

于 2012-02-12T14:18:38.510 回答
3

ES、FS、GS:Extra Segment Registers 可以作为额外的段寄存器使用;也用于跨越段的特殊指令(如字符串副本)。取自这里

http://www.hep.wisc.edu/~pinghc/x86AssmTutorial.htm


希望能帮助到你

于 2012-02-12T14:02:30.200 回答
3

在 AT&T 风格的汇编语言中,百分比符号通常表示一个寄存器。在从 386 开始的 x86 系列处理器中,GS 是所谓的段寄存器之一。但是,在保护模式环境中,段寄存器用作选择器寄存器

一个虚拟内存选择器代表它自己的虚拟地址空间映射以及它自己的访问机制。实际上,%gs:0x14可以将其视为对原点保存在 %gs 中的数组的引用(尽管 CPU 做了一些额外的取消引用)。在现代 GNU/Linux 系统上,%gs通常用于指向线程本地存储区域。然而,在您询问的代码中,只有一项 TLS 很重要——堆栈金丝雀。

这个想法是尝试通过在获取之前将一个随机但恒定的值放入堆栈中来检测缓冲区溢出错误——它被称为堆栈金丝雀,以纪念煤矿工人用来通过死亡来发出有毒气体水平增加信号的金丝雀。gets()调用,在它的堆栈框架之上,并检查它是否仍然存在,之后gets()将返回。 gets()没有业务覆盖堆栈的这一部分——它在自己的堆栈框架之外,并且没有给它一个指向它的指针——所以如果堆栈金丝雀已经死了,那么就以一种危险的方式出现了问题。(作为一种编程环境,C 恰好特别容易出现这种错误,在过去二十年左右的时间里,安全研究人员已经学会利用其中的许多错误。另外,gets()恰好是一个函数,它本身就有溢出其目标缓冲区的风险。)您没有在代码中提供地址,但 0x80484ac 可能是 的地址leave,并且在call 0x8048394不匹配的情况下执行(即跳过je 0x80484ac在匹配的情况下),可能是对__stack_chk_fail()libc 提供的调用,以通过逃离隐喻的毒矿来处理堆栈损坏。

堆栈金丝雀的规范值保存在线程本地存储中的原因是这样,每个线程都可以拥有自己的堆栈金丝雀。堆栈本身通常不在线程之间共享,因此也不共享金丝雀值是很自然的。

于 2018-04-01T22:38:20.460 回答