20242406 2025-2026-1 《网络与系统攻防技术》实验一实验报告

20242406 2025-2026-1 《网络与系统攻防技术》实验一实验报告

一.基础知识

  1. NOP 指令

机器码:0x90,单字节指令。作用是空操作,CPU 执行这条指令不做任何实质性运算,仅消耗一个指令周期,常用于 shellcode 填充、指令对齐,漏洞利用中常用 NOP sled(NOP 滑板)。

  1. CMP 比较指令

CMP 用于两个操作数做减法比较,只修改标志寄存器,不改变原操作数的值。后续 JE/JNE 等条件跳转指令依据 CMP 设置的 ZF 零标志位判断是否跳转。 例:CMP eax,ebx,机器码会根据寄存器 / 内存操作数不同而变化,是条件分支的前置指令。

  1. JE 相等则跳转(Jump if Equal)

条件跳转指令,ZF=1(上一步比较结果相等)时发生跳转;ZF=0 则顺序执行。短跳转机器码为0x74 偏移,后面 1 字节是相对跳转偏移量。

  1. JNE 不相等则跳转(Jump if Not Equal)

ZF=0(比较结果不相等)时跳转;ZF=1 顺序执行。短跳转机器码为0x75 偏移。

  1. 反汇编:把程序二进制机器码翻译成可读汇编指令的过程。可使用 objdump、gdb、IDA Pro 等工具。 作用:查看可执行文件内部真实指令,获取指令对应的机器码字节,定位需要修改的指令地址,为修改二进制打好基础。
  2. 十六进制编程器(十六进制编辑器):直接读写修改可执行文件原始二进制字节。可以直接修改文件里机器码,替换指令。 实验用途:不修改源代码,直接修改程序二进制,替换 JE/JNE/JMP/NOP 的机器码字节,篡改程序的分支逻辑,改变程序运行流程。

二.实验内容

1.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。

2.利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。

3.注入一个自己制作的shellcode并运行这段shellcode。

三.实验过程

(1)任务一

1.下载虚拟机,使用winSCP工具将pwn二进制文件传入虚拟机,按要求修改主机名和pwn文件名:

执行命令objdump -d pwn20242406 | grep -E "main|foo|getShell",反汇编筛选关键函数地址:

2.执行命令objdump -d pwn20242406 | grep -A 30 "<foo>:",从反汇编可以看到,main 函数里调用 foo 的指令:

  • e8 是 call 指令的操作码,后面 4 字节是相对偏移量
  • 相对偏移 = 目标函数地址 - call 指令下一条指令的地址
  • 只需要把后面的偏移量改成指向 getShell,就能让 main 直接调用 getShell

3.计算新的机器码

  • call 指令内存地址:0x080484b5
  • call 指令长度 5 字节,下一条指令地址:0x080484b5 + 5 = 0x080484ba
  • 目标 getShell 地址:0x0804847d

计算相对偏移

新偏移 = 0x0804847d - 0x080484ba = -0x3D 32 位补码小端序表示为:c3 ff ff ff

计算文件偏移

根据之前的.text 段参数:

  • .text 虚拟起始:0x08048380
  • .text 文件起始偏移:0x000380

目标指令文件偏移 = 0x080484b5 - 0x08048380 + 0x380 = 0x4b5

安装十六进制编辑器:

修改文件,将D7改为C3:

执行命令objdump -d pwn20242406 | grep -A 10 "<main>:"查看call指令:

运行程序验证效果,直接弹出 # 或 sh-# 提示符,代表成功跳转到getShell,任务 1 完成。

(2)任务二

1.恢复原始未修改数据:

2.执行objdump -d pwn20242406 | grep -E "<getShell>|<foo>|<main>" -A 15查看关键函数汇编:

确认信息:

  • 目标函数:getShell 入口地址为 0x0804847d,内部调用 system 函数,执行后可以拿到 shell
  • 漏洞点:foo 函数使用无边界检查的 gets 读取输入;缓冲区位于 ebp-0x1c,大小为 28 字节
  • 栈偏移计算:缓冲区起始 → 栈上保存的旧 ebp(4 字节) → 函数返回地址 覆盖返回地址的总偏移量 = 28 字节缓冲区 + 4 字节保存 ebp = 32 字节 即输入的第 33~36 个字节,会精准覆盖栈上的函数返回地址

