拆炸弹实验报告
专业:电子信息科学与技术 遮羞云。。。 学号:09300720051 姓名:陈文浩 【实验目的】
理解汇编语言,学会使用调试器。
【实验原理】
二进制炸弹是作为一个目标代码文件提供给学生们的程序,运行时,它提示用户输入6个不同的字符串。如果其中任何一个不正确,炸弹就会“爆炸”:打印出一条错误信息。学生通过反汇编和逆向工程来确定是哪六个字符串,从而解除他们各自炸弹的雷管。
【实验过程】
一、使用putty登录、修改密码
1、打开putty,输入用户名和密码(csapp),使用命令passwd username修改密码。 2、使用命令ls后看到有一个文件bomb49.tar,输入 tar xvf bomb49.tar 解压后,再用命令ls看到目录下新出现了bomb、bomb.c和README三个文件。一开始在看到.c文件后很开心,以为这样就可以看到c语言代码了,那拆炸弹的任务就变得简单多了。输入cat bomb.c后,把c代码看了一遍,发现原来这里只有主函数,每一关的具体代码都没有。看来只有从汇编代码入手了。
3、输入反汇编命令objdump -d bomb后,出现了大量汇编代码,在putty小窗口里看起来很麻烦。
二、用linux终端作准备
1、Ubuntu装好了之后,在终端输入ssh username@10.92.13.8连接到服务器,并开始新一轮的尝试。
2、输入objdump -d bomb > 1.txt将汇编代码输出到服务器上一个自动生成的叫1.txt的文件中。
3、中断连接,退回自己的系统桌面,使用命令scp username@10.92.8:1.txt 1.txt 将在桌面复制生成一个也叫1.txt的文件。这时候就可以很方便的查看汇编代码了。
三、开始拆炸弹
1、首先是找到main函数,发现它调用了从phase1到phase6这六个函数。这应该就是每一关需要看懂的函数了。
2、于是找到phase1,代码如下:
08048dd9
8048dd9: 55 push ?p
8048dda: 89 e5 mov %esp,?p 8048ddc: 83 ec 08 sub $0x8,%esp
2 8048ddf: c7 44 24 04 e4 98 04 movl $0x80498e4,0x4(%esp) ○
8048de6: 08
8048de7: 8b 45 08 mov 0x8(?p),êx 8048dea: 89 04 24 mov êx,(%esp)
8048ded: e8 89 01 00 00 call 8048f7b
1 8048df2: 85 c0 test êx,êx ○
8048df4: 74 05 je 8048dfb
8048dfc: 8d 74 26 00 lea 0x0(%esi),%esi 8048e00: c3 ret
可以看到在êx!=0的时候就会调用
$0x80498e4,0x4(%esp)有立即数,是将此处地址的值拿来用,输入gdb bomb进入调试状态,用x/s 0x80498e4查看内容,终端显示出字符串\make... billions?\。
下面一步 mov 0x8(?p),êx就是把我们输入的参数放进êx中,然后放进
(%esp) ,再调用函数
于是开始第一关的尝试。非常重要的一步是在
3、第二关 汇编代码略。
首先要注意到的是
-0x4(%esi,íx,4),êx 和cmp êx,(%esi,íx,4),其中êx 和íx在每次循环中加1,因此第二到第六个数分别为1*2=2, 2*3=6, 4*4=24, 24*5=120, 120*6=720。经检验,结果正确!
4、第三关 汇编代码略。
注意到movl $0x8049abb,0x4(%esp),输入指令x/s 0x8049abb,得到0x8049abb: \,显示出应该输入两个数字。而cmp $0x1,êx表明输入参数必须多于1个。再往下到达cmpl $0x7,-0x4(?p),即输入的第一个参数值必须小于等于7。然后看到jmp *0x8049920(,êx,4),这是典型的switch跳转语句,即跳转到以地址*0x8049920为基址的跳转表中。输入p/x *0x8049920,得到地址0x8048ea2,在代码中找到该处指令,得到第一个输入为0时对应的第二个输入为0x211,转换成十进制为529。经调试后结果正确。当然此题不止一个答案,当输入为1时,通过p/x *0x8049924,得到地址0x8048e97,找到第二个输入为447。其他答案不一一写出。
5、第四关
phase4汇编代码略。
同样的由movl $0x8049abe,0x4(%esp)我们知道这一关是要输入一个数字。由cmpl $0x0,-0x4(?p) 知道输入的参数必须大于0。注意到这里调用了函数
08048bd0
8048bd0: 55 push ?p
8048bd1: 89 e5 mov %esp,?p 8048bd3: 83 ec 18 sub $0x18,%esp
8048bd6: 89 5d f8 mov ?x,-0x8(?p) 8048bd9: 89 75 fc mov %esi,-0x4(?p) 8048bdc: 8b 75 08 mov 0x8(?p),%esi 8048bdf: b8 01 00 00 00 mov $0x1,êx 8048be4: 83 fe 01 cmp $0x1,%esi
8048be7: 7e 1a jle 8048c03
8048c03: 8b 5d f8 mov -0x8(?p),?x 8048c06: 8b 75 fc mov -0x4(?p),%esi 8048c09: 89 ec mov ?p,%esp 8048c0b: 5d pop ?p 8048c0c: c3 ret
由cmp $0x1,%esi 及下面的代码知如果所传递的参数小于等于1则结束
func4(0)=1;func4(1)=1;func4(2)=func4(0)+func4(1)...
这其实就是斐波那契数列。然后回到
$0xe9,êx,说明
6、第五关 汇编代码略。
由以下两句代码call 8048f60
接下来的两句movzbl (ìx,êx,1),êx 和mov %al,(?x,íx,1)将ìx 中根据êx的值位移后的字符的低四位放到目的地。接下来是循环体中íx的值再加1,直到全部6个字符串比较完毕。注意到movl $0x8049917,0x4(%esp)的立即数,使用x/s 0x8049917,得到字符串\。之后将调用函数
到此思路已经基本清晰,也就是要求我们输入含有6个字符的字符串,这六个字符的ASCII码的低四位所指示的0x8049940中的字符串中的位置所对应的字符分别是\六个字符。 首先是第一个字符g,它在0x8049940中的位置是f,所以输入的第一个字符的ASCII码的低四位就是f。其余的5个字符也是根据这种关系得到它们ASCII码的低四位。最后得到一种答案为“opekma”。经检验,结果正确。
7、第六关 代码如下:
08048d20
8048d20: 55 push ?p
8048d21: 89 e5 mov %esp,?p 8048d23: 53 push ?x
8048d24: 83 ec 14 sub $0x14,%esp
8048d27: c7 44 24 08 0a 00 00 movl $0xa,0x8(%esp) 8048d2e: 00 8048d2f: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp) 8048d36: 00
8048d37: 8b 45 08 mov 0x8(?p),êx 8048d3a: 89 04 24 mov êx,(%esp)
8048d3d: e8 0a fb ff ff call 804884c
8048d64: 74 05 je 8048d6b
还是先观察
然后研究
在je 8048d6b
经验证,结论是正确的。
但是真的就是这么简单吗?答案是否定的。因为在调试secret_phase时,我再次查看了0x804a62c地址处的值,却发现该处放着数字“500”,也就是我在进入第六关时的输入。说明之前的空字符串只是在我没有输入的时候为空,也就说明
为了更清楚第六关的变化,认真研究了
0x804a5bc : 0x00000000 0x000003b5 0x00000009 0x00000000 0x804a5cc : 0x000001b5 0x00000008 0x0804a5c0 0x00000377 0x804a5dc : 0x00000007 0x0804a5cc 0x0000032e 0x00000006 0x804a5ec : 0x0804a5d8 0x0000015f 0x00000005 0x0804a5e4 0x804a5fc : 0x000001f4 0x00000004 0x0804a5f0 0x000001b0 0x804a60c: 0x00000003 0x0804a5fc 0x00000306 0x00000002 0x804a61c: 0x0804a608 0x00000280 0x00000001 0x0804a614 0x804a62c: 0x00000000 0x00000000 0x0804a620 0x000003e9 (注:删除了
由此我们可以得到链表顺序如下(地址只写出最后三位):
62c->620->614->608->5fc->5f0->5e4->5d8->5cc->5c0->end 对应的值如下(这里使用十六进制,省略了前面的0x):
0->280->306->1b0->1f4->15f->32e->377->1b5->3b5 其中第一个值0用我们的输入代替即可。
然后在
0x804a5bc : 0x00000000 0x000003b5 0x00000009 0x0804a5d8 0x804a5cc : 0x000001b5 0x00000008 0x0804a608 0x00000377 0x804a5dc : 0x00000007 0x0804a5e4 0x0000032e 0x00000006 0x804a5ec : 0x0804a614 0x0000015f 0x00000005 0x0804a62c 0x804a5fc : 0x000001f4 0x00000004 0x0804a5cc 0x000001b0 0x804a60c : 0x00000003 0x0804a5f0 0x00000306 0x00000002 0x804a61c : 0x0804a620 0x00000280 0x00000001 0x0804a5fc 0x804a62c : 0x00000064 0x00000000 0x00000000 0x000003e9
发现链表顺序改变如下:
5c0->5d8->5e4->614->620->5fc->5cc->608->5f0->62c->end 对应的值如下: