void return_input (void) { char array[30]; gets (array); printf("%s\n", array); }
在gcc中编译后,此函数将转换为以下汇编代码:
push %ebp mov %esp,%ebp sub $0x28,%esp mov %gs:0x14,%eax mov %eax,-0x4(%ebp) xor %eax,%eax lea -0x22(%ebp),%eax mov %eax,(%esp) call 0x8048374 lea -0x22(%ebp),%eax mov %eax,(%esp) call 0x80483a4 mov -0x4(%ebp),%eax xor %gs:0x14,%eax je 0x80484ac call 0x8048394 leave ret
我不明白两行:
mov %gs:0x14,%eax xor %gs:0x14,%eax
什么是%gs,这两行究竟是做什么的?
这是编译命令:
cc -c -mpreferred-stack-boundary=2 -ggdb file.c
答案 0 :(得分:20)
GS是一个段寄存器,它在linux中的使用可以在here上读取(它主要用于每个进程数据)。
mov %gs:0x14,%eax
xor %gs:0x14,%eax
此代码用于验证堆栈是否未爆炸或已被破坏,使用存储在GS + 0x14的金丝雀值,请参阅this。
答案 1 :(得分:3)
ES,FS,GS:额外段注册 可以用作额外的段寄存器;也用于跨越段的特殊指令(如字符串副本)。 取自这里
http://www.hep.wisc.edu/~pinghc/x86AssmTutorial.htm
希望有所帮助
答案 2 :(得分:2)
在AT& T样式汇编语言中,百分比sigil通常表示寄存器。在从386开始的x86系列处理器中,GS是所谓的段寄存器之一。但是,在保护模式环境中,段寄存器用作选择器寄存器。
虚拟内存选择器表示自己的虚拟地址空间映射以及自己的访问机制。实际上,%gs:0x14
可以被认为是对数组的引用,该数组的原点保存在%gs中(虽然CPU进行了一些额外的解引用)。在现代GNU / Linux系统上,%gs
通常用于指向线程本地存储区域。但是,在你要问的代码中,只有一个TLS项很重要 - 堆栈canary。
这个想法是试图通过放置一个随机但恒定的值来检测缓冲区溢出错误 - 它被称为堆栈金丝雀在金丝雀煤矿工人的记忆中用于发出信号增加的水平有毒气体通过死亡 - 在gets()
被调用之前进入堆栈,在其堆栈帧之上,并在gets()
返回后检查它是否仍然存在。 gets()
没有业务覆盖堆栈的这一部分 - 它在它自己的堆栈框架之外,并且没有给它指向它 - 所以如果堆栈金丝雀已经死了,那么某些东西就会以危险的方式出错。 (C作为一个编程环境碰巧特别容易出现这种错误,安全研究人员已经学会在过去二十年左右的时间里利用它们中的许多。而且,gets()
碰巧是一个函数,本地存在溢出其目标缓冲区的风险。)您没有提供代码的地址,但0x80484ac可能是leave
的地址,而call 0x8048394
是在不匹配的情况下执行的(即,在匹配的情况下跳过je 0x80484ac
,可能是对__stack_chk_fail()
的调用,由libc提供,通过逃离隐喻的有毒矿来处理堆栈损坏。
堆栈canary的规范值保存在线程本地存储中的原因是这样,每个线程都可以拥有自己的堆栈canary。堆栈本身通常不在线程之间共享,因此自然也不共享金丝雀值。