CSAPP Bomblab

警告
本文最后更新于 2023-04-22,文中内容可能已过时。

Ubuntu 22.04.1。

将可执行文件bomb反汇编得到汇编代码,并存放到bomb.s中,方便后续查看分析。

1
objdump -d bomb > bomb.s

创建ans.txt存放输入,后面只需执行./bomb ans.txt即可方便地运行bomb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
00000000000015e7 <phase_1>:
    15e7:       f3 0f 1e fa             endbr64
    15eb:       48 83 ec 08             sub    $0x8,%rsp
    15ef:       48 8d 35 56 1b 00 00    lea    0x1b56(%rip),%rsi        # 314c <_IO_stdin_used+0x14c>
    15f6:       e8 26 05 00 00          call   1b21 <strings_not_equal>
    15fb:       85 c0                   test   %eax,%eax
    15fd:       75 05                   jne    1604 <phase_1+0x1d>
    15ff:       48 83 c4 08             add    $0x8,%rsp
    1603:       c3                      ret
    1604:       e8 2c 06 00 00          call   1c35 <explode_bomb>
    1609:       eb f4                   jmp    15ff <phase_1+0x18>

此题需要找到目标字符串。

在 0x15ef 处给 %rsi 传值,随后在 0x15f6 处调用strings_not_equal()函数,猜测 %rsi 中存储的值为目标字符串的地址。用 gdb 调试打印字符串的值即可。

1
2
3
4
5
b phase_1
r
<input>
si 3
x/s $rsi #或x/s $rip+0x1b56

答案如下:

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230420185915401.png

1
Crikey! I have lost my mojo!
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
000000000000160b <phase_2>:
    160b:       f3 0f 1e fa             endbr64
    160f:       55                      push   %rbp
    1610:       53                      push   %rbx
    1611:       48 83 ec 28             sub    $0x28,%rsp
    1615:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
    161c:       00 00
    161e:       48 89 44 24 18          mov    %rax,0x18(%rsp)
    1623:       31 c0                   xor    %eax,%eax
    1625:       48 89 e6                mov    %rsp,%rsi
    1628:       e8 34 06 00 00          call   1c61 <read_six_numbers>
    162d:       83 3c 24 00             cmpl   $0x0,(%rsp)
    1631:       75 07                   jne    163a <phase_2+0x2f>
    1633:       83 7c 24 04 01          cmpl   $0x1,0x4(%rsp)
    1638:       74 05                   je     163f <phase_2+0x34>
    163a:       e8 f6 05 00 00          call   1c35 <explode_bomb>
    163f:       48 89 e3                mov    %rsp,%rbx
    1642:       48 8d 6c 24 10          lea    0x10(%rsp),%rbp
    1647:       eb 09                   jmp    1652 <phase_2+0x47>
    1649:       48 83 c3 04             add    $0x4,%rbx
    164d:       48 39 eb                cmp    %rbp,%rbx
    1650:       74 11                   je     1663 <phase_2+0x58>
    1652:       8b 43 04                mov    0x4(%rbx),%eax
    1655:       03 03                   add    (%rbx),%eax
    1657:       39 43 08                cmp    %eax,0x8(%rbx)
    165a:       74 ed                   je     1649 <phase_2+0x3e>
    165c:       e8 d4 05 00 00          call   1c35 <explode_bomb>
    1661:       eb e6                   jmp    1649 <phase_2+0x3e>
    1663:       48 8b 44 24 18          mov    0x18(%rsp),%rax
    1668:       64 48 2b 04 25 28 00    sub    %fs:0x28,%rax
    166f:       00 00
    1671:       75 07                   jne    167a <phase_2+0x6f>
    1673:       48 83 c4 28             add    $0x28,%rsp
    1677:       5b                      pop    %rbx
    1678:       5d                      pop    %rbp
    1679:       c3                      ret
    167a:       e8 d1 fb ff ff          call   1250 <__stack_chk_fail@plt>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
0000000000001c61 <read_six_numbers>:
    1c61:       f3 0f 1e fa             endbr64
    1c65:       48 83 ec 08             sub    $0x8,%rsp
    1c69:       48 89 f2                mov    %rsi,%rdx
    1c6c:       48 8d 4e 04             lea    0x4(%rsi),%rcx
    1c70:       48 8d 46 14             lea    0x14(%rsi),%rax
    1c74:       50                      push   %rax
    1c75:       48 8d 46 10             lea    0x10(%rsi),%rax
    1c79:       50                      push   %rax
    1c7a:       4c 8d 4e 0c             lea    0xc(%rsi),%r9
    1c7e:       4c 8d 46 08             lea    0x8(%rsi),%r8
    1c82:       48 8d 35 82 16 00 00    lea    0x1682(%rip),%rsi        # 330b <array.0+0x16b>
    1c89:       b8 00 00 00 00          mov    $0x0,%eax
    1c8e:       e8 6d f6 ff ff          call   1300 <__isoc99_sscanf@plt>
    1c93:       48 83 c4 10             add    $0x10,%rsp
    1c97:       83 f8 05                cmp    $0x5,%eax
    1c9a:       7e 05                   jle    1ca1 <read_six_numbers+0x40>
    1c9c:       48 83 c4 08             add    $0x8,%rsp
    1ca0:       c3                      ret
    1ca1:       e8 8f ff ff ff          call   1c35 <explode_bomb>

