实验简介

本实验需要拆除一个“二进制炸弹”,“二进制炸弹”是一个可执行目标程序。运行时,它会提示用户键入6个不同的字符串。如果其中任何一个错误,炸弹就会“爆炸”。必须通过逆向工程和汇编语言知识,推导出六个字符串分别是什么。从而拆除炸弹。

开始实验

下载实验包后,会解压得到一个bomb.c源码和bomb.o可执行目标文件。bomb.c源码如下:

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;
}

可以看到代码中有许多没有给出定义(其实是定义在spport.h头文件中,这个头文件没有给出)的函数,也就是说我们仅仅通过bomb.c代码是不能得知我们要输入的六个字符串的,不过好在题目给出了代码编译出的可执行目标文件bomb.o,这样我们就有机会通过逆向工程确定bomb.c的源码了。
接下来运行可执行目标文件bomb.o,先随便输入个abc试一试效果:


果然会爆炸。。
看来现在可以对bomb.o的汇编代码进行分析了,使用objdump工具,将bomb.o的反汇编代码导入到txt文件中,这里我用
下面是主函数的反汇编代码:

0000000000400da0 <main>:400da0:    53                      push   %rbx400da1:  83 ff 01                cmp    $0x1,%edi400da4: 75 10                   jne    400db6 <main+0x16>400da6: 48 8b 05 9b 29 20 00    mov    0x20299b(%rip),%rax        # 603748 <stdin@@GLIBC_2.2.5>400dad:  48 89 05 b4 29 20 00    mov    %rax,0x2029b4(%rip)        # 603768 <infile>400db4:    eb 63                   jmp    400e19 <main+0x79>400db6: 48 89 f3                mov    %rsi,%rbx400db9: 83 ff 02                cmp    $0x2,%edi400dbc: 75 3a                   jne    400df8 <main+0x58>400dbe: 48 8b 7e 08             mov    0x8(%rsi),%rdi400dc2:    be b4 22 40 00          mov    $0x4022b4,%esi400dc7:    e8 44 fe ff ff          callq  400c10 <fopen@plt>400dcc: 48 89 05 95 29 20 00    mov    %rax,0x202995(%rip)        # 603768 <infile>400dd3:    48 85 c0                test   %rax,%rax400dd6: 75 41                   jne    400e19 <main+0x79>400dd8: 48 8b 4b 08             mov    0x8(%rbx),%rcx400ddc:    48 8b 13                mov    (%rbx),%rdx400ddf:   be b6 22 40 00          mov    $0x4022b6,%esi400de4:    bf 01 00 00 00          mov    $0x1,%edi400de9: e8 12 fe ff ff          callq  400c00 <__printf_chk@plt>400dee:  bf 08 00 00 00          mov    $0x8,%edi400df3: e8 28 fe ff ff          callq  400c20 <exit@plt>400df8:  48 8b 16                mov    (%rsi),%rdx400dfb:   be d3 22 40 00          mov    $0x4022d3,%esi400e00:    bf 01 00 00 00          mov    $0x1,%edi400e05: b8 00 00 00 00          mov    $0x0,%eax400e0a: e8 f1 fd ff ff          callq  400c00 <__printf_chk@plt>400e0f:  bf 08 00 00 00          mov    $0x8,%edi400e14: e8 07 fe ff ff          callq  400c20 <exit@plt>400e19:  e8 84 05 00 00          callq  4013a2 <initialize_bomb>400e1e:    bf 38 23 40 00          mov    $0x402338,%edi400e23:    e8 e8 fc ff ff          callq  400b10 <puts@plt>400e28:  bf 78 23 40 00          mov    $0x402378,%edi400e2d:    e8 de fc ff ff          callq  400b10 <puts@plt>400e32:  e8 67 06 00 00          callq  40149e <read_line>400e37:  48 89 c7                mov    %rax,%rdi400e3a: e8 a1 00 00 00          callq  400ee0 <phase_1>400e3f:    e8 80 07 00 00          callq  4015c4 <phase_defused>400e44:  bf a8 23 40 00          mov    $0x4023a8,%edi400e49:    e8 c2 fc ff ff          callq  400b10 <puts@plt>400e4e:  e8 4b 06 00 00          callq  40149e <read_line>400e53:  48 89 c7                mov    %rax,%rdi400e56: e8 a1 00 00 00          callq  400efc <phase_2>400e5b:    e8 64 07 00 00          callq  4015c4 <phase_defused>400e60:  bf ed 22 40 00          mov    $0x4022ed,%edi400e65:    e8 a6 fc ff ff          callq  400b10 <puts@plt>400e6a:  e8 2f 06 00 00          callq  40149e <read_line>400e6f:  48 89 c7                mov    %rax,%rdi400e72: e8 cc 00 00 00          callq  400f43 <phase_3>400e77:    e8 48 07 00 00          callq  4015c4 <phase_defused>400e7c:  bf 0b 23 40 00          mov    $0x40230b,%edi400e81:    e8 8a fc ff ff          callq  400b10 <puts@plt>400e86:  e8 13 06 00 00          callq  40149e <read_line>400e8b:  48 89 c7                mov    %rax,%rdi400e8e: e8 79 01 00 00          callq  40100c <phase_4>400e93:    e8 2c 07 00 00          callq  4015c4 <phase_defused>400e98:  bf d8 23 40 00          mov    $0x4023d8,%edi400e9d:    e8 6e fc ff ff          callq  400b10 <puts@plt>400ea2:  e8 f7 05 00 00          callq  40149e <read_line>400ea7:  48 89 c7                mov    %rax,%rdi400eaa: e8 b3 01 00 00          callq  401062 <phase_5>400eaf:    e8 10 07 00 00          callq  4015c4 <phase_defused>400eb4:  bf 1a 23 40 00          mov    $0x40231a,%edi400eb9:    e8 52 fc ff ff          callq  400b10 <puts@plt>400ebe:  e8 db 05 00 00          callq  40149e <read_line>400ec3:  48 89 c7                mov    %rax,%rdi400ec6: e8 29 02 00 00          callq  4010f4 <phase_6>400ecb:    e8 f4 06 00 00          callq  4015c4 <phase_defused>400ed0:  b8 00 00 00 00          mov    $0x0,%eax400ed5: 5b                      pop    %rbx400ed6:  c3                      retq   400ed7:  90                      nop400ed8:  90                      nop400ed9:  90                      nop400eda:  90                      nop400edb:  90                      nop400edc:  90                      nop400edd:  90                      nop400ede:  90                      nop400edf:  90                      nop

