CSAPP:BombLab(深入了解计算机系统配套实验)

CSAPP:BombLab

新手小白,第一次写博客,算是记录一下做的一些有意思的东西,分享给大家。有一些理解不够深刻,甚至出错的地方,还请大家见谅,并帮我指正,谢谢!
这是一个《深入了解计算机系统》教学的配套实验,肝了一个下午加一整个晚上,总结了一下整个过程。这里我使用的是Ubuntu16.04的虚拟机。

实验原理

通过gdb对二进制文件进行反汇编,阅读相应的汇编代码,并使用gdb进行断点调试和内存读取,从而破译共七个炸弹,包括六个常规炸弹和一个隐藏炸弹。

实验过程

在Ubuntu终端打开gdb,输入命令objdump -d bomb>bomb1.asm,将二进制文件反汇编为汇编代码文件,然后输入gdb ./bomb命令,使用gdb打开二进制文件bomb。这样可以在阅读汇编代码的同时对程序进行调试。
在main函数的汇编代码段中,可以看到分别调用了<phase_1>、<phase_2>、<phase_3>、<phase_4>、<phase_5>、<phase_6>代码段,它们分别对应着各个炸弹的函数。我们只要对这些代码段进行分析就可以找到拆解炸弹的方法。

phase 1

phase 1很简短,其汇编代码:

0000000000400ee0 <phase_1>:
  400ee0:	48 83 ec 08          	sub    $0x8,%rsp
  400ee4:	be 00 24 40 00       	mov    $0x402400,%esi
//%esi中的地址为0x402400
  400ee9:	e8 4a 04 00 00       	callq  401338 <strings_not_equal>
//调用字符串比较,相同则eax为0
  400eee:	85 c0                	test   %eax,%eax
//是否相同
  400ef0:	74 05                	je     400ef7 <phase_1+0x17>
  400ef2:	e8 43 05 00 00       	callq  40143a <explode_bomb>
  400ef7:	48 83 c4 08          	add    $0x8,%rsp
  400efb:	c3                   	retq

其中引用了<strings_not _equal>代码段,从字面上看是比较字符串,其汇编代码如下:

0000000000401338 <strings_not_equal>:
  401338:	41 54                	push   %r12
  40133a:	55                   	push   %rbp
  40133b:	53                   	push   %rbx
  40133c:	48 89 fb             	mov    %rdi,%rbx
//输入内容的地址
  40133f:	48 89 f5             	mov    %rsi,%rbp
//%rbp指向%rsi答案中的内容
  401342:	e8 d4 ff ff ff       	callq  40131b <string_length>
  401347:	41 89 c4             	mov    %eax,%r12d
//输入共多少字符
  40134a:	48 89 ef             	mov    %rbp,%rdi
//答案字符地址
  40134d:	e8 c9 ff ff ff       	callq  40131b <string_length>
  401352:	ba 01 00 00 00       	mov    $0x1,%edx
//标记
  401357:	41 39 c4             	cmp    %eax,%r12d
//比较字符个数
  40135a:	75 3f                	jne    40139b <strings_not_equal+0x63>
  40135c:	0f b6 03             	movzbl (%rbx),%eax
//输入首字符
  40135f:	84 c0                	test   %al,%al
//为空则跳转
  401361:	74 25                	je     401388 <strings_not_equal+0x50>
  401363:	3a 45 00             	cmp    0x0(%rbp),%al
//比较第一个字符
  401366:	74 0a                	je     401372 <strings_not_equal+0x3a>
//一样则跳转(包括都为\0)
  401368:	eb 25                	jmp    40138f <strings_not_equal+0x57>
  40136a:	3a 45 00             	cmp    0x0(%rbp),%al
//比较当前字符
  40136d:	0f 1f 00             	nopl   (%rax)
  401370:	75 24                	jne    401396 <strings_not_equal+0x5e>
  401372:	48 83 c3 01          	add    $0x1,%rbx
//输入指针加一
  401376:	48 83 c5 01          	add    $0x1,%rbp
//答案指针加一
  40137a:	0f b6 03             	movzbl (%rbx),%eax
//使用当前字符
  40137d:	84 c0                	test   %al,%al
//输入内容当前字符是否为\0
  40137f:	75 e9             	jne    40136a <strings_not_equal+0x32>
//不为空则跳转
  401381:	ba 00 00 00 00       	mov    $0x0,%edx
//标记0,输入正确
  401386:	eb 13                	jmp    40139b <strings_not_equal+0x63>
  401388:	ba 00 00 00 00       	mov    $0x0,%edx
  40138d:	eb 0c                	jmp    40139b <strings_not_equal+0x63>
  40138f:	ba 01 00 00 00       	mov    $0x1,%edx
  401394:	eb 05                	jmp    40139b <strings_not_equal+0x63>
  401396:	ba 01 00 00 00       	mov    $0x1,%edx
  40139b:	89 d0                	mov    %edx,%eax
//%eax为0,输入正确
  40139d:	5b                   	pop    %rbx
  40139e:	5d                   	pop    %rbp
  40139f:	41 5c                	pop    %r12
  4013a1:	c3                   	retq

可以看到,<strings_not _equal>代码段是用作字符串的比较的,<phase_1>调用它从而将地址为0x402400的字符串与输入的字符串进行对比。如果相同则进入下一个炸弹的<phase_2>代码段,否则引爆炸弹。使用x/s 0x402400命令读取该地址处的字符串,得到:
在这里插入图片描述
第一个拆弹密码即为:Border relations with Canada have never been better.

phase 2

phase 2的汇编代码:

0000000000400efc <phase_2>:
  400efc:	55                   	push   %rbp
  400efd:	53                   	push   %rbx
  400efe:	48 83 ec 28        	sub    $0x28,%rsp
  400f02:	48 89 e6          	mov    %rsp,%rsi
  400f05:	e8 52 05 00 00     	callq  40145c <read_six_numbers>
//读六个数据
  400f0a:	83 3c 24 01          	cmpl   $0x1,(%rsp)
//第一个数和1比较
  400f0e:	74 20                	je     400f30 <phase_2+0x34>
  400f10:	e8 25 05 00 00       	callq  40143a <explode_bomb>
//等于1跳转,否则爆炸
  400f15:	eb 19                	jmp    400f30 <phase_2+0x34>
  400f17:	8b 43 fc             	mov    -0x4(%rbx),%eax
//将rbx-4地址处的值赋给eax
  400f1a:	01 c0                	add    %eax,%eax
//%eax=2*%eax
  400f1c:	39 03                	cmp    %eax,(%rbx)
//比较此时eax和rbx地址处的值。其中eax是ebx-4地址处的值,即将rbx地址处的值和前一个数的两倍比较
  400f1e:	74 05                	je     400f25 <phase_2+0x29>
//相等则跳转至rbx+4地址处
  400f20:	e8 15 05 00 00       	callq  40143a <explode_bomb>
  400f25:	48 83 c3 04          	add    $0x4,%rbx
//rbx+4
  400f29:	48 39 eb             	cmp    %rbp,%rbx
//rbp为六个数的下一个地址,判断是否比较完毕
  400f2c:	75 e9                	jne    400f17 <phase_2+0x1b>
  400f2e:	eb 0c                	jmp    400f3c <phase_2+0x40>
  400f30:	48 8d 5c 24 04       	lea    0x4(%rsp),%rbx
  400f35:	48 8d 6c 24 18       	lea    0x18(%rsp),%rbp
  400f3a:	eb db                	jmp    400f17 <phase_2+0x1b>
  400f3c:	48 83 c4 28          	add    $0x28,%rsp
  400f40:	5b                  	pop    %rbx
  400f41:	5d                  	pop    %rbp
  400f42:	c3                   	retq   

<read_six_numbers>代码段将读到的第一个数字的地址存在%rsp存储的地址中。而第一个数必须是1,根据循环可以知道下一个数是前一个数的两倍。因此,这六个数应该为:1 2 4 8 16 32。

phase 3

phase 3的汇编代码如下:

0000000000400f43 <phase_3>:
  400f43:	48 83 ec 18          	sub    $0x18,%rsp
  400f47:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx
  400f4c:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx
  400f51:	be cf 25 40 00       	mov    $0x4025cf,%esi
  400f56:	b8 00 00 00 00       	mov    $0x0,%eax
  400f5b:	e8 90 fc ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
//读取输入
  400f60:	83 f8 01             	cmp    $0x1,%eax
  400f63:	7f 05                	jg     400f6a <phase_3+0x27>
  400f65:	e8 d0 04 00 00       	callq  40143a <explode_bomb>
  400f6a:	83 7c 24 08 07       	cmpl   $0x7,0x8(%rsp)
  400f6f:	77 3c                	ja     400fad <phase_3+0x6a>
