🌺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_3
phase_3需要使用逆向工程技术找到正确的密码。
vim bomb.asm
使用vim指令输入进入反汇编文件bomb.asm,
/phase_3
在未插入处输入查找phase_3的位置。
在Phase_3函数中,程序会首先使用sscanf函数从用户输入的字符串中读取一个整数,并将其存储在%eax寄存器中。接着,程序会将%eax的值与1进行按位与运算,并将结果存储到%eax寄存器中。如果%eax的值为0,则表示输入的整数为偶数;否则,表示输入的整数为奇数。 接下来,程序会调用一个名为fun3的函数,并将输入的整数和一个立即数0x7作为参数传递给这个函数。fun3函数的具体实现可以在IDA中查看。在fun3函数中,程序会将输入的整数与0x7进行按位与运算,然后将结果与一个名为“global_value”的全局变量进行比较。如果相等,则返回0;否则,返回一个非0的值。 最后,Phase_3函数会检查fun3函数的返回值是否为0,如果是,则表示密码正确;否则,表示密码错误。 为了寻找正确的密码,需要找到“global_value”的值。可以使用gdb调试工具,在程序运行时获取“global_value”的值。首先,需要在gdb中运行程序,并在输入密码之前,在第一行代码处打一个断点。接着,输入一个偶数作为密码,然后运行到断点处。在这里,可以使用命令“x/gx 0x地址”来查看“global_value”的值。 得到“global_value”的值后,需要使用Python等脚本语言,在0到1000之间枚举所有满足条件的偶数,并将其作为密码输入到程序中,以验证是否正确。如果找到了正确的密码,程序会输出“Congratulations!”;否则,程序会输出“Explosion!”。 除了上述步骤,我们可以注意到Phase_3函数中的第四行代码,程序将0x4025cf赋值给了%esi寄存器。这是一个地址常量,由于它在sscanf函数的前面,可以推测0x4025cf可能是一个格式字符串。因此使用gdb工具中的x/s命令来查看该地址常量的内容,以进一步了解输入内容的格式。
其中,第7-10行代码是关键的判断条件。在这几行代码中,程序会将输入的整数与1进行按位与运算,并将结果存储到%eax寄存器中。随后,程序会将%eax的值与1进行比较,如果小于等于1,则会引爆炸弹。 %eax寄存器一般是存储函数的返回值,在Bomblab中,sscanf函数的返回值就是成功读取的元素个数。因此可以推断出,这里输入的元素个数必须大于1才能通过验证,否则会引爆炸弹。 为了找到正确的密码,需要输入两个整数,可以通过IDA等反汇编工具来了解程序的具体实现。
在终端查看跳转表中储存的地址。
x/8xg 0x402470
仔细观察这些地址,可以发现都是函数phase_3范围内的地址。
当num1=0时,跳转到0x0000000000400f7c处执行。如果num2不等于0xcf,则触发炸弹。
当num1=1时,跳转到0x0000000000400fb9处执行。如果num2不等于0x137,则触发炸弹。
当num1=2时,跳转到0x0000000000400f83处执行。如果num2不等于0x2c3,则触发炸弹。
当num1=3时,跳转到0x0000000000400f8a处执行。如果num2不等于0x100,则触发炸弹。
当num1=4时,跳转到0x0000000000400f91处执行。如果num2不等于0x185,则触发炸弹。
当num1=5时,跳转到0x0000000000400f98处执行。如果num2不等于0xce,则触发炸弹。
当num1=6时,跳转到0x0000000000400f9f处执行。如果num2不等于0x2aa,则触发炸弹。
当num1=7时,跳转到0x0000000000400fa6处执行。如果num2不等于0x147,则触发炸弹。
所以拆弹密码有以下8种:
0 207; 1 311; 2 707; 3 256; 4 389; 5 206; 6 682; 7 327.
输入其中任意几种或者全部输均可。
在终端输入
./bomb
填入密钥0 207结果显示phase_3通关。
综上所述,解决Bomblab中的Phase_3题目需要一定的逆向工程技术和耐心,但通过对代码的分析和调试,我们可以找到正确的密码,进入下一个阶段。这是逆向工程学习中重要的一步,也可以帮助我们更深入地了解程序的运行机制。
对应C代码的实现为:
void phase_3(const char *input) { // 0x8(%rsp) 0xc(%rsp) int num1, num2; // %rdi %rsi %rdx %rcx int result = sscnaf(input, "%d %d", &num1, &num2); if (result <= 1) { explode_bomb(); } switch (num1) { case 0: // 0 207 if (num2 != 0xcf) { explode_bomb(); } break; case 1: // 1 311 if (num2 != 0x137) { explode_bomb(); } break; case 2: // 2 707 if (num2 != 0x2c3) { explode_bomb(); } break; case 3: // 3 256 if (num2 != 0x100) { explode_bomb(); } break; case 4: // 4 389 if (num2 != 0x185) { explode_bomb(); } break; case 5: // 5 206 if (num2 != 0xce) { explode_bomb(); } break; case 6: // 6 682 if (num2 != 0x2aa) { explode_bomb(); } break; case 7: // 7 327 if (num2 != 0x147) { explode_bomb(); } break; default: explode_bomb(); break; } }
将Phase_3中每句代码的作用解释如下所示。
0000000000400f43 <phase_3>: 400f43: 48 83 ec 18 sub $0x18,%rsp //给局部变量腾出空间 400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx //加载地址,将0xc(%rsp)设为num2 400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx //加载地址,将0x8(%rsp)设为num1 400f51: be cf 25 40 00 mov $0x4025cf,%esi //因为后续调用了函数scanf,此处需对输入格式进行测试 //经测试,0x4025cf对应字符串"%d %d" //因此可以猜测开头%rcx存放的为输入的第一个数据(设为num1)的地址,%rdx存放的为输入的第二个数据(设为num2)的地址 400f56: b8 00 00 00 00 mov $0x0,%eax //初始化%eax 400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt> //调用scanf函数,此时%eax放scanf函数的返回值(输入数据的个数) 400f60: 83 f8 01 cmp $0x1,%eax 400f63: 7f 05 jg 400f6a <phase_3+0x27> //jg:有符号大于则跳转,说明scanf输入数据的个数必须大于1 400f65: e8 d0 04 00 00 callq 40143a <explode_bomb> //否则爆炸 400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) 400f6f: 77 3c ja 400fad <phase_3+0x6a> //ja:无符号大于则跳转,至爆炸,说明num1为无符号数,大于0且需要小于等于7,所以num1=[0,7] 400f71: 8b 44 24 08 mov 0x8(%rsp),%eax //将num1储存到%eax中 400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8) //间接跳转,(此处%rax不完全等于%eax,由num1=1(%rax=1)时的跳转地址可推论) //0x402470+%rax*8的计算结果作为地址,跳转到该地址继续执行 //根据该指令和后续的指令格式,很容易判断此处是switch语句的跳转表,跳转表的首地址为0x402470 //以 *0x402470 处的值为基地址,再加上8 * %rax 进行跳转,不同的 %rax 跳转到不同的位置。 400f7c: b8 cf 00 00 00 mov $0xcf,%eax //case 0 %eax=0xcf=207 400f81: eb 3b jmp 400fbe <phase_3+0x7b> 400f83: b8 c3 02 00 00 mov $0x2c3,%eax //case 2 %eax=0x2c3=707 400f88: eb 34 jmp 400fbe <phase_3+0x7b> 400f8a: b8 00 01 00 00 mov $0x100,%eax //case 3 %eax=0x100=256 400f8f: eb 2d jmp 400fbe <phase_3+0x7b> 400f91: b8 85 01 00 00 mov $0x185,%eax //case 4 %eax=0x185=389 400f96: eb 26 jmp 400fbe <phase_3+0x7b> 400f98: b8 ce 00 00 00 mov $0xce,%eax //case 5 %eax=0xce=206 400f9d: eb 1f jmp 400fbe <phase_3+0x7b> 400f9f: b8 aa 02 00 00 mov $0x2aa,%eax //case 6 %eax=0x2aa=682 400fa4: eb 18 jmp 400fbe <phase_3+0x7b> 400fa6: b8 47 01 00 00 mov $0x147,%eax //case 7 %eax=0x147=327 400fab: eb 11 jmp 400fbe <phase_3+0x7b> 400fad: e8 88 04 00 00 callq 40143a <explode_bomb> 400fb2: b8 00 00 00 00 mov $0x0,%eax 400fb7: eb 05 jmp 400fbe <phase_3+0x7b> 400fb9: b8 37 01 00 00 mov $0x137,%eax //case 1 %eax=0x137=311 400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax //比较%eax的值和num2 400fc2: 74 05 je 400fc9 <phase_3+0x86> //je:若相等则跳转至结束 400fc4: e8 71 04 00 00 callq 40143a <explode_bomb> //否则爆炸 400fc9: 48 83 c4 18 add $0x18,%rsp //释放空间 400fcd: c3 retq
🍀2.4 实验结果
以上代码均存储在bomb_idea.txt文件中,每行代表对应的关卡,各阶段密钥如下所示:
在终端输入
./bomb result.txt
显示全部通关。
🍀2.5 实验体会
- 解密奥秘: 在CSAPP的BombLab实验中,深入研究了Phase_3,通过逆向分析揭示了其隐藏的机制。理解了程序背后的逻辑,成功解密了Phase_3,这一过程让我对计算机系统底层运行有了更深入的认识。
- 实战经验: 在攻克Phase_3的实战中,运用了各种适当的技术手段,包括逆向工程和程序调试等。通过实际动手的过程,加深了对计算机系统内部运作的理解,提升了解决实际问题的能力。
- 学术收获: 通过解密与实战,不仅提高了我的计算机系统安全意识,还培养了问题解决和分析的能力。CSAPP的BombLab实验为我打开了计算机科学领域的一扇大门,让我更加热衷于深入研究底层原理与系统编程。
📝 总结
计算机系统的世界,如同一座未被揭示奥秘的古老迷宫,引领你勇敢踏入计算机科学的神秘领域。CSAPP的Bomblab实验便是这场独特的学习冒险,从基本概念到底层实现,逐步揭示更深层次的计算机系统内核、汇编语言和数据结构的奥秘。