笔记
学习最新前沿软件逆向安全技术、游戏安全辅助脚本技术,B 站 小迪 xiaodi 老师
微信 i-xiaodi
用于栈的管理。
以下是 x86 32位模式下常见栈操作指令及其功能表:
指令 |
功能描述 |
栈指针变化 ( |
PUSH |
将寄存器值、立即数或内存操作数压入栈顶。例: |
|
POP |
将栈顶值弹出并存入寄存器或内存操作数。例: |
|
PUSHAD |
按顺序将通用寄存器( |
|
POPAD |
按顺序从栈中弹出值恢复到通用寄存器( |
|
PUSHFD |
将32位标志寄存器( |
|
POPFD |
从栈中弹出值并恢复到32位标志寄存器( |
|
CALL 地址 |
调用子程序,将当前指令的返回地址压入栈顶,然后跳转到目标地址。 |
|
RET |
从栈中弹出返回地址并跳转到该地址。 |
|
ENTER |
设置栈帧:将当前 |
|
LEAVE |
恢复栈帧:将 |
|
INT imm8 |
软中断:将标志寄存器、代码段( |
|
IRET |
中断返回:从栈中弹出标志寄存器、代码段( |
|
说明
- 栈增长方向:x86 的栈是向下增长的,压栈时
ESP
减小,弹栈时ESP
增大。 - 操作数大小:在32位模式下,栈操作默认是以4字节(32位)为单位。
PUSHAD
和POPAD
:
-
- 压栈顺序(PUSHAD):
-
-
EAX
ECX
EDX
EBX
- 当前
ESP
的值(未更新的栈指针) EBP
ESI
EDI
-
-
- 弹栈顺序(POPAD):
-
-
EDI
ESI
EBP
- 跳过 ESP(不恢复)
EBX
EDX
ECX
EAX
-
示例代码
PUSH/POP 示例
PUSH EAX ; 将 EAX 压入栈,ESP -= 4
PUSH 0x12345678 ; 将立即数 0x12345678 压入栈,ESP -= 4
POP EBX ; 从栈顶弹出值到 EBX,ESP += 4
函数调用示例
CALL MyFunction ; 将返回地址压入栈,ESP -= 4
RET ; 从栈中弹出返回地址并跳转,ESP += 4
栈帧设置示例
ENTER 0x10, 0 ; 设置栈帧,分配16字节的局部变量空间
LEAVE ; 恢复栈帧
拓展
1. LEAVE指令的本质
LEAVE 是 x86 汇编中的一条指令,常用于函数返回之前进行栈帧清理。
功能描述:
LEAVE 的功能相当于以下两条指令:
MOV ESP, EBP
POP EBP
具体操作:
MOV ESP, EBP
:
将当前的栈基指针(EBP
)的值赋给栈顶指针(ESP
),释放局部变量所占的栈空间。
这一步使ESP
恢复到进入函数之前的状态。POP EBP
:
从栈中弹出保存的调用者的EBP
值,恢复调用者的栈基指针。
本质:
LEAVE 是一种简化栈帧清理的指令,目的是恢复函数调用前的栈布局,使得调用者可以正常执行后续代码。
2. ENTER指令的本质
ENTER 是用于设置栈帧的一条复杂指令,功能是为函数调用创建栈帧。
格式:
ENTER imm16, imm8
imm16
:为栈帧分配的局部变量空间的大小(以字节为单位)。imm8
:嵌套层级深度,通常为 0。
功能描述:
ENTER imm16, imm8
相当于以下多条指令:
PUSH EBP ; 保存当前栈基指针
MOV EBP, ESP ; 设置新的栈基指针
SUB ESP, imm16 ; 为局部变量分配空间
如果嵌套深度(imm8
)为 0,则 ENTER
不会处理更多嵌套逻辑,直接执行上述三步操作。
嵌套层级(imm8
)的作用:
当函数有嵌套调用需求时,ENTER
会额外处理保存和管理多级 EBP
指针,用于支持更复杂的栈布局。不过现代编译器通常不会依赖这种功能。
本质:
ENTER 是一种为函数设置栈帧的复合指令,目的是方便管理函数调用过程中的局部变量和嵌套调用栈帧。但由于它的复杂性和效率问题,现代编译器一般直接使用 PUSH
、MOV
和 SUB
等指令代替。
对比总结
指令 |
作用 |
等效操作 |
常见用途 |
LEAVE |
清理栈帧,恢复调用者状态 |
|
函数退出前 |
ENTER |
创建栈帧,分配局部变量空间 |
|
函数入口时(已不常用) |
注:
- 在现代编译器中,
ENTER
和LEAVE
的功能可以被更简单和高效的指令组合替代,因此ENTER
较少使用,而LEAVE
仍较为常见。