第一个炸弹

从bomb.c的代码片段可以看出,每道题目都是由一个函数开始的,我们在反汇编代码中找到phase_1的代码:

0000000000400ee0 <phase_1>:400ee0: 48 83 ec 08             sub    $0x8,%rsp400ee4: be 00 24 40 00          mov    $0x402400,%esi400ee9:    e8 4a 04 00 00          callq  401338 <strings_not_equal>400eee:  85 c0                   test   %eax,%eax400ef0: 74 05                   je     400ef7 <phase_1+0x17>400ef2:  e8 43 05 00 00          callq  40143a <explode_bomb>400ef7:   48 83 c4 08             add    $0x8,%rsp400efb: c3                      retq

可以看到在第二行,程序将一个好像是地址的变量赋给了%esi,然后调用了<strings_not_equal>函数,从字面上可以看出这个函数好像是判断字符串是否相等。
容易想到,mov $0x402400,%esi这句指令是在为调用<strings_not_equal>构造参数,而此时寄存器%rdi的值并没有变,所以可以想到<strings_not_equal>函数有两个参数,一个是位于 0x402400 位置的数据,另一个就是 <phase_1> 本身的参数,也就是我们输入的字符串。
从<strings_not_equal>返回后,执行test指令,如果返回值不为0,那么就会执行<explode_bomb>,这个显然是让炸弹爆炸的函数,我们不能让程序执行到这里。
然后找到 0x402400:发现其位于可执行目标文件的.rodata节:



也就是说,第一个炸弹的逻辑是,输入的字符串与程序中内置的格式串比对,相同就通过测试。观察0x402400处的字节序列,可以发现答案就Border relations with Canada have never been better.,这句话最后的句号坑了我很长时间,因为.rodata节里的不可见字符全都会显示成"."。总的来说第一个炸弹十分简单。

第二个炸弹

找到phase_2的反汇编代码部分:

0000000000400efc <phase_2>:400efc: 55                      push   %rbp                            //被调用者保存寄存器400efd:   53                      push   %rbx                            //被调用者保存寄存器400efe:   48 83 ec 28             sub    $0x28,%rsp400f02:    48 89 e6                mov    %rsp,%rsi400f05: e8 52 05 00 00          callq  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          callq  40143a <explode_bomb>400f15:   eb 19                   jmp    400f30 <phase_2+0x34>400f17:  8b 43 fc                mov    -0x4(%rbx),%eax400f1a:   01 c0                   add    %eax,%eax400f1c: 39 03                   cmp    %eax,(%rbx)400f1e:   74 05                   je     400f25 <phase_2+0x29>400f20:  e8 15 05 00 00          callq  40143a <explode_bomb>400f25:   48 83 c3 04             add    $0x4,%rbx400f29: 48 39 eb                cmp    %rbp,%rbx400f2c: 75 e9                   jne    400f17 <phase_2+0x1b>400f2e:  eb 0c                   jmp    400f3c <phase_2+0x40>400f30:  48 8d 5c 24 04          lea    0x4(%rsp),%rbx400f35:    48 8d 6c 24 18          lea    0x18(%rsp),%rbp400f3a:   eb db                   jmp    400f17 <phase_2+0x1b>400f3c:  48 83 c4 28             add    $0x28,%rsp400f40:    5b                      pop    %rbx400f41:  5d                      pop    %rbp400f42:  c3                      retq

