• CSAPP Bomb Lab


    CSAPP Bomb Lab

    芜湖,完成实验来记录一下啦~

    这个lab看的我真是眼皮发麻,框框就是反汇编->c语言形式->优化c语言 最后我才能写出来,整的我睡觉都是 mov… lea…

    难道适中,感觉只要耐心一点就没问题,好了现在开始正文…

    小技巧

    用gdb调试的技巧

    gdb --tui bomb #打开类似界面的东东
    (gdb) layout asm #汇编界面
    (gdb) layout split #分割窗口
    (gdb) layout re #寄存器界面
    
    • 1
    • 2
    • 3
    • 4

    完整后整个界面就是下面的啦

    在这里插入图片描述

    我经常使用的gdb命令

    x/【n】(x)/(d)/(s) 内存地址  #以x/d进制查看n个字节对应的内存  括号里面x代表16进制,d是10进制,s是字符串显示
    x/x $rax 查看rax对应内存的值16进制显示
    b n  在n处打断点
    set $pc = 0x11111  将程序跳转到0x11111处执行
    info r 查看寄存器信息,不过有上面的也不用这个了
    stepi 单步执行汇编指令
    s 单步执行代码(遇到能进去的函数会进去)
    n 单步执行代码(不进去)
    finish 直接跳出当前函数
    #上面用到了界面,可以使用下面的指令切换焦点窗口
    focus n 切换到下面的窗口
    focus p 切换上面的窗口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    我这边用的windows终端,感觉也是很不错,接下来就开始我们的逆向工程了

    说实话,下面只是记录一下我的逆向过程,以及大致思路,没写出来的小伙伴不建议查看昂,还是自己一次一次stepi比较好,当然逆向的代码可以对比,看看我的有多少错误。

    phase1

    第一个锻炼一下看汇编代码的能力,只要进去把某个内存的东西输出一下就ok了

    phase2

    进入到执行函数会看到 read_six_numbers

    在这里插入图片描述

    这就直接猜到要输入6个数字了

    进去看一下

    在这里插入图片描述

    哟,居然有一个sscanf,这不太熟(仅限我不熟啊),百度一下

    int sscanf(const char *str, const char *format, ...)
    
    • 1

    昂~,这不就是个类似输入的函数吗

    然后使用gdb查看一下参数

    哦对,这个参数都是标准的,第一个参数都是%rdi ,然后以此类推, %rsi , % rdx , %rcx … 忘了就记住4个

    x/s $rsi
    ->   "%d %d %d %d %d %d"
    
    • 1
    • 2

    哟,果然是6个数字,直接输入6个数字,继续往下

    然后你就会发现一个规律,验证一下就能找到咯

    phase3

    这个继续做他,上手我就是一个stepi

    在这里插入图片描述

    哟呵,老朋友了,看一看这次要输入几个数

    x/s $rsi
    -> "%d %d"
    
    • 1
    • 2

    这次就俩个数啊

    然后往下就是一堆

    在这里插入图片描述

    诶?怎么这么熟悉呢(翻一番书),我擦这不就是switch吗

    我直接就是一手 反c语言, 反c语言中…(其实就是照着对应一条一条套, jmp改goto 寄存器改变量,然后优化)

    void phase(string input){
            usigned char  t ; // 0xc
            usigned char  t1 ; //0x8
            string fmt= "%d %d";
            char res = ssacnf(input , fmt , t1 ,t );        //sscanf("123456 ", "%s", buf);
            if(res <= 1){
                    explode_bomb();
                    return ;
            }
            if(t1 > 7){
                    goto L1;
            }
            switch(t1){
                    case 0: //  // 0x400f7c
                                    res = 0xcf;   //207
                                    break;
                    case 2: //      // 0x400f83
                                    res = 0x2c3; // 707
                                    break;
                    case 3: //      // 0x400f8a
                                    res = 0x100; // 256
                                    break;
                    case 4: //      // 0x400f91
                                    res = 0x185; // 389
                                    break;
                    case 5: //      // 0x400f98
                                    res = 0xce;  // 206
                                    break;
                    case 6: //      // 0x400f9f
                                    res = 0x2aa; // 682
                                    break;
                    case 7: //      // 0x400fa6
                                    res = 0x147; // 327
                                    break;
    L1:     explode_bomb();
                     case ?: //      // 0x400fb2
                                    res = 0x0;
                                    break;
                    case 1:
                                    res = 0x137; // 311
            }
    
            if(res == t){
                    //      success
                    return ;
            }
            explode_bomb();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    我这一看,好家伙,还不止一个正确答案,一堆啊,随便选一个吧

    其中有一个地方没搞明白他的结构,只优化成上面这样了。

    phase4

    继续就是一个stepi

    在这里插入图片描述

    老朋友又来了这次输入几个数呀

    这次是俩个数字(就不贴图了)

    看见这次汇编挺短还以为很容易呢,正高兴的看到个func4,好家伙还调函数了

    在这里插入图片描述

    先把上面的汇编弄成能看的c语言出来,挺短的

    这里面有个test %eax,%eax 标准是 eax&eax 其实就是判断 eax是不是0

    void phase_4(string input){ // di si dx cx
            // 3 int in the stack
            int t1 , t2;  //   ?   0
            auto res = sscanf(input , "%d %d" , t1 , t2);
            if(res != 2){
                    bomb();
            }
            if(t1 > 14){
                    bomb();
            }
            int res = func4(t1 , 0 , 14);
            if(res == 0){
                    if(t2 == 0){
                            goto L1;
                    }
            }
            bomb();
            L1: success
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    然后进去fun4看一看

    在这里插入图片描述

    这小子还调上自己了,继续一步一步转成能看的c

    int fun4(int a , int  b  int c){
            //1 byte in the stack
            //int res = c - b;
            //int d = res >> 31;  // -1 or 0
            //res = (d + res) / 2;
            //d = a + res;
            if(b > c) b += 1;
            int d = a + (c - b) / 2;
    
            if(d  >  a){
                    res = func4(a , b , d - 1);
                    return res * 2;
            }
            res = 0;
            if(c < a){
                    res = func4(a , c + 1 , c);
                    res = res * 2 + 1;
            }
            return res
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    emmm就算转成代码,这求解…用计算机?快算了带入一个0发现可以通过,其他答案看你们啦

    phase5

    这次看看能不能碰到老朋友啦

    在这里插入图片描述

    哟换人了,换成 string_length,这不是求字符串长度的吗,看起来这次是要求长度了

    看到了一个

    cmp $0x6 , %eax
    
    • 1

    啊这,不是6个字符就给我爆炸啊

    确定了数量,看看下面还有啥规则吧

    这一写就是好几小时呀…

    这个逆向的时候,会发现一系列从其他地放的字符串,譬如

    在这里插入图片描述

    这个0x40245e就有一个字符串

    还有一个

    在这里插入图片描述

    这里有一串字符串,需要自己辨别一下

    最后逆向出来的代码就是下面

    char * A[] = "maduiersnfotvbyl" // 0x4024b0
    void phase_5(string input){ // rdi
            // 32 byte
            int t1 = %fs:0x28; // rbx |  0x18 + rsp
            int res = string_length(input);
            char * result[6];
            if(res != 6){
                    bomb();
            }
            goto L1; // phase_5 + 39
            for(int i = 0; i < 6; i++){
                    char rdx = inptu[i];
                    int index = input[i] & 15;
                    rdx = A[index];
                    result[index] = rdx;
            }
            res = strings_not_equal(result , "flyers"); // 9 15 14 5 6 7
            if(res != 0){
                    bomb();
            }
    
            L1:
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    代码挺短,一看哈呀,这个地方还有 & 15 来确定字符呢,嘎嘎绕蒙

    昂~,就是根据我输入的字符然后 % 15 来确定下标位置呀,然后根据A[]数组的字符查出来连起来和flyers相同就行,于是拿出我的密码本来吧

    在这里插入图片描述

    根据顺序,框框就是画,然后每一列的值输入进去都是能通过的。

    终于解密了这个了,框框累,其实也可以直接写代码暴力搜索的,不过我还是选择了人工破译,至于为什么 ~

    不会写代码破解…

    phase6

    还是往常看stepi

    0X00000000004010f4 :
      0X4010f4:	push   %r14
      0X4010f6:	push   %r13
      0X4010f8:	push   %r12
      0X4010fa:	push   %rbp
      0X4010fb:	push   %rbx
      0X4010fc:	sub    $0x50,%rsp
      0X401100:	mov    %rsp,%r13
      0X401103:	mov    %rsp,%rsi
      0X401106:	callq  0X40145c 
      0X40110b:	mov    %rsp,%r14
      0X40110e:	mov    $0x0,%r12d
    
    #第一部分
    .L0
      0X401114:	mov    %r13,%rbp
      0X401117:	mov    0x0(%r13),%eax
      0X40111b:	sub    $0x1,%eax
      0X40111e:	cmp    $0x5,%eax
      0X401121:	jbe    .L1
      0X401123:	callq  0X40143a 
    .L1
      0X401128:	add    $0x1,%r12d
      0X40112c:	cmp    $0x6,%r12d
      0X401130:	je     .L4
      0X401132:	mov    %r12d,%ebx
    .L2
      0X401135:	movslq %ebx,%rax
      0X401138:	mov    (%rsp,%rax,4),%eax
      0X40113b:	cmp    %eax,0x0(%rbp)
      0X40113e:	jne    .L3
      0X401140:	callq  0X40143a 
    .L3
      0X401145:	add    $0x1,%ebx
      0X401148:	cmp    $0x5,%ebx
      0X40114b:	jle    .L2
      0X40114d:	add    $0x4,%r13
      0X401151:	jmp    .L0
    
    #第二部分
    
    .L4  
      0X401153:	lea    0x18(%rsp),%rsi
      0X401158:	mov    %r14,%rax
      0X40115b:	mov    $0x7,%ecx
    .L5
      0X401160:	mov    %ecx,%edx
      0X401162:	sub    (%rax),%edx
      0X401164:	mov    %edx,(%rax)
      0X401166:	add    $0x4,%rax
      0X40116a:	cmp    %rsi,%rax
      0X40116d:	jne    .L5
    
      0X40116f:	mov    $0x0,%esi
      0X401174:	jmp    .L9
    
    #第三部分
    .L6
      0X401176:	mov    0x8(%rdx),%rdx
      0X40117a:	add    $0x1,%eax
      0X40117d:	cmp    %ecx,%eax
      0X40117f:	jne    .L6
    
      0X401181:	jmp    .L8
    .L7  
      0X401183:	mov    $0x6032d0,%edx
    .L8
      0X401188:	mov    %rdx,0x20(%rsp,%rsi,2)
      0X40118d:	add    $0x4,%rsi
      0X401191:	cmp    $0x18,%rsi
      0X401195:	je     .L10
    .L9
      0X401197:	mov    (%rsp,%rsi,1),%ecx
      0X40119a:	cmp    $0x1,%ecx
      0X40119d:	jle    .L7
    
      0X40119f:	mov    $0x1,%eax
      0X4011a4:	mov    $0x6032d0,%edx
      0X4011a9:	jmp    .L6
    
    #第四部分
    .L10
      0X4011ab:	mov    0x20(%rsp),%rbx
      0X4011b0:	lea    0x28(%rsp),%rax
      0X4011b5:	lea    0x50(%rsp),%rsi
      0X4011ba:	mov    %rbx,%rcx
    .L11
      0X4011bd:	mov    (%rax),%rdx
      0X4011c0:	mov    %rdx,0x8(%rcx)
      0X4011c4:	add    $0x8,%rax
      0X4011c8:	cmp    %rsi,%rax
      0X4011cb:	je     .L12
      0X4011cd:	mov    %rdx,%rcx
      0X4011d0:	jmp    .L11
    
    #第五部分
    .L12
      0X4011d2:	movq   $0x0,0x8(%rdx)
      0X4011da:	mov    $0x5,%ebp
    .L13
      0X4011df:	mov    0x8(%rbx),%rax
      0X4011e3:	mov    (%rax),%eax
      0X4011e5:	cmp    %eax,(%rbx)
      0X4011e7:	jge    .L14
      0X4011e9:	callq  0X40143a 
    .L14
      0X4011ee:	mov    0x8(%rbx),%rbx
      0X4011f2:	sub    $0x1,%ebp
      0X4011f5:	jne    .L13
    
    
      0X4011f7:	add    $0x50,%rsp
      0X4011fb:	pop    %rbx
      0X4011fc:	pop    %rbp
      0X4011fd:	pop    %r12
      0X4011ff:	pop    %r13
      0X401201:	pop    %r14
      0X401203:	retq   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118

    我滴妈~,让我冷静一下 一下。这么长~,这真的这个代码耗时我好多天

    没办法,还是一步一步写呗

    这部分我逆向出来的代码丢了,没保存(真的很痛心)但是不想在逆向了,所以这里就简述一下过程吧

    前面2部分还是很好写的,还是判断输入的数量,以及输入类型

    void f1(int a[]){
        for(int i = 0; i < 6;i++){
            if( a[i] > 6 ) BOOM();
            for(int j = i + 1; j < 6; j++){
                if( a[i] == a[j]) BOOM();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    从前面就可以看出,输入的数字不可以超过6个,然后不能超过6,还不能重复

    然后第二部分是把每个a[i] = 7 - a[i];

    然后下面这部分代码真是框框goto跳,给我整晕乎的

    没办法,我就一步一步stepi,stepi,stepi,stepi,stepi,看寄存器的值

    最后发现,这玩意居然有个这个东西

    在这里插入图片描述

    这个是把rbx + 8处的内存放到rax然后这居然又把额,就是*rax 复制给了eax。

    这这这,相当于内存中存了个地址,然后猜一下估计是个链表了,然后就是框框用x/x n 查内存,

    最后还发现了一个 0x6032d0这直接纯地址,看见就不一般,直接查一下

    在这里插入图片描述

    撕,gdb直接给出这玩意是个node了,然后看一下他内存里的值,在查一下,发现这不就是个

    struct node{
    	int v1;
    	int v2;
    	struct node * next;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    v2记录的是我们输入的值(好像是)

    v1是他自己的值

    然后呢?继续stepi,跳了几个小时发现,

    phase_6 +130 到 pahse_6 + 181这部分的代码,就是上面的第三部分代码,好像是在排序啊,我擦。赶紧带入数字一看,我丢,这不就是给node里面的东西排序吗这下有眉目了,接着整。

    直接看第五部分怎么就爆炸的代码。发现满足序列递减就可以了,然后看node里的值,我们可以推出合法的链表项顺序应为3->4->5->6->1->2,因为第二步操作的存在,每个数x应该取7-x,即4 3 2 1 6 5

    secret_phase

    芜湖,终于搞完了实验上网看看别人写的代码吧~

    开心逛中… 我擦,怎么还有secret_phase这个我咋没有,看看代码

        /* 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! */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    最后一句看起来不像是完成的话呀,翻译一下,嗯?还有秘密的代码,我在找找

    最后发现,在最后面的phase_defused()中还有一个秘密入口呢

    在这里插入图片描述

    这不是老朋友sscanf又来了,但是这次好像没让我们输入啊。看到有个注释,num_input_strings.还看到莫名的地址,看到地址我就想看他一下是啥。

    在这里插入图片描述

    这怎么意思是输入2个数字一个字符串?,发现还是从0x603870这块获取的值,这块是哪的值啊

    看一下他的上下文

    x/128s 0x603870
    
    • 1

    翻着翻着发现

    在这里插入图片描述

    这不是我之前输入的吗,昂~,懂了,就是之前phase4的输入的东西,这里又获取到了,发现之前输入的后面还应该输入一个字符串,接着stepi看看要输入什么字符串。

    在这里插入图片描述

    然后在之前的4答案处补上这个字符串继续往下走

    发现了一个secret_phase函数进去看看

    在这里插入图片描述

    代码比较短(相比上一个)

    逆向一下吧

    你会发现这玩意也有指针,而且还不一一个,优化优化的代码就成下面这样的

    void secret_phase(){
            chaar * input = read_line();
            long int res = strcol(input , NULL , 10);
            if(res > 1001){ // 0x3e8 == 1000
                    bomb();
            }
            T * rdi = 0x6030f0;
            res = fun7(rdi , res);
            if(res != 2){
                    bomb();
            }
            ...
    }
    
    fun7(T * node , long int val){
            if(node != NULL){
                    return -1;
            }
    
            if(node->val == val){
                    return 0;
            }
            if(node->val > val){
                    return fun7(nqode->left , val) * 2;
            }
    
            return fun7(node->right , val) * 2 + 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    这不就是个二叉树吗,然后看看人家的值计算一下答案就会出来的,结果发现,还是改4层的二分树,这还能难道我这个算法高手?框一下一个答案

    24,错了,撕~,发现内存里的值看出了应该是22,最后就对了。(答案不止一个)

    至此我的第二个lab就完成啦~

  • 相关阅读:
    深入解析HTTPS与HTTP
    从内核角度看TCP三次握手
    vue组件的生命周期 笔记
    Git 开源的版本控制系统-04-branch manage 分支管理
    【前端】提升效率的快捷键,数学知识,ASCLL码,图片格式
    js排序的基础原理理解
    【Visual Leak Detector】QT 中 VLD 输出解析(二)
    【零基础学Python】Day5 Python基本数据类型之List
    EasyOCR 识别模型训练
    CCC联盟——UWB MAC(一)
  • 原文地址:https://blog.csdn.net/dyxing_/article/details/132891991