在 0x1628 处调用read_six_numbers()函数,我们可以用 gdb 验证确实是读取 6 个整数。

1
2
3
4
5
b *0x555555555c8e
r
<ans1>
<input>
x/s $rsi

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230420191545149.png

这里需要注意的是打断点用的是虚拟地址,而非偏移量,bomb.s中显示偏移量,gdb 调试时显示虚拟地址,对于read_six_numbers()中的__isoc99_sscanf()来说,偏移量是 0x1c8e,虚拟地址是 0x555555555c8e。

重新回到phase_2()的汇编代码,分析可得其核心部分用伪代码表示如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
if(M[rsp]!=0){		
    explode_bomb();
}else{
    if(M[rsp+0x4]==1){
        rbx=rsp;					
        rbp=rsp+0x10;	
        do{
            eax=M[rbx+0x4];
            eax=eax+M[rbx];
            if(M[rbx+0x8]==eax){
                rbx=rbx+0x4
            }else{
                explode_bomb();
            }
        }while(rbx!=rbp);
    }else{
        explode_bomb();
    }
}

M[rsp] 代表输入的第一个数,rsp 每加 0x4 代表下一个数。

我们需要构造这样的序列:第一个数为 0,第二个数为 1,此后的数为前面两项之和。

答案如下:

1
0 1 1 2 3 5
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
000000000000167f <phase_3>:
    167f:	f3 0f 1e fa          	endbr64 
    1683:	48 83 ec 18          	sub    $0x18,%rsp
    1687:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
    168e:	00 00 
    1690:	48 89 44 24 08       	mov    %rax,0x8(%rsp)
    1695:	31 c0                	xor    %eax,%eax
    1697:	48 8d 4c 24 04       	lea    0x4(%rsp),%rcx
    169c:	48 89 e2             	mov    %rsp,%rdx
    169f:	48 8d 35 71 1c 00 00 	lea    0x1c71(%rip),%rsi        # 3317 <array.0+0x177>
    16a6:	e8 55 fc ff ff       	call   1300 <__isoc99_sscanf@plt>
    16ab:	83 f8 01             	cmp    $0x1,%eax
    16ae:	7e 1e                	jle    16ce <phase_3+0x4f>
    16b0:	83 3c 24 07          	cmpl   $0x7,(%rsp)
    16b4:	0f 87 9a 00 00 00    	ja     1754 <phase_3+0xd5>
    16ba:	8b 04 24             	mov    (%rsp),%eax
    16bd:	48 8d 15 bc 1a 00 00 	lea    0x1abc(%rip),%rdx        # 3180 <_IO_stdin_used+0x180>
    16c4:	48 63 04 82          	movslq (%rdx,%rax,4),%rax
    16c8:	48 01 d0             	add    %rdx,%rax
    16cb:	3e ff e0             	notrack jmp *%rax
    16ce:	e8 62 05 00 00       	call   1c35 <explode_bomb>
    16d3:	eb db                	jmp    16b0 <phase_3+0x31>
    16d5:	b8 cf 02 00 00       	mov    $0x2cf,%eax
    16da:	2d 26 01 00 00       	sub    $0x126,%eax
    16df:	05 d8 02 00 00       	add    $0x2d8,%eax
    16e4:	2d 66 01 00 00       	sub    $0x166,%eax
    16e9:	05 66 01 00 00       	add    $0x166,%eax
    16ee:	2d 66 01 00 00       	sub    $0x166,%eax
    16f3:	05 66 01 00 00       	add    $0x166,%eax
    16f8:	2d 66 01 00 00       	sub    $0x166,%eax
    16fd:	83 3c 24 05          	cmpl   $0x5,(%rsp)
    1701:	7f 06                	jg     1709 <phase_3+0x8a>
    1703:	39 44 24 04          	cmp    %eax,0x4(%rsp)
    1707:	74 05                	je     170e <phase_3+0x8f>
    1709:	e8 27 05 00 00       	call   1c35 <explode_bomb>
    170e:	48 8b 44 24 08       	mov    0x8(%rsp),%rax
    1713:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax
    171a:	00 00 
    171c:	75 42                	jne    1760 <phase_3+0xe1>
    171e:	48 83 c4 18          	add    $0x18,%rsp
    1722:	c3                   	ret    
    1723:	b8 00 00 00 00       	mov    $0x0,%eax
    1728:	eb b0                	jmp    16da <phase_3+0x5b>
    172a:	b8 00 00 00 00       	mov    $0x0,%eax
    172f:	eb ae                	jmp    16df <phase_3+0x60>
    1731:	b8 00 00 00 00       	mov    $0x0,%eax
    1736:	eb ac                	jmp    16e4 <phase_3+0x65>
    1738:	b8 00 00 00 00       	mov    $0x0,%eax
    173d:	eb aa                	jmp    16e9 <phase_3+0x6a>
    173f:	b8 00 00 00 00       	mov    $0x0,%eax
    1744:	eb a8                	jmp    16ee <phase_3+0x6f>
    1746:	b8 00 00 00 00       	mov    $0x0,%eax
    174b:	eb a6                	jmp    16f3 <phase_3+0x74>
    174d:	b8 00 00 00 00       	mov    $0x0,%eax
    1752:	eb a4                	jmp    16f8 <phase_3+0x79>
    1754:	e8 dc 04 00 00       	call   1c35 <explode_bomb>
    1759:	b8 00 00 00 00       	mov    $0x0,%eax
    175e:	eb 9d                	jmp    16fd <phase_3+0x7e>
    1760:	e8 eb fa ff ff       	call   1250 <__stack_chk_fail@plt>

看看要读取什么。

1
2
3
4
5
6
b *0x5555555556a6
r
<ans1>
<ans2>
<input>
x/s $rsi

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230420202949947.png

需要读取两个整数。对这两个整数有什么要求呢?

16fd: cmpl $0x5,(%rsp)可得第一个数不能大于5,由1703: cmp %eax,0x4(%rsp)可得第二个数必须等于 %eax 的值。假设输入的第一个数是 4。使用 gdb 调试,查看 %eax 的值。

1
2
3
4
5
6
b *0x555555555707
r
<ans1>
<ans2>
<input>
p $eax

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230420211347580.png

答案如下:

1
4 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
00000000000017a0 <phase_4>:
    17a0:	f3 0f 1e fa          	endbr64 
    17a4:	48 83 ec 18          	sub    $0x18,%rsp
    17a8:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
    17af:	00 00 
    17b1:	48 89 44 24 08       	mov    %rax,0x8(%rsp)
    17b6:	31 c0                	xor    %eax,%eax
    17b8:	48 89 e1             	mov    %rsp,%rcx
    17bb:	48 8d 54 24 04       	lea    0x4(%rsp),%rdx
    17c0:	48 8d 35 50 1b 00 00 	lea    0x1b50(%rip),%rsi        # 3317 <array.0+0x177>
    17c7:	e8 34 fb ff ff       	call   1300 <__isoc99_sscanf@plt>
    17cc:	83 f8 02             	cmp    $0x2,%eax
    17cf:	75 0b                	jne    17dc <phase_4+0x3c>
    17d1:	8b 04 24             	mov    (%rsp),%eax
    17d4:	83 e8 02             	sub    $0x2,%eax
    17d7:	83 f8 02             	cmp    $0x2,%eax
    17da:	76 05                	jbe    17e1 <phase_4+0x41>
    17dc:	e8 54 04 00 00       	call   1c35 <explode_bomb>
    17e1:	8b 34 24             	mov    (%rsp),%esi
    17e4:	bf 07 00 00 00       	mov    $0x7,%edi
    17e9:	e8 77 ff ff ff       	call   1765 <func4>
    17ee:	39 44 24 04          	cmp    %eax,0x4(%rsp)
    17f2:	75 15                	jne    1809 <phase_4+0x69>
    17f4:	48 8b 44 24 08       	mov    0x8(%rsp),%rax
    17f9:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax
    1800:	00 00 
    1802:	75 0c                	jne    1810 <phase_4+0x70>
    1804:	48 83 c4 18          	add    $0x18,%rsp
    1808:	c3                   	ret    
    1809:	e8 27 04 00 00       	call   1c35 <explode_bomb>
    180e:	eb e4                	jmp    17f4 <phase_4+0x54>
    1810:	e8 3b fa ff ff       	call   1250 <__stack_chk_fail@plt>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0000000000001765 <func4>:
    1765:	f3 0f 1e fa          	endbr64 
    1769:	b8 00 00 00 00       	mov    $0x0,%eax
    176e:	85 ff                	test   %edi,%edi
    1770:	7e 2d                	jle    179f <func4+0x3a>
    1772:	41 54                	push   %r12
    1774:	55                   	push   %rbp
    1775:	53                   	push   %rbx
    1776:	89 fb                	mov    %edi,%ebx
    1778:	89 f5                	mov    %esi,%ebp
    177a:	89 f0                	mov    %esi,%eax
    177c:	83 ff 01             	cmp    $0x1,%edi
    177f:	74 19                	je     179a <func4+0x35>
    1781:	8d 7f ff             	lea    -0x1(%rdi),%edi
    1784:	e8 dc ff ff ff       	call   1765 <func4>
    1789:	44 8d 24 28          	lea    (%rax,%rbp,1),%r12d
    178d:	8d 7b fe             	lea    -0x2(%rbx),%edi
    1790:	89 ee                	mov    %ebp,%esi
    1792:	e8 ce ff ff ff       	call   1765 <func4>
    1797:	44 01 e0             	add    %r12d,%eax
    179a:	5b                   	pop    %rbx
    179b:	5d                   	pop    %rbp
    179c:	41 5c                	pop    %r12
    179e:	c3                   	ret    
    179f:	c3                   	ret   
1
2
3
4
5
6
7
b *0x5555555557c7
r
<ans1>
<ans2>
<ans3>
<input>
x/s $rsi

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230420212456205.png

仍然需要读取两个整数。

由汇编代码得,其中一个无符号整数 v 需满足 v - 2 <= 2。即 v ∈ [2, 4]。

1
2
3
4
5
    17d1:	8b 04 24             	mov    (%rsp),%eax
    17d4:	83 e8 02             	sub    $0x2,%eax
    17d7:	83 f8 02             	cmp    $0x2,%eax
    17da:	76 05                	jbe    17e1 <phase_4+0x41>
    17dc:	e8 54 04 00 00       	call   1c35 <explode_bomb>

