【CSAPP】探究BombLab奥秘:Phase_6的解密与实战

举报
SarPro 发表于 2024/02/04 23:55:28 2024/02/04
【摘要】 这篇博文深度挖掘了CSAPP(Computer Systems: A Programmer's Perspective)课程中的BombLab实验,特别关注了其中的Phase_6,揭示了解密与实战的关键技巧。通过对该实验的深入分析,读者将了解到底层系统编程、逆向工程和程序攻击等计算机系统关键概念的精髓。文章详细解读了Phase_6的奥秘,为读者提供了一次深入学习计算机科学的机会,同时探讨了在解决实

1.gif

7dd2b7f171704327b7d8987c49da4628.gif

📋 前言

🌈个人主页:SarPro
🔥 系列专栏:《CSAPP | 奇遇记》
⏰诗赋清音:笔墨奔雷动,心随翠浪飞。山川蕴壮志澎湃,梦驭风云意悠远。

 🎉欢迎大家关注🔍点赞👍收藏⭐️留言📝
 🔔作者留言:

欢迎来到我的【CSAPP】炸弹实验室!这里是探索计算机系统世界的秘境,我的学习笔记博客为你打开CSAPP的炸弹之门。在这里,我不仅分享计算机系统的基础知识和高级技巧,还有着涉猎实用技术和项目经验的爆炸药水。无论你是初学者还是计算机大师,这个实验室会为你施展出神秘的学习魔法,帮助你在CSAPP的炸弹领域中踏上一场惊险之旅。准备好了吗?跟着我,让我们一起解除那些迷人的炸弹代码,揭示计算机系统的神奇面纱!



🌺1. CSAPP与Bomb简介

🍀1.1 CSAPP

《CSAPP》是指计算机系统基础课程的经典教材《Computer Systems: A Programmer's Perspective》,由Randal E. Bryant和David R. O'Hallaron编写。该书的主要目标是帮助深入理解计算机系统的工作原理,包括硬件和软件的相互关系,其涵盖了计算机体系结构、汇编语言、操作系统、计算机网络等主题,旨在培养学生系统级编程和分析的能力。


🍀1.2 Bomb

"Bomb实验" 是与CSAPP教材相关的一项编程实验。它是一种反汇编和逆向工程任务,旨在教授如何分析和解决复杂的程序问题。Bomb实验的目标是解开一系列的"炸弹",每个炸弹都有不同的解锁方法,需要分析程序的汇编代码,理解其工作原理,并找到正确的输入来解除炸弹。这个实验教授了计算机系统的底层知识,包括汇编语言和程序执行的原理。

资源获取:关注公众号 科创视野 回复  CSAPP Bomb实验


🌺2. bomb

🍀2.1 实验环境

  • VMware Workstation虚拟机环境下的Ubuntu 64位。

🍀2.2 实验过程

实验准备阶段:首先需要使用ubuntu联网环境跳转到链接下载实验所需的bomblab:Bomblab源文件

下载bomblab压缩包并输入

tar –xvf bomb.tar

进行解压缩,进入该目录所有文件如下所示:


在终端输入

sudo apt-get install gdb

安装调试器。基本用法参考下图:



实验过程阶段:

“Binary bombs”是一个可在Linux系统上运行的C程序,它由6个不同的阶段(phase1~phase6)组成。在每个阶段,程序会要求输入一个特定的字符串。如果输入的字符串符合程序的预期输入,那么这个阶段的炸弹就会被“解除”,否则炸弹就会“爆炸”,并输出“BOOM!!!”的提示信息。实验的目的是尽可能多地解除这些炸弹的阶段。

每个炸弹阶段考察了机器级语言程序的一个不同方面,难度逐级递增:

* 阶段1:字符串比较

* 阶段2:循环

* 阶段3:条件/分支

* 阶段4:递归调用和栈

* 阶段5:指针

* 阶段6:链表/指针/结构

在炸弹拆除任务中,还存在一个隐藏阶段。然而,只有在第四个阶段解决后添加特定的字符串后,该隐藏阶段才会出现。为了完成任务,需要使用gdb调试器和objdump反汇编炸弹的可执行文件,然后单步跟踪每个阶段的机器代码,理解每个汇编语言的行为或作用。这将帮助“推断”出拆除炸弹所需的目标字符串。为了调试,可以在每个阶段的开始代码前和引爆炸弹的函数前设置断点。

在终端输入

objdump -d bomb > bomb.asm

得到bomb的反汇编文件bomb.asm如下所示。



🍀2.3 phase_6

phase_6是一道比较难的反汇编题目,需要使用逆向工程的技巧来解决。

为了便于表示,将phase_6拆分成四部分,分别使用对应的C代码进行描述。


🍁第一部分

该部分具有两层循环,说明输入的每个数字要求不大于6,且互不相同。

以上部分对应C代码:

r14 = 0;
r13 = 0;
r12d = 0;
while(1){
  rbp = r13;
  if(num[r13] - 1 > 5)
    goto bomb;
  r12d++;
  if(r12d == 6)
    break;
  for(ebx = r12d; ebx <= 5; ebx++){
    if(num[ebx] == num[rbp])
      goto bomb;
  }
  r13++;
}

🍁第二部分

该部分的作用为使用立即数7减去每个输入数据,覆盖原来的数据。

以上部分对应C代码:

rsi=7;
for(rax = 0; rax != rsi; rax++)
{
  num[rax] = 7 - num[rax];
}


🍁第三部分

在gdb中输入

x/28 0x6032d0

得到:


发现最后8字节数字每次都加了16字节,类似通过指针访问下一结点,并且可以通过前面的node1、node2、node3知道这是一个链表的结点,然后访问6304480,即node1的指针,发现这个指针指向的是下一个结点 node2,类似地如果访问6304496 得到的会是node3和后续结点,由此可以推断出: 前面的 332、168、924是结点数据, 1 2 3是结点编号,最后8字节是next指针。

该链表每个结点的结构为:

struct node{
    int value;
    int number;
    node* next;
}

得到第三部分为:

该循环根据输入数将链表中对应的第输入数个结点的地址复制到 0x20(%rsp) 开始的栈中


🍁第四部分

因为第四部分要求:链表第一项数据 > 第二项数据 >…,根据gdb调试输入

x /28 0x6032d0

查看地址0x6032d0为:

node[3]>node[4]>node[5]>node[6]>node[1]>node[2]

因为这个顺序,是经过了numx = 0x7 - numx 则原输入数据应该是:

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    //%r13=%rsp
  401103:  48 89 e6          mov    %rsp,%rsi    //%rsi=%rsp
                                            //4010f4~401103为保存参数,分配栈帧
  401106:  e8 51 03 00 00    callq  <read_six_numbers>   //输入6个数,调用的结果是调用者的栈上按顺序存储输入的6个数
  40110b:  49 89 e6          mov    %rsp,%r14    //%r14=%rsp
  40110e:  41 bc 00 00 00 00 mov    $0x0,%r12d  //%r12d=0   %r12d当做数组索引,类似i=0
  401114:  4c 89 ed          mov    %r13,%rbp    //初始 %rbp=%r13=%rsp
  401117:  41 8b 45 00       mov    0x0(%r13),%eax  //%eax=num[i]
  40111b:  83 e8 01          sub    $0x1,%eax
  40111e:  83 f8 05          cmp    $0x5,%eax
  401121:  76 05             jbe    401128 <phase_6+0x34>  //无符号数比较,说明num为无符号数,即大于等于0,40111b~401121 num[i]-1<=5,所以num[i]<=6
  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     //401128~401132 退出大循环的条件:6个数字全部遍历到
  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>  //401145~40114b 小循环,判断数组元素是否相等
  40114d:  49 83 c5 04       add    $0x4,%r13
  401151:  eb c1             jmp    401114 <phase_6+0x20>// 40114d~401151 大循环,每次将%r13加4,之后回到401114,%r13赋给了%eax
//第二部分
  401153:  48 8d 74 24 18    lea    0x18(%rsp),%rsi   //0x18=24,刚好为6个int型数据所占字节,将 %rsi 指向栈中跳过读入数据位置作为结束标记
  401158:  4c 89 f0          mov    %r14,%rax  //%rax=%r14=%rsp  (%rax)存放输入数
  40115b:  b9 07 00 00 00    mov    $0x7,%ecx  //%ecx=7
  401160:  89 ca             mov    %ecx,%edx  //%edx=%ecx=7
  401162:  2b 10             sub    (%rax),%edx  //7-(%rax)=7-(%r14) 立即数7减去 %r14 指向的数据
  401164:  89 10             mov    %edx,(%rax)  //将7减的结果存回 %r14 执行的内存单元
  401166:  48 83 c0 04       add    $0x4,%rax  // %rax 指向下一个输入数
  40116a:  48 39 f0          cmp    %rsi,%rax  // 比较是否达到输入数组的末尾
  40116d:  75 f1             jne    401160 <phase_6+0x6c>
//第三部分
  40116f:  be 00 00 00 00    mov    $0x0,%esi        //将 %rsi 置0
  401174:  eb 21             jmp    401197 <phase_6+0xa3>    //跳转至401197
  401176: 48 8b 52 08       mov 0x8(%rdx), %rdx  //将0x8(%rdx)指向内存单元的内容(即下一结点的指针值)复制到%rdx,指向链表下一个元素
  40117a: 83 c0 01          add $0x1, %eax        //将%eax加1
  40117d: 39 c8             cmp %ecx, %eax        //比较%ecx和%eax是否相等
  40117f: 75 f5             jne 401176 <phase_6+0x82>  //不相等,继续遍历链表 【【最终%rdx指向链表的第%ecx个节点】】
  401181: eb 05             jmp 401188 <phase_6+0x94>
  401183: ba d0 32 60 00    mov $0x6032d0, %edx   //重置链表首地址,%edx存放链表首结点地址
  401188: 48 89 54 74 20    mov %rdx, 0x20(%rsp,%rsi,2) //(%rsp+32+%rsi*2)=%rdx
  40118d: 48 83 c6 04       add $0x4, %rsi         //%rsi=%rsi+4
  401191: 48 83 fe 18       cmp $0x18, %rsi
  401195: 74 14             je 4011ab <phase_6+0xb7> //当%rsi=24时,跳转至4011ab
  401197: 8b 0c 34          mov (%rsp,%rsi,1), %ecx //将(%rsp+%rsi)指向的数据复制到%ecx,%ecx存放输入数据
  40119a: 83 f9 01          cmp $0x1, %ecx         //比较%ecx是否小于等于1
  40119d: 7e e4             jle 401183 <phase_6+0x8f> //若%ecx小于等于1,跳转(因为%ecx代表结点,结点标号从1开始,所以输入数据的范围为[1,6])
                                                    //即%ecx=1时,%edx存放链表首结点地址
  40119f: b8 01 00 00 00    mov $0x1, %eax         //若%ecx>1,则%eax=1
  4011a4: ba d0 32 60 00    mov $0x6032d0, %edx    //%edx存放链表首结点地址
  4011a9: eb cb             jmp 401176 <phase_6+0x82>
//第四部分
  4011ab: 48 8b 5c 24 20    mov 0x20(%rsp), %rbx  //将(%rsp+32)的链表节点地址复制到%rbx
  4011b0: 48 8d 44 24 28    lea 0x28(%rsp), %rax  //将%rax指向栈中下一个链表结点的地址(%rsp+40)
  4011b5: 48 8d 74 24 50    lea 0x50(%rsp), %rsi  //将%rsi指向保存的链表节点地址的末尾(%rsp+80)
  4011ba: 48 89 d9          mov %rbx, %rcx
  4011bd: 48 8b 10          mov (%rax), %rdx
  4011c0: 48 89 51 08       mov %rdx, 0x8(%rcx)  //将栈中指向的后一个节点的地址复制到前一个节点的next指针位置
  4011c4: 48 83 c0 08       add $0x8, %rax       //移动到下一个节点
  4011c8: 48 39 f0          cmp %rsi, %rax       //判断6个节点是否遍历完毕
  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)  //末尾链表next为NULL则设置为0x0
  //该循环按照7减去输入数据的索引重新调整链表
  4011d9: 00
  4011da: bd 05 00 00 00    mov $0x5, %ebp
  4011df: 48 8b 43 08       mov 0x8(%rbx), %rax   //将%rax指向%rbx下一个链表结点
  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   //将%rbx向后移动,指向栈中下一个链表节点的地址
  4011f2: 83 ed 01          sub $0x1, %ebp
  4011f5: 75 e8             jne 4011df <phase_6+0xeb> //判断循环是否结束
  //该循环判断栈中重新调整后的链表结点是否按照降序排列
  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 %r13 //释放空间
  401203: c3                retq



