惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
U
Unit 42
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
S
SegmentFault 最新的问题
阮一峰的网络日志
阮一峰的网络日志
雷峰网
雷峰网
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园_首页
Apple Machine Learning Research
Apple Machine Learning Research
罗磊的独立博客
GbyAI
GbyAI
Stack Overflow Blog
Stack Overflow Blog
Martin Fowler
Martin Fowler
宝玉的分享
宝玉的分享
L
LangChain Blog
Engineering at Meta
Engineering at Meta
量子位
有赞技术团队
有赞技术团队
博客园 - 【当耐特】
A
About on SuperTechFans
Y
Y Combinator Blog

MoonLab

VSCode + OpenOCD 远程调试开发STM32 Clangd + VSCode使用方法 Keil5 编译错误 error: call to undeclared function '__enable_irq' BTSNOOP is FUN! 算法 - 前缀和 2025蓝桥杯赛后总结 三星ZFold 3改造 JavaScript 逆向 Steam 登录二维码 快速求解平方根倒数算法 Protobuf Android设备安装Debian成为BT下载服务器 [双系统] Windows 更新摧毁了我的Linux系统 Reading List Golang embed 使用问题 Hexo博客自动备份插件 云盘备份支持 Git push 出现 permisson denied error 403 坑:Litepal save方法返回true却没有保存 Android Shizuku源码分析 第二篇 Android Shizuku源码分析 Android 监听第三方Activity的一举一动 Android笔记#1 View的事件分发机制解析 知乎日报的问题 使用Hexo Hello World Ubnutu 无法启动网易云音乐 - 总结 Windows 好软推荐 | 这一定是良心软件 typecho - http转https 如何评价Android P MoonLab MoonLab
通过汇编分析栈、函数调用 esp&ebp
LingC · 2023-12-15 · via MoonLab

📦 由AI生成的摘要
栈是一种遵循后进先出(LIFO)规则的数据结构,通常用于内存管理。重要寄存器包括栈指针(SP)和基指针(BP)。在函数调用中,使用`push`将参数压入栈中,`call`指令保存返回地址。ESP寄存器指向栈顶,函数执行后需平衡栈。C语言函数调用中,参数通过EBP寻址,编译器可能使用`mov`而非`push`,使得ESP不指向栈顶,简化栈平衡的处理。

栈 Stack

栈是一种数据结构,遵循Last in, First out (LIFO)规则,栈在中文语境中有时也被叫做堆栈。在实际中,是一片内存空间。

IMG

在栈中,有两个重要的寄存器

SP (Stack Pointer)

BP (Base Pointer)

在16位系统中是这样的,在32位系统中分别叫做esp ebp,在64位中

栈是从高地址向低地址搭起来的,因此栈顶位于低地址,栈底位于高地址。一般来说是这样,当然你的程序也可以不这样做,主打一个叛逆。

ESP 函数调用

要理解栈和那两个有关栈的寄存器,可以通过函数调用来理解。

以下是x86 32bit的汇编程序。

push 1
push 2
call 002110D
add esp,8
nop
; this func will add up a and b
mov eax,dword ptr [esp+4]
add eax,dword ptr [esp+8]
ret

其中push操作码不仅会将参数压入栈中,还会使esp寄存器的值减4,即sub esp,#4。以确保esp始终指向栈顶。

call首先会用push来把下一个操作码的地址压入栈中,这个地址叫做返回地址,然后跳转到函数所在的地址。

在进入到func后,此时esp指向之前call所压入栈内的地址。就可以直接通过exp+4向下拿到参数。

执行完函数后,就需要考虑栈平衡了。要让调用函数后的栈和调用函数前的保存一致。

ret 跳转到返回地址,执行add esp,8 清除栈内的函数参数。

EBP 函数调用

但分析C语言的函数调用,会发现与上文的esp寻址有些不同,一般是用ebp来进行参数的寻址。

int add(int a, int b) {
    return a + b;
}

int main() {
    add(1, 2);
    return 0;
}

这是一段简单的C代码,用gcc将其编译成32位可执行文件

gcc -m32 -o main main.c

对于Windows 64 bit要想编译32位程序,如果MinGW的exception model是SEH,那么可以用i686-w64-mingw32-gcc来编译32位程序

对main.exe进行反汇编

AEM1

在上图中可以看到,在调用call前,先处理两个参数,但这里因为编译器等等问题使用了mov而不是push,实现的效果也是一样的。这里没有用push的区别就是esp没有始终指向栈顶,,所以也不用考虑栈平衡了。

AEM2

这里就是通过ebp来使用两个形参,分别赋到了EDX, EAX。然后EAX储存函数返回值。


Refs

https://blog.csdn.net/song_lee/article/details/105297902