//如果第一个数大于7,炸弹爆炸
  400f71:	8b 44 24 08          	mov    0x8(%rsp),%eax
  400f75:	ff 24 c5 70 24 40 00 	jmpq   *0x402470(,%rax,8)
  400f7c:	b8 cf 00 00 00       	mov    $0xcf,%eax
  400f81:	eb 3b                	jmp    400fbe <phase_3+0x7b>
  400f83:	b8 c3 02 00 00       	mov    $0x2c3,%eax
  400f88:	eb 34                	jmp    400fbe <phase_3+0x7b>
  400f8a:	b8 00 01 00 00       	mov    $0x100,%eax
  400f8f:	eb 2d                	jmp    400fbe <phase_3+0x7b>
  400f91:	b8 85 01 00 00       	mov    $0x185,%eax
  400f96:	eb 26                	jmp    400fbe <phase_3+0x7b>
  400f98:	b8 ce 00 00 00       	mov    $0xce,%eax
  400f9d:	eb 1f                	jmp    400fbe <phase_3+0x7b>
  400f9f:	b8 aa 02 00 00       	mov    $0x2aa,%eax
  400fa4:	eb 18                	jmp    400fbe <phase_3+0x7b>
  400fa6:	b8 47 01 00 00       	mov    $0x147,%eax
  400fab:	eb 11                	jmp    400fbe <phase_3+0x7b>
  400fad:	e8 88 04 00 00       	callq  40143a <explode_bomb>
  400fb2:	b8 00 00 00 00       	mov    $0x0,%eax
  400fb7:	eb 05                	jmp    400fbe <phase_3+0x7b>
  400fb9:	b8 37 01 00 00       	mov    $0x137,%eax
  400fbe:	3b 44 24 0c          	cmp    0xc(%rsp),%eax
  400fc2:	74 05                	je     400fc9 <phase_3+0x86>
  400fc4:	e8 71 04 00 00       	callq  40143a <explode_bomb>
  400fc9:	48 83 c4 18          	add    $0x18,%rsp
  400fcd:	c3                   	retq

在执行scanf之前,将0x4025cf存储进了esi寄存器中,先来读取一下地址0x4025cf中的内容。
在这里插入图片描述
可以判断,scanf读取的数据格式为两个整型值。
phase 3的汇编代码中间部分有很多jmp,并且都跟着一个赋值的操作,之后紧接着就与第二个输入的数进行比较。初步判断这是一个switch语句,每一个jmp是一个case。这里一共有七个jmp,并且输入的第一个值不能大于7,说明case语句应该有7条,这进一步验证了这个想法。
选取case 2语句,即第一个数为2,此时对应的第二个数应该为0x2c3,即707。输入2 707,炸弹成功解除。
但后来测试时发现输入1 207(0xcf)并不能成功解除炸弹,但输入0 207可以解除。进一步测试输入1 707仍不能解除炸弹。查找原因,发现没有弄清楚switch语句中跳转表的对应关系。可以看到switch查找表的存储位置为0x402470(jmpq *0x402470(,%rax,8)),使用p/x *0x402470 @16查看跳转表:
在这里插入图片描述
则对应的关系为:

case标号跳转地址%eax
00x400f7c0xcf
10x400fb90x137
20x400f830x2c3
30x400f8a0x100
40x400f910x185
50x400f980xce
60x400f9f0x2aa
70x400fa60x147

这便解释了出现不对应现象的原因。

phase 4

phase 4的汇编代码如下:

000000000040100c <phase_4>:
  40100c:	48 83 ec 18          	sub    $0x18,%rsp
  401010:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx
//num2
  401015:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx
//num1
  40101a:	be cf 25 40 00       	mov    $0x4025cf,%esi
//%d %d
  40101f:	b8 00 00 00 00       	mov    $0x0,%eax
  401024:	e8 c7 fb ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  401029:	83 f8 02             	cmp    $0x2,%eax
  40102c:	75 07            	jne    401035 <phase_4+0x29>
//boom
  40102e:	83 7c 24 08 0e       	cmpl   $0xe,0x8(%rsp)
//num1应小于14
  401033:	76 05                	jbe    40103a <phase_4+0x2e>
  401035:	e8 00 04 00 00       	callq  40143a <explode_bomb>
  40103a:	ba 0e 00 00 00       	mov    $0xe,%edx
//%edx=14
  40103f:	be 00 00 00 00       	mov    $0x0,%esi
//%esi=0
  401044:	8b 7c 24 08          	mov    0x8(%rsp),%edi
  401048:	e8 81 ff ff ff          	callq  400fce <func4>
  40104d:	85 c0                	test   %eax,%eax
