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

网络攻防与实践

实验指导书

题目:

实践一 逆向破解与BOF

学生姓名: 牛宇轩

所在系别: 网络空间安全系

专 业: 网络空间安全专业

学 号: 20242429

指导教师: 王志强

2026 年 9月

目 录

1 实验目的

1.1掌握汇编指令机器码

1.2掌握反汇编与十六进制编辑

1.3掌握程序执行流程劫持

2 实验内容及实验原理

2.1手工修改可执行文件跳转

2.2 BOF 覆盖返回地址

2.3注入并运行 Shellcode

3 实验步骤

3.1信息收集

3.2方法一:手工修改可执行文件

3.3方法二:BOF 覆盖返回地址

3.4方法三:注入 Shellcode

4 问题及解决方案

5 实验思考

5.1学习感悟

1 实验目的

本次实践的目的是:

1.1掌握汇编指令机器码

掌握 NOP、JMP、JE、JNE、CMP 等常用汇编指令的机器码,理解相对偏移的编码方式与小端序的存储规则。
1.2掌握反汇编与十六进制编辑

能够使用 objdump、readelf、xxd 等工具对可执行文件进行反汇编与十六进制查看,并能够定位到指定虚拟地址对应的文件偏移。
1.3掌握程序执行流程劫持

理解 32 位 x86 函数调用与栈帧结构,能够通过修改机器指令或构造溢出输入,改变程序执行流程,跳转到隐藏函数或注入代码。

虚拟机

基本软件介绍

无

本实验在 Windows 主机上通过 WSL2 安装 Ubuntu 22.04 进行。WSL 提供 Linux 命令行环境,可运行 gcc、gdb、objdump 等工具;Ubuntu 22.04 为长期支持版本,软件源稳定,适合课程实验。

2 实验内容及实验原理

2.1手工修改可执行文件跳转

正常程序流程是 main 调用 foo,foo 回显用户输入;程序中还包含 getShell,正常不会被执行。

main 中调用 foo 的指令为 call,机器码形式为 E8 rel32。rel32 是 32 位有符号相对偏移,计算公式为:rel32 = 目标地址 - 下一条指令地址 = call 指令地址 + 5

把 rel32 改成指向 getShell 的偏移,即可在不改动代码结构的前提下改变调用目标。


2.2 BOF 覆盖返回地址

foo 中定义 char buf[28],并使用 gets(buf) 读入数据。gets 不检查长度,输入超过缓冲区长度即可溢出,覆盖栈上保存的 ebp 与返回地址。

32 位栈帧典型布局:[ebp+4] 返回地址[ebp] 保存的 ebp[ebp-4] 保存的 ebx[ebp-0x24] buf 起始

缓冲区到返回地址的偏移为 0x24 + 4 = 0x28 = 40 字节。将返回地址覆盖为 getShell 地址,foo 执行 ret 时即跳入 getShell。

2.3 注入并运行 Shellcode

若栈可执行(GNU_STACK 标记为 RWE),可将自定义 Shellcode 写入缓冲区,并把返回地址指向缓冲区起始地址,使程序跳到栈上执行注入代码。

Shellcode 一般使用 execve("/bin/sh", NULL, NULL) 系统调用,在 32 位 Linux 下通过 int 0x80 触发。

3 实验步骤

3.1信息收集

3.2方法一:手工修改可执行文件

备份原文件
47fef7553e6ca78097128256d27371ab
查 .text 节的文件偏移
380660955a505868f9f0cbbc9a81e600
确认要改的字节
037180188d98767ccd0f5c5e70916b20
改字节
545dcebfce780208aa7521eeb7a2f4ba
验证反汇编
1d65ab932287a8455486fc3e09d93f52
运行验证
a110fd3728d6ff465b06470b180eb926

3.3方法二:BOF 覆盖返回地址

在开始方法二之前,先恢复原文件

694d68b2cba1f7998cc01a6e2b4acb56

验证 pwntools已经装好

457eac400c4c096e5be65f99f4d70bf1

重新生成 payload

d71858dac5a083fe90672407cf8167cf

运行攻击。whoami 输出了 beibiao,说明 BOF 成功覆盖返回地址,跳到了 getShell,拿到了 shell。
30f73fc2505add058bf30c6fc1f6ff35

3.4方法三:注入 Shellcode

方法三需要先关掉 ASLR,先关闭 ASLR

2d86f2ecfda2881b8fa80527a9ab8fdb

拿到了buf 起始地址:0xffffcfc4

60f76aea42e8820044e204cc5f3ad42a

第一步,生成 shellcode payload

43941c1cb3d3f2b37b01bddb4ff771bf

攻击失败
04ea6ef1794c6bfba634f5d313ee7eb5

攻击

b6bda3afdfbb48ace4e11009036e57f2

成功

97192304da38fedffbc6fd7ef3b6507a

4 问题及解决方案

-问题1:方法三注入 Shellcode 后程序崩溃,未能获得 Shell

在 WSL2 + Ubuntu 22.04 环境下,按方法三构造 payload:shellcode = b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"payload = shellcode + b'\x90' * (40 - len(shellcode)) + p32(0xffffcfc4) + b'\n'

执行:(cat payload_shell_20242429; cat) | ./pwn1_20242429

程序回显了 Shellcode 内容,但在输入 whoami 后出现:Segmentation fault (core dumped)

未能进入 Shell。

04ea6ef1794c6bfba634f5d313ee7eb5

原因分析:

通过 gdb 逐步排查,发现以下现象:

x/40bx $ebp-0x24 显示缓冲区前 25 字节正是 Shellcode,内容正确;

x/wx $ebp+4 显示返回地址已被覆盖为 0xffffcfc4,与 payload 中的目标地址一致;

readelf -l pwn1_20242429 | grep GNU_STACK 显示 RWE,栈可执行;

单独编译测试程序,直接执行 int $0x80 退出,echo $? 返回 0,说明简单系统调用可用;

但执行 execve("/bin/sh", NULL, NULL) 时崩溃,崩溃地址在 int 0x80 附近。

进一步分析,失败原因主要有两点:

栈地址在 gdb 与真实运行中不一致。 gdb 会额外压入环境变量和参数,导致初始栈指针与直接运行不同。虽然已关闭 ASLR,但 buf 的实际地址在 gdb 中为 0xffffcfc4,在真实运行中可能偏移若干字节,返回地址跳到了错误位置。

WSL2 对 32 位 int 0x80 的 execve 支持不完整。 WSL2 是轻量级虚拟化环境,其 32 位兼容层对部分系统调用的处理与真实 Linux 内核存在差异。测试程序中的 exit 调用可以正常返回,但 execve 调用被拦截或处理异常,导致段错误。

解决方案:
不直接用 int 0x80,而是调用程序里已有的 system@plt,这样走的是 glibc,更稳。之前 getShell 里 system("/bin/sh") 能成功,就是这个道理。

5 实验思考

5.1学习感悟
这次实验是我第一次真正动手接触逆向和二进制漏洞利用。从最初连终端都不太会开,到最后能把三个任务做出来,中间踩了不少坑,也学到了很多课本上没有的东西。

第一,环境搭建本身就是一次锻炼。最开始我连 WSL 是什么都不知道,只知道 Windows 上可以装一个 Linux。从 wsl --install 开始,到装 Ubuntu 22.04、改主机名、装 gcc-multilib、修 apt 的 404 错误、装 pwntools,每一步都出过问题。比如 sudo 报 unable to resolve host,是因为改了主机名但 /etc/hosts 没有同步;apt install 报 404,是因为软件源索引过期。这些问题看起来小,但当时确实卡了很久。事后回头看,这些都是 Linux 的基本操作,只是以前没做过。

第二,我第一次直观理解了“机器码和汇编是一一对应的”。以前觉得可执行文件是个黑盒,这次用 readelf 找到 .text 节的文件偏移,用 xxd 看到 e8 b5 ff ff ff,再用 Python 把 b5 改成 8a,objdump 一看就变成了 call getShell。计算相对偏移的过程也让我真正理解了 call rel32 的编码方式:rel32 = 目标地址 - 下一条指令地址。改一个字节就能改变程序执行流程,这种感觉很奇妙。

第三,栈帧结构是真的复杂。buf 起始于 ebp-0x24,返回地址在 ebp+4,偏移是 40 字节。这个数字不是书上背来的,是我从 foo 的反汇编里一行一行推出来的。构造 payload 时还遇到 whoami 被 gets 一起读走的问题,后来在 payload 末尾加了一个换行才解决。这些细节让我对栈溢出的利用过程有了完整的认识。

最后,这次实验也让我意识到规范和细节的重要性。老师要求截图里主机名是姓名拼音、文件名带学号,一开始我没注意,差点白做。报告里的基础知识、问题与解决方案、学习感悟,每一项都有分值,不能忽略。参考文献也不能直接照搬模板,要换成自己实际用到的资料。

总的来说,这次实验让我从“不知道终端是什么”到能独立完成一次完整的漏洞利用,虽然过程曲折,但收获很大。以后再做类似的实验,我会先看清楚要求,再一步步来,遇到问题先自己排查,实在不行再查资料或问同学。

posted @ 2026-10-09 17:29  beibiao  阅读(12)  评论(0)    收藏  举报