标签:
在网上关于这个的实验有几个版本,这个版本只有三关,都比较基础,为了不让大家混淆,对本实验做一下说明:
1. bufbomb : 主程序,有四个选项,最常用的是 -t ,后面加自己的姓名等,运行时会根据加入的参数生成cookie,第二关和第三关都会用到。
2. sendstring : 翻译程序,实验要求将字符转化成ascii码输入,两位输入,即想输入0也要输入00,1对应01,以此类推。可以通过 sendstring < exploit.txt > exploit_raw..txt 将翻译结果输入到 exploit_raw.txt 文件中。一般如果输入的文件中有地址时都尽量不要打开输出文件,里面会有一些无法显示的字符,尽量用管道或者重定位来进行输入。gdb下可以提通过重定位将 exploit_raw.txt 文件中的内容作为输入,例如 : run -t pangjingyu < exploit_raw.txt
3. makecookie: 根据输入的字符串输出唯一的cookie,作为标识,bufbomb中含有此程序。以 -t 选项运行bufbomb时要求输入标识,会自动生成并显示对应的cookie。
1 getbuf()返回时,不返回到test(),而是直接返回到指定的smoke()函数。
2 getbuf()返回时,不返回到test(),而是直接返回到指定的fizz()函数,而且要求给fizz()函数传入一个黑客cookie值作为参数。
3 getbuf()返回时,不返回到test(),而是直接返回到指定的bang()函数,并且在返回到band()之前,先修改全局变量global_value为你的黑客cookie值。
1. 首先观察getbuf()和gets(),save_char()函数。第一关只涉及这三个函数,整理其调用顺序是:getbuf() -> gets()-> save_char()。
2. 观察getbuf()函数:
图1 getbuf()函数
图2 gdb设置断点并输入参数运行
通过单步执行可以得到如下栈数据:
图3 观察栈参数
图4 返回地址和栈参数
根据图3和4可以的到在进入Gets()前 栈的参数设置如图:
图5 getbuf()函数栈示意图
如图,0xffffb11c处存放的0x08048db2正是test()中调用getbuf()的命令接下来的地址。此外,由于只需要改动getbuf()栈,所以没有必要一开始就去深究Gets()和save_char()函数,只要用gdb先了解它们对getbuf()栈的影响。此时还不知道字符串的存储地址,但却可以知道字符串的保存必然会影响到0xffffb0f0 ---- 0xffffb118的地址段内容。所以先手动直接输入一个0x28/2长度(20个字符)的字符串来测试存放的规律:
图6 getbuf()函数调用前后命令处设置断点
图7 在Gets()执行前后getbuf()栈的变化
可以看到只有0xffffb100 --- 0xffffb117的内存发生变化,而且内容刚好是测试字符串 01234567890123456789的ASCII码。测试其他其他字符串也是对应的ASCII码。所以Gets()和save_char()的最终效果就是将输入的字符串存在0xffffb100处起始的内存中。所以要修改0xffffb11c只要输入一个长度为20+12长度的字符串,且最后四个为函数smoke()的地址。由于getbuf()栈会被破坏,所以对于smoke()函数的栈准备语句可以直接跳过,且samke()中有exit()函数,无需返回到任何地方,所以字符串其他部分不用考虑,可以随意填充。smoke()函数如图:
图8 smoke()函数
如图,根据以上分析,只要将0xffffb11c处的地址改为0x08048eb6即可。由于是小端机器,所以最后四个字符存储的方式应该为b68e0408。剩余的由0填充即可。根据给的材料,直接将想要存入的内容写在exploit.txt中,通过sendstring处理后输入到bufbomb中,所以将要处理的字符串应该如下:
00000000000000000000000000000000000000000000000000000000b68e0408
首先在exploit.txt中写入如上字符串,通过sendstring写入exploit_raw.txt中:
图9 处理答案字符串生成输入用文件
然后进入gdb并设置如图6的断点,通过重定位设置exploit_raw.txt为输入的字符串:
图10 输入字符串文件并运行
图11 Gets()前后内存对比
如图11,0xffffb11c处地址成功改为0x08048eb6。继续运行:
图12 成功跳转并结束
首先判断推断的正确性,设置图6的断点,将第一关的答案的最后四个字节改为 608e0408,通过gdb查看ebp,esp,eip的值的变化:
图13 leave/ret前后寄存器值的变化情况1
图14 leave/ret前后寄存器值的变化情况1
如上图,成功进入了fizz函数,观察fizz的参数准备过程:
由mov 0x8(%ebp), %eax以及cmp指令可以看出 $ebp+8 的地址存放的就是我们应该为fizz准备的参数,而0x804a1d4存放的则是程序运行时根据名字得到的cookie。根据图13得到的ebp,esp等数据可以知道该地址应该为0xffffb120 – 4 + 8 = 0xffffb124。而第一关的答案修改到0xffffb11c,所以要再加8字节,且最后4个字节为cookie,即2ac090b5的小端排列:b590c02a。其余四个字节随意填充。据此得到的答案为:
00000000000000000000000000000000000000000000000000000000608e040800000000b590c02a
将答案输入到exploit.txt并用 sendstring处理后存放到exploit_raw.txt中,用gdb进行测试:新建一个test.c文件如下:
图18 test.c
用-m32 -S选项生成汇编文件test.s,在如下所示的leave指令上方插入图17中的代码:
图19 编译生成汇编文件
图20 插入计划代码
继续编译链接test.s文件生成test,并用objdump反汇编出test1.s文件:
图21 编译生成可执行文件并反汇编
打开test1.s找到如下部分即为需要的指令代码:
图22 计划代码的指令码
jmp指令不能直接用,而是需要计算。根据图21得到的指令的长度( 5+5+5 ),再加上输入的字符串存储的地址(0xffffb100),可以推断出当运行到jmp指令(未执行jmp)时,eip的值为0xffffb10a,而该指令为PC相对寻址,所以e9后的数字应该为:0x8048e10– ( 0xffffb10a + 5 ) , 即0x0804dd01。所以字符串的代码部分为 b8b590c02a a3c4a10408 e901dd0408, 而对应答案1和2中跳转的最后四个字节则改为 00b1ffff,即字符串的首地址。 长度与关卡1的答案长度相同,因为不需要像关卡2去为函数准备参数:
a1d4a10408a3c4a10408e901dd04080000000000000000000000000000b1ffff
接下来用sendstring处理exploit.txt并输出到exploit_raw.txt,进入gdb:
图23 处理待测答案
继续通过gdb进行答案验证:
图24 重定位输入流输入待验证答案
如图,执行getbuf()最后的ret指令前查看是否成功把代码首地址0xffffb100写入0xffffb11c,如图表示写入成功。继续执行:
图26 插入代码的执行
对应的答案串为:
a1d4a10408a3c4a10408b8168e0408a320b1ffffc30000000000000000b1ffff
echo 0 > /proc/sys/kernel/randomize_va_space即可。每次开机时会重新开启栈随机化。
#include<stdio.h> unsigned int find_start(void) { __asm__("movl %esp,%eax"); } int main() { printf("0x%x\n",find_start()); }生成可执行文件后重复运行几次,如果输出的地址均相同则证明未开启栈随机化。
标签:
原文地址:http://blog.csdn.net/pjy19960808/article/details/51500830