//func4的输出应为0
  40104f:	75 07                	jne    401058 <phase_4+0x4c>
  401051:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp)
//num2应为0
  401056:	74 05                	je     40105d <phase_4+0x51>
  401058:	e8 dd 03 00 00       	callq  40143a <explode_bomb>
  40105d:	48 83 c4 18          	add    $0x18,%rsp
  401061:	c3                   	retq

与三相同,这里读取的数据格式是两个整型,分别为num1和num2。这里应该引起重视的是代码段,要求其返回值为0。它的三个输入参数分别为%rdx、%edx和%esi,其汇编代码如下:

0000000000400fce <func4>:
  400fce:	48 83 ec 08          	sub    $0x8,%rsp
  400fd2:	89 d0                	mov    %edx,%eax
//x=14(edx) ,y=0(esi)
  400fd4:	29 f0                	sub    %esi,%eax
//x=x-y
  400fd6:	89 c1                	mov    %eax,%ecx
//c=x
  400fd8:	c1 e9 1f             	shr    $0x1f,%ecx
//右移31位
  400fdb:	01 c8                	add    %ecx,%eax
  400fdd:	d1 f8                	sar    %eax
  400fdf:	8d 0c 30             	lea    (%rax,%rsi,1),%ecx
  400fe2:	39 f9                	cmp    %edi,%ecx
  400fe4:	7e 0c                	jle    400ff2 <func4+0x24>
  400fe6:	8d 51 ff             	lea    -0x1(%rcx),%edx
  400fe9:	e8 e0 ff ff ff       	callq  400fce <func4>
  400fee:	01 c0                	add    %eax,%eax
  400ff0:	eb 15                	jmp    401007 <func4+0x39>
  400ff2:	b8 00 00 00 00       	mov    $0x0,%eax
  400ff7:	39 f9                	cmp    %edi,%ecx
  400ff9:	7d 0c                	jge    401007 <func4+0x39>
  400ffb:	8d 71 01             	lea    0x1(%rcx),%esi
  400ffe:	e8 cb ff ff ff       	callq  400fce <func4>
  401003:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax
  401007:	48 83 c4 08          	add    $0x8,%rsp
  40100b:	c3                   	retq

写出对应的C代码:

int fun4(num, p, d)
{
	int a = (d – p + (d >= p) ? 0 : -1) / 2;
	int c = a;
	if (num > c)
	{
		d = c-1;
		int r1 = fun4(num, p, d);
		return r1 * 2;
	}
	else
	{
		if (c == num)
			return 0;
		else
		{
			p = c+1;
			int r2 = fun4(num, p, d);
			return (2 * r2 + 1);
		}
	}
}

要是返回值为0,则应有num=c。输入fun4的值中,num参数为num1,其值小于14;p=0,d=14。满足d>=p,此时相当于c=(d-p)/2=7。因此,num1=7(满足num1<14)。而num2必须为0(cmpl $0x0,0xc(%rsp)),因此第四个炸弹的密码为:7 0。

phase 5

先看phase 5的汇编代码:

0000000000401062 <phase_5>:
  401062:	53                   	push   %rbx
  401063:	48 83 ec 20          	sub    $0x20,%rsp
  401067:	48 89 fb             	mov    %rdi,%rbx
  40106a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  401071:	00 00 
  401073:	48 89 44 24 18       	mov    %rax,0x18(%rsp)
  401078:	31 c0                	xor    %eax,%eax
  40107a:	e8 9c 02 00 00       	callq  40131b <string_length>
  40107f:	83 f8 06             	cmp    $0x6,%eax
//string length=6
  401082:	74 4e                	je     4010d2 <phase_5+0x70>
  401084:	e8 b1 03 00 00       	callq  40143a <explode_bomb>
  401089:	eb 47                	jmp    4010d2 <phase_5+0x70>
  40108b:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx
//读入一个输入字符
  40108f:	88 0c 24             	mov    %cl,(%rsp)
//该输入字符后四位
  401092:	48 8b 14 24          	mov    (%rsp),%rdx
  401096:	83 e2 0f             	and    $0xf,%edx
//%edx=%dl只取后四位
  401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx
//maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
//在这个字符串中用已输入字符的后四位为索引进行查找   9 15 14 5 6 7
  4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1)
//从这里开始存放六个字符
  4010a4:	48 83 c0 01          	add    $0x1,%rax
//index++
  4010a8:	48 83 f8 06          	cmp    $0x6,%rax
