前言

​个人主页:Sarapines Programmer
系列专栏:《斯坦福大学之CSAPP》
⏰诗赋清音:桃花灼灼春风暖,心随乐曲扬徐徐。 苦尽甘来梦未阑,岁月长河任舟游。

欢迎大家关注点赞收藏⭐️留言
作者留言:

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

目录

前言

1. CSAPP与Bomb简介

1.1 CSAPP

1.2 Bomb

2. bomb

2.1 实验环境

2.2 实验过程

2.3phase_6

第一部分

第二部分

第三部分

第四部分

2.4 实验结果

2.5 实验体会

总结


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.3phase_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减去每个输入数据,覆盖原来的数据。

图2-38

以上部分对应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为:

图2-42

得node[i].value排序为:

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

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

4 3 2 1 6 5

终端验证:

系统显示通关成功。

系统的注释详细如下:

00000000004010f4 ://第一部分:4010f4:41 56 push %r144010f6:41 55 push %r134010f8:41 54 push %r124010fa:55push %rbp4010fb:53push %rbx4010fc:48 83 ec 50 sub$0x50,%rsp//申请空间401100:49 89 e5mov%rsp,%r13//%r13=%rsp401103:48 89 e6mov%rsp,%rsi//%rsi=%rsp//4010f4~401103为保存参数,分配栈帧401106:e8 51 03 00 00callq //输入6个数,调用的结果是调用者的栈上按顺序存储输入的6个数40110b:49 89 e6mov%rsp,%r14//%r14=%rsp40110e:41 bc 00 00 00 00 mov$0x0,%r12d//%r12d=0 %r12d当做数组索引,类似i=0401114:4c 89 edmov%r13,%rbp//初始 %rbp=%r13=%rsp401117:41 8b 45 00 mov0x0(%r13),%eax//%eax=num[i]40111b:83 e8 01sub$0x1,%eax40111e:83 f8 05cmp$0x5,%eax401121:76 05 jbe401128 //无符号数比较,说明num为无符号数,即大于等于0,40111b~401121 num[i]-1<=5,所以num[i]<=6401123:e8 12 03 00 00callq 40143a 401128:41 83 c4 01 add$0x1,%r12d40112c:41 83 fc 06 cmp$0x6,%r12d401130:74 21 je 401153 401132:44 89 e3mov%r12d,%ebx //401128~401132 退出大循环的条件:6个数字全部遍历到401135:48 63 c3movslq %ebx,%rax401138:8b 04 84mov(%rsp,%rax,4),%eax40113b:39 45 00cmp%eax,0x0(%rbp)40113e:75 05 jne401145 401140:e8 f5 02 00 00callq 40143a 401145:83 c3 01add$0x1,%ebx401148:83 fb 05cmp$0x5,%ebx40114b:7e e8 jle401135 //401145~40114b 小循环,判断数组元素是否相等40114d:49 83 c5 04 add$0x4,%r13401151:eb c1 jmp401114 // 40114d~401151 大循环,每次将%r13加4,之后回到401114,%r13赋给了%eax//第二部分401153:48 8d 74 24 18lea0x18(%rsp),%rsi //0x18=24,刚好为6个int型数据所占字节,将 %rsi 指向栈中跳过读入数据位置作为结束标记401158:4c 89 f0mov%r14,%rax//%rax=%r14=%rsp(%rax)存放输入数40115b:b9 07 00 00 00mov$0x7,%ecx//%ecx=7401160:89 ca mov%ecx,%edx//%edx=%ecx=7401162: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 f0cmp%rsi,%rax// 比较是否达到输入数组的末尾40116d:75 f1 jne401160 //第三部分40116f:be 00 00 00 00mov$0x0,%esi//将 %rsi 置0401174:eb 21 jmp401197 //跳转至401197401176: 48 8b 52 08 mov 0x8(%rdx), %rdx//将0x8(%rdx)指向内存单元的内容(即下一结点的指针值)复制到%rdx,指向链表下一个元素40117a: 83 c0 01add $0x1, %eax//将%eax加140117d: 39 c8 cmp %ecx, %eax//比较%ecx和%eax是否相等40117f: 75 f5 jne 401176 //不相等,继续遍历链表 【【最终%rdx指向链表的第%ecx个节点】】401181: eb 05 jmp 401188 401183: ba d0 32 60 00mov $0x6032d0, %edx //重置链表首地址,%edx存放链表首结点地址401188: 48 89 54 74 20mov %rdx, 0x20(%rsp,%rsi,2) //(%rsp+32+%rsi*2)=%rdx40118d: 48 83 c6 04 add $0x4, %rsi //%rsi=%rsi+4401191: 48 83 fe 18 cmp $0x18, %rsi401195: 74 14 je 4011ab  //当%rsi=24时,跳转至4011ab401197: 8b 0c 34mov (%rsp,%rsi,1), %ecx //将(%rsp+%rsi)指向的数据复制到%ecx,%ecx存放输入数据40119a: 83 f9 01cmp $0x1, %ecx //比较%ecx是否小于等于140119d: 7e e4 jle 401183  //若%ecx小于等于1,跳转(因为%ecx代表结点,结点标号从1开始,所以输入数据的范围为[1,6])//即%ecx=1时,%edx存放链表首结点地址40119f: b8 01 00 00 00mov $0x1, %eax //若%ecx>1,则%eax=14011a4: ba d0 32 60 00mov $0x6032d0, %edx//%edx存放链表首结点地址4011a9: eb cb jmp 401176 //第四部分4011ab: 48 8b 5c 24 20mov 0x20(%rsp), %rbx//将(%rsp+32)的链表节点地址复制到%rbx4011b0: 48 8d 44 24 28lea 0x28(%rsp), %rax//将%rax指向栈中下一个链表结点的地址(%rsp+40)4011b5: 48 8d 74 24 50lea 0x50(%rsp), %rsi//将%rsi指向保存的链表节点地址的末尾(%rsp+80)4011ba: 48 89 d9mov %rbx, %rcx4011bd: 48 8b 10mov (%rax), %rdx4011c0: 48 89 51 08 mov %rdx, 0x8(%rcx)//将栈中指向的后一个节点的地址复制到前一个节点的next指针位置4011c4: 48 83 c0 08 add $0x8, %rax //移动到下一个节点4011c8: 48 39 f0cmp %rsi, %rax //判断6个节点是否遍历完毕4011cb: 74 05 je 4011d2 4011cd: 48 89 d1mov %rdx, %rcx //继续遍历4011d0: eb eb jmp 4011bd 4011d2: 48 c7 42 08 00 00 00movq $0x0, 0x8(%rdx)//末尾链表next为NULL则设置为0x0//该循环按照7减去输入数据的索引重新调整链表4011d9: 004011da: bd 05 00 00 00mov $0x5, %ebp4011df: 48 8b 43 08 mov 0x8(%rbx), %rax //将%rax指向%rbx下一个链表结点4011e3: 8b 00 mov (%rax), %eax4011e5: 39 03 cmp %eax, (%rbx)//比较链表结点中第一个字段值的大小,如果前一个节点值大于后一个节点值,跳转4011e7: 7d 05 jge 4011ee 4011e9: e8 4c 02 00 00callq 40143a 4011ee: 48 8b 5b 08 mov 0x8(%rbx), %rbx //将%rbx向后移动,指向栈中下一个链表节点的地址4011f2: 83 ed 01sub $0x1, %ebp4011f5: 75 e8 jne 4011df  //判断循环是否结束//该循环判断栈中重新调整后的链表结点是否按照降序排列4011f7: 48 83 c4 50 add $0x50, %rsp4011fb: 5bpop %rbx4011fc: 5dpop %rbp4011fd: 41 5c pop %r124011ff: 41 5d pop %r13401201: 41 5e pop %r13 //释放空间401203: c3retq


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》,旨在深度探索计算机系统中安全编程技术的实际应用和创新。