CSAPP3.0-BombLab

本文详细分析了爆破程序的各个阶段,包括字符串比较、整数序列、密码表操作、数字序列比较和二叉搜索树操作。在每个阶段,都给出了如何解密输入的步骤和条件,最终揭示了隐藏关的存在,该关卡需要在二叉搜索树中找到特定节点。

此实验是CSAPP3.0的第二个实验,原理可参考书中第三章。此实验需要通过6关以及一个隐藏关,闯关失败会引爆炸弹。

phase_1

使用gdb bomb命令打开调试器

反汇编phase_1

请添加图片描述

其功能是比较输入字符串和0x402400位置处的字符串。

查看0x402400位置处的字符串:
请添加图片描述
可知phase_1输入字符串为:Border relations with Canada have never been better.

运行:

请添加图片描述

phase_2

反汇编phase_2

Dump of assembler code for function phase_2:
   0x0000000000400efc <+0>:		push   %rbp
   0x0000000000400efd <+1>:		push   %rbx
   0x0000000000400efe <+2>:		sub    $0x28,%rsp
   0x0000000000400f02 <+6>:		mov    %rsp,%rsi
   0x0000000000400f05 <+9>:		callq  0x40145c <read_six_numbers>
   0x0000000000400f0a <+14>:	cmpl   $0x1,(%rsp)
   0x0000000000400f0e <+18>:	je     0x400f30 <phase_2+52>
   0x0000000000400f10 <+20>:	callq  0x40143a <explode_bomb>
   0x0000000000400f15 <+25>:	jmp    0x400f30 <phase_2+52>
   0x0000000000400f17 <+27>:	mov    -0x4(%rbx),%eax
   0x0000000000400f1a <+30>:	add    %eax,%eax
   0x0000000000400f1c <+32>:	cmp    %eax,(%rbx)
   0x0000000000400f1e <+34>:	je     0x400f25 <phase_2+41>
   0x0000000000400f20 <+36>:	callq  0x40143a <explode_bomb>
   0x0000000000400f25 <+41>:	add    $0x4,%rbx
   0x0000000000400f29 <+45>:	cmp    %rbp,%rbx
   0x0000000000400f2c <+48>:	jne    0x400f17 <phase_2+27>
   0x0000000000400f2e <+50>:	jmp    0x400f3c <phase_2+64>
   0x0000000000400f30 <+52>:	lea    0x4(%rsp),%rbx
   0x0000000000400f35 <+57>:	lea    0x18(%rsp),%rbp
   0x0000000000400f3a <+62>:	jmp    0x400f17 <phase_2+27>
   0x0000000000400f3c <+64>:	add    $0x28,%rsp
   0x0000000000400f40 <+68>:	pop    %rbx
   0x0000000000400f41 <+69>:	pop    %rbp
   0x0000000000400f42 <+70>:	retq   
---Type <return> to continue, or q <return> to quit---
End of assembler dump.

此函数功能是读取6个整数并比较。由400f0a行cmpl $0x1,(%rsp)可知,第一个数为1;由400f30行可知,%rbx保存第二个数的地址,%rbp保存最后一个数的后一个位置,由400f17-400f1c行,得到第二个数是2;400f17-400f2e行是将当前数不断与前一个数的2倍比较,分别得到第3-6个数是:4,8,16,32。

运行:
请添加图片描述

phase_3

反汇编:

Dump of assembler code for function phase_3:
   0x0000000000400f43 <+0>:		sub    $0x18,%rsp
   0x0000000000400f47 <+4>:		lea    0xc(%rsp),%rcx
   0x0000000000400f4c <+9>:		lea    0x8(%rsp),%rdx
   0x0000000000400f51 <+14>:	mov    $0x4025cf,%esi
   0x0000000000400f56 <+19>:	mov    $0x0,%eax
   0x0000000000400f5b <+24>:	callq  0x400bf0 <__isoc99_sscanf@plt>
   0x0000000000400f60 <+29>:	cmp    $0x1,%eax
   0x0000000000400f63 <+32>:	jg     0x400f6a <phase_3+39>
   0x0000000000400f65 <+34>:	callq  0x40143a <explode_bomb>
   0x0000000000400f6a <+39>:	cmpl   $0x7,0x8(%rsp)
   0x0000000000400f6f <+44>:	ja     0x400fad <phase_3+106>
   0x0000000000400f71 <+46>:	mov    0x8(%rsp),%eax
   0x0000000000400f75 <+50>:	jmpq   *0x402470(,%rax,8)
   0x0000000000400f7c <+57>:	mov    $0xcf,%eax
   0x0000000000400f81 <+62>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f83 <+64>:	mov    $0x2c3,%eax
   0x0000000000400f88 <+69>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f8a <+71>:	mov    $0x100,%eax
   0x0000000000400f8f <+76>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f91 <+78>:	mov    $0x185,%eax
   0x0000000000400f96 <+83>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f98 <+85>:	mov    $0xce,%eax
   0x0000000000400f9d <+90>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400f9f <+92>:	mov    $0x2aa,%eax
   0x0000000000400fa4 <+97>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fa6 <+99>:	mov    $0x147,%eax
   0x0000000000400fab <+104>:	jmp    0x400fbe <phase_3+123>
   0x0000000000400fad <+106>:	callq  0x40143a <explode_bomb>
   0x0000000000400fb2 <+111>:	mov    $0x0,%eax
   0x0000000000400fb7 <+116>:	jmp    0x400fbe <phase_3+123>