可以看到,在调用func4()之前,传了两个参数,其中一个来自我们的输入,一个固定为 7。fun4()执行完成后,又用另一个输入值和返回值比较。

1
2
3
4
5
    17e1:	8b 34 24             	mov    (%rsp),%esi
    17e4:	bf 07 00 00 00       	mov    $0x7,%edi
    17e9:	e8 77 ff ff ff       	call   1765 <func4>
    17ee:	39 44 24 04          	cmp    %eax,0x4(%rsp)
    17f2:	75 15                	jne    1809 <phase_4+0x69>

随便输不违规的两个数,如3 2,再用 gdb 调试。

1
2
3
4
5
6
7
8
9
b *0x5555555557f2
r
<ans1>
<ans2>
<ans3>
3 2
x $rsp
x $rsp+4
p $eax

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230421201048311.png

于是我们可以知道第二个输入值作为func4()的一个参数,第一个输入值和返回值比较。在这里,顺便得出一个答案66 2

用伪代码描述一下func4(),加深对执行过程的理解。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int func4(int v1,int v2)
{
  int res=0;
  int v3;
  if (v1>0)
  {
    res=v2;
    if (v1!=1)
    {
      v3=func4(v1-1,v2)+v2;
      return v3+func4(v1-2,v2);
    }
  }
  return res;
}

答案如下:

1
66 2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
0000000000001815 <phase_5>:
    1815:	f3 0f 1e fa          	endbr64 
    1819:	48 83 ec 18          	sub    $0x18,%rsp
    181d:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
    1824:	00 00 
    1826:	48 89 44 24 08       	mov    %rax,0x8(%rsp)
    182b:	31 c0                	xor    %eax,%eax
    182d:	48 8d 4c 24 04       	lea    0x4(%rsp),%rcx
    1832:	48 89 e2             	mov    %rsp,%rdx
    1835:	48 8d 35 db 1a 00 00 	lea    0x1adb(%rip),%rsi        # 3317 <array.0+0x177>
    183c:	e8 bf fa ff ff       	call   1300 <__isoc99_sscanf@plt>
    1841:	83 f8 01             	cmp    $0x1,%eax
    1844:	7e 5a                	jle    18a0 <phase_5+0x8b>
    1846:	8b 04 24             	mov    (%rsp),%eax
    1849:	83 e0 0f             	and    $0xf,%eax
    184c:	89 04 24             	mov    %eax,(%rsp)
    184f:	83 f8 0f             	cmp    $0xf,%eax
    1852:	74 32                	je     1886 <phase_5+0x71>
    1854:	b9 00 00 00 00       	mov    $0x0,%ecx
    1859:	ba 00 00 00 00       	mov    $0x0,%edx
    185e:	48 8d 35 3b 19 00 00 	lea    0x193b(%rip),%rsi        # 31a0 <array.0>
    1865:	83 c2 01             	add    $0x1,%edx
    1868:	48 98                	cltq   
    186a:	8b 04 86             	mov    (%rsi,%rax,4),%eax
    186d:	01 c1                	add    %eax,%ecx
    186f:	83 f8 0f             	cmp    $0xf,%eax
    1872:	75 f1                	jne    1865 <phase_5+0x50>
    1874:	c7 04 24 0f 00 00 00 	movl   $0xf,(%rsp)
    187b:	83 fa 0f             	cmp    $0xf,%edx
    187e:	75 06                	jne    1886 <phase_5+0x71>
    1880:	39 4c 24 04          	cmp    %ecx,0x4(%rsp)
    1884:	74 05                	je     188b <phase_5+0x76>
    1886:	e8 aa 03 00 00       	call   1c35 <explode_bomb>
    188b:	48 8b 44 24 08       	mov    0x8(%rsp),%rax
    1890:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax
    1897:	00 00 
    1899:	75 0c                	jne    18a7 <phase_5+0x92>
    189b:	48 83 c4 18          	add    $0x18,%rsp
    189f:	c3                   	ret    
    18a0:	e8 90 03 00 00       	call   1c35 <explode_bomb>
    18a5:	eb 9f                	jmp    1846 <phase_5+0x31>
    18a7:	e8 a4 f9 ff ff       	call   1250 <__stack_chk_fail@plt>
1
2
3
4
5
6
7
8
b *0x55555555583c
r
<ans1>
<ans2>
<ans3>
<ans4>
<input>
x/s $rsi

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230420223722265.png

需要读取两个整数。

再看各个寄存器的作用:

  • %eax 用于存储数组元素的值,并作为下一次循环中数组的索引,还作为循环终止条件。
1
2
3
4
    186a:	8b 04 86             	mov    (%rsi,%rax,4),%eax
    186d:	01 c1                	add    %eax,%ecx
    186f:	83 f8 0f             	cmp    $0xf,%eax
    1872:	75 f1                	jne    1865 <phase_5+0x50>
  • %edx 用于存储循环次数。
  • %ecx 用于存储累加值。

因此我们得到输入值的所有限制:

  • 第一个输入值与 0xF 按位与后不能等于 15,这里我们还可以知道,第一个输入值将作为数组的第一次索引。
1
2
3
4
5
    1846:	8b 04 24             	mov    (%rsp),%eax
    1849:	83 e0 0f             	and    $0xf,%eax
    184c:	89 04 24             	mov    %eax,(%rsp)
    184f:	83 f8 0f             	cmp    $0xf,%eax
    1852:	74 32                	je     1886 <phase_5+0x71>
  • 累加循环次数必须为 15。
1
2
    187b:	83 fa 0f             	cmp    $0xf,%edx
    187e:	75 06                	jne    1886 <phase_5+0x71>
  • 第二个输入值必须等于累加值。
1
2
3
    1880:	39 4c 24 04          	cmp    %ecx,0x4(%rsp)
    1884:	74 05                	je     188b <phase_5+0x76>
    1886:	e8 aa 03 00 00       	call   1c35 <explode_bomb>

使用 gdb 打印数组元素的值:

1
2
3
4
5
6
7
8
b *0x555555555865
r
<ans1>
<ans2>
<ans3>
<ans4>
<input>
x/16wd $rsi

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230420231632269.png

要循环 15 次,且 %eax 在最后一次循环的值为 15,我们可以反推一下得到数组的首个索引:

15 -> 6 ->14 ->2 -> 1 ->10 ->0 -> 8 ->4 -> 9 ->13 ->11 -> 7 -> 3 -> 12 -> 5

因此,数组的首个索引为 5,除去 array[15] = 5 之外的所有值求和即为累加值 115。

答案如下:

1
5 115
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
00000000000018ac <phase_6>:
    18ac:	f3 0f 1e fa          	endbr64 
    18b0:	41 56                	push   %r14
    18b2:	41 55                	push   %r13
    18b4:	41 54                	push   %r12
    18b6:	55                   	push   %rbp
    18b7:	53                   	push   %rbx
    18b8:	48 83 ec 60          	sub    $0x60,%rsp
    18bc:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
    18c3:	00 00 
    18c5:	48 89 44 24 58       	mov    %rax,0x58(%rsp)
    18ca:	31 c0                	xor    %eax,%eax
    18cc:	49 89 e5             	mov    %rsp,%r13
    18cf:	4c 89 ee             	mov    %r13,%rsi
    18d2:	e8 8a 03 00 00       	call   1c61 <read_six_numbers>
    18d7:	41 be 01 00 00 00    	mov    $0x1,%r14d
    18dd:	49 89 e4             	mov    %rsp,%r12
    18e0:	eb 28                	jmp    190a <phase_6+0x5e>
    18e2:	e8 4e 03 00 00       	call   1c35 <explode_bomb>
    18e7:	eb 30                	jmp    1919 <phase_6+0x6d>
    18e9:	48 83 c3 01          	add    $0x1,%rbx
    18ed:	83 fb 05             	cmp    $0x5,%ebx
    18f0:	7f 10                	jg     1902 <phase_6+0x56>
    18f2:	41 8b 04 9c          	mov    (%r12,%rbx,4),%eax
    18f6:	39 45 00             	cmp    %eax,0x0(%rbp)
    18f9:	75 ee                	jne    18e9 <phase_6+0x3d>
    18fb:	e8 35 03 00 00       	call   1c35 <explode_bomb>
    1900:	eb e7                	jmp    18e9 <phase_6+0x3d>
    1902:	49 83 c6 01          	add    $0x1,%r14
    1906:	49 83 c5 04          	add    $0x4,%r13
    190a:	4c 89 ed             	mov    %r13,%rbp
    190d:	41 8b 45 00          	mov    0x0(%r13),%eax
    1911:	83 e8 01             	sub    $0x1,%eax
    1914:	83 f8 05             	cmp    $0x5,%eax
    1917:	77 c9                	ja     18e2 <phase_6+0x36>
    1919:	41 83 fe 05          	cmp    $0x5,%r14d
    191d:	7f 05                	jg     1924 <phase_6+0x78>
    191f:	4c 89 f3             	mov    %r14,%rbx
    1922:	eb ce                	jmp    18f2 <phase_6+0x46>
    1924:	be 00 00 00 00       	mov    $0x0,%esi
    1929:	8b 0c b4             	mov    (%rsp,%rsi,4),%ecx
    192c:	b8 01 00 00 00       	mov    $0x1,%eax
    1931:	48 8d 15 d8 38 00 00 	lea    0x38d8(%rip),%rdx        # 5210 <node1>
    1938:	83 f9 01             	cmp    $0x1,%ecx
    193b:	7e 0b                	jle    1948 <phase_6+0x9c>
    193d:	48 8b 52 08          	mov    0x8(%rdx),%rdx
    1941:	83 c0 01             	add    $0x1,%eax
    1944:	39 c8                	cmp    %ecx,%eax
    1946:	75 f5                	jne    193d <phase_6+0x91>
    1948:	48 89 54 f4 20       	mov    %rdx,0x20(%rsp,%rsi,8)
    194d:	48 83 c6 01          	add    $0x1,%rsi
    1951:	48 83 fe 06          	cmp    $0x6,%rsi
    1955:	75 d2                	jne    1929 <phase_6+0x7d>
    1957:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx
    195c:	48 8b 44 24 28       	mov    0x28(%rsp),%rax
    1961:	48 89 43 08          	mov    %rax,0x8(%rbx)
    1965:	48 8b 54 24 30       	mov    0x30(%rsp),%rdx
    196a:	48 89 50 08          	mov    %rdx,0x8(%rax)
    196e:	48 8b 44 24 38       	mov    0x38(%rsp),%rax
    1973:	48 89 42 08          	mov    %rax,0x8(%rdx)
    1977:	48 8b 54 24 40       	mov    0x40(%rsp),%rdx
    197c:	48 89 50 08          	mov    %rdx,0x8(%rax)
    1980:	48 8b 44 24 48       	mov    0x48(%rsp),%rax
    1985:	48 89 42 08          	mov    %rax,0x8(%rdx)
    1989:	48 c7 40 08 00 00 00 	movq   $0x0,0x8(%rax)
    1990:	00 
    1991:	bd 05 00 00 00       	mov    $0x5,%ebp
    1996:	eb 09                	jmp    19a1 <phase_6+0xf5>
    1998:	48 8b 5b 08          	mov    0x8(%rbx),%rbx
    199c:	83 ed 01             	sub    $0x1,%ebp
    199f:	74 11                	je     19b2 <phase_6+0x106>
    19a1:	48 8b 43 08          	mov    0x8(%rbx),%rax
    19a5:	8b 00                	mov    (%rax),%eax
    19a7:	39 03                	cmp    %eax,(%rbx)
    19a9:	7d ed                	jge    1998 <phase_6+0xec>
    19ab:	e8 85 02 00 00       	call   1c35 <explode_bomb>
    19b0:	eb e6                	jmp    1998 <phase_6+0xec>
    19b2:	48 8b 44 24 58       	mov    0x58(%rsp),%rax
    19b7:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax
    19be:	00 00 
    19c0:	75 0d                	jne    19cf <phase_6+0x123>
    19c2:	48 83 c4 60          	add    $0x60,%rsp
    19c6:	5b                   	pop    %rbx
    19c7:	5d                   	pop    %rbp
    19c8:	41 5c                	pop    %r12
    19ca:	41 5d                	pop    %r13
    19cc:	41 5e                	pop    %r14
    19ce:	c3                   	ret    
    19cf:	e8 7c f8 ff ff       	call   1250 <__stack_chk_fail@plt>

这道题本质上是输入 6 个 1~6 之间各不相同的数,据此对 node1~node6 排序,需要按节点值降序排序才能拆除炸弹。

使用 gdb 打印 node1~node6 的值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
b *0x555555555938
r
<ans1>
<ans2>
<ans3>
<ans4>
<ans5>
<input>
x/24wx $rdx
x/wx 0x555555559110

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230421000735539.png

按照节点值大小排序,可得答案为:

1
6 5 2 1 4 3

bomb.c末尾看到这句话:

1
2
/* Wow, they got it!  But isn't something... missing?  Perhaps
 * something they overlooked?  Mua ha ha ha ha! */

说明有隐藏炸弹,我们回到bomb.s看到phase_defused()藏了一个secret_phase()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
0000000000001dde <phase_defused>:
    1dde:       f3 0f 1e fa             endbr64
    1de2:       48 83 ec 78             sub    $0x78,%rsp
    1de6:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
    1ded:       00 00
    1def:       48 89 44 24 68          mov    %rax,0x68(%rsp)
    1df4:       31 c0                   xor    %eax,%eax
    1df6:       83 3d f3 38 00 00 06    cmpl   $0x6,0x38f3(%rip)        # 56f0 <num_input_strings>
    1dfd:       74 15                   je     1e14 <phase_defused+0x36>
    1dff:       48 8b 44 24 68          mov    0x68(%rsp),%rax
    1e04:       64 48 2b 04 25 28 00    sub    %fs:0x28,%rax
    1e0b:       00 00
    1e0d:       75 73                   jne    1e82 <phase_defused+0xa4>
    1e0f:       48 83 c4 78             add    $0x78,%rsp
    1e13:       c3                      ret
    1e14:       48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
    1e19:       48 8d 54 24 08          lea    0x8(%rsp),%rdx
    1e1e:       4c 8d 44 24 10          lea    0x10(%rsp),%r8
    1e23:       48 8d 35 37 15 00 00    lea    0x1537(%rip),%rsi        # 3361 <array.0+0x1c1>
    1e2a:       48 8d 3d bf 39 00 00    lea    0x39bf(%rip),%rdi        # 57f0 <input_strings+0xf0>
    1e31:       e8 ca f4 ff ff          call   1300 <__isoc99_sscanf@plt>
    1e36:       83 f8 03                cmp    $0x3,%eax
    1e39:       74 0e                   je     1e49 <phase_defused+0x6b>
    1e3b:       48 8d 3d 5e 14 00 00    lea    0x145e(%rip),%rdi        # 32a0 <array.0+0x100>
    1e42:       e8 d9 f3 ff ff          call   1220 <puts@plt>
    1e47:       eb b6                   jmp    1dff <phase_defused+0x21>
    1e49:       48 8d 7c 24 10          lea    0x10(%rsp),%rdi
    1e4e:       48 8d 35 15 15 00 00    lea    0x1515(%rip),%rsi        # 336a <array.0+0x1ca>
    1e55:       e8 c7 fc ff ff          call   1b21 <strings_not_equal>
    1e5a:       85 c0                   test   %eax,%eax
    1e5c:       75 dd                   jne    1e3b <phase_defused+0x5d>
    1e5e:       48 8d 3d db 13 00 00    lea    0x13db(%rip),%rdi        # 3240 <array.0+0xa0>
    1e65:       e8 b6 f3 ff ff          call   1220 <puts@plt>
    1e6a:       48 8d 3d f7 13 00 00    lea    0x13f7(%rip),%rdi        # 3268 <array.0+0xc8>
    1e71:       e8 aa f3 ff ff          call   1220 <puts@plt>
    1e76:       b8 00 00 00 00          mov    $0x0,%eax
    1e7b:       e8 95 fb ff ff          call   1a15 <secret_phase>
    1e80:       eb b9                   jmp    1e3b <phase_defused+0x5d>
    1e82:       e8 c9 f3 ff ff          call   1250 <__stack_chk_fail@plt>

