🌺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_4
Phase_4是Bomblab中的一道难度较大的炸弹题目,需要破解一个使用了跳转表的程序,以解除炸弹。在这个过程中需要运用一些调试技巧和汇编知识,逐步分析程序的运行逻辑,找到正确的输入值,解除炸弹。
通过逐行分析代码,发现phase_4函数的代码逻辑与phase_3函数的代码逻辑有很多相似之处。在代码的第12行,可以看到调用了scanf函数,该函数会读取用户输入的内容,并按照指定的格式进行解析。从这个函数的参数可以看出,它需要读取两个数字,并将它们分别存储在0x8(%rsp)、0xc(%rsp)中,因此可以暂时确定需要输入至少两个数字。但是,这并不意味着只有两个数字是正确答案,还需要进一步的分析才能确定正确的密钥。
在代码的40102c,可以看到调用了第一个explode_bomb函数. 这说明在输入错误的密钥时,程序会触发炸弹并终止运行。为了跳过这个炸弹,我们需要输入正确的密钥。根据代码的第401033行,我们可以发现需要满足输入的第一个数字不为2,才能跳过这个炸弹,因此,我们需要在输入时避免将第一个数字设置为2。
在401058行explode_bomb函数,这说明在输入错误的密钥时,程序会触发另一个炸弹并终止运行。为了跳过这个炸弹输入正确的密钥,根据代码的第40105f行,需要满足输入的第一个数字为0xe(14),才能跳过这个炸弹。
当成功输入6个参数并且满足特定的条件后,程序会进行一系列操作并将三个寄存器(%edx、%esi、%edi)赋值,接着将这些参数传入func函数中进行处理。首先,我们需要注意的是在输入6个参数后,程序会对第一个参数进行一系列的位运算操作,最终得到一个索引值并跳转到对应的地址处。这个地址指向了一个跳转表,其中每个元素都是一个8字节的地址。程序以输入的第一个参数作为索引,查找跳转表中对应的地址,并跳转到该地址处执行下一步操作。 接着,程序会将输入的6个参数按顺序存储到栈中,然后将第一个参数作为索引,从跳转表中查找对应的地址,并跳转到该地址处执行代码。这个地址指向了一个函数,这个函数的作用是将输入的参数分别存储到%edx、%esi、%edi寄存器中,并调用func函数进行处理。 在func函数中,程序依次对%edx、%esi、%edi寄存器中的值进行一系列位运算操作,并将结果存储到%eax寄存器中。最终,当func函数执行完毕后,程序会将%eax寄存器中的值与一个特定的值进行比较,如果相等则输出“Phase_4 defused”,表示成功解除炸弹;否则输出“BOOM!!!”,表示炸弹爆炸了。 在这个过程中,需要仔细分析程序的运行逻辑和寄存器的作用,理解程序的实现原理。通过逐步分析和调试,
程序执行fun4函数后,返回值存放在%eax寄存器中。为了避免调用explode_bomb函数,我们需要将%eax的返回值设置为0,并保证输入的第二个数为0(从地址0x(%rsp)获取)。
对于func4函数而言,它整体较为复杂,为了便于之后的分析,我们将其转换为对应的C代码:
//x: %edi y:%esi z:%edx k: %ecx t:%eax int func4(int x,int y,int z) {//x in %rdi,y in %rsi,z in %rdx,t in %rax,k in %ecx //y的初始值为0,z的初始值为14 int t=z-y; int k=t>>31; t=(t+k)>>1; k=t+y; if(k>x) { z=k-1; func4(x,y,z); t=2t; return t; } else { t=0; if(k<x) { y=k+1; func4(x,y,z); t=2*t+1; return t; } else { return t; } } }
根据之前传入func4函数的两个参数,我们得知func4函数会将7存储到%eax和%ecx寄存器中,并将%edi里存储的输入的第一个数与%ecx寄存器中的值进行比较。
在代码分析中,可以发现在400ff2的代码中,当%edi的值大于等于7时,会将%eax置为0。因此可以尝试将第一个输入的数字设置为7,以此来获取想要的返回值。
如果%edi的值小于7,程序会进入400fe9处并再次调用func4函数,形成递归调用。虽然看起来很复杂,但我们可以通过代入寄存器中的值并记录它们的变化来推导出最终的结果。据此,我们可以得知第一个输入的数字为0、1、3、7,而第二个输入的数字为0。
在终端输入
./bomb
填入密钥0 0结果显示phase_4通关。
综上所述,Phase_4程序使用了一个跳转表来实现多个分支语句。具体来说,程序先将输入的第一个值存储到%eax寄存器中,然后执行一个间接跳转,跳转的目标地址存储在内存地址0x402470(,%rax,8)处。这里的%rax寄存器就是我们输入的第一个值。通过将输入的值作为索引,程序可以从跳转表中查找对应的目标地址,然后进行跳转。跳转表通常使用数组或者指针来实现,每个元素对应一个分支语句的目标地址。在Phase_4中,跳转表使用的是数组实现方式,每个元素占据8个字节,因此需要将输入值乘以8来计算数组元素的偏移量。 在Phase_4中,程序使用了一些位运算操作,需要仔细分析代码,理解其运行逻辑。具体来说,程序使用了“and”、“shr”和“cmp”等指令来对输入值进行位运算操作,并判断其是否符合特定的条件。
将Phase_4中每句代码的作用解释如下所示。
000000000040100c <phase_4>: 40100c: 48 83 ec 18 sub $0x18,%rsp //申请空间 401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx //传参,加载有效地址,将0xc(%rsp)设为num2 401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx //传参,加载有效地址,将0x8(%rsp)设为num1 40101a: be cf 25 40 00 mov $0x4025cf,%esi //scanf函数输入格式, %d %d 40101f: b8 00 00 00 00 mov $0x0,%eax 401024: e8 c7 fb ff ff callq 400bf0 <__isoc99_sscanf@plt> //调用scanf函数 401029: 83 f8 02 cmp $0x2,%eax 40102c: 75 07 jne 401035 <phase_4+0x29> //当scanf输入数据个数不等于2时,跳转至爆炸 40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp) 401033: 76 05 jbe 40103a <phase_4+0x2e> //jdb:无符号小于等于跳转,当num1小于等于14时,跳转至40103a,否则爆炸,所以num1的限制条件为[0,14] 401035: e8 00 04 00 00 callq 40143a <explode_bomb> 40103a: ba 0e 00 00 00 mov $0xe,%edx //%edx=14 40103f: be 00 00 00 00 mov $0x0,%esi //%esi=0 401044: 8b 7c 24 08 mov 0x8(%rsp),%edi //%edi=num1 401048: e8 81 ff ff ff callq 400fce <func4> //调用函数fun4,三个参数%edx,%esi,%edi 40104d: 85 c0 test %eax,%eax 40104f: 75 07 jne 401058 <phase_4+0x4c> //如果fun4返回值%eax不等于0,则跳转至爆炸,所以需要知道当num1为何值时,%eax为0 401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp) 401056: 74 05 je 40105d <phase_4+0x51> //如果num2=0,则跳转至结束,否则爆炸,所以输入的第二个数据只能为0 401058: e8 dd 03 00 00 callq 40143a <explode_bomb> 40105d: 48 83 c4 18 add $0x18,%rsp //释放空间 401061: c3 retq
🍀2.4 实验结果
以上代码均存储在bomb_idea.txt文件中,每行代表对应的关卡,各阶段密钥如下所示:
在终端输入
./bomb result.txt
显示全部通关。
🍀2.5 实验体会
- 逆向分析挑战: 探索CSAPP中的BombLab Phase_4实验,着眼于逆向分析,挑战了解密阶段的复杂算法。通过深度解析程序逻辑,成功解开了Phase_4的神秘面纱。
- 实战经验分享: 在实验过程中,学到了许多实战技巧,包括调试器的巧妙运用和汇编代码的精准理解。这些经验不仅提升了解题效率,也增强了对计算机系统底层运作的理解。
- 深度学习与收获: 通过参与Phase_4的解密过程,深刻理解了程序的运行原理和安全设计,为深度学习计算机系统打下了坚实基础。这次实验不仅是知识的获取,更是对计算机科学深度思考的契机。
📝 总结
计算机系统的世界,如同一座未被揭示奥秘的古老迷宫,引领你勇敢踏入计算机科学的神秘领域。CSAPP的Bomblab实验便是这场独特的学习冒险,从基本概念到底层实现,逐步揭示更深层次的计算机系统内核、汇编语言和数据结构的奥秘。