HNU计算机系统实验之bombLab 2022级实战指南

实验题目

“二进制炸弹”是一个程序,以目标代码文件的形式提供给学生。运行时,它会提示用户键入 6 个不同的字符串。如果其中任何一个错误,炸弹就会“爆炸”,打印错误消息并将事件记录在打分服务器上。学生们必须通过反汇编和逆向程序,“拆除”他们自己独特的炸弹,以确定这 6 个字符串应该是什么。该实验教学生理解汇编语言,并迫使他们学习如何使用调试器(debugger)。

实验目的

通过 gdb 以及 objdump 等汇编工具,查看汇编代码,从而破解这个程序的每个 phase 的密码。

实验环境

  • Virtual Box 虚拟机架设的 Ubuntu 32 位操作系统
  • 使用工具就是 GDB 以及 Objdump
  • 实验内容及操作步骤

    input = read_line();             /* Get input                   */
        phase_1(input);                  /* Run the phase               */
        phase_defused();                 /* Drat!  They figured it out!
    				      * Let me know how they did it. */
        printf("Phase 1 defused. How about the next one?\n");
    
        /* The second phase is harder.  No one will ever figure out
         * how to defuse this... */
        input = read_line();
        phase_2(input);
        phase_defused();
        printf("That's number 2.  Keep going!\n");
    
        /* I guess this is too easy so far.  Some more complex code will
         * confuse people. */
        input = read_line();
        phase_3(input);
        phase_defused();
        printf("Halfway there!\n");
    
        /* Oh yeah?  Well, how good is your math?  Try on this saucy problem! */
        input = read_line();
        phase_4(input);
        phase_defused();
        printf("So you got that one.  Try this one.\n");
        
        /* Round and 'round in memory we go, where we stop, the bomb blows! */
        input = read_line();
        phase_5(input);
        phase_defused();
        printf("Good work!  On to the next...\n");
    
        /* This phase will never be used, since no one will get past the
         * earlier ones.  But just in case, make this one extra hard. */
        input = read_line();
        phase_6(input);
        phase_defused();
    
    

    bomb.c中共有6个函数phase_1~phase_6,每个函数都会从输入中读取input,即要输入的字符串。phase_defused函数用于判断炸弹是否被拆除。猜测phase函数中可能会有像exit(0)的终止方式,当输入的字符串不正确时,就会提前终止phase函数,导致炸弹拆除失败。
    在后面你会看到,函数<explode_bomb>会调用exit,使程序提前终止。你要做的就是阅读汇编程序,然后推出一个**不会使控制到达<explode_bomb>**的字符串,这样的字符串就是答案。

    为了更好的观察代码,我们需要把程序反汇编的代码放到一个文件中观察,这样的方法,我们同样放在答案输入上,这样可以大大节约时间,在调试的时候不用重复输入答案。

    objdump -d bomb > bomb.asm
    

    可以将答案存放到一个txt文件中,然后使用命令./bomb xxx.txt(xxx为txt文件名),这样就可以保留前面phase的答案,不需要依次输入答案

    phase_1

  • 反汇编代码
  • 08048b50 <phase_1>:
     8048b50:	83 ec 1c             	sub    $0x1c,%esp
     8048b53:	c7 44 24 04 a4 a1 04 	movl   $0x804a1a4,0x4(%esp)
     8048b5a:	08 
     8048b5b:	8b 44 24 20          	mov    0x20(%esp),%eax
     8048b5f:	89 04 24             	mov    %eax,(%esp)
     8048b62:	e8 5d 04 00 00       	call   8048fc4 <strings_not_equal>
     8048b67:	85 c0                	test   %eax,%eax
     8048b69:	74 05                	je     8048b70 <phase_1+0x20>
     8048b6b:	e8 66 05 00 00       	call   80490d6 <explode_bomb>
     8048b70:	83 c4 1c             	add    $0x1c,%esp
     8048b73:	c3                   	ret    
    
  • 解答
  • 8048b6b:	e8 66 05 00 00       	call   80490d6 <explode_bomb>
    

    这一步中调用的explode_bomb为引爆炸弹函数,我们的目标就是阻止炸弹被引爆。所以我们要求上面的跳转指令执行成功:

     8048b67:	85 c0                	test   %eax,%eax
     8048b69:	74 05                	je     8048b70 <phase_1+0x20>
    

    je跳转指令代表相等就跳转,且根据上一行设置ZF位,说明跳转条件为%eax=0。于是继续往上找到使%eax=0的条件:

     8048b53:	c7 44 24 04 a4 a1 04 	movl   $0x804a1a4,0x4(%esp)
     8048b5a:	08 
     8048b5b:	8b 44 24 20          	mov    0x20(%esp),%eax
     8048b5f:	89 04 24             	mov    %eax,(%esp)
     8048b62:	e8 5d 04 00 00       	call   8048fc4 <strings_not_equal>
    

    可以看到,程序首先准备了两个参数,接着调用了<strings_not_equal>函数:

    08048fc4 <strings_not_equal>:
     8048fc4:	83 ec 10             	sub    $0x10,%esp
     8048fc7:	89 5c 24 04          	mov    %ebx,0x4(%esp)
     8048fcb:	89 74 24 08          	mov    %esi,0x8(%esp)
     8048fcf:	89 7c 24 0c          	mov    %edi,0xc(%esp)
     8048fd3:	8b 5c 24 14          	mov    0x14(%esp),%ebx
     8048fd7:	8b 74 24 18          	mov    0x18(%esp),%esi
     8048fdb:	89 1c 24             	mov    %ebx,(%esp)
     8048fde:	e8 c8 ff ff ff       	call   8048fab <string_length>
     8048fe3:	89 c7                	mov    %eax,%edi
     8048fe5:	89 34 24             	mov    %esi,(%esp)
     8048fe8:	e8 be ff ff ff       	call   8048fab <string_length>
     8048fed:	ba 01 00 00 00       	mov    $0x1,%edx
     8048ff2:	39 c7                	cmp    %eax,%edi
     8048ff4:	75 33                	jne    8049029 <strings_not_equal+0x65>
     8048ff6:	0f b6 03             	movzbl (%ebx),%eax
     8048ff9:	b2 00                	mov    $0x0,%dl
     8048ffb:	84 c0                	test   %al,%al
     8048ffd:	74 2a                	je     8049029 <strings_not_equal+0x65>
     8048fff:	b2 01                	mov    $0x1,%dl
     8049001:	3a 06                	cmp    (%esi),%al
     8049003:	75 24                	jne    8049029 <strings_not_equal+0x65>
     8049005:	b8 00 00 00 00       	mov    $0x0,%eax
     804900a:	eb 08                	jmp    8049014 <strings_not_equal+0x50>
     804900c:	83 c0 01             	add    $0x1,%eax
     804900f:	3a 14 06             	cmp    (%esi,%eax,1),%dl
     8049012:	75 10                	jne    8049024 <strings_not_equal+0x60>
     8049014:	0f b6 54 03 01       	movzbl 0x1(%ebx,%eax,1),%edx
     8049019:	84 d2                	test   %dl,%dl
     804901b:	75 ef                	jne    804900c <strings_not_equal+0x48>
     804901d:	ba 00 00 00 00       	mov    $0x0,%edx
     8049022:	eb 05                	jmp    8049029 <strings_not_equal+0x65>
     8049024:	ba 01 00 00 00       	mov    $0x1,%edx
     8049029:	89 d0                	mov    %edx,%eax
     804902b:	8b 5c 24 04          	mov    0x4(%esp),%ebx
     804902f:	8b 74 24 08          	mov    0x8(%esp),%esi
     8049033:	8b 7c 24 0c          	mov    0xc(%esp),%edi
     8049037:	83 c4 10             	add    $0x10,%esp
     804903a:	c3                   	ret    
    

    具体每一步的功能不进行赘述了,这个函数首先是检查两个字符串的长度,如果长度不等,两个字符串不同,直接会返回 1。所以两个字符串分别为我们输入的和存在首地址为0x804a1a4的字符串。

    使用GDB查看并验证:

    image-20241022103706801

    image-20241022102726653

    所以There are rumors on the internets.即为phase_1的答案。

  • 伪代码
  • void phase_1(string a,string b)
    {
        int answer=string_not_equal(a,b);
        if(answer) explode_bomb();
    }
    

    phase_2

  • 反汇编代码
  • 08048b74 <phase_2>:
     8048b74:	56                   	push   %esi
     8048b75:	53                   	push   %ebx
     8048b76:	83 ec 34             	sub    $0x34,%esp
     8048b79:	8d 44 24 18          	lea    0x18(%esp),%eax
     8048b7d:	89 44 24 04          	mov    %eax,0x4(%esp)
     8048b81:	8b 44 24 40          	mov    0x40(%esp),%eax
     8048b85:	89 04 24             	mov    %eax,(%esp)
     8048b88:	e8 7e 06 00 00       	call   804920b <read_six_numbers>
     8048b8d:	83 7c 24 18 00       	cmpl   $0x0,0x18(%esp)
     8048b92:	75 07                	jne    8048b9b <phase_2+0x27>
     8048b94:	83 7c 24 1c 01       	cmpl   $0x1,0x1c(%esp)
     8048b99:	74 05                	je     8048ba0 <phase_2+0x2c>
     8048b9b:	e8 36 05 00 00       	call   80490d6 <explode_bomb>
     8048ba0:	8d 5c 24 20          	lea    0x20(%esp),%ebx
     8048ba4:	8d 74 24 30          	lea    0x30(%esp),%esi
     8048ba8:	8b 43 f8             	mov    -0x8(%ebx),%eax
     8048bab:	03 43 fc             	add    -0x4(%ebx),%eax
     8048bae:	39 03                	cmp    %eax,(%ebx)
     8048bb0:	74 05                	je     8048bb7 <phase_2+0x43>
     8048bb2:	e8 1f 05 00 00       	call   80490d6 <explode_bomb>
     8048bb7:	83 c3 04             	add    $0x4,%ebx
     8048bba:	39 f3                	cmp    %esi,%ebx
     8048bbc:	75 ea                	jne    8048ba8 <phase_2+0x34>
     8048bbe:	83 c4 34             	add    $0x34,%esp
     8048bc1:	5b                   	pop    %ebx
     8048bc2:	5e                   	pop    %esi
     8048bc3:	c3                   	ret    
    
  • 解答
  • 开头常规的开辟函数空间后,调用了 <read_six_numbers>函数,传给它的参数就有我们输入的字符串。首先我们先看一下这个函数:

    0804920b <read_six_numbers>:
     804920b:	83 ec 2c             	sub    $0x2c,%esp
     804920e:	8b 44 24 34          	mov    0x34(%esp),%eax
     8049212:	8d 50 14             	lea    0x14(%eax),%edx
     8049215:	89 54 24 1c          	mov    %edx,0x1c(%esp)
     8049219:	8d 50 10             	lea    0x10(%eax),%edx
     804921c:	89 54 24 18          	mov    %edx,0x18(%esp)
     8049220:	8d 50 0c             	lea    0xc(%eax),%edx
     8049223:	89 54 24 14          	mov    %edx,0x14(%esp)
     8049227:	8d 50 08             	lea    0x8(%eax),%edx
     804922a:	89 54 24 10          	mov    %edx,0x10(%esp)
     804922e:	8d 50 04             	lea    0x4(%eax),%edx
     8049231:	89 54 24 0c          	mov    %edx,0xc(%esp)
     8049235:	89 44 24 08          	mov    %eax,0x8(%esp)
     8049239:	c7 44 24 04 b7 a3 04 	movl   $0x804a3b7,0x4(%esp)
     8049240:	08 
     8049241:	8b 44 24 30          	mov    0x30(%esp),%eax
     8049245:	89 04 24             	mov    %eax,(%esp)
     8049248:	e8 23 f6 ff ff       	call   8048870 <__isoc99_sscanf@plt>
     804924d:	83 f8 05             	cmp    $0x5,%eax
     8049250:	7f 05                	jg     8049257 <read_six_numbers+0x4c>
     8049252:	e8 7f fe ff ff       	call   80490d6 <explode_bomb>
     8049257:	83 c4 2c             	add    $0x2c,%esp
     804925a:	c3                   	ret    
    

    在第19行,它调用了sscanf进行扫描,所以之前的指令都是设置参数为其做准备,传入sscanf的参数的地址为15行的0x804a3b7,查看这个地址的内容:

    image-20240417213223167

    说明传入的参数为连续6个整形数,且每两个数之间有空格。并且之后的跳转指令:

     804924d:	83 f8 05             	cmp    $0x5,%eax
     8049250:	7f 05                	jg     8049257 <read_six_numbers+0x4c>
     8049252:	e8 7f fe ff ff       	call   80490d6 <explode_bomb>
    

    也代表满足要求的个数小于5就引爆炸弹!所以可以推断函数的作用是读取字符串中数字的个数,且大于5才能通过。我们再回过头来看原汇编:

     8048b74:	56                   	push   %esi
     8048b75:	53                   	push   %ebx
     8048b76:	83 ec 34             	sub    $0x34,%esp
     8048b79:	8d 44 24 18          	lea    0x18(%esp),%eax
     8048b7d:	89 44 24 04          	mov    %eax,0x4(%esp)
     8048b81:	8b 44 24 40          	mov    0x40(%esp),%eax
     8048b85:	89 04 24             	mov    %eax,(%esp)
     8048b88:	e8 7e 06 00 00       	call   804920b <read_six_numbers>
    

    %esp+18这个地址作为<read_six_numbers>的参数传入,说明这个地址就是输入字符串的首地址。紧接着:

     8048b8d:	83 7c 24 18 00       	cmpl   $0x0,0x18(%esp)
     8048b92:	75 07                	jne    8048b9b <phase_2+0x27>
     8048b94:	83 7c 24 1c 01       	cmpl   $0x1,0x1c(%esp)
     8048b99:	74 05                	je     8048ba0 <phase_2+0x2c>
     8048b9b:	e8 36 05 00 00       	call   80490d6 <explode_bomb>
    

    把0与第一个数比较,若不等于则引爆炸弹,说明第一个数是0;把1与第二个数比较,相等则跳过炸弹,说明第二个数是1。紧接着:

     8048ba0:	8d 5c 24 20          	lea    0x20(%esp),%ebx
     8048ba4:	8d 74 24 30          	lea    0x30(%esp),%esi
     8048ba8:	8b 43 f8             	mov    -0x8(%ebx),%eax
     8048bab:	03 43 fc             	add    -0x4(%ebx),%eax
     8048bae:	39 03                	cmp    %eax,(%ebx)
     8048bb0:	74 05                	je     8048bb7 <phase_2+0x43>
     8048bb2:	e8 1f 05 00 00       	call   80490d6 <explode_bomb>
    

    第一行存入%ebx的就是第三个数的地址,三、四行将%ebx-8地址内的数(也就是第一个数),与%ebx-4地址内的数(也就是第二个数)相加,与第三个数比较,如果相等就跳过炸弹,说明第三个数是0+1=1。紧接着:

     8048bb7:	83 c3 04             	add    $0x4,%ebx
     8048bba:	39 f3                	cmp    %esi,%ebx
     8048bbc:	75 ea                	jne    8048ba8 <phase_2+0x34>
     8048bbe:	83 c4 34             	add    $0x34,%esp
    

    继续寻找下一个数,也就是地址加4,与%esp+30比较,若不等于,就跳转回上一步的操作,也就是说,每次判断是否超出了6位,未超出,则继续循环比较下一位是否等于前两位的和,全部满足才不会爆炸。

    所以本位是前两位的和,即结果为:0 1 1 2 3 5。验证:

    image-20241022102835478

  • 伪代码
  • void phase_2(int a[6])
    {
        read_six_number(a);
        if(a[0]!=0) explode_bomb();
        if(a[1]!=1) explode_bomb();
        for(int i=3;i<6;i++)
        {
            if(a[i]!=a[i-1]+a[i-2])
            explode_bomb();
        }
    }
    

    phase_3

  • 反汇编代码
  • 08048bc4 <phase_3>:
     8048bc4:	83 ec 2c             	sub    $0x2c,%esp
     8048bc7:	8d 44 24 1c          	lea    0x1c(%esp),%eax
     8048bcb:	89 44 24 0c          	mov    %eax,0xc(%esp)
     8048bcf:	8d 44 24 18          	lea    0x18(%esp),%eax
     8048bd3:	89 44 24 08          	mov    %eax,0x8(%esp)
     8048bd7:	c7 44 24 04 c3 a3 04 	movl   $0x804a3c3,0x4(%esp)
     8048bde:	08 
     8048bdf:	8b 44 24 30          	mov    0x30(%esp),%eax
     8048be3:	89 04 24             	mov    %eax,(%esp)
     8048be6:	e8 85 fc ff ff       	call   8048870 <__isoc99_sscanf@plt>
     8048beb:	83 f8 01             	cmp    $0x1,%eax
     8048bee:	7f 05                	jg     8048bf5 <phase_3+0x31>
     8048bf0:	e8 e1 04 00 00       	call   80490d6 <explode_bomb>
     8048bf5:	83 7c 24 18 07       	cmpl   $0x7,0x18(%esp)
     8048bfa:	77 66                	ja     8048c62 <phase_3+0x9e>
     8048bfc:	8b 44 24 18          	mov    0x18(%esp),%eax
     8048c00:	ff 24 85 00 a2 04 08 	jmp    *0x804a200(,%eax,4)
     8048c07:	b8 00 00 00 00       	mov    $0x0,%eax
     8048c0c:	eb 05                	jmp    8048c13 <phase_3+0x4f>
     8048c0e:	b8 5d 02 00 00       	mov    $0x25d,%eax
     8048c13:	2d a4 00 00 00       	sub    $0xa4,%eax
     8048c18:	eb 05                	jmp    8048c1f <phase_3+0x5b>
     8048c1a:	b8 00 00 00 00       	mov    $0x0,%eax
     8048c1f:	05 36 02 00 00       	add    $0x236,%eax
     8048c24:	eb 05                	jmp    8048c2b <phase_3+0x67>
     8048c26:	b8 00 00 00 00       	mov    $0x0,%eax
     8048c2b:	2d 53 02 00 00       	sub    $0x253,%eax
     8048c30:	eb 05                	jmp    8048c37 <phase_3+0x73>
     8048c32:	b8 00 00 00 00       	mov    $0x0,%eax
     8048c37:	05 53 02 00 00       	add    $0x253,%eax
     8048c3c:	eb 05                	jmp    8048c43 <phase_3+0x7f>
     8048c3e:	b8 00 00 00 00       	mov    $0x0,%eax
     8048c43:	2d 53 02 00 00       	sub    $0x253,%eax
     8048c48:	eb 05                	jmp    8048c4f <phase_3+0x8b>
     8048c4a:	b8 00 00 00 00       	mov    $0x0,%eax
     8048c4f:	05 53 02 00 00       	add    $0x253,%eax
     8048c54:	eb 05                	jmp    8048c5b <phase_3+0x97>
     8048c56:	b8 00 00 00 00       	mov    $0x0,%eax
     8048c5b:	2d 53 02 00 00       	sub    $0x253,%eax
     8048c60:	eb 0a                	jmp    8048c6c <phase_3+0xa8>
     8048c62:	e8 6f 04 00 00       	call   80490d6 <explode_bomb>
     8048c67:	b8 00 00 00 00       	mov    $0x0,%eax
     8048c6c:	83 7c 24 18 05       	cmpl   $0x5,0x18(%esp)
     8048c71:	7f 06                	jg     8048c79 <phase_3+0xb5>
     8048c73:	3b 44 24 1c          	cmp    0x1c(%esp),%eax
     8048c77:	74 05                	je     8048c7e <phase_3+0xba>
     8048c79:	e8 58 04 00 00       	call   80490d6 <explode_bomb>
     8048c7e:	83 c4 2c             	add    $0x2c,%esp
     8048c81:	c3                   	ret    
    
  • 解答
  •  8048bd7:	c7 44 24 04 c3 a3 04 	movl   $0x804a3c3,0x4(%esp)
     8048bde:	08 
     8048bdf:	8b 44 24 30          	mov    0x30(%esp),%eax
     8048be3:	89 04 24             	mov    %eax,(%esp)
     8048be6:	e8 85 fc ff ff       	call   8048870 <__isoc99_sscanf@plt>
     8048beb:	83 f8 01             	cmp    $0x1,%eax
     8048bee:	7f 05                	jg     8048bf5 <phase_3+0x31>
     8048bf0:	e8 e1 04 00 00       	call   80490d6 <explode_bomb>
    

    跟phase_2相同,将地址0x804a3c3传参后调用sscanf函数,返回满足要求的个数,要求为:

    image-20240418181143257

    函数返回值存放在%eax里,大于1才能跳过炸弹,即输入的是2个整形数。

     8048bf5:	83 7c 24 18 07       	cmpl   $0x7,0x18(%esp)
     8048bfa:	77 66                	ja     8048c62 <phase_3+0x9e>
     8048bfc:	8b 44 24 18          	mov    0x18(%esp),%eax
     8048c00:	ff 24 85 00 a2 04 08 	jmp    *0x804a200(,%eax,4)
    

    将%esp+18处的值与7比较,我们将其设为x,x必须小于7。第四行的汇编让我们很容易就想到了跳转表,事实证明确实如此:

    image-20240418191122207

    存放的全是汇编指令的地址!根据GDB结果,每条地址分别是x=0,1,2,3,4,5,6,7,8的地址,先看x=0的情况,即地址为0x8048c0e,我们看它每一步的操作:

     8048c0e:	b8 5d 02 00 00       	mov    $0x25d,%eax
     8048c13:	2d a4 00 00 00       	sub    $0xa4,%eax
     8048c18:	eb 05                	jmp    8048c1f <phase_3+0x5b>
     
     8048c1f:	05 36 02 00 00       	add    $0x236,%eax
     8048c24:	eb 05                	jmp    8048c2b <phase_3+0x67>
     
     8048c2b:	2d 53 02 00 00       	sub    $0x253,%eax
     8048c30:	eb 05                	jmp    8048c37 <phase_3+0x73>
     
     8048c37:	05 53 02 00 00       	add    $0x253,%eax
     8048c3c:	eb 05                	jmp    8048c43 <phase_3+0x7f>
     
     8048c43:	2d 53 02 00 00       	sub    $0x253,%eax
     8048c48:	eb 05                	jmp    8048c4f <phase_3+0x8b>
     
     8048c4f:	05 53 02 00 00       	add    $0x253,%eax
     8048c54:	eb 05                	jmp    8048c5b <phase_3+0x97>
     
     8048c5b:	2d 53 02 00 00       	sub    $0x253,%eax
     8048c60:	eb 0a                	jmp    8048c6c <phase_3+0xa8>
     
     8048c6c:	83 7c 24 18 05       	cmpl   $0x5,0x18(%esp)
     8048c71:	7f 06                	jg     8048c79 <phase_3+0xb5>
     8048c73:	3b 44 24 1c          	cmp    0x1c(%esp),%eax
     8048c77:	74 05                	je     8048c7e <phase_3+0xba>
     8048c79:	e8 58 04 00 00       	call   80490d6 <explode_bomb>
     8048c7e:	83 c4 2c             	add    $0x2c,%esp
     8048c81:	c3                   	ret    
    

    首先经过一系列的计算,最后判断x是否小于5,说明有6种结果:

    x=0:0x25d-0xa4+0x236-0x253+0x253-0x253+0x253-0x253=0x19c=412(10)

    剩下的经过计算有:1 -193,2 -29,3 -595,4 0,5 -595。

  • 伪代码
  • void phase_2(int x,int number)
    {
        switch (x)
        {
                 case(0): if(number!=412) explode_bomb();
                 case(1): if(number!=-193) explode_bomb();
                 case(2): if(number!=-29) explode_bomb();
                 case(3): if(number!=-595) explode_bomb();
                 case(4): if(number!=0) explode_bomb();
                 case(5): if(number!=-595) explode_bomb();
        }
    }
    

    phase_4

  • 反汇编代码
  • 08048cdf <phase_4>:
     8048cdf:	83 ec 2c             	sub    $0x2c,%esp
     8048ce2:	8d 44 24 18          	lea    0x18(%esp),%eax
     8048ce6:	89 44 24 0c          	mov    %eax,0xc(%esp)
     8048cea:	8d 44 24 1c          	lea    0x1c(%esp),%eax
     8048cee:	89 44 24 08          	mov    %eax,0x8(%esp)
     8048cf2:	c7 44 24 04 c3 a3 04 	movl   $0x804a3c3,0x4(%esp)
     8048cf9:	08 
     8048cfa:	8b 44 24 30          	mov    0x30(%esp),%eax
     8048cfe:	89 04 24             	mov    %eax,(%esp)
     8048d01:	e8 6a fb ff ff       	call   8048870 <__isoc99_sscanf@plt>
     8048d06:	83 f8 02             	cmp    $0x2,%eax
     8048d09:	75 0e                	jne    8048d19 <phase_4+0x3a>
     8048d0b:	8b 44 24 18          	mov    0x18(%esp),%eax
     8048d0f:	83 f8 01             	cmp    $0x1,%eax
     8048d12:	7e 05                	jle    8048d19 <phase_4+0x3a>
     8048d14:	83 f8 04             	cmp    $0x4,%eax
     8048d17:	7e 05                	jle    8048d1e <phase_4+0x3f>
     8048d19:	e8 b8 03 00 00       	call   80490d6 <explode_bomb>
     8048d1e:	8b 44 24 18          	mov    0x18(%esp),%eax
     8048d22:	89 44 24 04          	mov    %eax,0x4(%esp)
     8048d26:	c7 04 24 05 00 00 00 	movl   $0x5,(%esp)
     8048d2d:	e8 50 ff ff ff       	call   8048c82 <func4>
     8048d32:	3b 44 24 1c          	cmp    0x1c(%esp),%eax
     8048d36:	74 05                	je     8048d3d <phase_4+0x5e>
     8048d38:	e8 99 03 00 00       	call   80490d6 <explode_bomb>
     8048d3d:	83 c4 2c             	add    $0x2c,%esp
     8048d40:	c3                   	ret    
    
  • 解答
  •  8048d01:	e8 6a fb ff ff       	call   8048870 <__isoc99_sscanf@plt>
     8048d06:	83 f8 02             	cmp    $0x2,%eax
     8048d09:	75 0e                	jne    8048d19 <phase_4+0x3a>
    

    我们又见到了熟悉的sscanf函数,查看地址,并且返回值不等于2就爆炸,所以基本可以断定输入的是两个数。

    image-20240426191111139

     8048ce2:	8d 44 24 18          	lea    0x18(%esp),%eax
     8048ce6:	89 44 24 0c          	mov    %eax,0xc(%esp)
     8048cea:	8d 44 24 1c          	lea    0x1c(%esp),%eax
     8048cee:	89 44 24 08          	mov    %eax,0x8(%esp)
    

    这里我们可以设存入%esp+18的数为x,存入%esp+1c的数为y。

     8048d0b:	8b 44 24 18          	mov    0x18(%esp),%eax
     8048d0f:	83 f8 01             	cmp    $0x1,%eax
     8048d12:	7e 05                	jle    8048d19 <phase_4+0x3a>
     8048d14:	83 f8 04             	cmp    $0x4,%eax
     8048d17:	7e 05                	jle    8048d1e <phase_4+0x3f>
     8048d19:	e8 b8 03 00 00       	call   80490d6 <explode_bomb>
    

    这里将x与1比较,大于1则不爆炸;又与4比较,小于等于4则不爆炸,所以1<x<=4。

     8048d1e:	8b 44 24 18          	mov    0x18(%esp),%eax
     8048d22:	89 44 24 04          	mov    %eax,0x4(%esp)
     8048d26:	c7 04 24 05 00 00 00 	movl   $0x5,(%esp)
     8048d2d:	e8 50 ff ff ff       	call   8048c82 <func4>
    

    这里将x和5作为参数传入到了func4函数中,我们这就来看看这个函数:

    08048c82 <func4>:
     8048c82:	83 ec 1c             	sub    $0x1c,%esp
     8048c85:	89 5c 24 10          	mov    %ebx,0x10(%esp)
     8048c89:	89 74 24 14          	mov    %esi,0x14(%esp)
     8048c8d:	89 7c 24 18          	mov    %edi,0x18(%esp)
     8048c91:	8b 74 24 20          	mov    0x20(%esp),%esi
     8048c95:	8b 5c 24 24          	mov    0x24(%esp),%ebx
     8048c99:	85 f6                	test   %esi,%esi
     8048c9b:	7e 2b                	jle    8048cc8 <func4+0x46>
     8048c9d:	83 fe 01             	cmp    $0x1,%esi
     8048ca0:	74 2b                	je     8048ccd <func4+0x4b>
     8048ca2:	89 5c 24 04          	mov    %ebx,0x4(%esp)
     8048ca6:	8d 46 ff             	lea    -0x1(%esi),%eax
     8048ca9:	89 04 24             	mov    %eax,(%esp)
     8048cac:	e8 d1 ff ff ff       	call   8048c82 <func4>
     8048cb1:	8d 3c 18             	lea    (%eax,%ebx,1),%edi
     8048cb4:	89 5c 24 04          	mov    %ebx,0x4(%esp)
     8048cb8:	83 ee 02             	sub    $0x2,%esi
     8048cbb:	89 34 24             	mov    %esi,(%esp)
     8048cbe:	e8 bf ff ff ff       	call   8048c82 <func4>
     8048cc3:	8d 1c 07             	lea    (%edi,%eax,1),%ebx
     8048cc6:	eb 05                	jmp    8048ccd <func4+0x4b>
     8048cc8:	bb 00 00 00 00       	mov    $0x0,%ebx
     8048ccd:	89 d8                	mov    %ebx,%eax
     8048ccf:	8b 5c 24 10          	mov    0x10(%esp),%ebx
     8048cd3:	8b 74 24 14          	mov    0x14(%esp),%esi
     8048cd7:	8b 7c 24 18          	mov    0x18(%esp),%edi
     8048cdb:	83 c4 1c             	add    $0x1c,%esp
     8048cde:	c3                   	ret    
    
     8048c82:	83 ec 1c             	sub    $0x1c,%esp
     8048c85:	89 5c 24 10          	mov    %ebx,0x10(%esp)
     8048c89:	89 74 24 14          	mov    %esi,0x14(%esp)
     8048c8d:	89 7c 24 18          	mov    %edi,0x18(%esp)
     8048c91:	8b 74 24 20          	mov    0x20(%esp),%esi
     8048c95:	8b 5c 24 24          	mov    0x24(%esp),%ebx
    

    这里保存了旧的三个寄存器,防止被覆盖,并将x放在%esi中,将y放在%ebx中。

     8048c99:	85 f6                	test   %esi,%esi
     8048c9b:	7e 2b                	jle    8048cc8 <func4+0x46>
     
     8048cc8:	bb 00 00 00 00       	mov    $0x0,%ebx
     8048ccd:	89 d8                	mov    %ebx,%eax
    

    如果x<=0,则返回0。

     8048c9d:	83 fe 01             	cmp    $0x1,%esi
     8048ca0:	74 2b                	je     8048ccd <func4+0x4b>
     
     8048ccd:	89 d8                	mov    %ebx,%eax
    

    如果x=1,则返回y。

     8048ca2:	89 5c 24 04          	mov    %ebx,0x4(%esp)
     8048ca6:	8d 46 ff             	lea    -0x1(%esi),%eax
     8048ca9:	89 04 24             	mov    %eax,(%esp)
     8048cac:	e8 d1 ff ff ff       	call   8048c82 <func4>
     8048cb1:	8d 3c 18             	lea    (%eax,%ebx,1),%edi
    

    否则,将x-1与y作为参数,递归调用func4函数。将%eax+%ebx的值存入%edi中。

     8048cb4:	89 5c 24 04          	mov    %ebx,0x4(%esp)
     8048cb8:	83 ee 02             	sub    $0x2,%esi
     8048cbb:	89 34 24             	mov    %esi,(%esp)
     8048cbe:	e8 bf ff ff ff       	call   8048c82 <func4>
     8048cc3:	8d 1c 07             	lea    (%edi,%eax,1),%ebx
    

    接着将x-2与y作为参数,递归调用func4函数。将%eax+%edi的值存入%ebx中。函数返回%ebx中的值。所以这个函数的功能可以这样表示:

    image-20240418213836994

     8048d32:	3b 44 24 1c          	cmp    0x1c(%esp),%eax
     8048d36:	74 05                	je     8048d3d <phase_4+0x5e>
     8048d38:	e8 99 03 00 00       	call   80490d6 <explode_bomb>
    

    返回值与y比较,若相等则通过。

    image-20240418215359258

    我们根据函数对函数的分析,求得以上结果。所以我们最后比较的是y=12x。但不要忘记了上面的1<x<=4。所以结果有:24 2,36 3,48 4。

  • 伪代码
  • void phase_4(int y,int x)
    {
        if(x<=1||x>4) explode_bomb();
        int answer=fun4(5,x);
        if(answer!=y) explode_bomb();
    }
    int fun4(int n,int x)
    {
        if(n==0) return 0;
        if(n==1) return 1;
        else retrun fun4(n-1,x)+fun4(n-2,x)+x;
    }
    

    phase_5

  • 反汇编代码
  • 08048d41 <phase_5>:
     8048d41:	53                   	push   %ebx
     8048d42:	83 ec 18             	sub    $0x18,%esp
     8048d45:	8b 5c 24 20          	mov    0x20(%esp),%ebx
     8048d49:	89 1c 24             	mov    %ebx,(%esp)
     8048d4c:	e8 5a 02 00 00       	call   8048fab <string_length>
     8048d51:	83 f8 06             	cmp    $0x6,%eax
     8048d54:	74 05                	je     8048d5b <phase_5+0x1a>
     8048d56:	e8 7b 03 00 00       	call   80490d6 <explode_bomb>
     8048d5b:	ba 00 00 00 00       	mov    $0x0,%edx
     8048d60:	b8 00 00 00 00       	mov    $0x0,%eax
     8048d65:	0f be 0c 03          	movsbl (%ebx,%eax,1),%ecx
     8048d69:	83 e1 0f             	and    $0xf,%ecx
     8048d6c:	03 14 8d 20 a2 04 08 	add    0x804a220(,%ecx,4),%edx
     8048d73:	83 c0 01             	add    $0x1,%eax
     8048d76:	83 f8 06             	cmp    $0x6,%eax
     8048d79:	75 ea                	jne    8048d65 <phase_5+0x24>
     8048d7b:	83 fa 2e             	cmp    $0x2e,%edx
     8048d7e:	74 05                	je     8048d85 <phase_5+0x44>
     8048d80:	e8 51 03 00 00       	call   80490d6 <explode_bomb>
     8048d85:	83 c4 18             	add    $0x18,%esp
     8048d88:	5b                   	pop    %ebx
     8048d89:	c3                   	ret    
    
  • 解答
  •  8048d41:	53                   	push   %ebx
     8048d42:	83 ec 18             	sub    $0x18,%esp
     8048d45:	8b 5c 24 20          	mov    0x20(%esp),%ebx
     8048d49:	89 1c 24             	mov    %ebx,(%esp)
     8048d4c:	e8 5a 02 00 00       	call   8048fab <string_length>
     8048d51:	83 f8 06             	cmp    $0x6,%eax
     8048d54:	74 05                	je     8048d5b <phase_5+0x1a>
     8048d56:	e8 7b 03 00 00       	call   80490d6 <explode_bomb>
    

    首先是常规的开辟空间,然后读取一个字符串,其长度等于6才能防止爆炸,说明输入的字符串长度为6。

     8048d5b:	ba 00 00 00 00       	mov    $0x0,%edx
     8048d60:	b8 00 00 00 00       	mov    $0x0,%eax
     8048d65:	0f be 0c 03          	movsbl (%ebx,%eax,1),%ecx
     8048d69:	83 e1 0f             	and    $0xf,%ecx
     8048d6c:	03 14 8d 20 a2 04 08 	add    0x804a220(,%ecx,4),%edx
     8048d73:	83 c0 01             	add    $0x1,%eax
     8048d76:	83 f8 06             	cmp    $0x6,%eax
     8048d79:	75 ea                	jne    8048d65 <phase_5+0x24>
    

    这里首先将%edx赋0,%eax(用来计数)也赋为0;接着用movsbl语句将字符串中的字母(从第一个开始)传入%ecx,接着做与运算,只留下了字母ASCII码的低四位,然后将这低四位(假设值为x)乘于4加到0x804a220上作为地址(也就是0x804a220偏移4*x个字节);接着将这个地址中的内容累加到%edx中,同时将%eax加1;此时如果%eax!=6则说明字符串中的六个字符还未处理完,就要回到movsbl语句进行下一个字符的同样的处理。

     8048d7b:	83 fa 2e             	cmp    $0x2e,%edx
     8048d7e:	74 05                	je     8048d85 <phase_5+0x44>
     8048d80:	e8 51 03 00 00       	call   80490d6 <explode_bomb>
     8048d85:	83 c4 18             	add    $0x18,%esp
     8048d88:	5b                   	pop    %ebx
     8048d89:	c3                   	ret    
    

    最后的累加值应该等于0x2e,即46。

    我们先看看0x804a220处连续16个整型空间的值(因为x为4位,最大为1111,即15):

    image-20240419190343366

    所以我们可以得出能用到的ASCII码:0x02, 0x0a, 0x06, 0x01, 0x0c, 0x10, 0x09, 0x03, 0x04, 0x07, 0x0e, 0x05, 0x0b, 0x08, 0x0f, 0x0d。

    它们的对应关系为:

    02 0a 06 01 0c 10 09 03 04 07 0e 05 0b 08 0f 0d
    2 10 6 1 12 16 9 3 4 7 14 5 11 8 15 13

    我们的目标就是凑出相加等于46的情况,一种情况是两个2,一个6,三个12。它们对应的ASCII码的第四位分别为:0x0, 0x2, 0x4。根据ASCII表,可以找到字母:p(0x70), b(0x62), d(ox64)。

    所以答案可以是:ppbddd。当然,答案不唯一。

  • 伪代码
  • void phase_5(string a,string b)
    {
        char ans[7];
        for(int i=0;i<6;i++)
        {
            ans[i]=b[a[i]&0xf];
        }
        ans[6]='\0';
    }
    

    phase_6

  • 反汇编代码
  • 08048d8a <phase_6>:
     8048d8a:	56                   	push   %esi
     8048d8b:	53                   	push   %ebx
     8048d8c:	83 ec 44             	sub    $0x44,%esp
     8048d8f:	8d 44 24 10          	lea    0x10(%esp),%eax
     8048d93:	89 44 24 04          	mov    %eax,0x4(%esp)
     8048d97:	8b 44 24 50          	mov    0x50(%esp),%eax
     8048d9b:	89 04 24             	mov    %eax,(%esp)
     8048d9e:	e8 68 04 00 00       	call   804920b <read_six_numbers>
     8048da3:	be 00 00 00 00       	mov    $0x0,%esi
     8048da8:	8b 44 b4 10          	mov    0x10(%esp,%esi,4),%eax
     8048dac:	83 e8 01             	sub    $0x1,%eax
     8048daf:	83 f8 05             	cmp    $0x5,%eax
     8048db2:	76 05                	jbe    8048db9 <phase_6+0x2f>
     8048db4:	e8 1d 03 00 00       	call   80490d6 <explode_bomb>
     8048db9:	83 c6 01             	add    $0x1,%esi
     8048dbc:	83 fe 06             	cmp    $0x6,%esi
     8048dbf:	74 33                	je     8048df4 <phase_6+0x6a>
     8048dc1:	89 f3                	mov    %esi,%ebx
     8048dc3:	8b 44 9c 10          	mov    0x10(%esp,%ebx,4),%eax
     8048dc7:	39 44 b4 0c          	cmp    %eax,0xc(%esp,%esi,4)
     8048dcb:	75 05                	jne    8048dd2 <phase_6+0x48>
     8048dcd:	e8 04 03 00 00       	call   80490d6 <explode_bomb>
     8048dd2:	83 c3 01             	add    $0x1,%ebx
     8048dd5:	83 fb 05             	cmp    $0x5,%ebx
     8048dd8:	7e e9                	jle    8048dc3 <phase_6+0x39>
     8048dda:	eb cc                	jmp    8048da8 <phase_6+0x1e>
     8048ddc:	8b 52 08             	mov    0x8(%edx),%edx
     8048ddf:	83 c0 01             	add    $0x1,%eax
     8048de2:	39 c8                	cmp    %ecx,%eax
     8048de4:	75 f6                	jne    8048ddc <phase_6+0x52>
     8048de6:	89 54 b4 28          	mov    %edx,0x28(%esp,%esi,4)
     8048dea:	83 c3 01             	add    $0x1,%ebx
     8048ded:	83 fb 06             	cmp    $0x6,%ebx
     8048df0:	75 07                	jne    8048df9 <phase_6+0x6f>
     8048df2:	eb 1c                	jmp    8048e10 <phase_6+0x86>
     8048df4:	bb 00 00 00 00       	mov    $0x0,%ebx
     8048df9:	89 de                	mov    %ebx,%esi
     8048dfb:	8b 4c 9c 10          	mov    0x10(%esp,%ebx,4),%ecx
     8048dff:	b8 01 00 00 00       	mov    $0x1,%eax
     8048e04:	ba 3c c1 04 08       	mov    $0x804c13c,%edx
     8048e09:	83 f9 01             	cmp    $0x1,%ecx
     8048e0c:	7f ce                	jg     8048ddc <phase_6+0x52>
     8048e0e:	eb d6                	jmp    8048de6 <phase_6+0x5c>
     8048e10:	8b 5c 24 28          	mov    0x28(%esp),%ebx
     8048e14:	8b 44 24 2c          	mov    0x2c(%esp),%eax
     8048e18:	89 43 08             	mov    %eax,0x8(%ebx)
     8048e1b:	8b 54 24 30          	mov    0x30(%esp),%edx
     8048e1f:	89 50 08             	mov    %edx,0x8(%eax)
     8048e22:	8b 44 24 34          	mov    0x34(%esp),%eax
     8048e26:	89 42 08             	mov    %eax,0x8(%edx)
     8048e29:	8b 54 24 38          	mov    0x38(%esp),%edx
     8048e2d:	89 50 08             	mov    %edx,0x8(%eax)
     8048e30:	8b 44 24 3c          	mov    0x3c(%esp),%eax
     8048e34:	89 42 08             	mov    %eax,0x8(%edx)
     8048e37:	c7 40 08 00 00 00 00 	movl   $0x0,0x8(%eax)
     8048e3e:	be 05 00 00 00       	mov    $0x5,%esi
     8048e43:	8b 43 08             	mov    0x8(%ebx),%eax
     8048e46:	8b 10                	mov    (%eax),%edx
     8048e48:	39 13                	cmp    %edx,(%ebx)
     8048e4a:	7d 05                	jge    8048e51 <phase_6+0xc7>
     8048e4c:	e8 85 02 00 00       	call   80490d6 <explode_bomb>
     8048e51:	8b 5b 08             	mov    0x8(%ebx),%ebx
     8048e54:	83 ee 01             	sub    $0x1,%esi
     8048e57:	75 ea                	jne    8048e43 <phase_6+0xb9>
     8048e59:	83 c4 44             	add    $0x44,%esp
     8048e5c:	5b                   	pop    %ebx
     8048e5d:	5e                   	pop    %esi
     8048e5e:	c3                   	ret    
    
  • 解答
  •  8048d8a:	56                   	push   %esi
     8048d8b:	53                   	push   %ebx
     8048d8c:	83 ec 44             	sub    $0x44,%esp
     8048d8f:	8d 44 24 10          	lea    0x10(%esp),%eax
     8048d93:	89 44 24 04          	mov    %eax,0x4(%esp)
     8048d97:	8b 44 24 50          	mov    0x50(%esp),%eax
     8048d9b:	89 04 24             	mov    %eax,(%esp)
     8048d9e:	e8 68 04 00 00       	call   804920b <read_six_numbers>
    

    这一段先读取了一个字符串,判断是否输入了6个数。

     8048da3:	be 00 00 00 00       	mov    $0x0,%esi
     8048da8:	8b 44 b4 10          	mov    0x10(%esp,%esi,4),%eax
     8048dac:	83 e8 01             	sub    $0x1,%eax
     8048daf:	83 f8 05             	cmp    $0x5,%eax
     8048db2:	76 05                	jbe    8048db9 <phase_6+0x2f>
     8048db4:	e8 1d 03 00 00       	call   80490d6 <explode_bomb>
    

    这里将字符串的元素取出,减一后与5比较,要求小于等于5,即每个元素(设为ai)满足1<=ai<=6。

     8048db9:	83 c6 01             	add    $0x1,%esi
     8048dbc:	83 fe 06             	cmp    $0x6,%esi
     8048dbf:	74 33                	je     8048df4 <phase_6+0x6a>
    

    这里将%esi与6比较,保证6个元素,防止越界。

     8048dc1:	89 f3                	mov    %esi,%ebx
     8048dc3:	8b 44 9c 10          	mov    0x10(%esp,%ebx,4),%eax
     8048dc7:	39 44 b4 0c          	cmp    %eax,0xc(%esp,%esi,4)
     8048dcb:	75 05                	jne    8048dd2 <phase_6+0x48>
     8048dcd:	e8 04 03 00 00       	call   80490d6 <explode_bomb>
     8048dd2:	83 c3 01             	add    $0x1,%ebx
     8048dd5:	83 fb 05             	cmp    $0x5,%ebx
     8048dd8:	7e e9                	jle    8048dc3 <phase_6+0x39>
     8048dda:	eb cc                	jmp    8048da8 <phase_6+0x1e>
    

    这里将4%esi+%esp+10的元素与4%esi+%esp+c的元素比较,即字符串中相邻两个数比较,要求不相等。当%ebx<=5时,循环。

    以上的代码作用是输入6个数,并且逐步循环访问每一个数,比较判断是否∈[1,6],然后又开始不断地循环比较6个数中,每两个数都不能相等,一旦不满足这两个条件,则会爆炸。

    下面一段代码也是一个循环,这里给出每行的解释:

     8048ddc:	8b 52 08             	mov    0x8(%edx),%edx //edx+8->edx
     8048ddf:	83 c0 01             	add    $0x1,%eax //eax+1
     8048de2:	39 c8                	cmp    %ecx,%eax //eax比较ecx
     8048de4:	75 f6                	jne    8048ddc <phase_6+0x52> //不相等就跳转回去
     8048de6:	89 54 b4 28          	mov    %edx,0x28(%esp,%esi,4) //把当前编号对应的地址存到新的地方
     8048dea:	83 c3 01             	add    $0x1,%ebx //ebx+1
     8048ded:	83 fb 06             	cmp    $0x6,%ebx //与6比较,表示找完6个数
     8048df0:	75 07                	jne    8048df9 <phase_6+0x6f> //小于6就跳转
     8048df2:	eb 1c                	jmp    8048e10 <phase_6+0x86> //循环结束跳转
     8048df4:	bb 00 00 00 00       	mov    $0x0,%ebx //0->ebx
     8048df9:	89 de                	mov    %ebx,%esi //ebx->esi
     8048dfb:	8b 4c 9c 10          	mov    0x10(%esp,%ebx,4),%ecx //4*ebx+esp+10处的值存到ecx
     8048dff:	b8 01 00 00 00       	mov    $0x1,%eax //1->eax
     8048e04:	ba 3c c1 04 08       	mov    $0x804c13c,%edx //地址->edx
     8048e09:	83 f9 01             	cmp    $0x1,%ecx //比较ecx与1
     8048e0c:	7f ce                	jg     8048ddc <phase_6+0x52> //ecx>1跳转回开头
     8048e0e:	eb d6                	jmp    8048de6 <phase_6+0x5c> //跳转
    

    我们在理解的同时看到了这个地址0x804c13c,查看可得:

    image-20240419212702431

    看到这个我们可以断定这里存储的是链表,因为每一组都有一个标号,从1到6,那么自然标号前的数是权重,后面的数是下一结点的地址,并且每个地址之间相差12也印证了我们的猜想。所以这一段的代码作用就是把每一个结点的地址存到一个新的地方。

     8048e10:	8b 5c 24 28          	mov    0x28(%esp),%ebx
     8048e14:	8b 44 24 2c          	mov    0x2c(%esp),%eax
     8048e18:	89 43 08             	mov    %eax,0x8(%ebx)
     8048e1b:	8b 54 24 30          	mov    0x30(%esp),%edx
     8048e1f:	89 50 08             	mov    %edx,0x8(%eax)
     8048e22:	8b 44 24 34          	mov    0x34(%esp),%eax
     8048e26:	89 42 08             	mov    %eax,0x8(%edx)
     8048e29:	8b 54 24 38          	mov    0x38(%esp),%edx
     8048e2d:	89 50 08             	mov    %edx,0x8(%eax)
     8048e30:	8b 44 24 3c          	mov    0x3c(%esp),%eax
     8048e34:	89 42 08             	mov    %eax,0x8(%edx)
     8048e37:	c7 40 08 00 00 00 00 	movl   $0x0,0x8(%eax)
    

    这一段代码比较简单,即对链表进行排序,最后排序的结果为:p1->p2->p3->p4->p5->p6。

     8048e3e:	be 05 00 00 00       	mov    $0x5,%esi
     8048e43:	8b 43 08             	mov    0x8(%ebx),%eax
     8048e46:	8b 10                	mov    (%eax),%edx
     8048e48:	39 13                	cmp    %edx,(%ebx)
     8048e4a:	7d 05                	jge    8048e51 <phase_6+0xc7>
     8048e4c:	e8 85 02 00 00       	call   80490d6 <explode_bomb>
    

    这里将第一个节点的值与第二个节点的值比较,要求第一个节点的值大于等于第二个节点的值。

     8048e51:	8b 5b 08             	mov    0x8(%ebx),%ebx
     8048e54:	83 ee 01             	sub    $0x1,%esi
     8048e57:	75 ea                	jne    8048e43 <phase_6+0xb9>
     8048e59:	83 c4 44             	add    $0x44,%esp
     8048e5c:	5b                   	pop    %ebx
     8048e5d:	5e                   	pop    %esi
     8048e5e:	c3                   	ret    
    

    这就是上一段代码的循环条件,比较每个节点元素的大小,要求链表是递减的。

    所以根据我们之前查到的链表,根据权重排序序列号的:1 3 5 6 2 4。

  • 伪代码
  • int fun7(Node *addr,int x)
    {
        if(addr==0) return -1;
        int v=addr->value;
        if(v==x) return 0;
        else if(v>x) retrun 2*fun7(addr->left,x);
        else retrun 1+2*fun7(addr->right,x)
    }
    

    至此,我们拆除了6个炸弹!

    image-20241022103057375

    但是你以为这就结束了吗?

    secret_phase

    怎么发现这个的呢?在看代码的时候,最后有一句:

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

    这就是提示啊!于是翻看汇编,找到了这个代码!

  • 反汇编代码
  • 08048eb0 <secret_phase>:
     8048eb0:	53                   	push   %ebx
     8048eb1:	83 ec 18             	sub    $0x18,%esp
     8048eb4:	e8 44 02 00 00       	call   80490fd <read_line>
     8048eb9:	c7 44 24 08 0a 00 00 	movl   $0xa,0x8(%esp)
     8048ec0:	00 
     8048ec1:	c7 44 24 04 00 00 00 	movl   $0x0,0x4(%esp)
     8048ec8:	00 
     8048ec9:	89 04 24             	mov    %eax,(%esp)
     8048ecc:	e8 0f fa ff ff       	call   80488e0 <strtol@plt>
     8048ed1:	89 c3                	mov    %eax,%ebx
     8048ed3:	8d 40 ff             	lea    -0x1(%eax),%eax
     8048ed6:	3d e8 03 00 00       	cmp    $0x3e8,%eax
     8048edb:	76 05                	jbe    8048ee2 <secret_phase+0x32>
     8048edd:	e8 f4 01 00 00       	call   80490d6 <explode_bomb>
     8048ee2:	89 5c 24 04          	mov    %ebx,0x4(%esp)
     8048ee6:	c7 04 24 88 c0 04 08 	movl   $0x804c088,(%esp)
     8048eed:	e8 6d ff ff ff       	call   8048e5f <fun7>
     8048ef2:	83 f8 02             	cmp    $0x2,%eax
     8048ef5:	74 05                	je     8048efc <secret_phase+0x4c>
     8048ef7:	e8 da 01 00 00       	call   80490d6 <explode_bomb>
     8048efc:	c7 04 24 c8 a1 04 08 	movl   $0x804a1c8,(%esp)
     8048f03:	e8 f8 f8 ff ff       	call   8048800 <puts@plt>
     8048f08:	e8 4e 03 00 00       	call   804925b <phase_defused>
     8048f0d:	83 c4 18             	add    $0x18,%esp
     8048f10:	5b                   	pop    %ebx
     8048f11:	c3                   	ret    
    

    但这个好像并不能发现什么,所以我们又找到了这个:

    0804925b <phase_defused>:
     804925b:	81 ec 8c 00 00 00    	sub    $0x8c,%esp
     8049261:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
     8049267:	89 44 24 7c          	mov    %eax,0x7c(%esp)
     804926b:	31 c0                	xor    %eax,%eax
     804926d:	83 3d cc c3 04 08 06 	cmpl   $0x6,0x804c3cc
     8049274:	75 72                	jne    80492e8 <phase_defused+0x8d>
     8049276:	8d 44 24 2c          	lea    0x2c(%esp),%eax
     804927a:	89 44 24 10          	mov    %eax,0x10(%esp)
     804927e:	8d 44 24 28          	lea    0x28(%esp),%eax
     8049282:	89 44 24 0c          	mov    %eax,0xc(%esp)
     8049286:	8d 44 24 24          	lea    0x24(%esp),%eax
     804928a:	89 44 24 08          	mov    %eax,0x8(%esp)
     804928e:	c7 44 24 04 c9 a3 04 	movl   $0x804a3c9,0x4(%esp)
     8049295:	08 
     8049296:	c7 04 24 d0 c4 04 08 	movl   $0x804c4d0,(%esp)
     804929d:	e8 ce f5 ff ff       	call   8048870 <__isoc99_sscanf@plt>
     80492a2:	83 f8 03             	cmp    $0x3,%eax
     80492a5:	75 35                	jne    80492dc <phase_defused+0x81>
     80492a7:	c7 44 24 04 d2 a3 04 	movl   $0x804a3d2,0x4(%esp)
     80492ae:	08 
     80492af:	8d 44 24 2c          	lea    0x2c(%esp),%eax
     80492b3:	89 04 24             	mov    %eax,(%esp)
     80492b6:	e8 09 fd ff ff       	call   8048fc4 <strings_not_equal>
     80492bb:	85 c0                	test   %eax,%eax
     80492bd:	75 1d                	jne    80492dc <phase_defused+0x81>
     80492bf:	c7 04 24 98 a2 04 08 	movl   $0x804a298,(%esp)
     80492c6:	e8 35 f5 ff ff       	call   8048800 <puts@plt>
     80492cb:	c7 04 24 c0 a2 04 08 	movl   $0x804a2c0,(%esp)
     80492d2:	e8 29 f5 ff ff       	call   8048800 <puts@plt>
     80492d7:	e8 d4 fb ff ff       	call   8048eb0 <secret_phase>
     80492dc:	c7 04 24 f8 a2 04 08 	movl   $0x804a2f8,(%esp)
     80492e3:	e8 18 f5 ff ff       	call   8048800 <puts@plt>
     80492e8:	8b 44 24 7c          	mov    0x7c(%esp),%eax
     80492ec:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
     80492f3:	74 05                	je     80492fa <phase_defused+0x9f>
     80492f5:	e8 d6 f4 ff ff       	call   80487d0 <__stack_chk_fail@plt>
     80492fa:	81 c4 8c 00 00 00    	add    $0x8c,%esp
     8049300:	c3                   	ret    
    
     804926d:	83 3d cc c3 04 08 06 	cmpl   $0x6,0x804c3cc
     8049274:	75 72                	jne    80492e8 <phase_defused+0x8d>
    

    这两句代表只有成功破解6个炸弹才能触发隐藏炸弹,0x804c3cc这个地址里存放的是对我输入字符串的计数。

     804928e:	c7 44 24 04 c9 a3 04 	movl   $0x804a3c9,0x4(%esp)
     8049295:	08 
     8049296:	c7 04 24 d0 c4 04 08 	movl   $0x804c4d0,(%esp)
     804929d:	e8 ce f5 ff ff       	call   8048870 <__isoc99_sscanf@plt>
     80492a2:	83 f8 03             	cmp    $0x3,%eax
     80492a5:	75 35                	jne    80492dc <phase_defused+0x81>
    

    这里出现了两份地址,并作为参数调用sscanf函数,我们分别查看:

  • image-20240420140422414

    这里居然是空的!

  • image-20240420134213223

    说明开启条件是两个整数加一个字符串,但还不知道放在哪。再将返回结果与3比较,判断是否输入三个。

     80492a7:	c7 44 24 04 d2 a3 04 	movl   $0x804a3d2,0x4(%esp)
     80492ae:	08 
     80492af:	8d 44 24 2c          	lea    0x2c(%esp),%eax
     80492b3:	89 04 24             	mov    %eax,(%esp)
     80492b6:	e8 09 fd ff ff       	call   8048fc4 <strings_not_equal>
     80492bb:	85 c0                	test   %eax,%eax
     80492bd:	75 1d                	jne    80492dc <phase_defused+0x81>
    

    接着传参,测试输入的字符串与要求的是否相等,若不相等就跳过隐藏炸弹:

    image-20240420141336659

    表明上面两个数后面的字符串是DrEvil。但现在还不知道这个字符串放在哪,我们尝试设置断点看一下:

    image-20241022103302791

    这个就是我第四个炸弹的答案啊!我们尝试写入:

    image-20241022103228645

    成功触发!接下来就看隐藏炸弹的汇编了。

     8048eb0:	53                   	push   %ebx
     8048eb1:	83 ec 18             	sub    $0x18,%esp
     8048eb4:	e8 44 02 00 00       	call   80490fd <read_line>
     8048eb9:	c7 44 24 08 0a 00 00 	movl   $0xa,0x8(%esp)
     8048ec0:	00 
     8048ec1:	c7 44 24 04 00 00 00 	movl   $0x0,0x4(%esp)
     8048ec8:	00 
     8048ec9:	89 04 24             	mov    %eax,(%esp)
     8048ecc:	e8 0f fa ff ff       	call   80488e0 <strtol@plt>
     8048ed1:	89 c3                	mov    %eax,%ebx
     8048ed3:	8d 40 ff             	lea    -0x1(%eax),%eax
     8048ed6:	3d e8 03 00 00       	cmp    $0x3e8,%eax
     8048edb:	76 05                	jbe    8048ee2 <secret_phase+0x32>
     8048edd:	e8 f4 01 00 00       	call   80490d6 <explode_bomb>
    

    首先它读取了输入的字符串,并调用strtol函数,将其转化为长整数(设为s)。且这个长整数-1的大小得<=0x3e8。

     8048ee2:	89 5c 24 04          	mov    %ebx,0x4(%esp)
     8048ee6:	c7 04 24 88 c0 04 08 	movl   $0x804c088,(%esp)
     8048eed:	e8 6d ff ff ff       	call   8048e5f <fun7>
     8048ef2:	83 f8 02             	cmp    $0x2,%eax
     8048ef5:	74 05                	je     8048efc <secret_phase+0x4c>
     8048ef7:	e8 da 01 00 00       	call   80490d6 <explode_bomb>
    

    然后就是调用函数fun7,返回值必须等于2。所以我们只要搞懂fun7就可以了。

    08048e5f <fun7>:
     8048e5f:	53                   	push   %ebx
     8048e60:	83 ec 18             	sub    $0x18,%esp
     8048e63:	8b 54 24 20          	mov    0x20(%esp),%edx
     8048e67:	8b 4c 24 24          	mov    0x24(%esp),%ecx
     8048e6b:	85 d2                	test   %edx,%edx
     8048e6d:	74 37                	je     8048ea6 <fun7+0x47>
     8048e6f:	8b 1a                	mov    (%edx),%ebx
     8048e71:	39 cb                	cmp    %ecx,%ebx
     8048e73:	7e 13                	jle    8048e88 <fun7+0x29>
     8048e75:	89 4c 24 04          	mov    %ecx,0x4(%esp)
     8048e79:	8b 42 04             	mov    0x4(%edx),%eax
     8048e7c:	89 04 24             	mov    %eax,(%esp)
     8048e7f:	e8 db ff ff ff       	call   8048e5f <fun7>
     8048e84:	01 c0                	add    %eax,%eax
     8048e86:	eb 23                	jmp    8048eab <fun7+0x4c>
     8048e88:	b8 00 00 00 00       	mov    $0x0,%eax
     8048e8d:	39 cb                	cmp    %ecx,%ebx
     8048e8f:	74 1a                	je     8048eab <fun7+0x4c>
     8048e91:	89 4c 24 04          	mov    %ecx,0x4(%esp)
     8048e95:	8b 42 08             	mov    0x8(%edx),%eax
     8048e98:	89 04 24             	mov    %eax,(%esp)
     8048e9b:	e8 bf ff ff ff       	call   8048e5f <fun7>
     8048ea0:	8d 44 00 01          	lea    0x1(%eax,%eax,1),%eax
     8048ea4:	eb 05                	jmp    8048eab <fun7+0x4c>
     8048ea6:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
     8048eab:	83 c4 18             	add    $0x18,%esp
     8048eae:	5b                   	pop    %ebx
     8048eaf:	c3                   	ret    
    

    这是一个递归函数,我们根据汇编把这个函数的逻辑写出来:

    image-20240420145955269

    而函数中的M[x]为0x24,即36:

    image-20240420151912233

    依据函数逻辑推断这是一个二叉树,查看各节点:

    image-20240420153704235

    我们可以画出这个树:

    image-20240420153943847

    要想返回值为2,可以有:36>s , 指向8, 8<s,指向22。其中反递归的过程为:eax=0 ->2eax+1=1 ->2eax=2。所以答案是22。

    至此,我们拆完了6个炸弹和一个隐藏炸弹:

    image-20241022103333337

    image-20240420155815342

    在给的压缩包中,还有一个bomb-quiet可执行文件,反汇编后发现与bomb一样:

    image-20241022103436813

    感悟

    这个实验对于汇编语言的理解的要求很大,除了要学会用GDB查看各个内存地址存放的内容外,还要理解函数的目的究竟是什么,整个实验耗费了不少的时间,有的炸弹光要看懂汇编是在干什么就要耗费很长一段时间,理解的时候一直是在烧脑和惊喜之间反复横跳,但是解决之后成就感是值得花费大量的时间的,这次实验令我对反汇编破解有了一些深入的理解。

    作者:往虚

    物联沃分享整理
    物联沃-IOTWORD物联网 » HNU计算机系统实验之bombLab 2022级实战指南

    发表回复