---Type <return> to continue, or q <return> to quit---
   0x0000000000400fb9 <+118>:	mov    $0x137,%eax
   0x0000000000400fbe <+123>:	cmp    0xc(%rsp),%eax
   0x0000000000400fc2 <+127>:	je     0x400fc9 <phase_3+134>
   0x0000000000400fc4 <+129>:	callq  0x40143a <explode_bomb>
   0x0000000000400fc9 <+134>:	add    $0x18,%rsp
   0x0000000000400fcd <+138>:	retq   
End of assembler dump.

查看0x4025cf处的字符串:

请添加图片描述

此题需要输入两个整数。

由400f6a-400f6f行,可知第一个数应小于等于7;之后可以print出*0x402470地址的值,为0x400f7c,此处为跳转表实现的开始,因此,此题答案可为以下几种:

0 207
2 707
3 256
4 389
5 206
6 682
7 327 

phase_4

反汇编phase_4:

Dump of assembler code for function phase_4:
   0x000000000040100c <+0>:		sub    $0x18,%rsp
   0x0000000000401010 <+4>:		lea    0xc(%rsp),%rcx
   0x0000000000401015 <+9>:		lea    0x8(%rsp),%rdx
   0x000000000040101a <+14>:	mov    $0x4025cf,%esi
   0x000000000040101f <+19>:	mov    $0x0,%eax
   0x0000000000401024 <+24>:	callq  0x400bf0 <__isoc99_sscanf@plt>
   0x0000000000401029 <+29>:	cmp    $0x2,%eax
   0x000000000040102c <+32>:	jne    0x401035 <phase_4+41>
   0x000000000040102e <+34>:	cmpl   $0xe,0x8(%rsp)
   0x0000000000401033 <+39>:	jbe    0x40103a <phase_4+46>
   0x0000000000401035 <+41>:	callq  0x40143a <explode_bomb>
   0x000000000040103a <+46>:	mov    $0xe,%edx
   0x000000000040103f <+51>:	mov    $0x0,%esi
   0x0000000000401044 <+56>:	mov    0x8(%rsp),%edi
   0x0000000000401048 <+60>:	callq  0x400fce <func4>
   0x000000000040104d <+65>:	test   %eax,%eax
   0x000000000040104f <+67>:	jne    0x401058 <phase_4+76>
   0x0000000000401051 <+69>:	cmpl   $0x0,0xc(%rsp)
   0x0000000000401056 <+74>:	je     0x40105d <phase_4+81>
   0x0000000000401058 <+76>:	callq  0x40143a <explode_bomb>
   0x000000000040105d <+81>:	add    $0x18,%rsp
   0x0000000000401061 <+85>:	retq   
End of assembler dump.

和phase_3一样,输入两个数。由0x40102e-0x401035行,可知第一个数小于等于0xe;func4函数三个输入参数%edi=输入的第一个数,%esi=0,%edx=0xe,test测试返回值是否为0;由0x401051 <+69>: cmpl $0x0,0xc(%rsp),可知第二个数为0。

反汇编func4:

Dump of assembler code for function func4:
   0x0000000000400fce <+0>:		sub    $0x8,%rsp
   0x0000000000400fd2 <+4>:		mov    %edx,%eax	
   0x0000000000400fd4 <+6>:		sub    %esi,%eax	
   0x0000000000400fd6 <+8>:		mov    %eax,%ecx	
   0x0000000000400fd8 <+10>:	shr    $0x1f,%ecx	
   0x0000000000400fdb <+13>:	add    %ecx,%eax	
   0x0000000000400fdd <+15>:	sar    %eax				;右移一位
   0x0000000000400fdf <+17>:	lea    (%rax,%rsi,1),%ecx
   0x0000000000400fe2 <+20>:	cmp    %edi,%ecx
   0x0000000000400fe4 <+22>:	jle    0x400ff2 <func4+36>
   0x0000000000400fe6 <+24>:	lea    -0x1(%rcx),%edx 
   0x0000000000400fe9 <+27>:	callq  0x400fce <func4>
   0x0000000000400fee <+32>:	add    %eax,%eax
   0x0000000000400ff0 <+34>:	jmp    0x401007 <func4+57>
   0x0000000000400ff2 <+36>:	mov    $0x0,%eax
   0x0000000000400ff7 <+41>:	cmp    %edi,%ecx
   0x0000000000400ff9 <+43>:	jge    0x401007 <func4+57>
   0x0000000000400ffb <+45>:	lea    0x1(%rcx),%esi
   0x0000000000400ffe <+48>:	callq  0x400fce <func4>
   0x0000000000401003 <+53>:	lea    0x1(%rax,%rax,1),%eax
   0x0000000000401007 <+57>:	add    $0x8,%rsp
   0x000000000040100b <+61>:	retq   
End of assembler dump.

函数func4第一次执行到0x400fdf时,%ecx=7,后边连续比较是否小于等于和大于等于%edi,可知

①当%edi==7时,返回值为0;

②当%edi > 7时,第二次进入func4函数,%esi=8,执行到0x400fdf时,%ecx=15,第三次进入func4函数时%edx仍为14不变,陷入死循环;

③当%edi < 7时,第二次进入func4函数,%edx=6,执行到0x400fdf时,%ecx=3,只有当%edi==3时,函数才返回0;

④当%edi > 3时,情况和②类似,当%edi < 3时,情况和③类似,以此类推;

故%edi的值只能为0,1,3,7。

因此,此题答案可为以下几种:

0 0
1 0
3 0
7 0

phase_5

反汇编phase_5:

Dump of assembler code for function phase_5:
   0x0000000000401062 <+0>:		push   %rbx
   0x0000000000401063 <+1>:		sub    $0x20,%rsp
   0x0000000000401067 <+5>:		mov    %rdi,%rbx
   0x000000000040106a <+8>:		mov    %fs:0x28,%rax
   0x0000000000401073 <+17>:	mov    %rax,0x18(%rsp)
   0x0000000000401078 <+22>:	xor    %eax,%eax
   0x000000000040107a <+24>:	callq  0x40131b <string_length>
   0x000000000040107f <+29>:	cmp    $0x6,%eax
   0x0000000000401082 <+32>:	je     0x4010d2 <phase_5+112>
   0x0000000000401084 <+34>:	callq  0x40143a <explode_bomb>
   0x0000000000401089 <+39>:	jmp    0x4010d2 <phase_5+112>
   0x000000000040108b <+41>:	movzbl (%rbx,%rax,1),%ecx
   0x000000000040108f <+45>:	mov    %cl,(%rsp)
   0x0000000000401092 <+48>:	mov    (%rsp),%rdx
   0x0000000000401096 <+52>:	and    $0xf,%edx
   0x0000000000401099 <+55>:	movzbl 0x4024b0(%rdx),%edx
   0x00000000004010a0 <+62>:	mov    %dl,0x10(%rsp,%rax,1)
   0x00000000004010a4 <+66>:	add    $0x1,%rax
   0x00000000004010a8 <+70>:	cmp    $0x6,%rax
   0x00000000004010ac <+74>:	jne    0x40108b <phase_5+41>
   0x00000000004010ae <+76>:	movb   $0x0,0x16(%rsp)
   0x00000000004010b3 <+81>:	mov    $0x40245e,%esi
   0x00000000004010b8 <+86>:	lea    0x10(%rsp),%rdi
   0x00000000004010bd <+91>:	callq  0x401338 <strings_not_equal>
   0x00000000004010c2 <+96>:	test   %eax,%eax
   0x00000000004010c4 <+98>:	je     0x4010d9 <phase_5+119>
   0x00000000004010c6 <+100>:	callq  0x40143a <explode_bomb>
   0x00000000004010cb <+105>:	nopl   0x0(%rax,%rax,1)
   0x00000000004010d0 <+110>:	jmp    0x4010d9 <phase_5+119>
   0x00000000004010d2 <+112>:	mov    $0x0,%eax
---Type <return> to continue, or q <return> to quit---
   0x00000000004010d7 <+117>:	jmp    0x40108b <phase_5+41>
   0x00000000004010d9 <+119>:	mov    0x18(%rsp),%rax
   0x00000000004010de <+124>:	xor    %fs:0x28,%rax
   0x00000000004010e7 <+133>:	je     0x4010ee <phase_5+140>
   0x00000000004010e9 <+135>:	callq  0x400b30 <__stack_chk_fail@plt>
   0x00000000004010ee <+140>:	add    $0x20,%rsp
   0x00000000004010f2 <+144>:	pop    %rbx
   0x00000000004010f3 <+145>:	retq   
End of assembler dump.

由0x40107a-0x401082知,输入6个字符。

查看0x4024b0处的字符串:
请添加图片描述
这里的字符串是一个密码表,题意是分别取每个输入字符的后4位,设为bits,取0x4024b0+bits处的字符,存入%rsp+0x10为起始的6个位置,再将%rsp+0x10~%rsp+0x15处的字符与0x40245e处的字符串作比较。

查看0x40245e处的字符串:

请添加图片描述

可以得出对应于密码表中位置分别是:9, f, e, 5, 6, 7。也就是输入6个字符的后四位。查看ASCII码表,找到后四位对应的任意字符均可。参考答案如下几种:

IONEFG
ionefg
9?>567

phase_6

反汇编代码:

Dump of assembler code for function phase_6:
   0x00000000004010f4 <+0>:	push   %r14
   0x00000000004010f6 <+2>:	push   %r13
   0x00000000004010f8 <+4>:	push   %r12
   0x00000000004010fa <+6>:	push   %rbp
   0x00000000004010fb <+7>:	push   %rbx
   0x00000000004010fc <+8>:	sub    $0x50,%rsp
   0x0000000000401100 <+12>:	mov    %rsp,%r13
   0x0000000000401103 <+15>:	mov    %rsp,%rsi
   0x0000000000401106 <+18>:	callq  0x40145c <read_six_numbers>
   0x000000000040110b <+23>:	mov    %rsp,%r14
   0x000000000040110e <+26>:	mov    $0x0,%r12d
   0x0000000000401114 <+32>:	mov    %r13,%rbp
   0x0000000000401117 <+35>:	mov    0x0(%r13),%eax
   0x000000000040111b <+39>:	sub    $0x1,%eax
   0x000000000040111e <+42>:	cmp    $0x5,%eax
   0x0000000000401121 <+45>:	jbe    0x401128 <phase_6+52>
   0x0000000000401123 <+47>:	callq  0x40143a <explode_bomb>
   0x0000000000401128 <+52>:	add    $0x1,%r12d
   0x000000000040112c <+56>:	cmp    $0x6,%r12d
   0x0000000000401130 <+60>:	je     0x401153 <phase_6+95>
   0x0000000000401132 <+62>:	mov    %r12d,%ebx
   0x0000000000401135 <+65>:	movslq %ebx,%rax
   0x0000000000401138 <+68>+:	mov    (%rsp,%rax,4),%eax
   0x000000000040113b <+71>:	cmp    %eax,0x0(%rbp)
   0x000000000040113e <+74>:	jne    0x401145 <phase_6+81>
   0x0000000000401140 <+76>:	callq  0x40143a <explode_bomb>
   0x0000000000401145 <+81>:	add    $0x1,%ebx
   0x0000000000401148 <+84>:	cmp    $0x5,%ebx
   0x000000000040114b <+87>:	jle    0x401135 <phase_6+65>
   0x000000000040114d <+89>:	add    $0x4,%r13
---Type <return> to continue, or q <return> to quit---
   0x0000000000401151 <+93>:	jmp    0x401114 <phase_6+32>
   0x0000000000401153 <+95>:	lea    0x18(%rsp),%rsi
   0x0000000000401158 <+100>:	mov    %r14,%rax
   0x000000000040115b <+103>:	mov    $0x7,%ecx
   0x0000000000401160 <+108>:	mov    %ecx,%edx
   0x0000000000401162 <+110>:	sub    (%rax),%edx
   0x0000000000401164 <+112>:	mov    %edx,(%rax)
   0x0000000000401166 <+114>:	add    $0x4,%rax
   0x000000000040116a <+118>:	cmp    %rsi,%rax
   0x000000000040116d <+121>:	jne    0x401160 <phase_6+108>
   0x000000000040116f <+123>:	mov    $0x0,%esi
   0x0000000000401174 <+128>:	jmp    0x401197 <phase_6+163>
   0x0000000000401176 <+130>:	mov    0x8(%rdx),%rdx
   0x000000000040117a <+134>:	add    $0x1,%eax
   0x000000000040117d <+137>:	cmp    %ecx,%eax
   0x000000000040117f <+139>:	jne    0x401176 <phase_6+130>
   0x0000000000401181 <+141>:	jmp    0x401188 <phase_6+148>
   0x0000000000401183 <+143>:	mov    $0x6032d0,%edx
   0x0000000000401188 <+148>:	mov    %rdx,0x20(%rsp,%rsi,2)
   0x000000000040118d <+153>:	add    $0x4,%rsi
   0x0000000000401191 <+157>:	cmp    $0x18,%rsi
   0x0000000000401195 <+161>:	je     0x4011ab <phase_6+183>
   0x0000000000401197 <+163>:	mov    (%rsp,%rsi,1),%ecx
   0x000000000040119a <+166>:	cmp    $0x1,%ecx
   0x000000000040119d <+169>:	jle    0x401183 <phase_6+143>
   0x000000000040119f <+171>:	mov    $0x1,%eax
   0x00000000004011a4 <+176>:	mov    $0x6032d0,%edx
   0x00000000004011a9 <+181>:	jmp    0x401176 <phase_6+130>
   0x00000000004011ab <+183>:	mov    0x20(%rsp),%rbx
   0x00000000004011b0 <+188>:	lea    0x28(%rsp),%rax
   0x00000000004011b5 <+193>:	lea    0x50(%rsp),%rsi
