Assembly 理解在机器级别执行功能

Assembly 理解在机器级别执行功能,assembly,operating-system,machine-language,Assembly,Operating System,Machine Language,我正在学习一门基于语言的安全课程,我必须一步一步地了解当函数正确执行时堆栈中发生了什么,以便以后我可以学习如何防止漏洞攻击。到目前为止,我对从堆栈中推送和弹出的内容以及ESP、EBP如何移动以跟踪帧有了相当好的理解。另外,我知道EIP保存在堆栈上 我不知道函数中的代码在哪里实际执行以获得结果(我假设在内存中的其他地方,堆?)如果我对一个简单函数进行演练,是否有人可以解释缺少的位(我将用问题标记这些部分)。假设一个简单的函数: int add(int x, int y) { int sum

我正在学习一门基于语言的安全课程,我必须一步一步地了解当函数正确执行时堆栈中发生了什么,以便以后我可以学习如何防止漏洞攻击。到目前为止,我对从堆栈中推送和弹出的内容以及ESP、EBP如何移动以跟踪帧有了相当好的理解。另外,我知道EIP保存在堆栈上

我不知道函数中的代码在哪里实际执行以获得结果(我假设在内存中的其他地方,堆?)如果我对一个简单函数进行演练,是否有人可以解释缺少的位(我将用问题标记这些部分)。假设一个简单的函数:

int add(int x, int y)
{
   int sum = x + y;
   return sum;
}
在main()中用add(3,4)调用

在初始化新函数时,堆栈(从最低地址到最高地址)的ESP指向新帧的顶部,EBP指向新帧的底部。下面是main()

现在,参数从右向左推送到堆栈上。函数调用将EIP的内容保存在堆栈上。[这是函数返回后要执行的下一条指令的地址?]

现在,Prolog部分:旧的EBP地址被推送到堆栈上,EBP指向ESP。最后,局部变量被推送到堆栈上[这些仅仅是存储其值的地址吗?]

尾声是当前帧的堆栈展开时间。ESP被移动到EBP,因此本地变量不可访问(通常)。旧EBP从堆栈中弹出,并指向其原始地址。ESP移动到保存的EIP,该EIP在调用add(3,4)之前的位置

在我要研究的解释中,最后一部分是返回指令将保存的EIP值弹出回EIP寄存器。[这肯定不是函数中的返回语句,而是机器级的ret指令,对吗?]

最后一个问题,有人能解释一下函数中的代码执行时发生了什么,调用、prolog和epilog发生在什么时候?或者提供一个清晰解释的好链接


提前感谢堆(可以这么说:)

首先,我编译了您的函数,然后对其进行了反汇编,这样您就可以了解ASM级别的实际情况。我禁用了优化,并编译为32位代码以保持简单:

Dump of assembler code for function add:
   0x080483cb <+0>:     push   %ebp
   0x080483cc <+1>:     mov    %esp,%ebp
   0x080483ce <+3>:     sub    $0x10,%esp
   0x080483d1 <+6>:     mov    0x8(%ebp),%edx
   0x080483d4 <+9>:     mov    0xc(%ebp),%eax
   0x080483d7 <+12>:    add    %edx,%eax
   0x080483d9 <+14>:    mov    %eax,-0x4(%ebp)
   0x080483dc <+17>:    mov    -0x4(%ebp),%eax
   0x080483df <+20>:    leave  
   0x080483e0 <+21>:    ret    
End of assembler dump.
我们把它们加在一起:

0x080483d7 <+12>:    add    %edx,%eax
0x080483d7:添加%edx,%eax
现在我们将结果存储在局部变量中。这个局部变量实际上就是我们在prolog中分配的堆栈上的空间。我们为局部变量分配了0x10字节,这里我们只使用前4个字节来存储加法的结果:

0x080483d9 <+14>:    mov    %eax,-0x4(%ebp)
0x080483d9:mov%eax,-0x4(%ebp)
由于没有任何优化,我们立即将结果从局部变量加载回寄存器,以便返回:

0x080483dc <+17>:    mov    -0x4(%ebp),%eax
0x080483dc:mov-0x4(%ebp),%eax
正如您所看到的,代码效率极低,但至少相当容易阅读。 现在只剩下尾声了,非常简单:

0x080483df <+20>:    leave  
0x080483e0 <+21>:    ret   
0x080483df:离开
0x080483e0:ret
leave
将破坏我们在prolog中创建的帧,
ret
返回调用函数的下一条指令

尾声是当前帧的堆栈展开时间。ESP被移动到EBP,因此本地变量不可访问(通常)。旧EBP从堆栈中弹出,并指向其原始地址。ESP移动到保存的EIP,该EIP在调用add(3,4)之前的位置

在我要研究的解释中,最后一部分是返回指令将保存的EIP值弹出回EIP寄存器。[这肯定不是函数中的返回语句,而是机器级的ret指令,对吗?]

函数中的return语句对应于机器级别的ret指令。这是直译。 请记住,您的计算机并不直接运行C代码,所有的C代码都首先编译为机器代码,这里的
ret
指令实际上就是弹出EIP的指令

最后一个问题,有人能解释一下函数中的代码执行时发生了什么,调用、prolog和epilog发生在什么时候?或者提供一个清晰解释的好链接

上面看到的反汇编是计算机运行内容的粗略文本表示。EIP包含计算机将运行的下一条指令的地址。当程序运行时,它存储在内存中的某个地方,EIP直接指向内存中的指令

因此,计算机将按照编写的顺序运行函数,而prolog和epilog是函数的一部分

序言和结束语是一种约定,但它们只是代码。如果你愿意,你可以完全删除序言并写一篇疯狂的尾声,它也会起作用


我建议您使用反汇编程序和调试器,以熟悉它的实际工作方式。这并不难,也不是很合乎逻辑。

防止漏洞利用的方法是防止输入溢出分配给它的空间。函数参数和局部变量,如
inti
char*ptr
不能使堆栈溢出,但是
char[20]等数组can和使用动态分配给
char*ptr
的内存的数组可能会使堆上的分配溢出。为什么不编译一个程序并使用调试器逐步完成一个函数?通过这种方式,您将学到比我们在这里所能告诉您的更多的东西。当执行x86
调用
指令时,
调用
后的指令地址被推送到堆栈上。函数局部变量通常不会推送到堆栈上
0x080483df <+20>:    leave  
0x080483e0 <+21>:    ret