csapp lab2 bomb 二进制炸弹《深入理解计算机系统》

时间:2021-07-14 01:16:55

bomb炸弹实验

首先对bomb这个文件进行反汇编,得到一个1000+的汇编程序,看的头大。


phase_1:

0000000000400ef0 <phase_1>:
  400ef0:	48 83 ec 08          	sub    $0x8,%rsp
  400ef4:	be 18 1b 40 00       	mov    $0x401b18,%esi
  400ef9:	e8 10 04 00 00       	callq  40130e <strings_not_equal>
  400efe:	85 c0                	test   %eax,%eax
  400f00:	74 05                	je     400f07 <phase_1+0x17>
  400f02:	e8 dc 07 00 00       	callq  4016e3 <explode_bomb>
  400f07:	48 83 c4 08          	add    $0x8,%rsp
  400f0b:	c3                   	retq   


这个是炸弹一的汇编程序。可以看到,在程序里面,首先注意到了:

 callq  40130e <strings_not_equal>

strings_not_equal这个函数应该就是对字符串是否相等进行了判断,所以要找到原来系统设定的字符串。

这是看到前面那句:mov    $0x401b18,%esi

感觉有问题,后来去查了一下,发现esi这个寄存器主要进行串操作,嗯,那字符串应该藏在0x401b18里面了。

用gdb调试,

csapp lab2 bomb 二进制炸弹《深入理解计算机系统》

果然在哪个地址里面。

phase_2:



 400f0c:	55                   	push   %rbp
  400f0d:	53                   	push   %rbx
  400f0e:	48 83 ec 28          	sub    $0x28,%rsp
  400f12:	48 89 e6             	mov    %rsp,%rsi
  400f15:	e8 eb 07 00 00       	callq  401705 <read_six_numbers>
  400f1a:	83 3c 24 01          	cmpl   $0x1,(%rsp)
  400f1e:	74 25                	je     400f45 <phase_2+0x39>
  400f20:	e8 be 07 00 00       	callq  4016e3 <explode_bomb>
  400f25:	eb 1e                	jmp    400f45 <phase_2+0x39>
  400f27:	83 c3 01             	add    $0x1,%ebx
  400f2a:	89 d8                	mov    %ebx,%eax
  400f2c:	0f af 45 fc          	imul   -0x4(%rbp),%eax
  400f30:	39 45 00             	cmp    %eax,0x0(%rbp)
  400f33:	74 05                	je     400f3a <phase_2+0x2e>
  400f35:	e8 a9 07 00 00       	callq  4016e3 <explode_bomb>
  400f3a:	48 83 c5 04          	add    $0x4,%rbp
  400f3e:	83 fb 06             	cmp    $0x6,%ebx
  400f41:	75 e4                	jne    400f27 <phase_2+0x1b>
  400f43:	eb 0c                	jmp    400f51 <phase_2+0x45>
  400f45:	48 8d 6c 24 04       	lea    0x4(%rsp),%rbp
  400f4a:	bb 01 00 00 00       	mov    $0x1,%ebx
  400f4f:	eb d6                	jmp    400f27 <phase_2+0x1b>
  400f51:	48 83 c4 28          	add    $0x28,%rsp
  400f55:	5b                   	pop    %rbx
  400f56:	5d                   	pop    %rbp
  400f57:	c3                   	retq   


这个首先看到代码:

callq 401705<read_six_numbers>

应该是要输入6个数字。

然后接下来按照下面的几行的步骤一步一步看下去,可以知道ebx这个寄存器是一个计数器,rbp这个寄存器用来指向当前比较的数字的地址,经过分析,每个数字的计算方法是:%ebx*(-4(%rbp)).即当前的数字的编号(1,2,3,4,5,6,第几个数字就是第几号)和上一个数字的乘积,第一个数字为1.

所以最后6个数字就是:1 2 6 24 120 720

