20145203盖泽双 《网络对抗技术》 MAL_逆向与Bof基础
实践目标
(1)我们要通过修改程序代码,使得程序运行其自身中本不该运行的代码片段。
(2)在这里,我们有一个名为20145203pwn1的linux可执行文件,该代码的执行流程为:main调用foo函数,foo函数会简单回显任何用户输入的字符串。我们要想办法运行该程序中包含的另一个代码片段——getShell,并返回一个可用Shell。
实现思路
(1)强行修改程序执行流:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
(2)运行原本不可访问的代码片段:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
(3)注入运行任意代码:注入一个自己制作的shellcode并运行这段shellcode。
基础知识
(1)缓冲区溢出:
简单的说就是计算机对接收的输入数据没有进行有效的检测(理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符),向缓冲区内填充数据时超过了缓冲区本身的容量,而导致数据溢出到被分配空间之外的内存空间,使得溢出的数据覆盖了其他内存空间的数据。
(2)objdump -d test:
对test文件反汇编
(3)more:
用于分页显示文本文件,还支持直接跳转行等功能。
(4)e8:
机器指令e8为跳转之意。
(5)Perl:
Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。使用输出重定向“>”将perl生成的字符串存储到文件input中。
(6)bp :
16位寄存器;ebp:
32位寄存器;rbp:
64位寄存器。
(7)寄存器ebp:
指向当前的栈帧的底部(高地址);寄存器esp:
指向当前的栈帧的顶部(低址地)。
(8)寄存器eip:
保存程序下一步所要执行指令的地址。
实验步骤
方法一:直接修改程序机器指令,改变程序执行流程
(1)将文件进行备份。
cp pwn1 20145203pwn1
(2)验证可执行文件功能。
./20145203pwn1
(3)将可执行文件进行反汇编。
objdump -d 20145203pwn1 | more
(4)由图3、图4、图5可观察到,main函数第五行的汇编指令"call 8048491 ",指这条指令将调用位于地址8048491处的foo函数,其对应机器指令为“e8 d7ffffff”。getshell的地址为804847d。
“e8 d7ffffff”的作用: e8即跳转之意。即此时此刻EIP=80484ba的值应该是下条指令的地址,但因为有 “e8 d7ffffff”这条指令,CPU就会转而执行 “EIP + d7ffffff”这个地址的指令。“d7ffffff”是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491这个值。
因此,main函数调用foo,对应机器指令为“ e8 d7ffffff”,那我们想让它调用getShell,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。计算47d-4ba就能得到补码为c3ffffff。
(5)修改可执行文件。
①用vi编辑器进行修改。
vi 20145203pwn1
②将当前模式切换到16进制模式。
③找到要修改的指令。
④进行修改。
⑤修改后保存退出,反汇编查看修改结果。
⑥执行修改后的文件,得到shell提示符“#”。
方法2 :通过构造输入参数,造成BOF攻击,改变程序执行流
(1)对可执行文件进行gdb调试。
gdb 20145203pwn2
(gdb) r
(2)查看文件的执行信息。
(gdb) info r
(3)由图12我们可以观察到eip寄存器中的值为0x35353535,即5555的ASCII码。而eip寄存器的功能是保存程序下一步所要执行指令的地址,此处我们可以看出本来应返回到foo函数的返回地址已被"5555"覆盖。所以我们只需要将溢出的5555改为getshell的地址804847d,使getshell的地址刚好溢出到eip寄存器中就可以了。下面我们应该思考将修改哪几个5,以及怎样修改。
(4)将文件重新进行调试,确定溢出数据的具体信息。
(5)因为不知道这台机器采用的字节序是大端还是小端,所以无法确定输入数据应为x08x04x84x7d还是x7dx84x04x08。 所以我们在输入字符串的地方0x804849d处设置断点,查其eip 0x804849d 0x804849d ,再对比之前 eip 0x34333231 0x34333231 (4321对应ASCII码为34333231)所以得出正确应输入 11111111222222223333333344444444x7dx84x04x08
(6)我们无法通过键盘输入x7dx84x04x08这样的16进制值(foo函数的输入是字符串形式,不是数值),所以要先生成包括这样字符串的一个文件。如下图:
x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
(7)执行修改过的文件,将input的输入,通过管道符"|",作为pwn1的输入。