可以看到有一个叫<read_six_numbers>的函数,字面意思是要读取6个数字的输入。而且注意到400f02: 48 89 e6 mov %rsp,%rsi,它似乎需要栈指针作为第二个参数,和第一个炸弹一样,此时%rdi同样没有改变,说明<read_six_numbers>函数的第一个参数也是我们输入的字符串。

通过观察400f0e一行,可以得知调用<read_six_numbers>后,如果栈指针处的值为1,那么将跳转到400f30(核心代码所在处),否则直接执行炸弹。那么我们可以猜想如果我们以正确的格式输入六个数字的话,栈指针此时一定是等于0x1的。我们可以找到<read_six_numbers>的位置,看看这个函数到底做了什么。

000000000040145c <read_six_numbers>:40145c:    48 83 ec 18             sub    $0x18,%rsp401460:    48 89 f2                mov    %rsi,%rdx401463: 48 8d 4e 04             lea    0x4(%rsi),%rcx401467:    48 8d 46 14             lea    0x14(%rsi),%rax40146b:   48 89 44 24 08          mov    %rax,0x8(%rsp)401470:    48 8d 46 10             lea    0x10(%rsi),%rax401474:   48 89 04 24             mov    %rax,(%rsp)401478:   4c 8d 4e 0c             lea    0xc(%rsi),%r940147c: 4c 8d 46 08             lea    0x8(%rsi),%r8401480: be c3 25 40 00          mov    $0x4025c3,%esi401485:    b8 00 00 00 00          mov    $0x0,%eax40148a: e8 61 f7 ff ff          callq  400bf0 <__isoc99_sscanf@plt>40148f:   83 f8 05                cmp    $0x5,%eax401492: 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,%rsp40149d:    c3                      retq

可以看到整个函数的前半部分都在进行参数的构造,只是为了能够调用__isoc99_sscanf@plt,而之前我们推导过,这个函数的第二个参数%rsi其实是栈指针,也就是说这些操作%rsi的指令其实都是对栈指针的操作。这里的<__isoc99_sscanf>函数是动态链接进来的C标准库函数,在可执行目标文件里是看不到它的信息的,所以我们只能上网查这个函数的功能了。经过查资料,我大概可以知道这个函数在<read_six_numbers>里的作用是,将输入的六个整数保存在 (%rsp),(%rsp+0x4),(%rsp+0x8),(%rsp+0xc),(%rsp+0x10),(%rsp+0x14),这六个位置。也就是栈上的一片地址连续的空间中。

然后就是从位置400f30处开始的核心代码了,可以看到指令定义了两个指针,%rbx和%rbp,一个指向 0x4(%rsp),一个指向 0x18(%rsp),这中间就是刚才我们输入的六个整数保存的位置。从:

  400f17:    8b 43 fc                mov    -0x4(%rbx),%eax400f1a:   01 c0                   add    %eax,%eax400f1c: 39 03                   cmp    %eax,(%rbx)400f1e:   74 05                   je     400f25 <phase_2+0x29>400f20:  e8 15 05 00 00          callq  40143a <explode_bomb>

这几句可以看出,程序不断将 %rbx 与 -0x4(%rbx) 对比,400f1a 一句表明当且仅当 (%rbx) 是 -0x4(%rbx)的二倍时,炸弹才不会爆炸。

  400f25:    48 83 c3 04             add    $0x4,%rbx400f29: 48 39 eb                cmp    %rbp,%rbx400f2c: 75 e9                   jne    400f17 <phase_2+0x1b>

这三句又说明,%rbx只有和 %rbp 相等时,才能结束程序。否则将%rbx的值加4(一个int的大小),之后跳回400f17。这明显是一个循环语句。刚才我们了解到 %rbp = 0x18(rbx),也就是说当%rbx = %rbp时,程序刚好扫描到我们输入的六个数字,而且每个数字都应该是前一个的二倍,第一个数字等于 (%rsp) = 0x1,那么这个炸弹就被成功解开了。答案是:
1 2 4 8 16 32

第三个炸弹

截至最终更新时,作者已经解开了五个炸弹,但这个实验总归还是比较繁琐,还剩下两个没有解开,而且像这样详细地复盘也比较费时间,没有写出来的部分之后再补