phase_3:

 400f58:	48 83 ec 18          	sub    $0x18,%rsp
  400f5c:	4c 8d 44 24 08       	lea    0x8(%rsp),%r8
  400f61:	48 8d 4c 24 07       	lea    0x7(%rsp),%rcx
  400f66:	48 8d 54 24 0c       	lea    0xc(%rsp),%rdx
  400f6b:	be 6e 1b 40 00       	mov    $0x401b6e,%esi
  400f70:	b8 00 00 00 00       	mov    $0x0,%eax
  400f75:	e8 86 fc ff ff       	callq  400c00 <__isoc99_sscanf@plt>
  400f7a:	83 f8 02             	cmp    $0x2,%eax
  400f7d:	7f 05                	jg     400f84 <phase_3+0x2c>
  400f7f:	e8 5f 07 00 00       	callq  4016e3 <explode_bomb>
  400f84:	83 7c 24 0c 07       	cmpl   $0x7,0xc(%rsp)
  400f89:	0f 87 fc 00 00 00    	ja     40108b <phase_3+0x133>
  400f8f:	8b 44 24 0c          	mov    0xc(%rsp),%eax
  400f93:	ff 24 c5 80 1b 40 00 	jmpq   *0x401b80(,%rax,8)
  400f9a:	b8 6a 00 00 00       	mov    $0x6a,%eax
  400f9f:	81 7c 24 08 40 02 00 	cmpl   $0x240,0x8(%rsp)
  400fa6:	00 
  400fa7:	0f 84 e8 00 00 00    	je     401095 <phase_3+0x13d>
  400fad:	e8 31 07 00 00       	callq  4016e3 <explode_bomb>
  400fb2:	b8 6a 00 00 00       	mov    $0x6a,%eax
  400fb7:	e9 d9 00 00 00       	jmpq   401095 <phase_3+0x13d>
  400fbc:	b8 66 00 00 00       	mov    $0x66,%eax
  400fc1:	81 7c 24 08 bc 03 00 	cmpl   $0x3bc,0x8(%rsp)
  400fc8:	00 
  400fc9:	0f 84 c6 00 00 00    	je     401095 <phase_3+0x13d>
  400fcf:	e8 0f 07 00 00       	callq  4016e3 <explode_bomb>
  400fd4:	b8 66 00 00 00       	mov    $0x66,%eax
  400fd9:	e9 b7 00 00 00       	jmpq   401095 <phase_3+0x13d>
  400fde:	b8 6a 00 00 00       	mov    $0x6a,%eax
  400fe3:	81 7c 24 08 2a 02 00 	cmpl   $0x22a,0x8(%rsp)
  400fea:	00 
  400feb:	0f 84 a4 00 00 00    	je     401095 <phase_3+0x13d>
  400ff1:	e8 ed 06 00 00       	callq  4016e3 <explode_bomb>
  400ff6:	b8 6a 00 00 00       	mov    $0x6a,%eax
  400ffb:	e9 95 00 00 00       	jmpq   401095 <phase_3+0x13d>
  401000:	b8 76 00 00 00       	mov    $0x76,%eax
  401005:	81 7c 24 08 c9 00 00 	cmpl   $0xc9,0x8(%rsp)
  40100c:	00 
  40100d:	0f 84 82 00 00 00    	je     401095 <phase_3+0x13d>
  401013:	e8 cb 06 00 00       	callq  4016e3 <explode_bomb>
  401018:	b8 76 00 00 00       	mov    $0x76,%eax
  40101d:	eb 76                	jmp    401095 <phase_3+0x13d>
  40101f:	b8 62 00 00 00       	mov    $0x62,%eax
  401024:	81 7c 24 08 07 01 00 	cmpl   $0x107,0x8(%rsp)
  40102b:	00 
  40102c:	74 67                	je     401095 <phase_3+0x13d>
  40102e:	e8 b0 06 00 00       	callq  4016e3 <explode_bomb>
  401033:	b8 62 00 00 00       	mov    $0x62,%eax
  401038:	eb 5b                	jmp    401095 <phase_3+0x13d>
  40103a:	b8 69 00 00 00       	mov    $0x69,%eax
  40103f:	81 7c 24 08 3b 03 00 	cmpl   $0x33b,0x8(%rsp)
  401046:	00 
  401047:	74 4c                	je     401095 <phase_3+0x13d>
  401049:	e8 95 06 00 00       	callq  4016e3 <explode_bomb>
  40104e:	b8 69 00 00 00       	mov    $0x69,%eax
  401053:	eb 40                	jmp    401095 <phase_3+0x13d>
  401055:	b8 71 00 00 00       	mov    $0x71,%eax
  40105a:	81 7c 24 08 c6 00 00 	cmpl   $0xc6,0x8(%rsp)
  401061:	00 
  401062:	74 31                	je     401095 <phase_3+0x13d>
  401064:	e8 7a 06 00 00       	callq  4016e3 <explode_bomb>
  401069:	b8 71 00 00 00       	mov    $0x71,%eax
  40106e:	eb 25                	jmp    401095 <phase_3+0x13d>
  401070:	b8 77 00 00 00       	mov    $0x77,%eax
  401075:	81 7c 24 08 74 01 00 	cmpl   $0x174,0x8(%rsp)
  40107c:	00 
  40107d:	74 16                	je     401095 <phase_3+0x13d>
  40107f:	e8 5f 06 00 00       	callq  4016e3 <explode_bomb>
  401084:	b8 77 00 00 00       	mov    $0x77,%eax
  401089:	eb 0a                	jmp    401095 <phase_3+0x13d>
  40108b:	e8 53 06 00 00       	callq  4016e3 <explode_bomb>
  401090:	b8 68 00 00 00       	mov    $0x68,%eax
  401095:	3a 44 24 07          	cmp    0x7(%rsp),%al
  401099:	74 05                	je     4010a0 <phase_3+0x148>
  40109b:	e8 43 06 00 00       	callq  4016e3 <explode_bomb>
  4010a0:	48 83 c4 18          	add    $0x18,%rsp
  4010a4:	c3                   	retq   

