CSAPP lab2 经典的bomblab二次学习

csapp上的实验一直收到广泛好评,其中第二个实验bomblab则是一个学习C语言及其转化汇编指令的非常好的小项目,前段时间刚刚借鉴了很多教程完成了整个lab的6个小实验(还有一个secret phase实在太忙一直没顾得上),于是现在打算自己重新做一遍整个实验,好好回忆一下当初学到的知识。

电脑系统是Ubuntu20.04,采用objdump对二进制文件进行反汇编

首先把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. */
    // Border relations with Canada have never been better.
    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();
    //1 2 4 8 16 32
    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");
    // [0, 207] [1, 311] [2, 707] [3, 256] [4, 389] [5, 206] [6, 682]

    /* 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");
    //[7, 0]
    
    /* 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");
    //ionefg

    /* 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();
    //4 3 2 1 6 5
    /* Wow, they got it!  But isn't something... missing?  Perhaps
     * something they overlooked?  Mua ha ha ha ha! */
    
    return 0;
}

简要阅读一下代码可以看到有6个phase,每一个会检索你从终端的输入,如果符合要求就进入下一个phase,如果不符合就会错误。我们需要通过阅读反汇编出的汇编代码以及利用gdb调试功能来对输入进行推导。

直接来看phase_1的汇编代码

0000000000400ee0 <phase_1>:
  400ee0:    48 83 ec 08              sub    $0x8,%rsp
  400ee4:    be 00 24 40 00           mov    $0x402400,%esi
  400ee9:    e8 4a 04 00 00           call   401338 <strings_not_equal>
  400eee:    85 c0                    test   %eax,%eax
  400ef0:    74 05                    je     400ef7 <phase_1+0x17>
  400ef2:    e8 43 05 00 00           call   40143a <explode_bomb>
  400ef7:    48 83 c4 08              add    $0x8,%rsp
  400efb:    c3                       ret    

从其它教程中学到的非常有利于阅读汇编代码的方法,就是先把带有汇编指令的代码转化为无需指令的方式,变量名称可以用寄存器名称代替。于是phase_1可以转化为:

rsp -= 8;
esi = 0x402400;
call strings_not_equal;
test eax, eax
je 400ef7;
explode_bomb();

400ef7:
    rsp += 8;

首先这里test je指令的用法是:

x86平台上使用汇编如何判断一个值是否为0?
一般会使用该指令:
test %rax %rax
je xxx
test指令会判断后面两个操作数执行AND操作,结果为0就设置zero flag,然后搭配je跳转指令从而实现对一个值是否为0的判断。
如果%rax值为0,那么他们相与才会等于0,否则该值不会为0.
————————————————
版权声明:本文为CSDN博主「程序猿Ricky的日常干货」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接: https://blog.csdn.net/rikeyone/article/details/112251708

还有一些需要加入的知识是,对于函数来说,一些寄存器有其独特的作用,比如edi寄存器用来保存函数的第一个参数,esi寄存器用来保存第二个参数,并且函数的(如果有)前六个参数都有其独特的寄存器保存,超过6个的参数则会保存在程序栈中,而函数的返回值会保存在eax寄存器中,我们将上述转化成c风格的代码:

phase_1(edi) {
    rsp -= 8;
    esi = 0x402400;
    eax = strings_not_equal(edi,esi);
    if(eax != 0) explode_bomb();
    rsp += 8;    
}    

比较的字符串首地址就是0x402400这个首地址,我们可以在gdb中对这个地址进行查看

gdb中 x/s addr指令能以字符流的形式查看addr内存地址,由此我们可以得出第一题的答案。

马不停蹄我们来看phase_2的汇编:

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           call   40145c <read_six_numbers>
  400f0a:    83 3c 24 01              cmpl   $0x1,(%rsp)
  400f0e:    74 20                    je     400f30 <phase_2+0x34>
  400f10:    e8 25 05 00 00           call   40143a <explode_bomb>
  400f15:    eb 19                    jmp    400f30 <phase_2+0x34>
  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           call   40143a <explode_bomb>
  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>
  400f30:    48 8d 5c 24 04           lea    0x4(%rsp),%rbx
  400f35:    48 8d 6c 24 18           lea    0x18(%rsp),%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                       ret    

代码长度一下子起来了,但是没关系,先按照上面的情况来,先把它变成能看得懂的方式(省略开头和结尾处的运行状态保存与复位代码):

rsp -= 40;
rsi = rsp;
call read_six_numbers;
if(*(rsp) == 1) goto 400f30
else explode_bomb();

400f30:
    rbx = rsp + 4;
    rbp = rsp + 24
    goto 400f17;

400f17:
    eax = *(rbx - 4);
    eax += eax;
    if(*(rbx) == eax) goto 400f25;
    else explode_bomb();

400f25:
    rbx += 4;
    if(rbx != rbp) goto 400f17;
    else goto 400f3c;  //即运行正常退出进入下一个阶段

将它转化为c风格代码后为:

phase_2() {
    //栈
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值