---Type <return> to continue, or q <return> to quit---
   0x00000000004011ba <+198>:	mov    %rbx,%rcx
   0x00000000004011bd <+201>:	mov    (%rax),%rdx
   0x00000000004011c0 <+204>:	mov    %rdx,0x8(%rcx)
   0x00000000004011c4 <+208>:	add    $0x8,%rax
   0x00000000004011c8 <+212>:	cmp    %rsi,%rax
   0x00000000004011cb <+215>:	je     0x4011d2 <phase_6+222>
   0x00000000004011cd <+217>:	mov    %rdx,%rcx
   0x00000000004011d0 <+220>:	jmp    0x4011bd <phase_6+201>
   0x00000000004011d2 <+222>:	movq   $0x0,0x8(%rdx)
   0x00000000004011da <+230>:	mov    $0x5,%ebp
   0x00000000004011df <+235>:	mov    0x8(%rbx),%rax
   0x00000000004011e3 <+239>:	mov    (%rax),%eax
   0x00000000004011e5 <+241>:	cmp    %eax,(%rbx)
   0x00000000004011e7 <+243>:	jge    0x4011ee <phase_6+250>
   0x00000000004011e9 <+245>:	callq  0x40143a <explode_bomb>
   0x00000000004011ee <+250>:	mov    0x8(%rbx),%rbx
   0x00000000004011f2 <+254>:	sub    $0x1,%ebp
   0x00000000004011f5 <+257>:	jne    0x4011df <phase_6+235>
   0x00000000004011f7 <+259>:	add    $0x50,%rsp
   0x00000000004011fb <+263>:	pop    %rbx
   0x00000000004011fc <+264>:	pop    %rbp
   0x00000000004011fd <+265>:	pop    %r12
   0x00000000004011ff <+267>:	pop    %r13
   0x0000000000401201 <+269>:	pop    %r14
   0x0000000000401203 <+271>:	retq   
End of assembler dump.

+12 - +18:读入6个数字,分别存入[rsp]-[rsp+0x18],记为a0-a5;

+32 - +93:循环

​ +32 - +45:判断ai是否小于等于6,否则引爆;

​ +52 - +93:对于任意i!=j,有ai!=aj,否则引爆;

+95 - +121:循环,赋值ai = 7 - ai;

+123 - +181:循环

​ 0x6032d0是一个链表的头节点地址,gdb查看链表:

(gdb) x/80a 0x6032d0
0x6032d0 <node1>:	0x10000014c	0x6032e0 <node2>
0x6032e0 <node2>:	0x2000000a8	0x6032f0 <node3>
0x6032f0 <node3>:	0x30000039c	0x603300 <node4>
0x603300 <node4>:	0x4000002b3	0x603310 <node5>
0x603310 <node5>:	0x5000001dd	0x603320 <node6>
0x603320 <node6>:	0x6000001bb	0x0
0x603330:	0x0	0x0
0x603340 <host_table>:	0x402629	0x402643
0x603350 <host_table+16>:	0x40265d	0x0
0x603360 <host_table+32>:	0x0	0x0
0x603370 <host_table+48>:	0x0	0x0
0x603380 <host_table+64>:	0x0	0x0
0x603390 <host_table+80>:	0x0	0x0
0x6033a0 <host_table+96>:	0x0	0x0
0x6033b0 <host_table+112>:	0x0	0x0
0x6033c0 <host_table+128>:	0x0	0x0
0x6033d0 <host_table+144>:	0x0	0x0
0x6033e0 <host_table+160>:	0x0	0x0
0x6033f0 <host_table+176>:	0x0	0x0
0x603400 <host_table+192>:	0x0	0x0
0x603410 <host_table+208>:	0x0	0x0
0x603420 <host_table+224>:	0x0	0x0
0x603430 <host_table+240>:	0x0	0x0
0x603440 <host_table+256>:	0x0	0x0
0x603450 <host_table+272>:	0x0	0x0
0x603460 <host_table+288>:	0x0	0x0
0x603470 <host_table+304>:	0x0	0x0
0x603480 <host_table+320>:	0x0	0x0
0x603490 <host_table+336>:	0x0	0x0
0x6034a0 <host_table+352>:	0x0	0x0
0x6034b0 <host_table+368>:	0x0	0x0
---Type <return> to continue, or q <return> to quit---
0x6034c0 <host_table+384>:	0x0	0x0
0x6034d0 <host_table+400>:	0x0	0x0
0x6034e0 <host_table+416>:	0x0	0x0
0x6034f0 <host_table+432>:	0x0	0x0
0x603500 <host_table+448>:	0x0	0x0
0x603510 <host_table+464>:	0x0	0x0
0x603520 <host_table+480>:	0x0	0x0
0x603530 <host_table+496>:	0x0	0x0
0x603540 <host_table+512>:	0x0	0x0
节点地址0x006032d00x006032e00x006032f00x006033000x006033100x00603320
value0x0000014c0x000000a80x0000039c0x000002b30x000001dd0x000001bb
next0x006032e00x006032f00x006033000x006033100x006033200x00000000

