这条指令的作用是什么?: – mov%gs:0x14,%eax

我的问候和对所有人的问候。 我有一个C程序,基本上是为了测试缓冲区溢出而写的。

#include void display() { char buff[8]; gets(buff); puts(buff); } main() { display(); return(0); } 

现在我使用GDB反汇编它的显示和主要部分。 代码:-

函数main的汇编代码转储:

  0x080484ae : push %ebp # saving ebp to stack 0x080484af : mov %esp,%ebp # saving esp in ebp 0x080484b1 : call 0x8048474  # calling display function 0x080484b6 : mov $0x0,%eax # move 0 into eax , but WHY ???? 0x080484bb : pop %ebp # remove ebp from stack 0x080484bc : ret # return 

汇编程序转储结束。

用于function显示的汇编代码转储:

  0x08048474 : push %ebp #saves ebp to stack 0x08048475 : mov %esp,%ebp # saves esp to ebp 0x08048477 : sub $0x10,%esp # making 16 bytes space in stack 0x0804847a : mov %gs:0x14,%eax # what does it mean ???? 0x08048480 : mov %eax,-0x4(%ebp) # move eax contents to 4 bytes lower in stack 0x08048483 : xor %eax,%eax # xor eax with itself (but WHY??) 0x08048485 : lea -0xc(%ebp),%eax #Load effective address of 12 bytes lower placed value ( WHY???? ) 0x08048488 : mov %eax,(%esp) #make esp point to the address inside of eax 0x0804848b : call 0x8048374  # calling get, what is "@plt" ???? 0x08048490 : lea -0xc(%ebp),%eax # LEA of 12 bytes lower to eax 0x08048493 : mov %eax,(%esp) # make esp point to eax contained address 0x08048496 : call 0x80483a4  # again what is "@plt" ???? 0x0804849b : mov -0x4(%ebp),%eax # move (ebp - 4) location's contents to eax 0x0804849e : xor %gs:0x14,%eax # # again what is this ???? 0x080484a5 : je 0x80484ac  # Not known to me 0x080484a7 : call 0x8048394  # not known to me 0x080484ac : leave # a new instruction, not known to me 0x080484ad : ret # return to MAIN's next instruction 

汇编程序转储结束。

伙计们,你应该考虑我的作业。 rest所有的代码都是我知道的,除了几行。 我加了一个大“为什么????” 每行前面的评论中还有一些问题。 对我来说,第一个障碍是“mov%gs:0x14,%eax”指令,我不能在此指令后制作流程图。 有人请解释一下,这几条指令是什么意思并在程序中做什么? 谢谢…

 0x080484b6 <+8>: mov $0x0,%eax # move 0 into eax , but WHY ???? 

你有这个吗?:

 return(0); 

它们可能是相关的。 🙂

 0x0804847a <+6>: mov %gs:0x14,%eax # what does it mean ???? 

这意味着从地址gs:0x14的内存中读取4个字节到eax。 gs是一个段寄存器。 最有可能通过此寄存器引用线程本地存储(AKA TLS )。

 0x08048483 <+15>: xor %eax,%eax # xor eax with itself (but WHY??) 

不知道。 可能与优化相关。

 0x08048485 <+17>: lea -0xc(%ebp),%eax #Load effective address of 12 bytes lower placed value ( WHY???? ) 

它使eax指向一个存在于堆栈中的局部变量。 sub $0x10,%esp为它们分配了一些空间。

 0x08048488 <+20>: mov %eax,(%esp) #make esp point to the address inside of eax 

错误。 它将eax写入堆栈,堆栈顶部。 它将作为堆栈参数传递给被调用函数:

 0x0804848b <+23>: call 0x8048374  # calling get, what is "@plt" ???? 

我不知道。 可能是一些名字毁了。

到现在为止你应该猜到了什么是局部变量。 buff ,还有什么呢?

 0x080484ac <+56>: leave # a new instruction, not known to me 

为什么不在CPU手册中查找?

现在,我可以解释一下gs / TLS的事情……

 0x08048474 <+0>: push %ebp #saves ebp to stack 0x08048475 <+1>: mov %esp,%ebp # saves esp to ebp 0x08048477 <+3>: sub $0x10,%esp # making 16 bytes space in stack 0x0804847a <+6>: mov %gs:0x14,%eax # what does it mean ???? 0x08048480 <+12>: mov %eax,-0x4(%ebp) # move eax contents to 4 bytes lower in stack ... 0x0804849b <+39>: mov -0x4(%ebp),%eax # move (ebp - 4) location's contents to eax 0x0804849e <+42>: xor %gs:0x14,%eax # # again what is this ???? 0x080484a5 <+49>: je 0x80484ac  # Not known to me 0x080484a7 <+51>: call 0x8048394 <__stack_chk_fail@plt> # not known to me 0x080484ac <+56> 

因此,此代码从TLS获取值(在gs:0x14处)并将其存储在保存的ebp值(ebp-4)的正下方。 那么你的东西是get()put() 。 然后,此代码检查来自TLS的值的副本是否未更改。 xor %gs:0x14,%eax进行比较。

如果XORed值相同,则XOR的结果为0且flags.zf为1.否则,结果不为0且flags.zf为0。

je 0x80484ac 检查flags.zf并跳过call 0x8048394 <__stack_chk_fail@plt> if flags.zf = 1. IOW,如果来自TLS的值副本未更改,则跳过此调用。

那是什么一回事? 这是一种尝试捕获缓冲区溢出的方法。 如果写入超出缓冲区末尾,则会覆盖从TLS复制到堆栈的值。

为什么我们从TLS中获取这个值,为什么不只是一个恒定的硬编码值呢? 我们可能希望使用不同的非硬编码值来更频繁地捕获溢出(因此TLS中的值将从程序的运行更改为另一个运行程序,并且在程序的不同线程中它将有所不同)。 如果每次程序运行时随机选择值,这也会降低攻击者成功利用缓冲区溢出的几率。

最后,如果发现由于缓冲区溢出而覆盖了值的副本,则call 0x8048394 <__stack_chk_fail@plt>将调用专用于执行任何必要操作的特殊function,例如报告问题并终止程序。

 0x0804849e <+42>: xor %gs:0x14,%eax # # again what is this ???? 0x080484a5 <+49>: je 0x80484ac  # Not known to me 0x080484a7 <+51>: call 0x8048394 <__stack_chk_fail@plt> # not known to me 0x080484ac <+56>: leave # a new instruction, not known to me 0x080484ad <+57>: ret # return to MAIN's next instruction 

gs 段可用于线程本地存储 。 例如,它用于errno ,因此multithreading程序中的每个线程都有效地拥有自己的errno变量。

上面的函数名称是一个很大的线索。 这必须是堆栈金丝雀 。

leave一些CISC指令,在实际退回之前完成你需要做的所有事情。我不知道细节)。

其他人已经解释了GS的事情(与线程有关)..

 0x08048483 <+15>: xor %eax,%eax # xor eax with itself (but WHY??) 

解释这需要一些X86架构的历史:

xor eax,eax指令清除寄存器eax中的所有位(加载零),但正如你已经发现的那样,这似乎是不必要的,因为寄存器在下一条指令中加载了一个新值。

但是,xor eax,eax在x86上也有其他function。 您可能知道您可以使用al,ah和ax访问寄存器eax的部分内容。 从386开始就是这样,当时eax确实只是一个寄存器就可以了。

但是,这已经不复存在了。 您在代码中看到和使用的寄存器只是占位符。 CPU内部使用更多内部寄存器和完全不同的指令集。 您编写的指令将转换为此内部指令集。

例如,如果使用AL,AH和EAX,则从CPU的角度来看,您使用的是三个不同的寄存器。

现在,如果在使用AL或AH后访问EAX,CPU必须合并这些不同的寄存器以构建有效的EAX值。

这条线:

 0x08048483 <+15>: xor %eax,%eax # xor eax with itself (but WHY??) 

不仅清除寄存器eax。 它还告诉CPU所有重命名的子寄存器:AL,AH和AX现在可以认为是无效的(设置为零),并且CPU不必进行任何子寄存器合并。

为什么编译器发出此指令?

因为编译器不知道display()将在哪个上下文中被调用。 您可以从使用AL和AH进行大量字节算术的代码中调用它。 如果它不能通过XOR清除EAX寄存器,那么CPU将不得不进行昂贵的寄存器合并,这需要花费很多周期。

因此,在函数启动时执行此额外工作可提高性能。 在您的情况下,它是不必要的,但由于编译器无法知道发出指令以确定。

stack_check_fail是gcc缓冲区溢出检查的一部分。 它使用libssp(stack-smash-protection),你在开始时的移动为堆栈设置了一个守卫,而xor%gs:0x14 …则检查守卫是否还可以。 当它没问题时,它跳转到leave(检查汇编程序doc,它是堆栈处理的辅助指令)并跳过跳转到stack_chk_fail,这将中止程序并发出错误消息。

您可以使用gcc选项-fno-stack-protector禁用此溢出检查的发出。

正如评论中已经提到的,xor x,x只是一个快速命令来清除x,最后的mov 0,%eax是你的main的返回值。