怎么触发secret_phase()调用?

先看看在phase_defused()__isoc99_sscanf()读取了什么。

1
2
3
    1e23:       48 8d 35 37 15 00 00    lea    0x1537(%rip),%rsi        # 3361 <array.0+0x1c1>
	1e2a:       48 8d 3d bf 39 00 00    lea    0x39bf(%rip),%rdi        # 57f0 <input_strings+0xf0>
    1e31:       e8 ca f4 ff ff          call   1300 <__isoc99_sscanf@plt>

使用 gdb 调试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
b *0x555555555e31
r
<ans1>
<ans2>
<ans3>
<ans4>
<ans5>
<ans6>
x/s $rsi
x/s $rdi

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230421125653816.png

由下面汇编代码可得只有当__isoc99_sscanf()的返回值为 3 时才有机会调用secret_phase()

1
2
    1e36:       83 f8 03                cmp    $0x3,%eax
    1e39:       74 0e                   je     1e49 <phase_defused+0x6b>

继续往下打印 %eax 的值:

1
2
ni
p $eax

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230421130500345.png

%eax 的值为 2,说明__isoc99_sscanf()读取的是 %rdi 中的存储的地址存储的字符串66 2,这实际上是我们在 phase_4 的答案。也就是说,我们要进入secret_phase(),需要在 phase_4 的答案后面追加一个目标字符串。

这里进行了两次地址传送,并调用strings_not_equal(),可以推断一个是我们追加的字符串的地址,一个是目标字符串的地址。

1
2
3
    1e49:       48 8d 7c 24 10          lea    0x10(%rsp),%rdi
    1e4e:       48 8d 35 15 15 00 00    lea    0x1515(%rip),%rsi        # 336a <array.0+0x1ca>
    1e55:       e8 c7 fc ff ff          call   1b21 <strings_not_equal>

在 phase_4 的答案后面追加字符串,重新使用 gdb 调试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
b *0x555555555e55
r
<ans1>
<ans2>
<ans3>
<ans4> <append>
<ans5>
<ans6>
x/s $rsi
x/s $rdi

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230421131919759.png

我们得到了目标字符串为DrEvil

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
0000000000001a15 <secret_phase>:
    1a15:       f3 0f 1e fa             endbr64
    1a19:       53                      push   %rbx
    1a1a:       e8 87 02 00 00          call   1ca6 <read_line>
    1a1f:       48 89 c7                mov    %rax,%rdi
    1a22:       ba 0a 00 00 00          mov    $0xa,%edx
    1a27:       be 00 00 00 00          mov    $0x0,%esi
    1a2c:       e8 af f8 ff ff          call   12e0 <strtol@plt>
    1a31:       89 c3                   mov    %eax,%ebx
    1a33:       83 e8 01                sub    $0x1,%eax
    1a36:       3d e8 03 00 00          cmp    $0x3e8,%eax
    1a3b:       77 26                   ja     1a63 <secret_phase+0x4e>
    1a3d:       89 de                   mov    %ebx,%esi
    1a3f:       48 8d 3d ea 36 00 00    lea    0x36ea(%rip),%rdi        # 5130 <n1>
    1a46:       e8 89 ff ff ff          call   19d4 <fun7>
    1a4b:       83 f8 03                cmp    $0x3,%eax
    1a4e:       75 1a                   jne    1a6a <secret_phase+0x55>
    1a50:       48 8d 3d 89 17 00 00    lea    0x1789(%rip),%rdi        # 31e0 <array.0+0x40>
    1a57:       e8 c4 f7 ff ff          call   1220 <puts@plt>
    1a5c:       e8 7d 03 00 00          call   1dde <phase_defused>
    1a61:       5b                      pop    %rbx
    1a62:       c3                      ret
    1a63:       e8 cd 01 00 00          call   1c35 <explode_bomb>
    1a68:       eb d3                   jmp    1a3d <secret_phase+0x28>
    1a6a:       e8 c6 01 00 00          call   1c35 <explode_bomb>
    1a6f:       eb df                   jmp    1a50 <secret_phase+0x3b>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
