摘要:在 x86 汇编语言 中,"PUSH" 指令溢出(Stack Overflow) 是指 栈空间耗尽,导致无法继续压入数据。这通常是由于 递归调用过深 或 栈空间分配不足 引起的。以下是几种典型的 "PUSH" 溢出场景及示例:
在 x86 汇编语言 中,"PUSH" 指令溢出(Stack Overflow) 是指 栈空间耗尽,导致无法继续压入数据。这通常是由于 递归调用过深 或 栈空间分配不足 引起的。以下是几种典型的 "PUSH" 溢出场景及示例:
***
1. 递归调用过深(无限递归)
问题描述
- 函数 无限递归调用自身,每次调用都会 "PUSH" 返回地址和局部变量,最终耗尽栈空间。
- 栈溢出错误:"Stack Overflow"(程序崩溃)。
示例代码(32位模式)
section .text
global _start
_start:
call recursive_func ; 调用递归函数
mov eax, 1 ; 退出程序
int 0x80
recursive_func:
push eax ; 保存寄存器(模拟局部变量)
call recursive_func ; 无限递归!
pop eax ; 永远不会执行到这里
ret
运行结果
- 每次 "call recursive_func" 会:
1. "PUSH" 返回地址(4字节)。
2. "PUSH EAX"(4字节)。
- 栈指针 "ESP" 不断减小,直到 超出栈空间边界,触发 "Stack Overflow"。
***
2. 栈空间分配不足
问题描述
- 程序 未分配足够栈空间,但频繁 "PUSH" 数据。
- 常见于 嵌入式系统 或 内核开发(栈大小受限)。
示例代码(16位实模式)
org 100h
start:
mov sp, 0x100 ; 手动设置栈指针(SP=0x100,栈空间仅256字节)
mov cx, 100 ; 循环100次
loop_start:
push ax ; 每次循环 PUSH AX(2字节)
dec cx
jnz loop_start
mov ah, 4Ch ; 退出程序
int 21h
运行结果
- 每次 "PUSH AX" 会减少 "SP" 2字节。
- 循环 100 次后,"SP" 从 "0x100" 减少到 "0x100 - 200 = 0x38"。
- 如果栈空间不足(如 "SP
***
3. 中断嵌套过深
问题描述
- 中断处理函数 中再次触发中断(嵌套中断),每次中断都会 "PUSH" 标志寄存器、返回地址等。
- 如果中断频率过高,栈会迅速耗尽。
示例场景
; 假设中断处理函数(IRQ0 时钟中断)
irq0_handler:
pushad ; 保存所有通用寄存器(32字节)
pushfd ; 保存标志寄存器(4字节)
; ...(处理中断)
call nested_irq ; 嵌套调用其他中断
popfd
popad
iret
nested_irq:
pushad
pushfd
; ...(嵌套中断逻辑)
popfd
popad
ret
运行结果
- 每次中断会 "PUSH" 至少 36 字节("PUSHAD" + "PUSHF")。
- 如果中断嵌套 10 次,栈将消耗 "36 * 10 = 360 字节"。
- 默认栈空间可能不足(如 DOS 下栈仅 1KB),导致溢出。
***
4. 缓冲区溢出攻击(恶意 PUSH)
问题描述
- 攻击者通过 缓冲区溢出 覆盖返回地址,并注入恶意代码。
- 恶意代码可能包含 大量 "PUSH" 指令,故意耗尽栈空间。
示例攻击
; 恶意代码片段
malicious_code:
push eax ; 填充栈
push ebx
; ...(重复数千次)
jmp exploit ; 跳转到攻击代码
运行结果
- 恶意 "PUSH" 指令会快速耗尽栈空间,导致程序崩溃或执行攻击代码。
***
如何避免 "PUSH" 溢出?
1. 控制递归深度:
- 确保递归函数有 终止条件。
recursive_func:
cmp ecx, 0 ; 终止条件
je exit
dec ecx
push eax
call recursive_func
pop eax
exit:
ret
2. 增加栈空间:
- 在 链接器脚本 或 程序初始化 时分配更大栈空间。
; Linux 32位程序示例
section .stack
resb 8192 ; 分配 8KB 栈空间
3. 避免中断嵌套:
- 在中断处理中 禁用中断("CLI"),防止嵌套。
irq_handler:
cli ; 禁用中断
pushad
; ...(处理中断)
popad
sti ; 启用中断
iret
4. 检查栈指针:
- 在关键代码前检查 "ESP" 是否接近栈底。
check_stack:
mov eax, [stack_bottom]
sub eax, esp ; 计算剩余栈空间
cmp eax, 1024 ; 至少保留 1KB
jb stack_overflow
ret
stack_overflow:
; 处理栈溢出
***
总结
| 场景 | 原因 | 后果 |
||||
| 无限递归 | 递归无终止条件,"PUSH" 过多 | "Stack Overflow" 崩溃 |
| 栈空间不足 | 手动分配栈太小,"PUSH" 超出范围 | 内存覆盖,程序异常 |
| 中断嵌套 | 中断处理中再次触发中断 | 栈快速耗尽 |
| 缓冲区溢出攻击 | 恶意代码故意 "PUSH" 耗尽栈 | 程序崩溃或执行恶意代码 |
关键点:"PUSH" 溢出本质是 栈空间耗尽,需通过合理设计算法、分配足够栈空间和防御性编程来避免。
来源:动人教育