​ 若ai<=1,则[rsp+0x20+2*i] = 头节点的地址(0x6032d0),否则,[rsp+0x20+2*i] = 第ai个节点的地址;

+183 - +222:循环,将第a0-a4节点的next值赋为a1-a5节点的地址,最后一个节点的next值赋为0(个人觉得这里的操作有点重复,因为栈中的值没有发生变化);

+230 - +257:循环,判断栈中第a0-a5个节点value,前一个节点的value是否大于等于后一个,否则引爆,也就是判断此时链表是否降序。

由此,倒推可得出输入的数字。原链表的大小顺序为:5,6,1,2,3,4。重排之后链表的第0-5个节点对应于原链表的第a0-a5个节点,可以得到a0-a5依次为:3,4,5,6,1,2。注意到对输入数组做了处理:ai=7-ai,由此得到输入数组为:4,3,2,1,6,5。

secret_phase

把前六关答案输入之后,程序就正常结束了,隐藏关是看了其他博客之后才发现,此题完全是抄的https://zhuanlan.zhihu.com/p/272861057

使用objdump -d ./bomb > bomb.asm查看完整反汇编代码,发现main函数中并未调用secret_phase,但是每个phase之后都会调用phase_defused,搜索整个文件也只有phase_defused函数中调用了secret_phase

反汇编phase_defused:

Dump of assembler code for function phase_defused:
   0x00000000004015c4 <+0>:	sub    $0x78,%rsp
   0x00000000004015c8 <+4>:	mov    %fs:0x28,%rax
   0x00000000004015d1 <+13>:	mov    %rax,0x68(%rsp)
   0x00000000004015d6 <+18>:	xor    %eax,%eax
   0x00000000004015d8 <+20>:	cmpl   $0x6,0x202181(%rip)        # 0x603760 <num_input_strings>
   0x00000000004015df <+27>:	jne    0x40163f <phase_defused+123>
   0x00000000004015e1 <+29>:	lea    0x10(%rsp),%r8
   0x00000000004015e6 <+34>:	lea    0xc(%rsp),%rcx
   0x00000000004015eb <+39>:	lea    0x8(%rsp),%rdx
   0x00000000004015f0 <+44>:	mov    $0x402619,%esi
   0x00000000004015f5 <+49>:	mov    $0x603870,%edi
   0x00000000004015fa <+54>:	callq  0x400bf0 <__isoc99_sscanf@plt>
   0x00000000004015ff <+59>:	cmp    $0x3,%eax
   0x0000000000401602 <+62>:	jne    0x401635 <phase_defused+113>
   0x0000000000401604 <+64>:	mov    $0x402622,%esi
   0x0000000000401609 <+69>:	lea    0x10(%rsp),%rdi
   0x000000000040160e <+74>:	callq  0x401338 <strings_not_equal>
   0x0000000000401613 <+79>:	test   %eax,%eax
   0x0000000000401615 <+81>:	jne    0x401635 <phase_defused+113>
   0x0000000000401617 <+83>:	mov    $0x4024f8,%edi
   0x000000000040161c <+88>:	callq  0x400b10 <puts@plt>
   0x0000000000401621 <+93>:	mov    $0x402520,%edi
   0x0000000000401626 <+98>:	callq  0x400b10 <puts@plt>
   0x000000000040162b <+103>:	mov    $0x0,%eax
   0x0000000000401630 <+108>:	callq  0x401242 <secret_phase>
   0x0000000000401635 <+113>:	mov    $0x402558,%edi
   0x000000000040163a <+118>:	callq  0x400b10 <puts@plt>
   0x000000000040163f <+123>:	mov    0x68(%rsp),%rax
   0x0000000000401644 <+128>:	xor    %fs:0x28,%rax
   0x000000000040164d <+137>:	je     0x401654 <phase_defused+144>
---Type <return> to continue, or q <return> to quit---
   0x000000000040164f <+139>:	callq  0x400b30 <__stack_chk_fail@plt>
   0x0000000000401654 <+144>:	add    $0x78,%rsp
   0x0000000000401658 <+148>:	retq   
End of assembler dump.
  • +0到+18:开栈,金丝雀;

  • +20到+27:如果0x603760 <num_input_strings> 不等于6就跳到+123;

  • +123到+148:出栈,检测栈保护,返回。

    • 因此调用中间代码需要0x603760 == 6,是目前总共输入的行数,也就是说在第六关结束后进入中间部分的代码:
  • +29到+54:调用sscanf读入

    • 第一个参数立即数0x603870,是目标来源字符串;
    • 第二个参数是0x402619 = “%d %d %s”,格式读入参数;
    • 第三到五个参数是存储地址,分别存在[rsp + 8], [rsp + c], [rsp + 10];