00000000000019d4 <fun7>:
    19d4:       f3 0f 1e fa             endbr64
    19d8:       48 85 ff                test   %rdi,%rdi
    19db:       74 32                   je     1a0f <fun7+0x3b>
    19dd:       48 83 ec 08             sub    $0x8,%rsp
    19e1:       8b 17                   mov    (%rdi),%edx
    19e3:       39 f2                   cmp    %esi,%edx
    19e5:       7f 0c                   jg     19f3 <fun7+0x1f>
    19e7:       b8 00 00 00 00          mov    $0x0,%eax
    19ec:       75 12                   jne    1a00 <fun7+0x2c>
    19ee:       48 83 c4 08             add    $0x8,%rsp
    19f2:       c3                      ret
    19f3:       48 8b 7f 08             mov    0x8(%rdi),%rdi
    19f7:       e8 d8 ff ff ff          call   19d4 <fun7>
    19fc:       01 c0                   add    %eax,%eax
    19fe:       eb ee                   jmp    19ee <fun7+0x1a>
    1a00:       48 8b 7f 10             mov    0x10(%rdi),%rdi
    1a04:       e8 cb ff ff ff          call   19d4 <fun7>
    1a09:       8d 44 00 01             lea    0x1(%rax,%rax,1),%eax
    1a0d:       eb df                   jmp    19ee <fun7+0x1a>
    1a0f:       b8 ff ff ff ff          mov    $0xffffffff,%eax
    1a14:       c3                      ret

strtol()函数用于将一个字符串转换为长整型数值,函数的原型如下:

1
long int strtol(const char *str, char **endptr, int base);

函数接受三个参数:

  • str:要转换的字符串。
  • endptr:可选的输出参数,用于返回转换后第一个无效字符的指针。
  • base:进制数,可以是 2、8、10 或 16,或者是 0。当base为 0 时,函数会根据str的前缀来确定进制数:如果str以 “0x” 或 “0X” 开头,则按十六进制解释;如果str以 “0” 开头,则按八进制解释;否则按十进制解释。

函数返回被转换后的长整型数值。如果转换失败,则返回 0。

%rdi,%esi,%edx 分别存储了strtol()函数将接收的三个参数。

1
2
3
    1a1f:       48 89 c7                mov    %rax,%rdi
    1a22:       ba 0a 00 00 00          mov    $0xa,%edx
    1a27:       be 00 00 00 00          mov    $0x0,%esi

由下面汇编代码可得strtol()的返回值要在 [0x1, 0x3E9] 之间,否则爆炸。

1
2
3
4
    1a31:       89 c3                   mov    %eax,%ebx
    1a33:       83 e8 01                sub    $0x1,%eax
    1a36:       3d e8 03 00 00          cmp    $0x3e8,%eax
    1a3b:       77 26                   ja     1a63 <secret_phase+0x4e>

我们还需要让fun7()的返回值为 3。

1
2
3
4
5
    1a3d:       89 de                   mov    %ebx,%esi
    1a3f:       48 8d 3d ea 36 00 00    lea    0x36ea(%rip),%rdi        # 5130 <n1>
    1a46:       e8 89 ff ff ff          call   19d4 <fun7>
    1a4b:       83 f8 03                cmp    $0x3,%eax
    1a4e:       75 1a                   jne    1a6a <secret_phase+0x55>

用伪代码描述fun7()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int fun7(int rdi,int esi){
    if(rdi==0){
        eax=0xffffffff;
    }else{
        edx=M[rdi];
        if(edx>esi){
            rdi=M[rdi+0x8];
            eax=fun7(rdi,esi);
            eax=eax+eax;
        }else if(edx!=esi){
            rdi=M[rdi+0x10];
            eax=fun7(rdi,esi);
            eax=2*eax+1
        }else{
            eax=0;
        }
    }
    return eax;
}

根据伪代码edx=M[rdi]rdi=M[rdi+0x8]rdi=M[rdi+0x10]我们可以抽象出一个二叉树结构:

1
2
3
4
5
struct Node{
    int val;
    Node* left;
    Node* right;
}

在调用第一层fun7()前,二叉树根节点的地址已经先存在了 %rdi 中,据此我们可以打印出所有节点的信息。

1
2
3
4
b *0x555555555a46
r
x/3gx $rdi
[...]

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230421192639669.png

怎样才能使fun7()的返回值为 3?

由于 3 是奇数,我们只能希望从 %edx != %esi (在fun7()里等价于 %edx < %esi )的分支得到返回值。

第一层fun7()的返回值为 3 -> 第二层fun7()的返回值为 1 -> 第三层fun7()的返回值为 0。

使第三层fun7()的返回值为 0 又有两种情况:

  • 走 %edx == %esi 分支。
  • 走 %edx > %esi 分支,且第四层fun7()的返回值为 0。

注意在调用第一层fun7()前,strtol()的返回值先存在了 %esi 中,也就是说,我们输入的字符串转换成的长整型结果将决定我们在调用func7()时走哪些分支。

现在我们不难得到两种可行的结果,即 n34 的值或 n47 的值:

1
107

1
99

最终,ans.txt中的内容如下:

1
2
3
4
5
6
7
Crikey! I have lost my mojo!
0 1 1 2 3 5
4 0
66 2 DrEvil
5 115
6 5 2 1 4 3
107

通关截图:

https://f005.backblazeb2.com/file/img-buckets-oqh/2023/04/image-20230421193930795.png