被第三个炸弹卡了好久……主要原因是汇编不行啊。当年学的汇编是学的8位单片机上的汇编,所有的寄存器都是单独的不相关的。而这个程序是在64位环境下运行的,所以像eax rax al 这些寄存器都是相关的,都是扩展的寄存器。不知道这个东西,一直以为几个寄存器无关,在gdb调试的时候,就随便输了几个数进去,然后看al和rax这两个寄存器里面的值,以为是定值,就一直按照这个定值来分析的,导致后面怎么弄都不对……其实炸弹三就是利用这些相关的寄存器来实现不同的跳转的

分析:

首先,看到:mov    $0x401b6e,%esi  先看看那个地址里面是什么东西。

结果: “%d,%c,%d”

应该是输入的格式,输入三个数据,其中两个整数,一个char型的。

接着看下去,

400f84:	83 7c 24 0c 07       	cmpl   $0x7,0xc(%rsp)
400f89:	0f 87 fc 00 00 00    	ja     40108b <phase_3+0x133>
其中一个输入的数字不能大于7,否则就爆炸了……

mov    0xc(%rsp),%eax

接下去eax被赋值为那个输入不能大于7的数据。

jmpq   *0x401b80(,%rax,8)

一个间接跳转,这里一开始以为rax是个独立的寄存器,导致这个程序都理解错了……rax是eax的64为扩展,所以这里rax里面的值和eax一样,所以跳转到(0x401b80+8*rax)地址里面的值所对应的地址(比较绕,就是间接跳转)。这里跳转的值和前面那个输入有关,我输入1,所以跳转到(0x401b88)里面那个值对应的地址,查了一下,地址是0x400fbc.直接看哪里的指令。

  400fbc:	b8 66 00 00 00       	mov    $0x66,%eax
  400fc1:	81 7c 24 08 bc 03 00 	cmpl   $0x3bc,0x8(%rsp)
  400fc8:	00 
  400fc9:	0f 84 c6 00 00 00    	je     401095 <phase_3+0x13d>
  400fcf:	e8 0f 07 00 00       	callq  4016e3 <explode_bomb>

eax被赋值成0x66,并且第二个整数是0X3BC,跳转到0x401095

 

 401095: 3a 44 24 07          cmp    0x7(%rsp),%al


这里那个char型的大小要和al一样(老坑),eax=0x66,al是eax的低8位,也是0x66,查一下ascii,是f

所以输入就是1 f 956

这里1 和956的输入顺序偷了个懒,直接试了两次给试出来的,没有看程序来推他的顺序……



phase_4:

00000000004010bf <phase_4>:
  4010bf:	48 83 ec 18          	sub    $0x18,%rsp
  4010c3:	48 8d 54 24 0c       	lea    0xc(%rsp),%rdx
  4010c8:	be 74 1b 40 00       	mov    $0x401b74,%esi
  4010cd:	b8 00 00 00 00       	mov    $0x0,%eax
  4010d2:	e8 29 fb ff ff       	callq  400c00 <__isoc99_sscanf@plt>
  4010d7:	83 f8 01             	cmp    $0x1,%eax
  4010da:	75 07                	jne    4010e3 <phase_4+0x24>
  4010dc:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp)
  4010e1:	7f 05                	jg     4010e8 <phase_4+0x29>
  4010e3:	e8 fb 05 00 00       	callq  4016e3 <explode_bomb>
  4010e8:	8b 7c 24 0c          	mov    0xc(%rsp),%edi
  4010ec:	e8 b4 ff ff ff       	callq  4010a5 <func4>
  4010f1:	3d 00 5f 37 00       	cmp    $0x375f00,%eax
  4010f6:	74 05                	je     4010fd <phase_4+0x3e>
  4010f8:	e8 e6 05 00 00       	callq  4016e3 <explode_bomb>
  4010fd:	48 83 c4 18          	add    $0x18,%rsp
  401101:	c3                   	retq   


第四个炸弹是关于递归的。

首先看到

4010c8:	be 74 1b 40 00       	mov    $0x401b74,%esi
看看0x401b74里面的内容。

得到:"%d"

这次只要一个数就行了。

继续下去,看到输入的数被放入edi这个寄存器里面,然后调用了func4这个函数。下面看看func4函数

00000000004010a5 <func4>:
  4010a5:	53                   	push   %rbx
  4010a6:	89 fb                	mov    %edi,%ebx
  4010a8:	b8 01 00 00 00       	mov    $0x1,%eax
  4010ad:	83 ff 01             	cmp    $0x1,%edi
  4010b0:	7e 0b                	jle    4010bd <func4+0x18>
  4010b2:	8d 7f ff             	lea    -0x1(%rdi),%edi
  4010b5:	e8 eb ff ff ff       	callq  4010a5 <func4>
  4010ba:	0f af c3             	imul   %ebx,%eax
  4010bd:	5b                   	pop    %rbx
  4010be:	c3                   	retq   

fun4分析:

首先,进行初始化

ebx=edi(函数的输入),eax=1

然后比较edi和1的大小,若edi<=1 退出函数,若edi>1,edi=edi-1;继续调用func4,再计算eax=eax*ebx;

这里就比较清楚了,func4整个函数就是一个递归调用,他的输入是edi,eax寄存器相当于一个累乘器,eax=eax*edi,直到edi<=1;

用c++写,func4就是:

func4(int x)
{
      if(x>=1)
          return 1;
      return x*func4(x-1);
}

fun4调用结束后,

4010f1: 3d 00 5f 37 00 cmp $0x375f00,%eax

把eax和0x375F00比较,也就是求x!=0x375F00

最后求出来X=10;

输入结果,正确!


phase_5:

0000000000401102 <phase_5>:
  401102:	53                   	push   %rbx
  401103:	48 89 fb             	mov    %rdi,%rbx
  401106:	e8 e6 01 00 00       	callq  4012f1 <string_length>
  40110b:	83 f8 06             	cmp    $0x6,%eax
  40110e:	74 05                	je     401115 <phase_5+0x13>
  401110:	e8 ce 05 00 00       	callq  4016e3 <explode_bomb>
  401115:	b8 00 00 00 00       	mov    $0x0,%eax
  40111a:	ba 00 00 00 00       	mov    $0x0,%edx
  40111f:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx
  401123:	83 e1 0f             	and    $0xf,%ecx
  401126:	03 14 8d c0 1b 40 00 	add    0x401bc0(,%rcx,4),%edx
  40112d:	48 83 c0 01          	add    $0x1,%rax
  401131:	48 83 f8 06          	cmp    $0x6,%rax
  401135:	75 e8                	jne    40111f <phase_5+0x1d>
  401137:	83 fa 3e             	cmp    $0x3e,%edx
  40113a:	74 05                	je     401141 <phase_5+0x3f>
  40113c:	e8 a2 05 00 00       	callq  4016e3 <explode_bomb>
  401141:	5b                   	pop    %rbx
  401142:	c3                   	retq   