CSAPP实验记录(2)--------- Bomb相关推荐

  1. CSAPP实验记录(二)Bomb Lab

    CSAPP实验记录(二)Bomb Lab 二进制炸弹是由一系列阶段组成的程序.每个阶段都要求你在 stdin 上键入一个特定的字符串.如果你输入了正确的字符串,那么这个阶段就被拆除,炸弹进入下一个阶段 ...

  2. CSAPP实验记录(一):环境配置datalab

    CSAPP实验记录(一):环境配置&datalab 1.环境配置 下载Ubuntu虚拟机.我之前用的是Ubuntu18.04,非常坑,强烈建议换成Ubuntu20.04 windows和Ubu ...

  3. csapp实验记录 - Cachelab partA

    Cachelab partA 这是该实验的 partA 部分,主要是用 c 语言模拟 cpu 对cache的存取过程,以及其缓存命中,不命中和不命中时的替换的情况 实验准备 实验的环境在 Linux ...

  4. CSAPP实验记录(1)--------- DataLab

    Datalab Datalab实验是关于数据的机器级表示,实验要求实现给定的位级运算符,同时要满足一些要求,如只能使用某些限定的运算符,运算符总数不超过某数字等.第一次刷感觉难度还是很大的. 题目一: ...

  5. 实验记录配置华为NAT Server

    1)将内部地址10.1.1.11/24的80端口静态转换为公网地址200.1.1.11/28的80端口,以便被外网(Client2)访问:同时Server1和Client2可以互相ping通. 2)将 ...

  6. eNSP动态NAT实验记录

    将内部网络10.1.1.0/24转换为公网地址200.1.1.1-200.1.1.10/28上网(访问Server3),并抓包分析 验证动态NAT是单向转换 搭建实验环境 实现此案例需要按照如下步骤进 ...

  7. CSAPP实验——DataLab

    CSAPP - DataLab CSAPP实验记录 Data Lab   实验的内容是关于计算机信息的表示,主要包括位操作.整型及浮点型相关知识. 题目列表 名称 任务 难度 bitXor(x, y) ...

  8. CSAPP Lab2 实验记录 ---- Bomb Lab(Phase 1 - Phase 6详细解答 + Secret Phase彩蛋解析)

    文章目录 Lab 总结博客链接 实验前提引子 实验需要指令及准备 Phase 1 Phase 2 Phase 3 Phase 4 Phase 5 Phase 6 Phase Secret(彩蛋Phas ...

  9. CSAPP实验二——bomb lab实验

    CSAPP实验二-- bomb lab实验 实验前准备 第一部分(phase_1) 第二部分(phase_2) 第三部分(phase_3) 第四部分(phase_4) 第五部分(phase_5) 第六 ...

最新文章

  1. 怎么在html中写当前时间,当前时间(Javascript)在HTML
  2. Java版开发原生App支付
  3. 关于学习Python的一点学习总结(13->浅复制和深复制)
  4. 《C和C++代码精粹》——2.5 普通指针
  5. 在Linux环境下mysql的root密码忘记解决方法
  6. java手写实现BST
  7. .NET跨平台实践:用C#开发Linux守护进程
  8. 【Python】Python里的复数运算
  9. 作为一个女程序员,有感而发
  10. Python实战从入门到精通第九讲——字符串与文本3之字符串匹配和搜索
  11. 2021-09-18
  12. 基于F407ZGT6的WS2812B彩灯驱动
  13. 解决微信公共号开发出现 redirect_uri域名与后台配置不一致,错误码10003 错误
  14. Draft:IPv6 Neighbor Discovery Multicast Address Listener Registration翻译
  15. 领先三星、华为,全球首款可折叠柔性屏手机惊艳上市
  16. debug基本命令及全称
  17. 中国量化金融行业 全解 金融工程 计算机 统计学 金融 专业领域 就业指南
  18. URI URL区别及转换
  19. 分析智联招聘的API接口,进行数据爬取
  20. 干货 | 这样做轻松复现顶会论文

热门文章

  1. 2022年中级经济师考试中级金融专业练习题及答案
  2. 聊聊猎洞的残酷真相:一腔孤勇,为爱发电
  3. 「译」一起探讨 JavaScript 的对象
  4. vivado仿真出错:[USF-XSim 62] 'compile' step failed with error(s) while executing
  5. [二维DP] 洛谷P1736 创意吃鱼法(预处理)
  6. Java常用算法——迭代 递归篇
  7. Vue+nodejs开发的一个处理闲置物品的校园线上交易平台
  8. 从外包到互联网,加油,打工人!
  9. SCADA软件平台数据库功能的应用
  10. Keras深度学习实战——使用深度Q学习进行SpaceInvaders游戏