南京大学《计算机系统基础》(ICS)课程实验之缓冲区溢出攻击(Bufolab)实战解析

张开发
2026/4/14 3:38:36 15 分钟阅读

分享文章

南京大学《计算机系统基础》(ICS)课程实验之缓冲区溢出攻击(Bufolab)实战解析
1. 缓冲区溢出攻击基础概念缓冲区溢出是计算机安全领域最经典的漏洞类型之一也是南京大学ICS课程Buflab实验的核心内容。简单来说当程序向固定长度的缓冲区写入超过其容量的数据时多出来的数据就会溢出到相邻内存区域。这就好比往一个300ml的杯子里倒500ml水多出来的200ml会流到桌面上。在x86-64架构中函数调用时的返回地址就保存在栈上紧邻着局部变量缓冲区。如果能够精确控制溢出内容就能改写返回地址从而改变程序执行流程。我在第一次接触这个实验时发现理解栈帧结构是关键。通过GDB调试器查看内存布局可以清晰地看到(gdb) x/20xg $rsp 0x7fffffffe3a0: 0x0000000000000000 0x00000000004008c7 0x7fffffffe3b0: 0x0000000000000000 0x0000000000000000 ...这里的0x4008c7就是函数返回地址。Buflab实验的Phase1就是利用这个原理通过构造特定输入数据覆盖这个地址让程序跳转到我们指定的位置。这种攻击方式看似简单但包含了系统安全最基础的内存管理概念。2. Phase1-3实战解析2.1 Phase1基础栈溢出Phase1是缓冲区溢出的Hello World。实验要求修改message函数的参数传递关键点在于理解x86-64的函数调用约定。在Linux系统下前六个参数通过寄存器传递%rdi存放第一个参数。通过objdump反汇编4008c0: bf 00 00 00 00 mov $0x0,%edi 4008c5: e8 96 ff ff ff callq 400860 message我们需要让程序跳过对%rdi的清零操作。经过计算缓冲区起始于%rsp0x10大小0x28字节。构造的攻击字符串前40字节填充缓冲区后8字节写入目标地址0x4008d1小端序。这里有个易错点现代CPU采用小端存储地址0x4008d1要写成d1 08 40 00。2.2 Phase2Shellcode注入Phase2难度明显提升需要注入可执行代码。目标是将全局变量buffer_overflowed设为1但直接修改会触发abort()。我的解决步骤用objdump找到buffer_overflowed地址0x6021b4编写汇编指令mov $0x1, 0x6021b4通过push/ret实现跳转用GDB确定buffer地址0x3ffff80绕过secret_number检查填充0x137f84ef最终Shellcode包含三条指令movl $0x1, 0x6021b4 push $0x400a89 ret这个阶段最大的收获是理解了代码注入的基本原理。通过将机器指令直接写入缓冲区并精确控制执行流程就能实现任意代码执行。2.3 Phase3带参数的函数调用Phase3引入了更复杂的函数调用场景。需要调用visit函数处理特定数组{1,3,-1}。关键点在于保护栈空间不被覆盖用sub $0x50,%rsp预留空间在栈上构造数组参数使用lea指令准备参数地址处理call指令的绝对地址问题恢复栈指针add $0x50,%rsp这个阶段我踩过的坑是忽略了rbp的保护。因为在do_phase开头有push %rbp操作溢出时需要保持rbp原值不变。通过GDB可以获取原始值(gdb) x/gx $rbp 0x3fffffd0: 0x00000000000000003. 高级防护绕过技术3.1 Phase4对抗栈随机化现代系统采用地址空间随机化(ASLR)防护缓冲区溢出。Phase4模拟这种情况解决方法是用NOP雪橇技术在Shellcode前填充大量0x90(NOP指令)计算可能的栈地址范围跳转到NOP区域任意位置都能滑向Shellcode通过GDB多次运行获取栈地址分布(gdb) info registers rsp rsp 0x3ffff780 ... rsp 0x3ffffd80选择最大地址作为跳转目标确保覆盖所有可能情况。这个技术让我理解了概率性攻击的威力——不需要精确命中只要在足够大的范围内就能成功。3.2 Phase5ROP攻击实战Phase5引入了最先进的ROP(Return-Oriented Programming)技术。当栈不可执行时通过串联现有代码片段(gadget)实现攻击。具体步骤在程序代码中寻找有用gadget构造ROP链每个gadget执行后ret到下一个通过栈传递参数最终完成字符串修改关键发现是phase函数中异常的movabs指令序列实际包含多个有用gadget。例如0x400928处的指令可以用于参数设置400928: 5f pop %rdi 400929: 5e pop %rsi 40092a: 5a pop %rdx 40092b: 59 pop %rcx 40092c: c3 retqROP攻击的精妙之处在于它不需要注入新代码而是像拼积木一样组合现有指令。这让我对现代攻击技术的进化有了深刻认识。4. 实验经验与调试技巧完成Buflab实验需要熟练掌握GDB调试技巧。以下是我总结的实用命令查看内存布局(gdb) x/20xg $rsp反汇编函数(gdb) disas do_phase查看寄存器值(gdb) info registers设置断点(gdb) b *0x4008c5单步执行(gdb) si此外理解调用约定很重要。在x86-64中%rdi, %rsi, %rdx, %rcx, %r8, %r9用于参数传递返回值存放在%rax调用者负责栈对齐16字节边界调试Shellcode时可以用以下方法验证(gdb) x/i 0x3ffff80 0x3ffff80: movl $0x1,0x6021b4遇到段错误时用bt命令查看调用栈(gdb) bt这些技巧不仅适用于实验在实际漏洞分析中也非常有用。通过Buflab的五个阶段我系统性地掌握了从基础溢出到高级绕过的完整知识体系。

更多文章