zoukankan      html  css  js  c++  java
  • CS:APP3e 深入理解计算机系统_3e bomblab实验

    **bomb.c**
    /***************************************************************************
     * Dr. Evil's Insidious Bomb, Version 1.1
     * Copyright 2011, Dr. Evil Incorporated. All rights reserved.
     *
     * LICENSE:
     *
     * Dr. Evil Incorporated (the PERPETRATOR) hereby grants you (the
     * VICTIM) explicit permission to use this bomb (the BOMB).  This is a
     * time limited license, which expires on the death of the VICTIM.
     * The PERPETRATOR takes no responsibility for damage, frustration,
     * insanity, bug-eyes, carpal-tunnel syndrome, loss of sleep, or other
     * harm to the VICTIM.  Unless the PERPETRATOR wants to take credit,
     * that is.  The VICTIM may not distribute this bomb source code to
     * any enemies of the PERPETRATOR.  No VICTIM may debug,
     * reverse-engineer, run "strings" on, decompile, decrypt, or use any
     * other technique to gain knowledge of and defuse the BOMB.  BOMB
     * proof clothing may not be worn when handling this program.  The
     * PERPETRATOR will not apologize for the PERPETRATOR's poor sense of
     * humor.  This license is null and void where the BOMB is prohibited
     * by law.
     ***************************************************************************/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include "support.h"
    #include "phases.h"
    
    /* 
     * Note to self: Remember to erase this file so my victims will have no
     * idea what is going on, and so they will all blow up in a
     * spectaculary fiendish explosion. -- Dr. Evil 
     */
    
    FILE *infile;
    
    int main(int argc, char *argv[])
    {
        char *input;
    
        /* Note to self: remember to port this bomb to Windows and put a 
         * fantastic GUI on it. */
    
        /* When run with no arguments, the bomb reads its input lines 
         * from standard input. */
        if (argc == 1) {  
    	infile = stdin;
        } 
    
        /* When run with one argument <file>, the bomb reads from <file> 
         * until EOF, and then switches to standard input. Thus, as you 
         * defuse each phase, you can add its defusing string to <file> and
         * avoid having to retype it. */
        else if (argc == 2) {
    	if (!(infile = fopen(argv[1], "r"))) {
    	    printf("%s: Error: Couldn't open %s
    ", argv[0], argv[1]);
    	    exit(8);
    	}
        }
    
        /* You can't call the bomb with more than 1 command line argument. */
        else {
    	printf("Usage: %s [<input_file>]
    ", argv[0]);
    	exit(8);
        }
    
        /* Do all sorts of secret stuff that makes the bomb harder to defuse. */
        initialize_bomb();
    
        printf("Welcome to my fiendish little bomb. You have 6 phases with
    ");
        printf("which to blow yourself up. Have a nice day!
    ");
    
        /* Hmm...  Six phases must be more secure than one phase! */
        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?
    ");
    
        /* 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!
    ");
    
        /* 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!
    ");
    
        /* 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.
    ");
        
        /* 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...
    ");
    
        /* 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();
    
        /* Wow, they got it!  But isn't something... missing?  Perhaps
         * something they overlooked?  Mua ha ha ha ha! */
        
        return 0;
    }
    

    phase_1: Border relations with Canada have never been better.

    0x400e32 <main+146>     callq  0x40149e <read_line> 
    #在此随意输入“asdfghjkl".
    #换用stepi命令进入phase_1函数:(后面的phase相同)
    0x400e3a <main+154>     callq  0x400ee0 <phase_1>
    #-----------------------------------------------------------------------------------
    #phase_1函数内部:
    0000000000400ee0 <phase_1>:
      400ee0:	48 83 ec 08          	sub    $0x8,%rsp
      400ee4:	be 00 24 40 00       	mov    $0x402400,%esi
      #这里出现了一个内存地址,进行查看:
      #(gdb) x/20bc $esi
      #0x402400:       66 'B'  111 'o' 114 'r' 100 'd' 101 'e' 114 'r' 32 ' '  114 'r'
      #0x402408:       101 'e' 108 'l' 97 'a'  116 't' 105 'i' 111 'o' 110 'n' 115 's'
      #0x402410:       32 ' '  119 'w' 105 'i' 116 't'
      #貌似是一串字符,继续查看完整得到:Border relations with Canada have never been better.
     #猜想该字符串可能是用来比较的
      400ee9:	e8 4a 04 00 00       	callq  401338 <strings_not_equal>
      400eee:	85 c0                	test   %eax,%eax
      400ef0:	74 05                	je     400ef7 <phase_1+0x17>
      #如果strings_not_equal的返回后eax是0,则不进入explode_bomb,所以我们要进入strings_not_equal看看
      400ef2:	e8 43 05 00 00       	callq  40143a <explode_bomb>
      #注意这里的explode_bomb函数,要避免进入
      400ef7:	48 83 c4 08          	add    $0x8,%rsp
      400efb:	c3                   	retq 
      #-----------------------------------------------------------------------------------
      #0000000000401338 <strings_not_equal>:
      #401338:	41 54                	push   %r12
      #40133a:	55                   	push   %rbp
      #40133b:	53                   	push   %rbx
      #40133c:	48 89 fb             	mov    %rdi,%rbx
      #40133f:	48 89 f5             	mov    %rsi,%rbp
      #401342:	e8 d4 ff ff ff       	callq  40131b <string_length>
      ##该处换用nexti,不进入string_length,完成后发现rax变为9,即我们输入字符串的长度
      #401347:	41 89 c4             	mov    %eax,%r12d
      #40134a:	48 89 ef             	mov    %rbp,%rdi
      #40134d:	e8 c9 ff ff ff       	callq  40131b <string_length>
      ##该处rax变为52,即Border relations with Canada have never been better.的长度,随后比较两处长度
      #401352:	ba 01 00 00 00       	mov    $0x1,%edx
      #401357:	41 39 c4             	cmp    %eax,%r12d
      #40135a:	75 3f                	jne    40139b <strings_not_equal+0x63>
      ##.........
      #到此我们已经可以大致猜想该处答案可能就是Border relations with Canada have never been better.经尝试,果然如此。
    

    phase_2: 1 2 4 8 16 32

    #输入”asdfghjkl“
    0000000000400efc <phase_2>:
      400efc:	55                   	push   %rbp
      400efd:	53                   	push   %rbx
      400efe:	48 83 ec 28          	sub    $0x28,%rsp
      400f02:	48 89 e6             	mov    %rsp,%rsi
      400f05:	e8 52 05 00 00       	callq  40145c <read_six_numbers>
      
      #进入read_six_numbers,看这个名字好像就是读入六个数字
      #-------------------------------------------------------------------
      #000000000040145c <read_six_numbers>:
      #40145c:	48 83 ec 18          	sub    $0x18,%rsp
      #401460:	48 89 f2             	mov    %rsi,%rdx
      #401463:	48 8d 4e 04          	lea    0x4(%rsi),%rcx
      #401467:	48 8d 46 14          	lea    0x14(%rsi),%rax
      #40146b:	48 89 44 24 08       	mov    %rax,0x8(%rsp)
      #401470:	48 8d 46 10          	lea    0x10(%rsi),%rax
      #401474:	48 89 04 24          	mov    %rax,(%rsp)
      #401478:	4c 8d 4e 0c          	lea    0xc(%rsi),%r9
      #40147c:	4c 8d 46 08          	lea    0x8(%rsi),%r8
      #401480:	be c3 25 40 00       	mov    $0x4025c3,%esi
      ##注意这里的一个内存地址,经查看为字符串:
      ##0x4025c3:       37 '%'  100 'd' 32 ' '  37 '%'  100 'd' 32 ' '  37 '%'  100 'd'
      ##0x4025cb:       32 ' '  37 '%'  100 'd' 32 ' '  37 '%'  100 'd' 32 ' '  37 '%'
      ##0x4025d3:       100 'd' 0 '00'
      ##明显为sscanf读入六个数字的format部分
      #401485:	b8 00 00 00 00       	mov    $0x0,%eax
      #40148a:	e8 61 f7 ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
      ##这里是一个sscanf,nexti跳过即可
      #40148f:	83 f8 05             	cmp    $0x5,%eax
      ##注意这里在比较了eax是否大于5(即是否读入了6个数字),当我们第一次的输入没有数字时,eax为0,从而引发explode_bomb
      #401492:	7f 05                	jg     401499 <read_six_numbers+0x3d>
      #401494:	e8 a1 ff ff ff       	callq  40143a <explode_bomb>
      #401499:	48 83 c4 18          	add    $0x18,%rsp
      #40149d:	c3                   	retq   
      ##由此,我们输入字符串”1 2 3 4 5 6 7 8 9 0",在该函数内未引爆炸弹,且eax为6.
      
      400f0a:	83 3c 24 01          	cmpl   $0x1,(%rsp)
      #查看rsp:
      #(gdb) x/28bc $rsp
      #0x7fffffffdba0: 1 '01'        0 '00'        0 '00'        0 '00'        2 '02'        0 '00'        0 '00'        0 '00'
      #0x7fffffffdba8: 3 '03'        0 '00'        0 '00'        0 '00'        4 '04'        0 '00'        0 '00'        0 '00'
      #0x7fffffffdbb0: 5 '05'        0 '00'        0 '00'        0 '00'        6 '06'        0 '00'        0 '00'        0 '00'
      #0x7fffffffdbb8: 49 '1'  20 '24'       64 '@'  0 '00'
      #可以发现rsp指向的就是刚刚读入的6个数字(多余数字并未读入)
      
      400f0e:	74 20                	je     400f30 <phase_2+0x34>
      400f10:	e8 25 05 00 00       	callq  40143a <explode_bomb>
      400f15:	eb 19                	jmp    400f30 <phase_2+0x34>
       #首先是比较了第一个读入的数字是不是1,否则爆炸
      400f17:	8b 43 fc             	mov    -0x4(%rbx),%eax
      400f1a:	01 c0                	add    %eax,%eax
      400f1c:	39 03                	cmp    %eax,(%rbx)
      400f1e:	74 05                	je     400f25 <phase_2+0x29>
      400f20:	e8 15 05 00 00       	callq  40143a <explode_bomb>
      #第三步跳转到这里,首先将rbx指向的前一个数赋值给eax,并将eax*2,比较rbx指向的数和前一个数的两倍,如果不相等,就爆炸。
      400f25:	48 83 c3 04          	add    $0x4,%rbx
      400f29:	48 39 eb             	cmp    %rbp,%rbx
      400f2c:	75 e9                	jne    400f17 <phase_2+0x1b>
      400f2e:	eb 0c                	jmp    400f3c <phase_2+0x40>
      #第四部跳转到这里,将rbx指向后一个数,并比较rbp rbx的值(是否所有的数都被指向比较了一遍),如果不相等就从刚刚的第三部进行新的比较,否则退出函数。
      400f30:	48 8d 5c 24 04       	lea    0x4(%rsp),%rbx
      400f35:	48 8d 6c 24 18       	lea    0x18(%rsp),%rbp
      #第二步跳转到这里,将第二个数的地址给rbx,第六个数后一位的地址给rbp
      400f3a:	eb db                	jmp    400f17 <phase_2+0x1b>
      400f3c:	48 83 c4 28          	add    $0x28,%rsp
      400f40:	5b                   	pop    %rbx
      400f41:	5d                   	pop    %rbp
      400f42:	c3                   	retq   
      
      #由以上分析可知,该函数就是检查读入的前6个数是否是按照*2依次递增的,并且第一个数还必须是1,所以输入1 2 4 8 16 32拆除炸弹。
    

    phase_3: 1 311

    0000000000400f43 <phase_3>:
      400f43:	48 83 ec 18          	sub    $0x18,%rsp
      400f47:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx
      400f4c:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx
      400f51:	be cf 25 40 00       	mov    $0x4025cf,%esi
      400f56:	b8 00 00 00 00       	mov    $0x0,%eax
      400f5b:	e8 90 fc ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
      400f60:	83 f8 01             	cmp    $0x1,%eax
      400f63:	7f 05                	jg     400f6a <phase_3+0x27>
      400f65:	e8 d0 04 00 00       	callq  40143a <explode_bomb>
      #这个地方和phase_3很相似,都是读入两个数字,否则爆炸,于是输入“1 2”
      400f6a:	83 7c 24 08 07       	cmpl   $0x7,0x8(%rsp)
      #查看rsp知,0x8(%rsp)表示读取的第一个数,0xc(%rsp)表示读取的第二个数
      400f6f:	77 3c                	ja     400fad <phase_3+0x6a>
      #比较第一个是否大于7,如果大于,则发生爆炸。
      400f71:	8b 44 24 08          	mov    0x8(%rsp),%eax
      400f75:	ff 24 c5 70 24 40 00 	jmpq   *0x402470(,%rax,8)
      #用第一个数作为switch的条件,判断该跳转到jump table的哪个位置,这里rax为1.
      400f7c:	b8 cf 00 00 00       	mov    $0xcf,%eax
      400f81:	eb 3b                	jmp    400fbe <phase_3+0x7b>
      400f83:	b8 c3 02 00 00       	mov    $0x2c3,%eax
      400f88:	eb 34                	jmp    400fbe <phase_3+0x7b>
      400f8a:	b8 00 01 00 00       	mov    $0x100,%eax
      400f8f:	eb 2d                	jmp    400fbe <phase_3+0x7b>
      400f91:	b8 85 01 00 00       	mov    $0x185,%eax
      400f96:	eb 26                	jmp    400fbe <phase_3+0x7b>
      400f98:	b8 ce 00 00 00       	mov    $0xce,%eax
      400f9d:	eb 1f                	jmp    400fbe <phase_3+0x7b>
      400f9f:	b8 aa 02 00 00       	mov    $0x2aa,%eax
      400fa4:	eb 18                	jmp    400fbe <phase_3+0x7b>
      400fa6:	b8 47 01 00 00       	mov    $0x147,%eax
      400fab:	eb 11                	jmp    400fbe <phase_3+0x7b>
      400fad:	e8 88 04 00 00       	callq  40143a <explode_bomb>
      400fb2:	b8 00 00 00 00       	mov    $0x0,%eax
      400fb7:	eb 05                	jmp    400fbe <phase_3+0x7b>
      400fb9:	b8 37 01 00 00       	mov    $0x137,%eax
      400fbe:	3b 44 24 0c          	cmp    0xc(%rsp),%eax
      400fc2:	74 05                	je     400fc9 <phase_3+0x86>
      #第一步跳转到这里,发现其在比较第二个数是不是0x137,如果是的话就退出函数,于是我们输入“1 311”即可。
      400fc4:	e8 71 04 00 00       	callq  40143a <explode_bomb>
      400fc9:	48 83 c4 18          	add    $0x18,%rsp
      400fcd:	c3                   	retq  
    

    phase_4: [0, 1, 3, 7] 0

    000000000040100c <phase_4>:
      40100c:	48 83 ec 18          	sub    $0x18,%rsp
      401010:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx
      401015:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx
      40101a:	be cf 25 40 00       	mov    $0x4025cf,%esi
      40101f:	b8 00 00 00 00       	mov    $0x0,%eax
      401024:	e8 c7 fb ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
      401029:	83 f8 02             	cmp    $0x2,%eax
      40102c:	75 07                	jne    401035 <phase_4+0x29>
      #跟phase_3一样,读入两个数字,否则爆炸,于是输入“1 2"
      40102e:	83 7c 24 08 0e       	cmpl   $0xe,0x8(%rsp)
      401033:	76 05                	jbe    40103a <phase_4+0x2e>
      401035:	e8 00 04 00 00       	callq  40143a <explode_bomb>
      #如果第一个数字大于14,就爆炸
      40103a:	ba 0e 00 00 00       	mov    $0xe,%edx
      40103f:	be 00 00 00 00       	mov    $0x0,%esi
      401044:	8b 7c 24 08          	mov    0x8(%rsp),%edi
      401048:	e8 81 ff ff ff       	callq  400fce <func4>
      #edx=14 esi=0 edi=第一个读入的数字
      #--------------------------------------------------------------------------------
    #0000000000400fce <func4>:
      #400fce:	48 83 ec 08          	sub    $0x8,%rsp
      #400fd2:	89 d0                	mov    %edx,%eax
      #400fd4:	29 f0                	sub    %esi,%eax
      ##eax=14
      #400fd6:	89 c1                	mov    %eax,%ecx
      #400fd8:	c1 e9 1f             	shr    $0x1f,%ecx
      ##ecx中是14逻辑右移31位,ecx=0
      #400fdb:	01 c8                	add    %ecx,%eax
      ##eax=14
      #400fdd:	d1 f8                	sar    %eax
      ##这个地方我犯了一个错误,书上说位移操作需要imm8或者%cl来指示位移的个数,这两个都是要显示写出来的,如果单单写一个位移操作符,默认是位移一位。
      ##eax=eax>>1=14/2=7
      #400fdf:	8d 0c 30             	lea    (%rax,%rsi,1),%ecx
      ##ecx=7+0*1=7
      #400fe2:	39 f9                	cmp    %edi,%ecx
      #400fe4:	7e 0c                	jle    400ff2 <func4+0x24>
      ##比较7和第一个读入的数字,如果第一个数字大于等于7,跳至400ff2
      #400fe6:	8d 51 ff             	lea    -0x1(%rcx),%edx
      ##edx=7-1=6
      #400fe9:	e8 e0 ff ff ff       	callq  400fce <func4>
      ##递归调用
      #400fee:	01 c0                	add    %eax,%eax
      #400ff0:	eb 15                	jmp    401007 <func4+0x39>
      ##eax=eax*2 退出函数
      #400ff2:	b8 00 00 00 00       	mov    $0x0,%eax
      #400ff7:	39 f9                	cmp    %edi,%ecx
      #400ff9:	7d 0c                	jge    401007 <func4+0x39>
      ##如果7>=第一个读入的数,就退出函数,且eax=0
      #400ffb:	8d 71 01             	lea    0x1(%rcx),%esi
      ##esi=7+1=8
      #400ffe:	e8 cb ff ff ff       	callq  400fce <func4>
      ##递归调用
      #401003:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax
      #401007:	48 83 c4 08          	add    $0x8,%rsp
      #40100b:	c3                   	retq 
      ##首先来看第一个数字小于7的情况,不断递归调用直到第一个数字大于等于ecx(((7-1)/2)-1.....)(7->3->1-   >0),跳至400ff2,此时如果ecx大于等于第一个数,则退出函数。所以要想拆除炸弹,可以在小于7的数中间找一   个(并且要能够等于之后的ecx),所以0 1 3都可以,此时返回的eax为0。
      ##当数字大于等于7时,直接跳转到400ff2,可以使第一个数就是7,从而退出函数。
      40104d:	85 c0                	test   %eax,%eax
      40104f:	75 07                	jne    401058 <phase_4+0x4c>
      #如果返回的eax不为0,则爆炸。
      401051:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp)
      #比较第二个数是否为0,否则爆炸。
      401056:	74 05                	je     40105d <phase_4+0x51>
      401058:	e8 dd 03 00 00       	callq  40143a <explode_bomb>
      40105d:	48 83 c4 18          	add    $0x18,%rsp
      401061:	c3                   	retq
    
    

    phase_5: [ionefg]

    0000000000401062 <phase_5>:
      401062:	53                   	push   %rbx
      401063:	48 83 ec 20          	sub    $0x20,%rsp
      401067:	48 89 fb             	mov    %rdi,%rbx
      40106a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
      #这个地方是在设置“Stack	Canaries”,后面会查看这只“金丝雀”是否被改变了,防止栈溢出攻击。
      401071:	00 00 
      401073:	48 89 44 24 18       	mov    %rax,0x18(%rsp)
      401078:	31 c0                	xor    %eax,%eax
      40107a:	e8 9c 02 00 00       	callq  40131b <string_length>
      40107f:	83 f8 06             	cmp    $0x6,%eax
      401082:	74 4e                	je     4010d2 <phase_5+0x70>
      401084:	e8 b1 03 00 00       	callq  40143a <explode_bomb>
      #判断读入字符串长度是否为6,否则爆炸,于是输入字符串”abcdef“测试。
      401089:	eb 47                	jmp    4010d2 <phase_5+0x70>
      40108b:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx
      #测试rbx是刚刚输入的字符串的地址:
      #(gdb) x/20bc $rbx
      #0x6038c0 <input_strings+320>:   97 'a'  98 'b'  99 'c'  100 'd' 101 'e' 102 'f' 0 '00'        0 '00'
      #所以此时ecx内部为第一个字符
      40108f:	88 0c 24             	mov    %cl,(%rsp)
      401092:	48 8b 14 24          	mov    (%rsp),%rdx
      401096:	83 e2 0f             	and    $0xf,%edx
      #此时edx内为字符ascii码十六进制的低位。
      401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx
      #测试0x4024b0这个地址:
      #(gdb) x/s 0x4024b0
      #0x4024b0 <array.3449>:  "maduiersnfotvbylSo you think you can stop the bomb with ctrl-   #c, do you?"
      #注意到”maduiersnfotvbyl“刚好是16个,代表字母十六进制低位的16种可能。所以说,这里就是将字母的十六进制低位加上0x4024b0这个基地址选出的字母放到edx中。
      4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1)
      #验证rsp对应内容:为杂乱序列,这里就是将刚刚生成的分别6个字母分别放到以rsp+0x10为基地址的内存中。
      4010a4:	48 83 c0 01          	add    $0x1,%rax
      4010a8:	48 83 f8 06          	cmp    $0x6,%rax
      4010ac:	75 dd                	jne    40108b <phase_5+0x29>
      #此处为一个6次的循环
      4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)
      #0x16=0x10+6将刚刚生成的6个字母形成一个字符串。
      4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi
      #(gdb) x/s 0x40245e
      #0x40245e:       "flyers" 这可能就是等会要比较的字符串了
      4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
      #rdi指向刚刚生成的新字符串
      4010bd:	e8 76 02 00 00       	callq  401338 <strings_not_equal>
      4010c2:	85 c0                	test   %eax,%eax
      #测试两个字符串是否相等
      4010c4:	74 13                	je     4010d9 <phase_5+0x77>
      4010c6:	e8 6f 03 00 00       	callq  40143a <explode_bomb>
      4010cb:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
      4010d0:	eb 07                	jmp    4010d9 <phase_5+0x77>
      4010d2:	b8 00 00 00 00       	mov    $0x0,%eax
      4010d7:	eb b2                	jmp    40108b <phase_5+0x29>
      #第一步跳到这里,就是把eax置了个0 。。。
      4010d9:	48 8b 44 24 18       	mov    0x18(%rsp),%rax
      4010de:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
      4010e5:	00 00 
      4010e7:	74 05                	je     4010ee <phase_5+0x8c>
      4010e9:	e8 42 fa ff ff       	callq  400b30 <__stack_chk_fail@plt>
      4010ee:	48 83 c4 20          	add    $0x20,%rsp
      4010f2:	5b                   	pop    %rbx
      4010f3:	c3                   	retq   
      
      #由以上分析,maduiersnfotvbyl分别对应低位为0123456789ABCDEF的字符,所以为了生成”flyers“字符串,低位必须为”9FE567“,例如”ionefg“就可以。(查ascii码表)
    

    phase_6: 4 3 2 1 6 5

    00000000004010f4 <phase_6>:
      4010f4:	41 56                	push   %r14
      4010f6:	41 55                	push   %r13
      4010f8:	41 54                	push   %r12
      4010fa:	55                   	push   %rbp
      4010fb:	53                   	push   %rbx
      4010fc:	48 83 ec 50          	sub    $0x50,%rsp
      401100:	49 89 e5             	mov    %rsp,%r13
      401103:	48 89 e6             	mov    %rsp,%rsi
      401106:	e8 51 03 00 00       	callq  40145c <read_six_numbers>
      #以rsp为基地址(栈)读取六个数字。
      40110b:	49 89 e6             	mov    %rsp,%r14
      40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d
      401114:	4c 89 ed             	mov    %r13,%rbp
      401117:	41 8b 45 00          	mov    0x0(%r13),%eax
      40111b:	83 e8 01             	sub    $0x1,%eax
      40111e:	83 f8 05             	cmp    $0x5,%eax
      401121:	76 05                	jbe    401128 <phase_6+0x34>
      401123:	e8 12 03 00 00       	callq  40143a <explode_bomb>
      401128:	41 83 c4 01          	add    $0x1,%r12d
      40112c:	41 83 fc 06          	cmp    $0x6,%r12d
      401130:	74 21                	je     401153 <phase_6+0x5f>
      401132:	44 89 e3             	mov    %r12d,%ebx
      401135:	48 63 c3             	movslq %ebx,%rax
      401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax
      40113b:	39 45 00             	cmp    %eax,0x0(%rbp)
      40113e:	75 05                	jne    401145 <phase_6+0x51>
      401140:	e8 f5 02 00 00       	callq  40143a <explode_bomb>
      401145:	83 c3 01             	add    $0x1,%ebx
      401148:	83 fb 05             	cmp    $0x5,%ebx
      40114b:	7e e8                	jle    401135 <phase_6+0x41>
      #这个上面是一个嵌套循环,外侧循环6次,分别测试读入的数字是否小于等于6(实际是无符号数减1后是否小于等   #于5,所以0是不行的),内部的循环比较当前的数字和剩下的数字是否相同。总结起来就是要求这六个数字各不相   #同,且大于0小于7,所以这六个数字可以是123456的组合。
      40114d:	49 83 c5 04          	add    $0x4,%r13
      401151:	eb c1                	jmp    401114 <phase_6+0x20>
      401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi
      401158:	4c 89 f0             	mov    %r14,%rax
      40115b:	b9 07 00 00 00       	mov    $0x7,%ecx
      401160:	89 ca                	mov    %ecx,%edx
      401162:	2b 10                	sub    (%rax),%edx
      401164:	89 10                	mov    %edx,(%rax)
      401166:	48 83 c0 04          	add    $0x4,%rax
      40116a:	48 39 f0             	cmp    %rsi,%rax
      40116d:	75 f1                	jne    401160 <phase_6+0x6c>
      #上面这个循环是依次将刚刚读入的六个数字将7作为被减数得到的结果。如输入654321将得到123456.
      40116f:	be 00 00 00 00       	mov    $0x0,%esi
      401174:	eb 21                	jmp    401197 <phase_6+0xa3>
      401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx
      40117a:	83 c0 01             	add    $0x1,%eax
      40117d:	39 c8                	cmp    %ecx,%eax
      40117f:	75 f5                	jne    401176 <phase_6+0x82>
      401181:	eb 05                	jmp    401188 <phase_6+0x94>
      401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx
      401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2)
      40118d:	48 83 c6 04          	add    $0x4,%rsi
      401191:	48 83 fe 18          	cmp    $0x18,%rsi
      401195:	74 14                	je     4011ab <phase_6+0xb7>
      401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
      40119a:	83 f9 01             	cmp    $0x1,%ecx
      40119d:	7e e4                	jle    401183 <phase_6+0x8f>
      40119f:	b8 01 00 00 00       	mov    $0x1,%eax
      4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx
      4011a9:	eb cb                	jmp    401176 <phase_6+0x82>
      4011ab:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx
      4011b0:	48 8d 44 24 28       	lea    0x28(%rsp),%rax
      4011b5:	48 8d 74 24 50       	lea    0x50(%rsp),%rsi
      4011ba:	48 89 d9             	mov    %rbx,%rcx
      4011bd:	48 8b 10             	mov    (%rax),%rdx
      4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)
      4011c4:	48 83 c0 08          	add    $0x8,%rax
      4011c8:	48 39 f0             	cmp    %rsi,%rax
      4011cb:	74 05                	je     4011d2 <phase_6+0xde>
      4011cd:	48 89 d1             	mov    %rdx,%rcx
      4011d0:	eb eb                	jmp    4011bd <phase_6+0xc9>
      4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)
      4011d9:	00 
      #上面这一段是本实验最难理解的部分,循环的大致意思是设置0x6032d0为基地址的内存模块和以rsp为基地址的内存模块保存刚刚输入的数字和0x6032d0/e0/f0 0x603300/10/20,其实并不用完全弄懂该部分,结合下面的”爆炸导火索“更好理解一些。
      
      4011da:	bd 05 00 00 00       	mov    $0x5,%ebp
      4011df:	48 8b 43 08          	mov    0x8(%rbx),%rax
      4011e3:	8b 00                	mov    (%rax),%eax
      4011e5:	39 03                	cmp    %eax,(%rbx)
      4011e7:	7d 05                	jge    4011ee <phase_6+0xfa>
      4011e9:	e8 4c 02 00 00       	callq  40143a <explode_bomb>
      4011ee:	48 8b 5b 08          	mov    0x8(%rbx),%rbx
      4011f2:	83 ed 01             	sub    $0x1,%ebp
      4011f5:	75 e8                	jne    4011df <phase_6+0xeb>
      #当输入123456
      #(gdb) x/20wx $rsp
    #0x7fffffffdb60: 0x00000006      0x00000005      0x00000004      0x00000003
    #0x7fffffffdb70: 0x00000002      0x00000001      0x00000000      0x00000000
    #0x7fffffffdb80: 0x00603320      0x00000000      0x00603310      0x00000000
    #0x7fffffffdb90: 0x00603300      0x00000000      0x006032f0      0x00000000
    #0x7fffffffdba0: 0x006032e0      0x00000000      0x006032d0      0x00000000
    # (gdb) x/20wx $rdx
    #0x6032d0 <node1>:       0x0000014c      0x00000001      0x00000000      0x00000000
    #0x6032e0 <node2>:       0x000000a8      0x00000002      0x006032d0      0x00000000
    #0x6032f0 <node3>:       0x0000039c      0x00000003      0x006032e0      0x00000000
    #0x603300 <node4>:       0x000002b3      0x00000004      0x006032f0      0x00000000
    #0x603310 <node5>:       0x000001dd      0x00000005      0x00603300      0x00000000
    #0x603320 <node6>:       0x000001bb      0x00000006      0x00603310      0x00000000
    #该测试循环是这样工作的,从栈中后部的第一个地址开始(这里地址1是0x00603320)判读(地址1)>=((地址1+0x8)),注意有两层括号,是否成立,不成立即爆炸,若成立,则令地址1=(地址1+0x8),进行下一次循环。在这个例子中,由于1bb<1dd,发生爆炸。于是乎我们的任务就变成了寻找地址1是哪一个的规律以及地址+0x8保存的是哪些地址的规律。
    #输入654321后,得到:
    #(gdb) x/24wx $rsp    
    #0x7fffffffdb60: 0x00000001      0x00000002      0x00000003      0x00000004
    #0x7fffffffdb70: 0x00000005      0x00000006      0x00000000      0x00000000
    #0x7fffffffdb80: 0x006032d0      0x00000000      0x006032e0      0x00000000
    #0x7fffffffdb90: 0x006032f0      0x00000000      0x00603300      0x00000000
    #0x7fffffffdba0: 0x00603310      0x00000000      0x00603320      0x00000000
    
    #0x6032d0 <node1>:       0x0000014c      0x00000001      0x006032e0      0x00000000
    #0x6032e0 <node2>:       0x000000a8      0x00000002      0x006032f0      0x00000000
    #0x6032f0 <node3>:       0x0000039c      0x00000003      0x00603300      0x00000000
    #0x603300 <node4>:       0x000002b3      0x00000004      0x00603310      0x00000000
    #0x603310 <node5>:       0x000001dd      0x00000005      0x00603320      0x00000000
    #0x603320 <node6>:       0x000001bb      0x00000006      0x00000000      0x00000000
    #结合动态运行可知,地址1为rsp后面保存的第一个地址,并且将依次将随后的5个地址存入0x60...+0x8的地方。结合之前123456的测试,这里可以观察到,7-x操作后数字与地址对应关系:
    # 1 - > 0x6032d0
    # 2 - > 0x6032e0
    # 3 - > 0x6032f0
    # 4 - > 0x603300
    # 5 - > 0x603310
    # 6 - > 0x603320
    
    #现在结合上面循环测试的规则,我们对(0x603...)的六个常数进行排列:0x0000039c > 0x000002b3 > 0x000001dd > 0x000001bb > 0x0000014c > 0x000000a8,用地址表示就是(0x6032f0) > (0x603300) > (0x603310) > (0x603320) > (0x6032d0) > (0x6032e0)
    
    #所以我们应该得到这样一个内存:
    #0x6032d0 <node1>:       0x0000014c      0x00000001      0x006032e0      0x00000000
    #0x6032e0 <node2>:       0x000000a8      0x00000002      0x00000000      0x00000000
    #0x6032e0+0x8应该是0,因为0x0a8它是最小的了,且只比较5次。
    #0x6032f0 <node3>:       0x0000039c      0x00000003      0x00603300      0x00000000
    #0x603300 <node4>:       0x000002b3      0x00000004      0x00603310      0x00000000
    #0x603310 <node5>:       0x000001dd      0x00000005      0x00603320      0x00000000
    #0x603320 <node6>:       0x000001bb      0x00000006      0x006032d0      0x00000000
    
    #即得到这样一个栈空间:
    #0x7fffffffdb60: xxxxxxxxxx      xxxxxxxxxx      xxxxxxxxxx      xxxxxxxxxx
    #0x7fffffffdb70: xxxxxxxxxx      xxxxxxxxxx      xxxxxxxxxx      xxxxxxxxxx
    #0x7fffffffdb80: 0x006032f0      0x00000000      0x00603300      0x00000000
    #0x7fffffffdb90: 0x00603310      0x00000000      0x00603320      0x00000000
    #0x7fffffffdba0: 0x006032d0      0x00000000      0x006032e0      0x00000000
    
    #由于:
    # 1 - > 0x6032d0
    # 2 - > 0x6032e0
    # 3 - > 0x6032f0
    # 4 - > 0x603300
    # 5 - > 0x603310
    # 6 - > 0x603320
    #所以7-x操作后应该是345612,即应该输入432165。
    
      4011f7:	48 83 c4 50          	add    $0x50,%rsp
      4011fb:	5b                   	pop    %rbx
      4011fc:	5d                   	pop    %rbp
      4011fd:	41 5c                	pop    %r12
      4011ff:	41 5d                	pop    %r13
      401201:	41 5e                	pop    %r14
      401203:	c3                   	retq
    

    综上,输入文件应该如下:

    psol.txt

    Border relations with Canada have never been better.
    1 2 4 8 16 32
    1 311
    0 0
    ionefg
    4 3 2 1 6 5
    
    

    运行输出:(也可以手动输入)

    frank@under:~/Desktop/cs:app/lab/bomblab$ ./bomb psol.txt 
    Welcome to my fiendish little bomb. You have 6 phases with
    which to blow yourself up. Have a nice day!
    Phase 1 defused. How about the next one?
    That's number 2.  Keep going!
    Halfway there!
    So you got that one.  Try this one.
    Good work!  On to the next...
    Congratulations! You've defused the bomb!
    




    [2017.10.27]更新:学校重新安排了一次相似的实验,只是把一些常量改变了,然后添加了一个“secret_phase”。这次把最后一个阶段将输入逐个7减后的算法详细分析下,上次是利用结果推测过程,没有具体研究算法。

    注:下面分析的第六阶段我们学校改过的,密码每个人都不一样(这个的bomb_id为0x28),但算法是和cmu的那个炸弹一样的。secret阶段原CMU炸弹并没有

      ..........(参见前文)
      #后部分的代码主要分两个算法
      #“混淆算法开始”
      #混淆阶段1
      40116e:   be 00 00 00 00          mov    $0x0,%esi
      401173:   eb 1a                   jmp    40118f <phase_6+0xad>
      401175:   48 8b 52 08             mov    0x8(%rdx),%rdx
      401179:   83 c0 01                add    $0x1,%eax
      40117c:   39 c8                   cmp    %ecx,%eax
      40117e:   75 f5                   jne    401175 <phase_6+0x93>
      401180:   48 89 54 74 20          mov    %rdx,0x20(%rsp,%rsi,2)
      401185:   48 83 c6 04             add    $0x4,%rsi
      401189:   48 83 fe 18             cmp    $0x18,%rsi
      40118d:   74 14                   je     4011a3 <phase_6+0xc1>
      40118f:   8b 0c 34                mov    (%rsp,%rsi,1),%ecx
      401192:   b8 01 00 00 00          mov    $0x1,%eax
      401197:   ba f0 32 60 00          mov    $0x6032f0,%edx
      40119c:   83 f9 01                cmp    $0x1,%ecx
      40119f:   7f d4                   jg     401175 <phase_6+0x93>
      4011a1:   eb dd                   jmp    401180 <phase_6+0x9e>
      #混淆阶段2
      4011a3:   48 8b 5c 24 20          mov    0x20(%rsp),%rbx
      4011a8:   48 8d 44 24 20          lea    0x20(%rsp),%rax
      4011ad:   48 8d 74 24 48          lea    0x48(%rsp),%rsi
      4011b2:   48 89 d9                mov    %rbx,%rcx
      4011b5:   48 8b 50 08             mov    0x8(%rax),%rdx
      4011b9:   48 89 51 08             mov    %rdx,0x8(%rcx)
      4011bd:   48 83 c0 08             add    $0x8,%rax
      4011c1:   48 89 d1                mov    %rdx,%rcx
      4011c4:   48 39 c6                cmp    %rax,%rsi
      4011c7:   75 ec                   jne    4011b5 <phase_6+0xd3>
      4011c9:   48 c7 42 08 00 00 00    movq   $0x0,0x8(%rdx)
      #验证算法开始
      4011d0:   00 
      4011d1:   bd 05 00 00 00          mov    $0x5,%ebp
      4011d6:   48 8b 43 08             mov    0x8(%rbx),%rax
      4011da:   8b 00                   mov    (%rax),%eax
      4011dc:   39 03                   cmp    %eax,(%rbx)
      4011de:   7d 05                   jge    4011e5 <phase_6+0x103>
      4011e0:   e8 52 02 00 00          callq  401437 <explode_bomb>
      4011e5: 48 8b 5b 08               mov    0x8(%rbx),%rbx
      4011e9:   83 ed 01                sub    $0x1,%ebp
      4011ec:   75 e8                   jne    4011d6 <phase_6+0xf4>
      .......(见前文)
    

    经过前面的7减算法后,我们如果输入1 6 2 5 3 4, 7减后为6 1 5 2 4 3,查看rsp为:

    另外注意到这一句话有一个常量地址

      401197:   ba f0 32 60 00          mov    $0x6032f0,%edx
    

    查看该地址内容:

    可以观察到每一行(16个字节)是由 4字节xxx常数 + 4字节1~6 + 下一个16字节的地址(除了最后一个16字节除外)


    分析混淆算法的第一阶段

      40116e:   be 00 00 00 00          mov    $0x0,%esi
      401173:   eb 1a                   jmp    40118f <phase_6+0xad>
      #小循环(根据取到的数在0x6032f0起始的内存块中寻找第几个地址,例如为5的话就是0x6032f0 + (5-1)*16 + 8代表的0x603330
      401175:   48 8b 52 08             mov    0x8(%rdx),%rdx
      401179:   83 c0 01                add    $0x1,%eax
      40117c:   39 c8                   cmp    %ecx,%eax
      40117e:   75 f5                   jne    401175 <phase_6+0x93>
      #大循环,取出六个数,如果是1的话直接在栈中append 0x6032f0这个地址(最小的那个),否则进入上面的小循环取得一个地址然后append到栈中。
      401180:   48 89 54 74 20          mov    %rdx,0x20(%rsp,%rsi,2)
      401185:   48 83 c6 04             add    $0x4,%rsi
      401189:   48 83 fe 18             cmp    $0x18,%rsi
      40118d:   74 14                   je     4011a3 <phase_6+0xc1>
      40118f:   8b 0c 34                mov    (%rsp,%rsi,1),%ecx
      401192:   b8 01 00 00 00          mov    $0x1,%eax
      401197:   ba f0 32 60 00          mov    $0x6032f0,%edx
      40119c:   83 f9 01                cmp    $0x1,%ecx
      40119f:   7f d4                   jg     401175 <phase_6+0x93>
      4011a1:   eb dd                   jmp    401180 <phase_6+0x9e>
    

    所以,我们如果输入的是1 6 2 5 3 4,7减操作后为 6 1 5 2 4 3,栈中就应该被依次append

    3340
    32f0
    3330
    3300
    3320
    3310

    如图:


    接着分析混淆算法的第二阶段

      4011a3:   48 8b 5c 24 20          mov    0x20(%rsp),%rbx	#栈中第一个append进的地址
      4011a8:   48 8d 44 24 20          lea    0x20(%rsp),%rax	#栈中第一个append进的地址的地址
      4011ad:   48 8d 74 24 48          lea    0x48(%rsp),%rsi	#栈中最后一个append进的地址的地址
      4011b2:   48 89 d9                mov    %rbx,%rcx	#第一个append进的地址
      
      #开始循环直到最后一个append进的地址也存入0x6032f0起始的内存块中
      4011b5:   48 8b 50 08             mov    0x8(%rax),%rdx
      4011b9:   48 89 51 08             mov    %rdx,0x8(%rcx)	#将rax指向的下一个地址存入0x6032f0起始的内存块中
      4011bd:   48 83 c0 08             add    $0x8,%rax		#更新使rax指向栈中下一个之前存入的地址
      4011c1:   48 89 d1                mov    %rdx,%rcx		#这次存入的地址变为下次要存入的基地址
      4011c4:   48 39 c6                cmp    %rax,%rsi
      4011c7:   75 ec                   jne    4011b5 <phase_6+0xd3>
      4011c9:   48 c7 42 08 00 00 00    movq   $0x0,0x8(%rdx)
    

    由上面的栈图,我们的操作就是(用<-代表写入):603340+8<-6032f0 6032f0+8<-603330 603330+8<-603300 603300+8<-603320 603320+8<-603310 而603310+8原本存储的603320没改变为0,验证:


    开始分析导火索(验证算法)

      4011d0:   00 
      4011d1:   bd 05 00 00 00          mov    $0x5,%ebp	#设计循环量为5次
      #开始循环
      4011d6:   48 8b 43 08             mov    0x8(%rbx),%rax	#第一次rbx保存的是第一个存入栈中的地址,rax将变为rbx+8中保存的地址
      4011da:   8b 00                   mov    (%rax),%eax
      4011dc:   39 03                   cmp    %eax,(%rbx)
      4011de:   7d 05                   jge    4011e5 <phase_6+0x103>	#rbx指向的常数必须大于等于rbx+8指向的常数,否则爆炸
      4011e0:   e8 52 02 00 00          callq  401437 <explode_bomb>
      4011e5: 48 8b 5b 08               mov    0x8(%rbx),%rbx	#将rbx替换为rbx+8保存的地址
      
      4011e9:   83 ed 01                sub    $0x1,%ebp
      4011ec:   75 e8                   jne    4011d6 <phase_6+0xf4>
    

    逆推构建输入

    由导火索算法的分析,又因为常量如下:

    2e4(0x603310) > 257(0x6032f0) > 235(0x603330) > 169(0x603300) > 098(0x603340) > 05b(0x603320)

    所以我们应该将0x6032f0起始的内存块构建为这个样子:

    0x6032f0 <node1>:       0x0000000100000257      0x0000000000603330
    0x603300 <node2>:       0x0000000200000169      0x0000000000603340
    0x603310 <node3>:       0x00000003000002e4      0x00000000006032f0
    0x603320 <node4>:       0x000000040000005b      0x0000000000000000
    0x603330 <node5>:       0x0000000500000235      0x0000000000603300
    0x603340 <node6>:       0x0000000600000098      0x0000000000603320
    

    即我们的存入操作为:603310+8<-6032f0 6032f0+8<-603330 603330+8<-603300 603300+8<-603340 603340+8<-603320

    由混淆算法的第二阶段分析,我们的存入我们的栈append的地址应该是这个顺序:

    603310 6032f0 603330 603300 603340 603320

    由混淆算法的第一阶段分析,我们我们输入的数字7减操作后应该为:

    3 1 5 2 6 4

    所以减7前的密码为:4 6 2 5 1 3


    secret_phase

    默认情况下并不能激发隐藏关卡:

    在bomb的反汇编代码内找到调用secret_phase的函数,居然是phase_defused ...

    phase_defused内关键的代码:

      4015fe:	be c2 25 40 00       	mov    $0x4025c2,%esi
      401603:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
      401608:	e8 2b fd ff ff       	callq  401338 <strings_not_equal>
      40160d:	85 c0                	test   %eax,%eax
      40160f:	75 1e                	jne    40162f <phase_defused+0x71>
      401611:	bf 98 24 40 00       	mov    $0x402498,%edi
      401616:	e8 c5 f4 ff ff       	callq  400ae0 <puts@plt>
      40161b:	bf c0 24 40 00       	mov    $0x4024c0,%edi
      401620:	e8 bb f4 ff ff       	callq  400ae0 <puts@plt>
      401625:	b8 00 00 00 00       	mov    $0x0,%eax
      40162a:	e8 1f fc ff ff       	callq  40124e <secret_phase>
    

    注意到常数地址0x4025c2。

    所以phase_defused应该是读入了字符,判断是否等于DrEvil,相等的话就进入隐藏关卡。

    于是我们在phase_defused中寻找是否有读入的命令,发现就在上面相邻处有:

      4015ea:	be b9 25 40 00       	mov    $0x4025b9,%esi
      4015ef:	bf 90 38 60 00       	mov    $0x603890,%edi
      4015f4:	e8 b7 f5 ff ff       	callq  400bb0 <__isoc99_sscanf@plt>
      4015f9:	83 f8 03             	cmp    $0x3,%eax
      4015fc:	75 31                	jne    40162f <phase_defused+0x71>
    

    查看0x4025b9

    可见是读入两个整数和一个字符串

    查看0x603890

    就是我在第四阶段输入的密码——两个整数,所以我们在第四阶段输入10 5 DrEvil即可进入隐藏关卡:


    分析隐藏关卡的算法

    000000000040124e <secret_phase>:
      40124e:   53                      push   %rbx
      40124f:   e8 44 02 00 00          callq  401498 <read_line>	#读入一行数据
      401254:   ba 0a 00 00 00          mov    $0xa,%edx
      401259:   be 00 00 00 00          mov    $0x0,%esi
      40125e:   48 89 c7                mov    %rax,%rdi
      401261:   e8 2a f9 ff ff          callq  400b90 <strtol@plt>	#将读入的数据转化为整数
      401266:   48 89 c3                mov    %rax,%rbx
      401269:   8d 40 ff                lea    -0x1(%rax),%eax
      40126c:   3d e8 03 00 00          cmp    $0x3e8,%eax	#不能读入0x3e8+1 = 1001
      401271:   76 05                   jbe    401278 <secret_phase+0x2a>
      401273:   e8 bf 01 00 00          callq  401437 <explode_bomb>
      401278:   89 de                   mov    %ebx,%esi	#参数二为刚刚读入的数
      40127a:   bf 10 31 60 00          mov    $0x603110,%edi	#参数一为
      40127f:   e8 8c ff ff ff          callq  401210 <fun7>                
      401284:   83 f8 03                cmp    $0x3,%eax	#必须返回3
      401287:   74 05                   je     40128e <secret_phase+0x40>
      401289:   e8 a9 01 00 00          callq  401437 <explode_bomb>
      .......
    

    检查调用fun7传入的第一个参数0x603110

    进入fun7 (必须返回3)

    0000000000401210 <fun7>:
      401210:   48 83 ec 08             sub    $0x8,%rsp
      401214:   48 85 ff                test   %rdi,%rdi
      401217:   74 2b                   je     401244 <fun7+0x34>	#第一次由secret_phase调用的话,不可能为0
      401219:   8b 17                   mov    (%rdi),%edx	#0x24
      40121b:   39 f2                   cmp    %esi,%edx	#比较读入的数和0x24
      40121d:   7e 0d                   jle    40122c <fun7+0x1c>
      40121f:   48 8b 7f 08             mov    0x8(%rdi),%rdi
      401223:   e8 e8 ff ff ff          callq  401210 <fun7>	#又是TMD的递归。。史老师您就这么热爱递归嘛。。。(我们学校把第4阶段也改成了递归)
      401228:   01 c0                   add    %eax,%eax
      40122a:   eb 1d                   jmp    401249 <fun7+0x39>
      40122c:   b8 00 00 00 00          mov    $0x0,%eax
      401231:   39 f2                   cmp    %esi,%edx
      401233:   74 14                   je     401249 <fun7+0x39>
      401235:   48 8b 7f 10             mov    0x10(%rdi),%rdi
      401239:   e8 d2 ff ff ff          callq  401210 <fun7>
      40123e:   8d 44 00 01             lea    0x1(%rax,%rax,1),%eax
      401242:   eb 05                   jmp    401249 <fun7+0x39>
      401244:   b8 ff ff ff ff          mov    $0xffffffff,%eax
      401249:   48 83 c4 08             add    $0x8,%rsp
      40124d:   c3                      retq 
    

    为了方便分析算法,抽象成伪代码:

    需要返回3,初始rdi=0x603110,esi = input_number, eax=input_number-1
    
    若rdi=0,返回eax = -1
    edx = (rdi)
    若rdi指向的常数小于等于input_number,跳L1
    rdi += 8;
    递归
    eax = eax*2
    return
    L1:   eax = 0
    如果esi=edx,return
    rdi = (%rdi+0x10)
    递归
    eax = 2*eax+1
    return
    eax = -1
    return
    

    可以观察到,返回值只有eax = eax*2(偶数)eax = 2*eax+1(奇数)和-1与0这四种情况,所以为了获得3的返回值,我们的递归顺序应该如下:

    返回3: 2*1 + 1 -> 返回1: 2*0 + 1 -> 返回0 : 0*2 -> eax=0, esi=edx,return

    观察0x603110起始的内存块:

    0x603110 <n1>:          0x0000000000000024      0x0000000000603130
    0x603120 <n1+16>:       0x0000000000603150      0x0000000000000000
    0x603130 <n21>:         0x0000000000000008      0x00000000006031b0
    0x603140 <n21+16>:      0x0000000000603170      0x0000000000000000
    
    0x603150 <n22>:         0x0000000000000032      0x0000000000603190
    0x603160 <n22+16>:      0x00000000006031d0      0x0000000000000000
    0x603170 <n32>:         0x0000000000000016      0x0000000000603290
    0x603180 <n32+16>:      0x0000000000603250      0x0000000000000000
    0x603190 <n33>:         0x000000000000002d      0x00000000006031f0
    0x6031a0 <n33+16>:      0x00000000006032b0      0x0000000000000000
    0x6031b0 <n31>:         0x0000000000000006      0x0000000000603210
    0x6031c0 <n31+16>:      0x0000000000603270      0x0000000000000000
    
    0x6031d0 <n34>:         0x000000000000006b      0x0000000000603230
    0x6031e0 <n34+16>:      0x00000000006032d0      0x0000000000000000
    0x6031f0 <n45>:         0x0000000000000028      0x0000000000000000
    0x603200 <n45+16>:      0x0000000000000000      0x0000000000000000
    0x603210 <n41>:         0x0000000000000001      0x0000000000000000
    0x603220 <n41+16>:      0x0000000000000000      0x0000000000000000
    
    0x603230 <n47>:         0x0000000000000063      0x0000000000000000
    0x603240 <n47+16>:      0x0000000000000000      0x0000000000000000
    0x603250 <n44>:         0x0000000000000023      0x0000000000000000
    .......
    

    按照递归分析逐步写出rdi的变化和对input_number的限制条件的缩小:

    %rdi : 0x603110 -> 0x603150 -> 0x6031d0 -> 603230

    input_number的范围:input_number >= 0x24 (要跳L1)input_number != 0x24-> input_number >= 0x32 (要跳L1)input_number != 0x32 -> input_number < 0x6b(不能跳L1)-> input_number >= 0x63(要跳L1)input_number = edx = 0x63

    所以,密码应该为0x63即99。

  • 相关阅读:
    财富感悟:赚大钱的经典语录
    男人成为富翁必备五大特质
    上帝不会辜负每一个有梦的孩子(转)
    一百个绝佳句型
    欢迎光临腾飞品牌网和腾飞悬赏任务网
    金钱在哪里?财富就在你心里
    小本创业30条生意妙经
    告诉你钱途无量的六大创业领域
    告诉你一个简单、深刻的经商道理
    十二条创业经验:包你做梦都赚钱
  • 原文地址:https://www.cnblogs.com/liqiuhao/p/7624880.html
Copyright © 2011-2022 走看看