
1) 【一句话结论】堆溢出是程序向堆分配的缓冲区写入超长数据,覆盖堆上的控制信息(如函数指针),劫持执行流程,最终实现远程代码执行(RCE)或权限提升。
2) 【原理/概念讲解】老师口吻解释关键概念:
栈是程序运行时自动管理的内存区域,用于存储局部变量、函数参数、返回地址,遵循“后进先出”原则,地址从高地址向低地址增长(如x86中esp寄存器指向栈顶)。
堆是程序手动通过malloc等函数分配的动态内存区域,生命周期由程序控制,地址从低地址向高地址增长(如brk指针指向堆顶)。
内存布局中,栈在堆的上方(高地址方向),堆在栈的下方(低地址方向)。指针操作中,栈指针管理栈,堆指针管理堆。堆溢出时,写入数据超过缓冲区容量,会覆盖堆上的相邻内存(如结构体中的函数指针字段),导致程序调用时执行恶意代码。
3) 【对比与适用场景】
| 特性 | 栈 (Stack) | 堆 (Heap) |
|---|---|---|
| 定义 | 自动分配,用于局部变量、函数参数等 | 手动分配,动态调整大小的内存区域 |
| 分配方式 | 系统自动(函数调用时入栈,返回时出栈) | 程序主动调用malloc等函数分配 |
| 生命周期 | 与函数调用栈绑定,函数结束即释放 | 程序控制,需手动free释放(否则泄漏) |
| 地址增长方向 | 高地址→低地址(如x86中esp递减) | 低地址→高地址(如brk递增) |
| 指针类型 | 栈指针(如esp) | 堆指针(如brk、malloc返回的指针) |
| 常见问题 | 栈溢出(覆盖返回地址,RCE) | 堆溢出(覆盖函数指针、指针字段,劫持控制流) |
| 使用场景 | 存储局部变量、函数参数、返回地址 | 存储链表、动态缓冲区等需灵活调整的数据 |
4) 【示例】
伪代码示例(堆结构体包含函数指针):
void vulnerable_function(char *input) {
struct {
char data[64]; // 堆上的数据缓冲区
void (*func_ptr)(void); // 函数指针字段(控制流劫持目标)
} heap_struct;
// 错误复制输入,导致缓冲区溢出
memcpy(heap_struct.data, input, strlen(input) + 1);
// 输入长度超过64,覆盖func_ptr字段为恶意代码地址
heap_struct.func_ptr(); // 调用覆盖后的函数指针,执行恶意代码
}
解释:函数接收输入后,将输入复制到堆上的结构体data字段,因输入过长,覆盖了func_ptr字段。程序调用时执行恶意代码,实现RCE。
5) 【面试口播版答案】
“堆溢出漏洞的核心是程序向堆分配的缓冲区写入超长数据,导致覆盖堆上的控制信息(如函数指针),进而劫持程序执行流程。具体来说,栈用于局部变量和函数调用,堆是动态分配的内存。当输入超过缓冲区容量时,会覆盖堆上的结构体字段(如函数指针),程序调用时执行恶意代码。比如,假设函数中有一个堆结构体,包含数据缓冲区和函数指针,输入超过缓冲区大小后,覆盖函数指针为shellcode地址,调用时执行shellcode,从而获取系统权限。”
6) 【追问清单】
7) 【常见坑/雷区】