//共6个
  4010ac:	75 dd                	jne    40108b <phase_5+0x29>
  4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi
//flyers
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  4010bd:	e8 76 02 00 00       	callq  401338 <strings_not_equal>
  4010c2:	85 c0                	test   %eax,%eax
//字符串相同
  4010c4:	74 13                	je     4010d9 <phase_5+0x77>
  4010c6:	e8 6f 03 00 00       	callq  40143a <explode_bomb>
  4010cb:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
  4010d0:	eb 07                	jmp    4010d9 <phase_5+0x77>
  4010d2:	b8 00 00 00 00       	mov    $0x0,%eax
  4010d7:	eb b2                	jmp    40108b <phase_5+0x29>
  4010d9:	48 8b 44 24 18       	mov    0x18(%rsp),%rax
  4010de:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
  4010e5:	00 00 
  4010e7:	74 05                	je     4010ee <phase_5+0x8c>
  4010e9:	e8 42 fa ff ff       	callq  400b30 <__stack_chk_fail@plt>
  4010ee:	48 83 c4 20          	add    $0x20,%rsp
  4010f2:	5b                   	pop    %rbx
  4010f3:	c3                   	retq

先用x/s指令看看汇编代码中出现的两个地址中存放了什么:
在这里插入图片描述
语言有些许嘲讽,这个炸弹的密码不是像第一个那样那么简单。但这句话里值得注意的是前面的一些乱码,也许其中有相关信息。
另一个:
在这里插入图片描述
得到这两个信息后,开始分析汇编代码。代码要求输入共六个字符,然后进入循环,每次取其中的一个字符并依此增加,直到六个字符都被取完。在取得一个字符之后,用这个字符的ASCII码的后四位作为索引,在第一个字符串即maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?中取得相应的字符。由于四位索引是0-16,故这里只能取到前面 的一串乱码maduiersnfotvbyl,正好16个字符,这印证了之前的想法。
在从这些乱码中去除相应字符后,将所有的字符与第二个字符串即flyers进行比较,它们二者应该相同。故可以反推,在乱码中取得字符的索引依次是:9、15、14、5、6、7。对应的二进制码为1001、1111、1110、0101、0110、0111。通过查找ASCII码表,可以确定这样一组字符(答案不唯一):IONUVW。输入后成功拆弹。

phase 6

phase 6的汇编代码为:

00000000004010f4 <phase_6>:
  4010f4:	41 56                	push   %r14
  4010f6:	41 55                	push   %r13
  4010f8:	41 54                	push   %r12
  4010fa:	55                   	push   %rbp
  4010fb:	53                   	push   %rbx
  4010fc:	48 83 ec 50          	sub    $0x50,%rsp
  401100:	49 89 e5             	mov    %rsp,%r13
  401103:	48 89 e6             	mov    %rsp,%rsi
  401106:	e8 51 03 00 00       	callq  40145c <read_six_numbers>
  40110b:	49 89 e6             	mov    %rsp,%r14
  40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d

  401114:	4c 89 ed             	mov    %r13,%rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax
  40111b:	83 e8 01             	sub    $0x1,%eax
  40111e:	83 f8 05             	cmp    $0x5,%eax
//num<=6
  401121:	76 05                	jbe    401128 <phase_6+0x34>
  401123:	e8 12 03 00 00       	callq  40143a <explode_bomb>
  401128:	41 83 c4 01          	add    $0x1,%r12d
  40112c:	41 83 fc 06          	cmp    $0x6,%r12d
  401130:	74 21                	je     401153 <phase_6+0x5f>
//已读入六个数
  401132:	44 89 e3             	mov    %r12d,%ebx
  401135:	48 63 c3             	movslq %ebx,%rax
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp)
  40113e:	75 05                	jne    401145 <phase_6+0x51>
//相邻两个num不等
  401140:	e8 f5 02 00 00       	callq  40143a <explode_bomb>
  401145:	83 c3 01             	add    $0x1,%ebx
  401148:	83 fb 05             	cmp    $0x5,%ebx
  40114b:	7e e8                	jle    401135 <phase_6+0x41>
//六个数字读取完毕
  40114d:	49 83 c5 04          	add    $0x4,%r13
//指向num的指针,依次+4
  401151:	eb c1                	jmp    401114 <phase_6+0x20>


  401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi
  401158:	4c 89 f0             	mov    %r14,%rax
  40115b:	b9 07 00 00 00       	mov    $0x7,%ecx
  401160:	89 ca                	mov    %ecx,%edx
  401162:	2b 10                	sub    (%rax),%edx