那么0x603870是什么呢?应该是与我们的键盘输入相关的。不难发现,各关函数的读入都是调用sscanf,而不是scanf,其来源字符串是由main里read_line读入,然后在main里mov eax, edi传给各函数。

用gdb在main里设置断点查看read_line的返回值,分别是0x603780, 0x6037d0, 0x603820, 0x603870,对应第一到四关的读入,因此secret-phase这里的sscanf是从第四关输入的完整字符串中读取,而前两个”%d %d”就是第四关的答案。

注意隐藏关进入仍然是在第六关结束后。

  • +59:如果sscanf返回值不是3就跳到+113输出,结束。

  • +64到+74:比对输入

    • 比对字符串0x402622和[rsp + 10],也就是我们输入的第三个参数,如果不一样就跳到+113结束。
    • gdb查看这里的0x402622是”DrEvil”,也就是第四关我们应该输入”7 0 DrEvil”或第四关其他合法答案。
  • +83到+108:输出进入隐藏关的信息,调用secret_phase;

  • +113到+118:输入正常结束的信息。

反汇编secret_phase:

Dump of assembler code for function secret_phase:
   0x0000000000401242 <+0>:	push   %rbx
   0x0000000000401243 <+1>:	callq  0x40149e <read_line>
   0x0000000000401248 <+6>:	mov    $0xa,%edx
   0x000000000040124d <+11>:	mov    $0x0,%esi
   0x0000000000401252 <+16>:	mov    %rax,%rdi
   0x0000000000401255 <+19>:	callq  0x400bd0 <strtol@plt>
   0x000000000040125a <+24>:	mov    %rax,%rbx
   0x000000000040125d <+27>:	lea    -0x1(%rax),%eax
   0x0000000000401260 <+30>:	cmp    $0x3e8,%eax
   0x0000000000401265 <+35>:	jbe    0x40126c <secret_phase+42>
   0x0000000000401267 <+37>:	callq  0x40143a <explode_bomb>
   0x000000000040126c <+42>:	mov    %ebx,%esi
   0x000000000040126e <+44>:	mov    $0x6030f0,%edi
   0x0000000000401273 <+49>:	callq  0x401204 <fun7>
   0x0000000000401278 <+54>:	cmp    $0x2,%eax
   0x000000000040127b <+57>:	je     0x401282 <secret_phase+64>
   0x000000000040127d <+59>:	callq  0x40143a <explode_bomb>
   0x0000000000401282 <+64>:	mov    $0x402438,%edi
   0x0000000000401287 <+69>:	callq  0x400b10 <puts@plt>
   0x000000000040128c <+74>:	callq  0x4015c4 <phase_defused>
   0x0000000000401291 <+79>:	pop    %rbx
   0x0000000000401292 <+80>:	retq   
End of assembler dump.
  • +0到+1:保护现场,读入;

  • +6到+19:

    • 调用strtol,这是一个c标准库函数。
    • 第一个参数是rax,read_line的返回值,刚才的输入;第二个参数esi是0;第三个参数是0xa。
    • 这个函数把rax处的字符串转换成a进制,也就是10进制,等于说我们输入了一个10进制数字。
  • +24到+37:如果刚才的输入> 0x3e8就爆炸;

  • +42到+49:

    • 调用fun7,第一个参数是一个立即数0x6030f0,第二个参数是输入的数字。
    • 这个立即数是什么呢?是一个地址,一个二叉树根节点的地址!
  • +54到+59:如果fun7的返回值不是2就引爆炸弹;

  • +64到+74:输出通关信息;

  • +79到+80:还原现场,返回。

因此需要输入的数能使fun7返回的是2。

反汇编fun7:

Dump of assembler code for function fun7:
   0x0000000000401204 <+0>:	sub    $0x8,%rsp
   0x0000000000401208 <+4>:	test   %rdi,%rdi
   0x000000000040120b <+7>:	je     0x401238 <fun7+52>
   0x000000000040120d <+9>:	mov    (%rdi),%edx
   0x000000000040120f <+11>:	cmp    %esi,%edx
   0x0000000000401211 <+13>:	jle    0x401220 <fun7+28>
   0x0000000000401213 <+15>:	mov    0x8(%rdi),%rdi
   0x0000000000401217 <+19>:	callq  0x401204 <fun7>
   0x000000000040121c <+24>:	add    %eax,%eax
   0x000000000040121e <+26>:	jmp    0x40123d <fun7+57>
   0x0000000000401220 <+28>:	mov    $0x0,%eax
   0x0000000000401225 <+33>:	cmp    %esi,%edx
   0x0000000000401227 <+35>:	je     0x40123d <fun7+57>
   0x0000000000401229 <+37>:	mov    0x10(%rdi),%rdi
   0x000000000040122d <+41>:	callq  0x401204 <fun7>
   0x0000000000401232 <+46>:	lea    0x1(%rax,%rax,1),%eax
   0x0000000000401236 <+50>:	jmp    0x40123d <fun7+57>
   0x0000000000401238 <+52>:	mov    $0xffffffff,%eax
   0x000000000040123d <+57>:	add    $0x8,%rsp
   0x0000000000401241 <+61>:	retq   