第五个炸弹,感觉有点像密码转化的意思。

首先看到

  40110b:	83 f8 06             	cmp    $0x6,%eax

输入6个数据,而且根据上面的指令,数据时字符串形式的,也就是字符串长度为6.

  40111f:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx

这条指令中,rax已经初始化为0,但是一开始没有搞明白rbx的大小,回过头看上面,有一句

  401103:	48 89 fb             	mov    %rdi,%rbx
表示rbx就是输入的数据的存储地址,那前面那句的意思就是一个一个读取输入的字符,将其赋值给ecx这个寄存器。

401123:	83 e1 0f             	and    $0xf,%ecx
这句就是取ecx的低4位,其他为置零。

  401126:	03 14 8d c0 1b 40 00 	add    0x401bc0(,%rcx,4),%edx

这句就是从(0x401bc0+rcx*4)里面拿数据出来,加到edx上。

因为rcx只能是0~F的数,所以0x401bc0这个地址里面存的应该是一个数据大小为16的数组,用gdb看,得到:

csapp lab2 bomb 二进制炸弹《深入理解计算机系统》


果然是一个数组,然后下面就是把6个输入一个一个的转化为这个数组,得到累加值edx
401137:	83 fa 3e             	cmp    $0x3e,%edx
最后得到的edx要是0x3e,这个就凑,最后凑了一个答案出来LMNOKA

输入,正确!

这题总结一下,就是输入6个字符,然后程序会根据6个字符的ascii码的低4位作为索引,在这里,可以看成是一个预先设定好的数组的下标,通过索引,把数组中相应的值拿出来求和,最后要求求得的和为0x3e就可以了。


最后一个了

phase_6:

00000000004011b2 <phase_6>:
  4011b2:	48 83 ec 08          	sub    $0x8,%rsp
  4011b6:	ba 0a 00 00 00       	mov    $0xa,%edx
  4011bb:	be 00 00 00 00       	mov    $0x0,%esi
  4011c0:	e8 1b fa ff ff       	callq  400be0 <strtol@plt>
  4011c5:	89 05 55 21 20 00    	mov    %eax,0x202155 (%rip)        # 603320 <node0>
  4011cb:	bf 20 33 60 00       	mov    $0x603320,%edi
  4011d0:	e8 6e ff ff ff       	callq  401143 <fun6>
  4011d5:	48 8b 40 08          	mov    0x8(%rax),%rax
  4011d9:	8b 0d 41 21 20 00    	mov    0x202141(%rip),%ecx        # 603320 <node0>
  4011df:	39 08                	cmp    %ecx,(%rax)
  4011e1:	74 05                	je     4011e8 <phase_6+0x36>
  4011e3:	e8 fb 04 00 00       	callq  4016e3 <explode_bomb>
  4011e8:	48 83 c4 08          	add    $0x8,%rsp
  4011ec:	c3                   	retq   

这个,太坑了。一开始在做的时候,看到一个func6,就一头扎了进去,被里面的跳转搞得快疯了,以前的程序员真是牛逼,写这种代码应该还能接受,但是要是去读这种汇编级别的代码,逻辑能被搞死啊……

后来实在受不了了,不看func6,直接给了一个输入,gdb一步一步调试,竟然成功了,我也是醉了。

从调用func6之后看这段程序。

可以看到:

  4011df:	39 08                	cmp    %ecx,(%rax)



这句是关键,他其实比较的是ecx和rax两个寄存器,经过调试,发现ecx就是输入的那个数,而rax竟然是一个定值……

通过查看rax这个寄存器,取出里面地址所对应的的值,得到:

csapp lab2 bomb 二进制炸弹《深入理解计算机系统》


发现就是和692比较,最后输入692,正确。

最后一个太坑,要是跳到func6里面就完蛋了。


结果:

csapp lab2 bomb 二进制炸弹《深入理解计算机系统》