这是来自于CS:APP的一个著名实验“拆解二进制炸弹”,也是我们的计算机组成与体系结构课程的家庭作业
这个实验也让我学会了怎么进行反汇编和使用GDB调试程序
解决问题的过程如下:
Phase_1
代码如下:
0x0000000000400f2d <+0>: sub $0x8,%rsp
0x0000000000400f31 <+4>: mov $0x402660,%esi
0x0000000000400f36 <+9>: callq 0x4013d6 <strings_not_equal>
0x0000000000400f3b <+14>: test %eax,%eax
0x0000000000400f3d <+16>: je 0x400f44 <phase_1+23>
0x0000000000400f3f <+18>: callq 0x4016aa <explode_bomb>
0x0000000000400f44 <+23>: add $0x8,%rsp
0x0000000000400f48 <+27>: retq
可以看到第一个炸弹的触发条件在于%eax
(函数返回值)是否为0.而由函数名字strings_not_equal
可以看出当字符串不相等时返回1.我们只需让输入的第一个字符串和某个字符串内容相等即可。
观察程序可以发现,这个用来和我们输入的字符串作比较的字符串应该储存在0x402660
这个地址。
打开GDB 输入
(gdb)disassemble x/s 0x402660
"And they have no disregard for human life."
因此第一道题的答案便是上面的字符串。
Phase_2
代码如下:
0x0000000000400f67 <+30>: cmpl $0x0,(%rsp) # %rsp>0 which is the the top of the stack.
0x0000000000400f6b <+34>: jns 0x400f72 <phase_2+41>
0x0000000000400f6d <+36>: callq 0x4016aa <explode_bomb>
0x0000000000400f72 <+41>: mov %rsp,%rbp #save the first parameter's address
0x0000000000400f75 <+44>: mov $0x1,%ebx #save 1 in %ebx
0x0000000000400f7a <+49>: mov %ebx,%eax #save %ebx in %eax
0x0000000000400f7c <+51>: add 0x0(%rbp),%eax #add the first,second... parameter to %eax(1+bomb?if first is 1)
0x0000000000400f7f <+54>: cmp %eax,0x4(%rbp) #compare %eax with second parameter
0x0000000000400f82 <+57>: je 0x400f89 <phase_2+64> #jump the bomb if it is equal
0x0000000000400f84 <+59>: callq 0x4016aa <explode_bomb>
0x0000000000400f89 <+64>: add $0x1,%ebx # %ebx=%edx+1. I am defusing bomb(%ebx)
0x0000000000400f8c <+67>: add $0x4,%rbp #next parameter
0x0000000000400f90 <+71>: cmp $0x6,%ebx
0x0000000000400f93 <+74>: jne 0x400f7a <phase_2+49> #This is a circle until cmp $0x6,ebx is equal. Which means we need defuse 5 bombs.
这是一个有坑的程序,首先炸弹的触发条件很明确,一旦%eax
和参数不相等,那么炸弹便会爆炸。在第一个循环时,%eax
和parameter(i+1)
做比较,以此类推。而%eax=%ebx+parameter(i)
,同时我们可以很惊奇的发现%ebx=i
,所以整个问题便迎刃而解了。只要满足parameter(i+1)=parameter(i)+i
即可,同时由开头的代码处的炸弹有,parameter1
必须大于等于0.
而从最后的代码可以看出这个循环会进行5次,也就是对输入的前6个参数进行比较。所以答案可以是
1 2 4 7 11 16
Phase_3
代码如下:
0x0000000000400fc5 <+20>: lea 0x4(%rsp),%rcx # %rcx=%rsp+4 store the second parameter
0x0000000000400fca <+25>: mov %rsp,%rdx # store the first parameter
0x0000000000400fcd <+28>: mov $0x40295d,%esi # store address"0x40295d" it's"%d %d"
0x0000000000400fd2 <+33>: callq 0x400c40 <__isoc99_sscanf@plt>
0x0000000000400fd7 <+38>: cmp $0x1,%eax # %eax = the number of parameters sscanf read
0x0000000000400fda <+41>: jg 0x400fe1 <phase_3+48> # %eax>1
0x0000000000400fdc <+43>: callq 0x4016aa <explode_bomb>
0x0000000000400fe1 <+48>: cmpl $0x7,(%rsp)
0x0000000000400fe5 <+52>: ja 0x40104c <phase_3+155> #bomb! if para1 >7
0x0000000000400fe7 <+54>: mov (%rsp),%eax # %eax=para1
0x0000000000400fea <+57>: jmpq *0x4026c0(,%rax,8) #shenmegui??? jump to address (*0x4026c0)+para1*8 400ff1
0x0000000000400ff1 <+64>: mov $0x298,%eax #0x298
0x0000000000400ff6 <+69>: jmp 0x400ffd <phase_3+76>
0x0000000000400ff8 <+71>: mov $0x0,%eax
0x0000000000400ffd <+76>: sub $0x160,%eax #0x298-0x160=0x138
0x0000000000401002 <+81>: jmp 0x401009 <phase_3+88>
0x0000000000401004 <+83>: mov $0x0,%eax
0x0000000000401009 <+88>: add $0x33b,%eax #0x138+0x33b=0x473
0x000000000040100e <+93>: jmp 0x401015 <phase_3+100>
0x0000000000401010 <+95>: mov $0x0,%eax
0x0000000000401015 <+100>: sub $0x3de,%eax #0x473-990
0x000000000040101a <+105>: jmp 0x401021 <phase_3+112>
0x000000000040101c <+107>: mov $0x0,%eax
0x0000000000401021 <+112>: add $0x3de,%eax #0x473
0x0000000000401026 <+117>: jmp 0x40102d <phase_3+124>
0x0000000000401028 <+119>: mov $0x0,%eax
0x000000000040102d <+124>: sub $0x3de,%eax #0x473-990
0x0000000000401032 <+129>: jmp 0x401039 <phase_3+136>
0x0000000000401034 <+131>: mov $0x0,%eax
0x0000000000401039 <+136>: add $0x3de,%eax #0x473
0x000000000040103e <+141>: jmp 0x401045 <phase_3+148>
0x0000000000401040 <+143>: mov $0x0,%eax
0x0000000000401045 <+148>: sub $0x3de,%eax #0x473-990
0x000000000040104a <+153>: jmp 0x401056 <phase_3+165>
0x000000000040104c <+155>: callq 0x4016aa <explode_bomb>
0x0000000000401051 <+160>: mov $0x0,%eax
0x0000000000401056 <+165>: cmpl $0x5,(%rsp) # <=5
0x000000000040105a <+169>: jg 0x401062 <phase_3+177>
0x000000000040105c <+171>: cmp 0x4(%rsp),%eax # ==
0x0000000000401060 <+175>: je 0x401067 <phase_3+182>
0x0000000000401062 <+177>: callq 0x4016aa <explode_bomb>
这是一个看似很长,实则很简单的程序。
首先我们要确定自己应当输入什么,阅读代码后有两个提示
- 在地址
0x40295d
隐藏着一个字符串,打开后发现是“%d %d”
,也就是说我们要以这样的方式输入 - 整块代码只用到了
%rsp
,%rsp+4
。而且,没有移动过%rsp
,所以可以断定,我们需要输入两个数。
接着便是对代码分析
第一个炸弹要求输入参数个数大于1
第二个炸弹要求para1<=7
第三个炸弹有两个要求: para1<=5
para2==%eax
而%eax是原有的%eax
经过一系列计算后的结果,我们发现对%eax
的操作只有mov
,add
,sub
三种。而jmpq *0x4026c0(,%rax,8)
表明了随着para1
的值的不同,进行的运算也不同.有个问题是当para1!=0
时,该语句跳转到的位置并不符合规范,这个需要进一步的调试探究。由代码中的注释可以看到,当para1=0
时,para2=0x95
,也就是149
。在探索过程中我还发现当para1
为4
时para
为0
.这一结果更加表示了jmpq
命令跳转到的位置有必要进行研究,感兴趣的可以探究一下。Phase_4 5 6
剩下的三个虽然做出来了但写起来好麻烦,两场考试还都没复习,我要去复习了。老师还是对程序进行了很大的修改的,网上的参考只能借鉴一部分,真正的答案还是要自己去研究才能做出来。大家加油!等考完期中考试再继续写。