End of assembler dump.
  • +0到+7:开栈,若第一个实际参数(地址)是0就跳到+52,返回-1;
  • +9到+46:递归
 if ( [rdi] > esi ) 
     return 2 * fun7( [rdi + 8], esi );
 else if ( [rdi] < esi )
     return 2 * fun7( [rdi + 10], esi ) + 1;
 else 
     return 0;
  • +57到+61:出栈,返回。

我们希望fun7( 0x6030f0, ? ) 的返回值是2,2 = ((((0*2)*2)*2…)*2+1)*2,因此一定需要第一次调用进入第一个分支,第二次调用(第一次递归)进入第二个分支,后面如果继续递归,不能进入第二个分支。

想要知道递归进入了哪个分支,需要看[0x6030f0]的信息及[0x6030f0 + 8], [0x6030f0 + 10]指向的内存:

(gdb) x/80a 0x6032f0查看后面的内容:

0x6030f0 <n1>: 0x24 0x603110 <n21>
0x603100 <n1+16>: 0x603130 <n22> 0x0
0x603110 <n21>: 0x8 0x603190 <n31>
0x603120 <n21+16>: 0x603150 <n32> 0x0
0x603130 <n22>: 0x32 0x603170 <n33>
0x603140 <n22+16>: 0x6031b0 <n34> 0x0
0x603150 <n32>: 0x16 0x603270 <n43>
0x603160 <n32+16>: 0x603230 <n44> 0x0
0x603170 <n33>: 0x2d 0x6031d0 <n45>
0x603180 <n33+16>: 0x603290 <n46> 0x0
0x603190 <n31>: 0x6 0x6031f0 <n41>
0x6031a0 <n31+16>: 0x603250 <n42> 0x0
0x6031b0 <n34>: 0x6b 0x603210 <n47>
0x6031c0 <n34+16>: 0x6032b0 <n48> 0x0
0x6031d0 <n45>: 0x28 0x0
0x6031e0 <n45+16>: 0x0 0x0
0x6031f0 <n41>: 0x1 0x0
0x603200 <n41+16>: 0x0 0x0
0x603210 <n47>: 0x63 0x0
0x603220 <n47+16>: 0x0 0x0
0x603230 <n44>: 0x23 0x0
0x603240 <n44+16>: 0x0 0x0
0x603250 <n42>: 0x7 0x0
0x603260 <n42+16>: 0x0 0x0
0x603270 <n43>: 0x14 0x0
0x603280 <n43+16>: 0x0 0x0
0x603290 <n46>: 0x2f 0x0
0x6032a0 <n46+16>: 0x0 0x0
0x6032b0 <n48>: 0x3e9 0x0
0x6032c0 <n48+16>: 0x0 0x0
0x6032d0 <node1>: 0x10000014c 0x6032e0 <node2>
0x6032e0 <node2>: 0x2000000a8 0x6032f0 <node3>
0x6032f0 <node3>: 0x30000039c 0x603300 <node4>
0x603300 <node4>: 0x4000002b3 0x603310 <node5>
0x603310 <node5>: 0x5000001dd 0x603320 <node6>
0x603320 <node6>: 0x6000001bb 0x0
0x603330: 0x0 0x0
0x603340 <host_table>: 0x402629 0x402643
0x603350 <host_table+16>: 0x40265d 0x0
0x603360 <host_table+32>: 0x0 0x0

其中0x6032d0以后就不是二叉树的内容了,0x6032d0是第六题的链表;

内存组织如下:root为节点地址;[root]为节点的值;[root + 8]是左儿子地址,0代表没有;[root + 10]是右儿子地址,0代表没有;root+18为空。root + 20是下一个节点。

从节点的值的关系里,我们可以看出来这是一棵二叉搜索树!(甚至还是平衡树)

知道数据的组织结构后,不难看出fun7就是在BST上查找一个特定的值,第一个参数是节点地址,第二个是目标值。

要想fun7( *root, val )返回2,查找的路径可能是root -> Lch -> Rch或者root -> Lch -> Rch -> Lch -> (都是Lch) -> Lch,本题的二叉树深度只有4,因此只有两种可能性,目标节点是或,值val是0x16=22或0x14=20,就是本题的答案。

把所有答案保存在result文件中,运行:
请添加图片描述

参考

https://zhuanlan.zhihu.com/p/272861057

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刷子c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值