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\n", 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>]\n", 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\n");
    printf("which to blow yourself up. Have a nice day!\n");

    /* 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?\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();

    /* 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 '\000'
  ##明顯為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 '\001'        0 '\000'        0 '\000'        0 '\000'        2 '\002'        0 '\000'        0 '\000'        0 '\000'
  #0x7fffffffdba8: 3 '\003'        0 '\000'        0 '\000'        0 '\000'        4 '\004'        0 '\000'        0 '\000'        0 '\000'
  #0x7fffffffdbb0: 5 '\005'        0 '\000'        0 '\000'        0 '\000'        6 '\006'        0 '\000'        0 '\000'        0 '\000'
  #0x7fffffffdbb8: 49 '1'  20 '\024'       64 '@'  0 '\000'
  #可以發現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 '\000'        0 '\000'
  #所以此時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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM