- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一个用 C 编写的小程序,echo():
/* Read input line and write it back */
void echo() {
char buf[8]; /* Way too small! */
gets(buf);
puts(buf);
}
对应的汇编代码:
1 echo:
2 pushl %ebp //Save %ebp on stack
3 movl %esp, %ebp
4 pushl %ebx //Save %ebx
5 subl $20, %esp //Allocate 20 bytes on stack
6 leal -12(%ebp), %ebx //Compute buf as %ebp-12
7 movl %ebx, (%esp) //Store buf at top of stack
8 call gets //Call gets
9 movl %ebx, (%esp) //Store buf at top of stack
10 call puts //Call puts
11 addl $20, %esp //Deallocate stack space
12 popl %ebx //Restore %ebx
13 popl %ebp //Restore %ebp
14 ret //Return
我有几个问题。
为什么 %esp 分配 20 个字节? buf是8个字节,为什么多了12个?
返回地址就在我们推送 %ebp 的正上方,对吗? (假设我们把堆栈倒过来画,它向下增长)旧的 %ebp 的目的是什么(当前的 %ebp 指向第 3 行的结果)?
如果我想更改返回地址(通过输入超过 12 个字节的任何内容),它会更改 echo() 返回的位置。更改旧的 %ebp(即返回地址前 4 个字节)的结果是什么?是否有可能通过更改旧的 %ebp 来更改返回地址或 echo 返回的位置?
%ebp 的目的是什么?我知道它是帧指针,但是那是什么?
编译器是否有可能将缓冲区放在不紧邻旧 %ebp 存储位置的位置?就像我们声明 buf[8] 但它在第 6 行将它存储在 -16(%ebp) 而不是 -12(%ebp)?
*从 Computer Systems - A programmer's Perspective 2nd ed 复制的 c 代码和程序集。
** 使用 gets()
因为缓冲区溢出
最佳答案
分配20个字节的原因是为了栈对齐。 GCC 4.5+ 生成的代码确保被调用者的本地堆栈空间与 16 字节边界对齐,以确保编译后的代码可以以定义明确的方式在堆栈上执行对齐的 SSE 加载和存储。出于这个原因,在这种情况下,编译器需要丢弃一些堆栈空间,以确保 gets
/puts
获得正确对齐的帧。
本质上,这就是堆栈的样子,其中每一行都是一个 4 字节的字,除了表示 16 字节地址边界的 ---
行:
...
Saved EIP from caller
Saved EBP
---
Saved EBX # This is where echo's frame starts
buf
buf
Unused
---
Unused
Parameter to gets/puts
Saved EIP
Saved EBP
---
... # This is where gets'/puts' frame starts
正如您希望从我出色的 ASCII 图形中看到的那样,如果不是“未使用”部分,gets
/puts
将得到未对齐的帧。但是,还要注意,并非 12 个字节未使用;其中4个保留给参数。
Is it ever possible for the compiler to put the buffer somewhere that is not right next to where the old %ebp is stored? Like if we declare buf[8] but it stores it at -16(%ebp) instead of -12(%ebp) on line 6?
当然。编译器可以随意组织堆栈。为了可预测地进行缓冲区溢出,您必须查看程序的特定编译二进制文件。
至于 EBP 的目的是什么(从而回答您的问题 2、3 和 5),请参阅有关如何组织调用堆栈的任何介绍性文字,例如 the Wikipedia article .
关于c - 插入和改变 %esp 帧指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30161249/
回车和有什么区别 push ebp mov ebp, esp sub esp, imm 说明?有性能差异吗?如果是这样,哪个更快,为什么编译器总是使用后者? 与离开和类似 mov esp, eb
我有以下 NASM 汇编程序,运行时间约为 9.5 秒: section .text global _start _start: mov eax, 0 mov ebx, 8 loop:
根据互联网上的许多教程,据说您可以找到以下结构的命令行参数: 然而,在花一些时间测试我为 NASM 编写的汇编代码后,我发现 ESP 的值是一些数字,例如: -144807325 实际参数计数存储在更
1. 设备烧录的程序rainmaker自带gpio示例 2. swaggerapis登录账户 3. 调用Rainmaker封装好的py
文章结构: 项目概述 成品预览 项目框架 硬件资料,代码 项目槽点 -项目
我是一个学习汇编的初学者,在函数调用之前保留 ESP 寄存器时,通过加法或减法来实现是否重要?很难解释,请考虑以下内容 mov esi, esp sub esp, 12 // on 32bit OS
我反汇编了一个小程序,该程序询问用户的姓名,然后输出“Hello + [user's_name]” 这是反汇编的输出: 主要功能: 打招呼功能: 我注意到,对于 main() 函数,ESP 寄存器递减
我正在从事 Visual Studio 项目项目 A(在编译时生成静态库) 有课 using namespace mynamespace; class projectAclass { virtua
从上图中可以看出,函数setAttribute 时发生了错误。从它的调用返回。 有谁知道如何解决图中显示的这个错误?我知道这是调用约定之间的错误,但是如何找出 setAttribute 的调用约定是什
我用 QT 制作了一个 .dll 文件并将其加载到我的应用程序中。当它即将从一个函数返回时,我收到: The value of ESP was not properly saved across a
我找不到答案。从我读到的 %ebp 有 32 位,将 %esp 移动到 %ebp 你仍然有 32 位,然后减去 70 到 32,我不明白其余的。我对此很陌生,所以我不是很精通。请给出详细的解释。谢谢!
我有一个从 c 程序调用的简单汇编函数,我必须使用需要内存操作数的指令( FIDIV )。 将值移动到 [esp - 2] 是否安全并在下一条指令中使用它,或者以这种方式使用堆栈永远不会安全? 我知道
以下陈述有什么区别? mov %eax,%esp mov %eax,(%esp) 我正在努力散布一个二元炸弹,但在处理一些 mov 时遇到了问题。和 leal在程序集的早期命令。 最佳答案 这会将 %
我使用 duinotech XC-3800 在 ESP32 芯片上使用 ESP IDF 测试运行裸机代码,并在图像大小方面获得以下结果。 ESP32 的分析二进制大小 文件夹结构 温度/ 主要的/ C
我正在 OS X(32 位)上执行系统调用,如下所示: push 123 mov eax, 1 sub esp, 4 int 0x80 而且我不太明白 sub esp, 4 间隙。 我在某处读到,BS
当我在 gdb 中反汇编 main() 时,它会重新调整此结果: 0x0804854c : push ebp 0x0804854d : mov ebp,esp 0x0804
我目前正在学习英特尔处理器的汇编。既然堆栈“向下增长”,为什么我们必须添加才能访问特定元素 [ebp + 8] ;; This will access the first param 我知道我们必须跳
我试图了解如何将堆栈与程序集一起使用,在我尝试时,我在 SO 中的一个问题中遇到了以下代码,即: push ecx mov eax, 4 mov ebx, 1 mov ecx, result m
我有这个 C 代码部分: #include void main() { int n, array[1000], c, d, t, e; char step; puts("Enter a number
我不太明白为什么 gcc 在调用函数之前要先将 esp 减去 12。 pushl %ebp movl %esp,%ebp sub $12,%esp socke
我是一名优秀的程序员,十分优秀!