//7-num
  401164:	89 10                	mov    %edx,(%rax)
  401166:	48 83 c0 04          	add    $0x4,%rax
  40116a:	48 39 f0             	cmp    %rsi,%rax
  40116d:	75 f1                	jne    401160 <phase_6+0x6c>
//6个全部循环
  40116f:	be 00 00 00 00       	mov    $0x0,%esi
  401174:	eb 21                	jmp    401197 <phase_6+0xa3>
  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx   //0x6032d8
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 <phase_6+0x82>
  401181:	eb 05                	jmp    401188 <phase_6+0x94>
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx
//L\001
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2)
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab <phase_6+0xb7>
//六个比较完毕则跳转
  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 <phase_6+0x8f>
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx
//第一个结点的地址0x6032d0
//L\001
  4011a9:	eb cb                	jmp    401176 <phase_6+0x82>

  4011ab:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx
  4011b0:	48 8d 44 24 28       	lea    0x28(%rsp),%rax
  4011b5:	48 8d 74 24 50       	lea    0x50(%rsp),%rsi
  4011ba:	48 89 d9             	mov    %rbx,%rcx
  4011bd:	48 8b 10             	mov    (%rax),%rdx
  4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)
//newN放在(newN-1+8)
  4011c4:	48 83 c0 08          	add    $0x8,%rax
  4011c8:	48 39 f0             	cmp    %rsi,%rax
  4011cb:	74 05                	je     4011d2 <phase_6+0xde>
//六个数处理完毕
  4011cd:	48 89 d1             	mov    %rdx,%rcx
  4011d0:	eb eb                	jmp    4011bd <phase_6+0xc9>

  4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)
//(new6+8),链表尾指针为空
  4011d9:	00 
  4011da:	bd 05 00 00 00       	mov    $0x5,%ebp
//按照降序重建链表
  4011df:	48 8b 43 08          	mov    0x8(%rbx),%rax
//new2
  4011e3:	8b 00                	mov    (%rax),%eax
  4011e5:	39 03                	cmp    %eax,(%rbx)
//第一个数是否大于第二个数 
  4011e7:	7d 05                	jge    4011ee <phase_6+0xfa>
  4011e9:	e8 4c 02 00 00       	callq  40143a <explode_bomb>
  4011ee:	48 8b 5b 08          	mov    0x8(%rbx),%rbx
  4011f2:	83 ed 01             	sub    $0x1,%ebp
  4011f5:	75 e8                	jne    4011df <phase_6+0xeb>
  4011f7:	48 83 c4 50          	add    $0x50,%rsp
  4011fb:	5b                   	pop    %rbx
  4011fc:	5d                   	pop    %rbp
  4011fd:	41 5c                	pop    %r12
  4011ff:	41 5d                	pop    %r13
  401201:	41 5e                	pop    %r14
  401203:	c3                   	retq

从汇编代码中可以判断,这里访问了一个共有六个元素的链表,链表的首地址为0x6032d0,每个元素共有四个字节。先用p/x *0x6032d0 @24指令看看这个链表里储存的是什么。
在这里插入图片描述
链表中每个元素共四个字节,前两个字节用于储存数据,第三个字节存放着链表下一个元素的地址,最后一个字节均为0。可以看到第六个元素的地址字节存放的数据为0,说明该链表确实只有六个字节。其中第一个字节为需要排序的数,汇编语言将其从大到小降序排列,重新更改链表顺序,然后再按顺序读取第二个字节的数,与输入进行比较。但是输入的数据在比较之前进行了一步额外的处理,即:若输入的数为num,则将要与链表中的数据进行比较的数为temp=7-num。
由此可知道,重新排序后的链表中第二个字节的数据为:3、4、5、6、1、2。故输入的数应该为:4、3、2、1、6、5。

secret phase

在寻找第三个炸弹密码的那个尚不能解释的现象的原因时发现,在紧跟着<phase_6>后面的代码段之后有一个<secret_phase>代码段,这段代码好像一直没有被运行过。翻看汇编代码发现它也可以跳转至<explode_bomb>使炸弹爆炸。由于并不知道这段代码在哪里被调用,将所有的代码复制到编辑器中,全局查找“secret_phase”字样,发现在<phase_defused>中可以跳转至该代码段,为了找到避免炸弹爆炸的方法,需要进一步找出跳转条件。
在这里插入图片描述
<phase_defused>代码段如下:

00000000004015c4 <phase_defused>:
  4015c4:	48 83 ec 78          	sub    $0x78,%rsp
  4015c8:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  4015cf:	00 00 
  4015d1:	48 89 44 24 68       	mov    %rax,0x68(%rsp)
  4015d6:	31 c0                	xor    %eax,%eax
  4015d8:	83 3d 81 21 20 00 06 	cmpl   $0x6,0x202181(%rip)        # 603760 <num_input_strings>
//输入的字符串是否是六个
  4015df:	75 5e                	jne    40163f <phase_defused+0x7b>
//不等于则跳转
  4015e1:	4c 8d 44 24 10       	lea    0x10(%rsp),%r8
  4015e6:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx
  4015eb:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx
  4015f0:	be 19 26 40 00       	mov    $0x402619,%esi
//%d %d %s
  4015f5:	bf 70 38 60 00       	mov    $0x603870,%edi
//7 0
  4015fa:	e8 f1 f5 ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  4015ff:	83 f8 03             	cmp    $0x3,%eax
//输入是否为3个
  401602:	75 31                	jne    401635 <phase_defused+0x71>
//不是则跳转
  401604:	be 22 26 40 00       	mov    $0x402622,%esi
//DrEvil
  401609:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  40160e:	e8 25 fd ff ff       	callq  401338 <strings_not_equal>
  401613:	85 c0                	test   %eax,%eax
//是否相同
  401615:	75 1e                	jne    401635 <phase_defused+0x71>
//不

//相同
  401617:	bf f8 24 40 00       	mov    $0x4024f8,%edi//Curses, you've found the secret phase!
  40161c:	e8 ef f4 ff ff       	callq  400b10 <puts@plt>
  401621:	bf 20 25 40 00       	mov    $0x402520,%edi
//7 0
  401626:	e8 e5 f4 ff ff       	callq  400b10 <puts@plt>
  40162b:	b8 00 00 00 00       	mov    $0x0,%eax
  401630:	e8 0d fc ff ff       	callq  401242 <secret_phase>

  401635:	bf 58 25 40 00       	mov    $0x402558,%edi
  40163a:	e8 d1 f4 ff ff       	callq  400b10 <puts@plt>
  40163f:	48 8b 44 24 68       	mov    0x68(%rsp),%rax
  401644:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
  40164b:	00 00 
  40164d:	74 05                	je     401654 <phase_defused+0x90>
  40164f:	e8 dc f4 ff ff       	callq  400b30 <__stack_chk_fail@plt>
  401654:	48 83 c4 78          	add    $0x78,%rsp
  401658:	c3                   	retq   
  401659:	90                   	nop
  40165a:	90                   	nop
  40165b:	90                   	nop
  40165c:	90                   	nop
  40165d:	90                   	nop
  40165e:	90                   	nop
  40165f:	90                   	nop

可以看到,<phase_defused>代码段在每次拆解炸弹成功后都被执行,判断是否已经输入了共六个字符串。如果已经输入了六个字符串(说明六个炸弹已经都被成功拆解),就进入代码段的主体部分。
可以看到紧接着,程序又读取了一次字符,但读取后判断是否是三个字符。不是就跳出<phase_defused>,说明这里应该读取共三个字符。但看之前的输入,并没有三个字符的情况。说明如果想打开这个隐藏炸弹的破解过程,必须将某一个炸弹的密码改为3个字符(串)。而在<phase_defused>代码段读取这三个字符之前,代码已经设置了字符读取的地址:0x603870(mov $0x603870,%edi),我们在这里设置断点,将之前的正确答案都输入后,使用x/s 0x603870指令看看读取的到底是之前输入的哪一个字符串。
在这里插入图片描述
由此我们可以判断,需要在第四个炸弹的破解密码后添加一个字符(串)才能开启隐藏炸弹。为了知道第三个输入是什么类型,使用x/s 0x402619查看0x402619地址中的内容(mov $0x402619,%esi):
在这里插入图片描述
第三个输入应为字符串。而接着又进行了一次字符串比较,被比较的字符串的地址位于0x402622,查看0x402622处的内容:
在这里插入图片描述
在第四个炸弹密码后加入DrEvil后,成功进入隐藏炸弹secret phase:
在这里插入图片描述
<secret_phase>代码如下:

0000000000401242 <secret_phase>:
  401242:	53                   	push   %rbx
  401243:	e8 56 02 00 00       	callq  40149e <read_line>
  401248:	ba 0a 00 00 00       	mov    $0xa,%edx
  40124d:	be 00 00 00 00       	mov    $0x0,%esi
  401252:	48 89 c7             	mov    %rax,%rdi
  401255:	e8 76 f9 ff ff       	callq  400bd0 <strtol@plt>
  40125a:	48 89 c3             	mov    %rax,%rbx
  40125d:	8d 40 ff             	lea    -0x1(%rax),%eax
  401260:	3d e8 03 00 00       	cmp    $0x3e8,%eax
//1000<=%eax
  401265:	76 05                	jbe    40126c <secret_phase+0x2a>
  401267:	e8 ce 01 00 00       	callq  40143a <explode_bomb>
  40126c:	89 de                	mov    %ebx,%esi
  40126e:	bf f0 30 60 00       	mov    $0x6030f0,%edi
//address:0x24 36
  401273:	e8 8c ff ff ff       	callq  401204 <fun7>
  401278:	83 f8 02             	cmp    $0x2,%eax
//return 2
  40127b:	74 05                	je     401282 <secret_phase+0x40>
  40127d:	e8 b8 01 00 00       	callq  40143a <explode_bomb>
  401282:	bf 38 24 40 00       	mov    $0x402438,%edi
  401287:	e8 84 f8 ff ff       	callq  400b10 <puts@plt>
  40128c:	e8 33 03 00 00       	callq  4015c4 <phase_defused>
  401291:	5b                   	pop    %rbx
  401292:	c3                   	retq   
  401293:	90                   	nop
  401294:	90                   	nop
  401295:	90                   	nop
  401296:	90                   	nop
  401297:	90                   	nop
  401298:	90                   	nop
  401299:	90                   	nop
  40129a:	90                   	nop
  40129b:	90                   	nop
  40129c:	90                   	nop
  40129d:	90                   	nop
  40129e:	90                   	nop
  40129f:	90                   	nop

其中代码如下:

0000000000401204 <fun7>:
  401204:	48 83 ec 08          	sub    $0x8,%rsp
  401208:	48 85 ff             	test   %rdi,%rdi
  40120b:	74 2b                	je     401238 <fun7+0x34>
  40120d:	8b 17                	mov    (%rdi),%edx   36
  40120f:	39 f2                	cmp    %esi,%edx
  401211:	7e 0d                	jle    401220 <fun7+0x1c>
  401213:	48 8b 7f 08          	mov    0x8(%rdi),%rdi
  401217:	e8 e8 ff ff ff       	callq  401204 <fun7>
  40121c:	01 c0                	add    %eax,%eax
  40121e:	eb 1d                	jmp    40123d <fun7+0x39>
  401220:	b8 00 00 00 00       	mov    $0x0,%eax
  401225:	39 f2                	cmp    %esi,%edx
  401227:	74 14                	je     40123d <fun7+0x39>
  401229:	48 8b 7f 10          	mov    0x10(%rdi),%rdi
  40122d:	e8 d2 ff ff ff       	callq  401204 <fun7>
  401232:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax
  401236:	eb 05                	jmp    40123d <fun7+0x39>
  401238:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
  40123d:	48 83 c4 08          	add    $0x8,%rsp
  401241:	c3                   	retq

将改写为C代码:

int fun7(num, *p)
{
	int temp = *p;
	if(num < temp)
	{
		p=p + 8;
		int r1 = fun7(num, *p);
		return 2 * r1;
	}
	else
	{
		if(num == temp)
			return 0;
		else
		{
			p = p + 16;
			int r2 = fun7(num, *p);
			return 2 * r2 + 1;
		}
	}
}

要求fun7的返回值为2。fun7递归的底层应该是返回0,我们由此往前推,只有在第三步num==temp返回0,第二步num>temp返回20+1=1,第三部num<temp返回21=2,fun7最终的返回值才为2。也就是说,最开始输入的参数*p所指向的内存中的值应该大于输入的num,内存增加8个字节后其中的值应该小于输入的num,再增加16和字节后其中的值应该等于输入的num。而初次输入的地址的值为:0x6030f0,使用p/x *0x6030f0 @25指令读取包括该地址在内及后续的24个字节的数据,得到:
在这里插入图片描述
可以看到增加8个字节再增加16个字节后地址中存储的值为0x16即22。若输入的num为22(0x16),0x16<0x24,0x16>0x8,均满足条件,故22应为输入的数字。输入22后,隐藏炸弹也被拆除。
在这里插入图片描述
至此,所有的炸弹都已被拆除了。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值