🍀2.4 实验结果

以上代码均存储在bomb_idea.txt文件中,每行代表对应的关卡,各阶段密钥如下所示:


在终端输入

./bomb result.txt

显示全部通关。



🍀2.5 实验体会

  1. 深度解析Phase_6: 在CSAPP的BombLab实验中,深入研究了Phase_6,透过逆向分析和程序攻击手段,揭示了解密这一阶段的复杂性。通过理解程序逻辑和数据结构,成功解锁了Phase_6的奥秘。

  2. 实战经验分享: 通过实际操作,积累了丰富的实战经验。从调试器的使用到汇编代码的分析,逐步攻克了Phase_6中的各个难关。这一过程不仅提升了对计算机系统底层原理的理解,也增强了程序逆向工程的技能。

  3. 学术收获与挑战感: BombLab实验是一场学术冒险,解密Phase_6的过程既充满挑战感,又带来了深刻的学术收获。通过对程序的分析和攻击,更深刻地理解了计算机系统的运行机制,为进一步的研究和学习打下了坚实基础。


📝 总结 

计算机系统的世界,如同一座未被揭示奥秘的古老迷宫,引领你勇敢踏入计算机科学的神秘领域。CSAPP的Bomblab实验便是这场独特的学习冒险,从基本概念到底层实现,逐步揭示更深层次的计算机系统内核、汇编语言和数据结构的奥秘。

渴望挑战计算机系统中的安全学习路径和掌握底层系统编程的技术?不妨点击下方链接,一同探讨更多计算机科学的奇迹吧。我们推出引领趋势的💻 计算机科学专栏:《斯坦福大学之CSAPP》,旨在深度探索计算机系统中安全编程技术的实际应用和创新。🌐🔍


7dd2b7f171704327b7d8987c49da4628.gif

end.gif

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。