3.安装gdb调试工具:

偏移量验证结果,eip = 0x34333231,对应 ASCII 字符 4 3 2 1,正好是输入的 1234 的小端序,证明 32 个 A 之后正好就是栈上的返回地址,偏移量 32 字节完全正确:

4. 执行完 payload 之后,输入ls成功列出目录文件,已经拿到 shell,缓冲区溢出攻击完成。

(3)任务三

1. 安装 execstack 工具,开启栈可执行,输出大写X,栈可执行设置成功。

2.关闭内核 ASLR 地址随机化。ASLR 会让栈地址每次运行都变化,无法写死 shellcode 地址,临时关闭

输出0,关闭成功

3.调试获取shellcode所在栈地址,终端A生成占位payload文件

终端B设置断点

当程序运行到断点时,我们发现可以发现esp寄存器的内容为:

修改跳转地址,执行溢出拿shell:

任务三成功。

四.问题及解决方案

问题1:任务一修改文件时,多次搜索(leave,ret)会跳转到文件的不同位置,无法精确定位到foo函数末尾的正确位置。

问题1解决方案:通过询问AI工具得知,(leave; ret)在整个二进制文件里不止一处,很多函数结尾都会用 leave+ret,hexedit 的 Ctrl+S 搜索是从当前光标位置向后找,光标位置不同,搜到的匹配结果就不一样,所以多次跳到不同地址。AI提供的解决方法时直接计算文件偏移,执行命令查看代码段信息:

计算文件偏移后精确定位需要修改的位置。

问题2:payload 送入时机错误,提前回车导致程序直接跑完。执行(cat input_shellcode; cat) | ./pwn20242406之后,在还没有完成 gdb 附加进程、设置断点的情况下就按下回车,payload 一次性全部输入给gets()函数。函数执行完毕,函数 ret、程序直接运行结束退出。后续再执行ps -ef已经找不到该进程 PID,无法使用gdb attach附加调试,不能查看栈上 payload 的内存布局,拿不到 shellcode 的跳转地址,后续攻击无法开展。

问题2解决方案:严格遵守操作时序:先执行管道命令,让程序阻塞在gets()等待输入,此时不要按回车,使用ps -ef | grep pwn20242406获取阻塞进程的 PID,使用gdb attach PID附加目标进程,在 foo 函数的ret返回指令处设置断点,输入c让 gdb 继续等待触发,全部调试准备工作完成之后,再回到运行管道的终端按下回车,送入 payload 命中断点。若已经误操作提前回车,进程已经退出,需要重新执行管道命令,回到阻塞状态,完整重复上述调试流程。

五.学习感悟与思考

本次缓冲区溢出实验完成了修改二进制劫持函数、覆盖返回地址、注入 shellcode 获取 shell 等任务,让我把课本中栈、函数调用的理论知识落到实操当中。

实验过程遇到不少问题,文件权限、地址偏移混淆、GDB 调试与实际运行地址存在差异,多次出现攻击失败、程序崩溃的情况,我也体会到纸上理论和真实环境之间存在差距,需要借助调试工具反复排查。

通过实验我理解了缓冲区溢出的原理:程序未对输入做边界检查,溢出数据能够覆盖返回地址,篡改程序执行流程。也认识到 ASLR 等保护机制对抵御溢出攻击的作用,不安全的编码会带来严重安全隐患。同时熟练了 GDB 调试、构造 payload 等操作。

攻防技术是双刃剑,作为网安专业学生,技术应当用于合法的安全防护。本次实验也发现我对栈内存底层掌握尚有不足,今后会加强底层知识学习,重视安全编码,树立正确的网络安全观念。

参考资料

博客园.Linux 栈溢出 RNS 模式实验笔记 [EB/OL].

0x11 逆向与Bof基础 - 实验指导

posted @ 2026-10-01 15:26  好记的名字  阅读(8)  评论(0)    收藏  举报