Bomb Lab

一、拆弹环境

操作系统:Ubuntu
GDB:GNU gdb (Ubuntu 12.0.90-0ubuntu1) 12.0.90(gdb -v查看)
linux—常用gdb调试命令汇总_就要 宅在家的博客-CSDN博客

二、安装炸弹

下载炸弹:wget csapp.cs.cmu.edu/3e/bomb.tar
解压炸弹:tar xvf bomb.tar
生成一个bomb目录,包含三个文件:bomb(可执行文件), bomb.c(文本), README(文本),在新建一个文件ans.txt用于记录密码比较方便
在这里插入图片描述

三、拆解炸弹

优质文章: 手把手教你拆解 CSAPP 的 炸弹实验室 BombLab
首先,大致浏览一下bomb.c了解一下构架

  • 思路:为了不call explode_bomb,需要合理利用各个跳转指令,使phase_1、phase_2、…phase_6函数在不调用函数explode_bomb的情况下成功ret

comb目录下输入gdb bomb,开始运行

phase_1

  • 为了不进行call 0x40143a <explode_bomb>必须让je 0x400ef7 <phase_1+23>成功执行,从而跳转到add的位置,再ret返回

  • 向上追溯,为了让je 0x400ef7 <phase_1+23>成功执行,源操作数与目的操作数就应该相等,又由于在这段代码中test %eax,%eax的结果用于判断%eax的值是否为0(ZF==1),可得%eax的值必须为0

  • 由于函数的返回值一般存储在%eax容器中,再向上追溯strings_not_equal的返回值必须是0

  • 虽然也可以从字面上理解strings_not_equal,就是要让两个字符串相同,而第一个字符串就是通过栈传递的密码,第二个字符串就是%esi的值。由于%esi的值已知,执行在这里插入图片描述
    得到密码"Border relations with Canada have never been better."

  • 如果不取巧,查看函数strings_not_equal的执行过程判断
    在这里插入图片描述
    传参对应的寄存器分别为:第一个参数%rdi,第二个参数%rsi…第一个红框表示传入两个字符串,然后两个箭头代表计算两个字符串长度;第二个红框表示表示若字符串一样长则继续比较,不跳转,若不一样长则跳转(ret=1爆炸);第三个红框检查是否都为空,接着检查字符串每位数据是否相同…若都一致,则(ret=0,第一个炸弹拆解成功)
    在这里插入图片描述

phase_2

在这里插入图片描述

  • 由read_six_numbers函数可知,密码为6个数,具体可查看read_six_numbers的执行过程得出结论
    在这里插入图片描述
    首先可得到相对偏移量从0x0到0x14,间隔为4的6个空间,第二个框大概率是格式字符串的地址(可由x/s 0x4025c3结果验证),第三个框大概是程序计数器(0~5间6个数,验证是否输入6个数,目的也在此)。验证完成后,又add回去,释放空间

  • 由<+14><+18><+20>的语句可得,此时(%rsp)必然等于1,即该开辟的栈中地址最小的位置数据为1

  • 由<+52><+62><+27><+30><+32><+34><+36>的语句可得,此时%eax的值必然等于(%rbx),即该开辟的栈中地址倒二小的位置数据为2

  • 由<+41><+45><+48><+27>…可知,此时%rbp的值等于%rbx的值等于4,即该开辟的栈中地址倒三小的位置数据为4

  • …(以此类推,相当于循环)结果为
    在这里插入图片描述

  • 用C语言goto语句模拟汇编代码控制流程更为清晰

void phase_2() {
    int numbers[6];
    int *rbx, *rbp;
start:
    rbp = &numbers[0];
    rbx = &numbers[1];
    read_six_numbers(numbers); // call 0x40145c <read_six_numbers>
    if (*rbp != 1) {
        explode_bomb(); // call 0x40143a <explode_bomb>
        goto start;
    }
loop:
    int eax = *(rbx - 1);
    eax += eax;
    if (eax != *rbx) {
        explode_bomb(); // call 0x40143a <explode_bomb>
        goto start;
    }
    rbx++;
    if (rbx != rbp) {
        goto loop;//实际上有一个循环
    }
    return;
}
  • 成功(但先试了32 16 8 4 2 1失败了(⊙﹏⊙))
    在这里插入图片描述

phase_3

在这里插入图片描述
在这里插入图片描述

  • 从<+0>到<+34>这一段与 phase_2中read_six_numbers函数的过程大致相同,0xc(%rsp)存第一个参数的地址,0x8(%rsp)存第二个参数的地址,立即数0x4025cf作为第三个参数存格式字符串的地址,立即数0作第四个参数,结果说明密码是2个十位数整数
  • 然后<+39><+46>得到第一个十进制整数<=7,一直到<+50>跳转到0x402470+%rax的值*8所存储的地址处(这里%rax是我们输入的第一个值)
  • 就将计就计,查询0x402470后的指令,经反复调试与观察发现后16个指令的地址
    在这里插入图片描述
    刚好对应
    在这里插入图片描述
    可知,这是switch汇编代码,通过跳转表来访问代码位置
  • 有8种答案
    在这里插入图片描述

phase_4

在这里插入图片描述

  • 过程和前面几个phase很像,能推出的结论就是①密码为两个10进制的数字②%eax=0
    ③0x8(%rsp)<=0xe=14(第一个数小于等于14) && 0xc(%rsp)=0(第二个数等于0) ④<+45>到<+56>在为进入func4准备数据

在这里插入图片描述
在这里插入图片描述

  • 由<+27><+48>可看出这是递归操作,大致理一下思路
    -在这里插入图片描述
  • 可以大致分为三条路,①路只有当两个数相等时能行(两个数都为0),②路万万行不通 ③路也许行得通,但得带值去试

phase_5

在这里插入图片描述
在这里插入图片描述

  • 从<+0>到<+34>可知字符串长度为6(xor %eax %eax异或,目的是清空eax寄存器,将其值设为0)
  • <+41>到<+74>之间在循环[for(rax=0;rax!=6;rax++)],可分析一下<+41>源操作数是个什么东东p *(char*)($rbx) p *(char*)($rbx+0x1) p *(char*)($rbx+0x2) p *(char*)($rbx+0x3)…刚好对应猜的6个数,说明%rbx指向字符串最低位;值得注意的是%ecx和%cl是一个寄存器,只是一个32字节一个8字节,%edx和%dl同理;and $0xf,%edx是将%edx寄存器的值与0xf进行按位运算,并将结果存回%edx中因为0xf的二进制表示为00001111,与%edx寄存器的每一位进行与操作,可以将%edx寄存器的高四位都置为0,只有低四位保留下来;
  • 于是<+41>到<+74>具体过程如下:假设输入的字符串为abcdef,每个字符刚好对应一个循环,对应一个字符输出,所以这个区间同样生成长度为6的新字符串。拿a举例,a的ASICC码为98,98的二进制为01100010,与0xf进行按位与操作后,结果为00000010,即%rdx=2;由x/s 0x4024b0的结果maduiersnfotvbyl…(后面不需要知道,因为二进制0000~1111只有16个数),movzbl 0x4024b0(%rdx) ,%edx,计算内存地址0x4024b0+2的字节内容d加载到%edx寄存器中(%edx=d),以此类推
  • 继续向后看,从<+81>到<+100>类似于phase_1的操作,说明<+41>到<+74>得到的字符串要等于0x40245e的字节内容。x/s 0x40245e为flyers
  • 最后,反向推,flyers对应maduiersnfotvbyl的9/15/14/5/6/7,进行二进制,查ASCII码,得出输入的密码应该为ionefg
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值