BombLab

Deadline:2023-11-01 23:59:59

一、实验简介

CSAPP 第三章配套实验。

本学期,我们对 csapp 的经典 bomblab 进行了全面升级(除了难度)。你将假扮一位精通 x86汇编语言 / Linux / gdb / 数据结构 的逆向工程大师,在这款全新的代码世界冒险游戏中冒险,发现隐藏在机器码背后的真相……

本次Lab由两个部分组成:

  • 一个互动式 gdb 教程:gdb-tutor
  • 一个包含若干个关卡的二进制炸弹:Bomb+

你的目标是拆除炸弹,无伤通过Bomb+的所有关卡。

二、实验内容

下载链接:bomblab-handout

互动式 gdb 教程 gdb-tutor

本部分不占分,也不要求必须完成。

在挑战 BombLab 时,gdb是一个非常有用的工具,但以往的 BombLab 只会列出一些常用的 gdb 指令,并不会教大家如何使用。

为了降低本 Lab 的上手难度,帮助大家掌握 gdb,认识这一强大的工具,我们编写了这个互动式的 gdb 教程。

使用方法:直接执行 ./gdb-tutor,教程源码见 gdb-tutor.c

二进制炸弹 Bomb+

这是一个包含若干关卡的可执行文件。只有你输入满足条件的关卡口令时,才能通过该关卡,到达下一个关卡。如果口令错误,炸弹就会爆炸!(程序会打印爆炸信息并立刻退出)

你的目标是找到所有关卡的正确口令,让炸弹成功解除。炸弹制造者不小心给出了 main.cpp ,其中包含了Bomb+的主要流程,你可以参考这个源文件了解炸弹的大致逻辑。

如果炸弹爆炸,它将会输出 BOOM!!!,否则通过每一关后炸弹都会恭喜你;在通关后,它将会输出 Cool! your skill on Reverse Engineering is great.;而通关真结局后,它将会输出 You are really a Master of Reverse Engineering!。(详见 main.cpp

炸弹会读取 fail.txt 并在爆炸时打印其内容,如果你想要很酷炫的爆炸,可以自行修改 fail.txt 或直接将其删去。

实验各个关卡涉及的知识大致如下:

  1. 函数调用
  2. 循环
  3. 分支
  4. 递归
  5. 面向对象
  6. 循环队列

Secret. ████

请注意,Secret的解锁条件在第三关中进行了提示,或许你能在 main.cpp 的某个函数中发现一些违和之处……

三、实验步骤

  • 完整阅读本实验文档、下载题目附件
  • 跟随 gdb-tutor 学习 gdb 的基础使用方法
  • 使用静态分析与动态分析方法分析 Bomb+,尽力找出正确的口令
  • 编写实验报告,在 elearning 上提交压缩包

四、提交事项

提交格式

  1. 新建文件夹,以你的学号命名(例如 22307130001)。
  2. 将实验报告 report.pdf 以及正确口令 password.txt 放入这个文件夹。注意,password.txt 需要满足可以用重定向秒杀 Bomb+ 的要求(我们会在后面介绍什么是重定向),我们会用这个来判断你通过了几关。
  3. 在上级文件夹使用 tar -cf <ID>.tar <ID> 将文件夹压缩成 tar 包,你会得到一个名为 <ID>.tar 的文件。(请把 <ID> 替换为你的学号)

你的实验报告应包含以下内容:

  1. 姓名和学号
  2. 每个关卡的推演过程(重点),如你推测的函数的功能
  3. 拆弹成功的截图(随便弄下)
  4. 如果有,请列出引用的内容以及参考的资料
  5. 意见+建议(可选)

助教の温馨提示

实验报告是用来判断你是否 独立思考 来解决本次Lab的。

我们不希望看到冗长或表意混乱的报告,请一定不要把报告写成论文QaQ

评分

项目 分值
提交格式正确 5%
通过正常关卡 6*13% 78%
通过隐藏关卡 7%
实验报告 10%

另:抄袭倒扣分!!勿谓言之不预。

反卷斗士の承诺

实验过程非抄袭、提交格式正确且通过所有关卡的同学,实验报告部分赋满分。

五、实验指导

本题本质上是一道逆向工程题,需要你在没有源代码的情况下复原程序编写者的意图与程序的逻辑。你可能会好奇,我们为什么要学习逆向工程、学习汇编:作为科班CS学生,你需要知道自己写出来的程序到底会变成什么样子,到底如何运行,这样你才能知道如何写出更好的程序——正是这些底层的细节将科班程序员和培训班程序员区分开来。这个Lab将会帮助你搞清楚机器码 / 汇编语言具体而言如何运作。

除此以外,这个Lab还会带你入门逆向工程。逆向工程作为信息安全的一个研究领域,有着许多有趣的应用领域——注册机、软件破解、游戏安全(如修改器、外挂)……除了这些看起来很恶意的应用场景以外,逆向工程一个非常重要的作用就是帮助白帽黑客们找到程序的漏洞,发掘程序的安全问题。不过,最纯粹的逆向工程热爱者,往往是出于自己的好奇心来逆向闭源的软件——这玩意到底怎么实现的?

如果你对逆向工程感兴趣,可以试着从CTF(Capture The Flag)竞赛开始。逆向工程是CTF竞赛的一个主要领域,比赛中出题人会用各种语言、工具编写赛题,试图隐藏程序的逻辑。参赛者需要使用各种工具、结合各种资料来还原程序逻辑。在比赛与练习中,你会接触到各种软件开发技术(如python、rust、golang、android……)、各种编码和密码学算法(如Base64、TEA、RC4……),了解其底层的实现原理。我们学校也有相应的社团/CTF战队——信息网络安全协会/六星战队,大家可以了解一下哦~

在逆向工程中,分析方法可以大致归类为两类:动态分析与静态分析。简单来说,动态分析就是运行程序、观察其行为;静态分析就是借助可执行文件中的信息与数据(如机器码)还原程序逻辑。在本节中,我们会分别介绍动态分析、静态分析的方法与工具,然后再介绍一些别的小工具。

动态分析

最简单的动态分析就是直接运行程序。你可以试试引爆炸弹先。

正经的动态分析就是使用 gdb(全称为 GNU Debugger)等调试工具进行动态调试。动态调试的坏处是可能会一不小心让程序“跑飞了”,错过你想要观察的代码;而好处就是你可以直观地看到程序运行时,各种数据的实际值。

至于如何使用,请参考 gdb-tutor

你可以去网上找一个 gdb cheetsheet(小抄),方便你查找想用的指令。我们也给出一个挺好的英文教程:Tudor‘s gdb crash course

静态分析

反汇编

我们回顾一下C程序的编译过程:源代码 -> 汇编代码 -> 机器码,中间两步分别称为编译汇编

在源代码到汇编代码的过程中,编译器作了许多的优化、也删去了很多源代码中的信息,比如局部变量名等等……而汇编代码和机器码则是几乎等价的,汇编语言是机器码的助记符。

因此,有工具能帮我们将可执行文件中的机器码转化为汇编代码也就不足为奇了,这种工具被称为反汇编器。至于汇编代码到源代码,这种工具也存在,但由于本次实验旨在帮助大家熟悉汇编代码,因此在本次实验中我们不会介绍它们。我们这里介绍一个非常经典且常用的反汇编器——objdump。

我们在命令行中输入 objdump -d ./bomb > bomb.S 就可以获得反汇编文件 bomb.S

objdump --help 会打印出 objdump 的所有用法,并且会给出精简的解释。

助教の温馨提示

你可以尝试在objdump后加上 -D 和/或 -x 和/或 -C,就像上面出现的-d一样。你可以通过搜索或读文档的方式了解这会导致什么后果;TA相信这几个参数会对你的实验有很大帮助!

X86 / AMD64 汇编语言的格式

在CSAPP课本上,有一个拓展框介绍了两种汇编语言的格式——AT&T 以及 Intel。

他们最显著的区别有两个(当然还有别的区别,见课本):

  • 两者的源操作数和目的操作数顺序相反
  • AT&T 中寄存器需要加上 % 前缀,而 Intel 语法则不用。

本课程上课教学时使用的语法是 AT&T,这也是 objdump 默认使用的语法。但我们同样推荐你试试 Intel 语法,因为这是安全研究人员更常用的一种语法。想让 objdump 输出语法变为 Intel,只需要给 objdump 添加参数 -M intel

阅读汇编

在拿到汇编代码文件后,我们就需要通过分析它来理解程序的逻辑。如果是一个简短的函数,或许汇编也就几十行;但你很有可能会遇到百来行的函数。如何应对这种复杂的代码?

我们介绍一种理解汇编代码的方法,叫做 CFG(Control Flow Graph,控制流图)。显然,汇编代码中没有 if-else/for loop/while loop 这样的结构,而只有各种jump、conditional jump。很容易想到,我们可以把各种jump指令把它们的目标用一个箭头连起来,来让代码变得稍微好看懂一点。不过,我们还可以做得更加好一些:

这是一个伪代码的例子,我们同样可以将这个方法运用到汇编语言中。构造CFG的基础方法很简单:我们只需要把不含跳转代码当作一个Block,然后把Block们连接起来即可。你可以在草稿纸上使用这个方法,将函数的跳转逻辑理清楚。不要小看了CFG的作用,亲自试试吧,或许你会从此觉得读汇编语言也不过如此。

另外一种推荐的技巧是……将汇编代码打印出来。没错,虽然我知道手写代码一定是痛苦的体验,但在纸上阅读汇编语言代码确实是一种推荐的方法。这是因为,汇编语言的阅读可能需要大量的标注,在纸上标记会方便很多。不过也请注意,Bomb+的汇编代码很长,包含了许多不会执行或与实验无关的函数。你可以只在遇到应付不了的关卡时才选择把看不懂的那些函数打印出来。

其他实验帮助

重定向与管道

上过C语言和C++的大家应该知道,命令行程序有三个默认打开的“流”,分别是stdin,stdout以及stderr。在我们运行命令行程序时,标准输入就是我们敲进去的东西,而程序打印东西到stdout或者stderr,其实就是打印到命令行上。

在学习C或C++的文件操作的时候,会发现一个很巧的事情——文件读写用到的API,和标准输入输出用的那些API其实都差不多,这是因为文件和标准输入输出本来就是一样的。在Linux系统中,一切皆文件。不仅传统意义上的文本文件、多媒体文件等普通文件是文件,套接字(网络接口)、键盘鼠标设备等等都是文件,可以用统一的一套文件API进行处理。

这种设计不仅带来了极大的统一性,也带来了极大的便捷性。本节介绍的iostream重定向和pipes管道就与这种设计有关。

既然stdin和stdout也是文件流,那么我们当然可以把他们重定向到一个普通文件!我们让一个文件被定向到一个程序的标准输入,或者让一个程序的标准输出定向到一个文件当中。前者我们使用 < file,后者我们使用 > file,如下所示:

1
2
3
4
5
6
7
8
9
10
$ echo hello > hello.txt		# 把stdout重定向到hello.txt中
$ cat hello.txt
hello

$ cat < hello.txt # 把hello.txt重定向到cat的标准输入
hello

$ cat < hello.txt > hello2.txt # 同时重定向cat的标准输入和标准输出
$ cat hello2.txt
hello

如果你找到了所有的正确口令,那么把它们写到一个文本文件 password.txt 中,那么使用 ./bomb < password.txt 就可以直接通过所有的关卡。

除此以外,还有更加便利的 管道 操作,可以帮助我们将两个程序的输入和输出相连接,也就是构造一条虚拟的管道。这种神奇的操作是通过 | 来完成的:

1
2
3
4
5
6
7
8
9
$ cat 1.txt           	# 假设我们有这么一个文件
GODEL
ESCHER
BACH
$ cat 1.txt | tail -n1 # 打印1.txt的最后一行
BACH
$ cat 1.txt | grep CH # 寻找1.txt中带有CH的行
ESCHER
BACH

利用这两种操作,我们可以方便地处理BombLab的输入。我们知道 cat 是打开文件,也知道标准输入同样也是一个文件,因此我们可以用 cat 打开标准输入文件,这时候 cat 就相当于一个 echo,可以复读我们说的话:

1
2
3
$ cat -
Hello, world!
Hello, world!

除此以外,cat还可以打开多个文件,如下所示:

1
2
3
4
5
6
7
$ cat 1.txt
hello
$ cat 2.txt
world
$ cat 1.txt 2.txt
hello
world

如果你在做BombLab时,已经找到了前面某些阶段的口令,不想在后续步骤中再一遍遍重新输入它们的话,就可以将已知的口令写入一个文本文件中,然后用 cat 打开那个文本文件以及标准输入,然后利用管道机制将这两个文件的内容导向bomb+的输入:

1
$ cat password.txt - | ./bomb

助教の温馨提示

使用这种操作时注意,由于Bomb+每一阶段都会重新读取一行,所以文件结尾多出的换行符会导致Bomb+下一阶段读到一个寂寞,从而导致炸弹爆炸。因此,在存放口令的文本文件中,注意不要多加一个空白的结尾行。

文件拓展名

熟悉Windows的各位同学一定对诸如 .exe .docx .xls .png .jpeg 等拓展名见惯不惯了,Windows系统会通过文件名中的这些后缀来判断如何打开这个程序。

不过在使用 Linux 时,通常不需要你为文件加上拓展名,这是因为 Linux 系统往往通过文件头中的魔数来区分文件类型。(这个概念我们在DataLab讲义中简单介绍过,如果你不知道的话可以自己去百度/谷歌看看)因此,我们在这里为文本文件标上 .txt 后缀类似于注释一样,是为了让大家看得更清楚一些。

“超纲”的汇编指令

大家在实验过程中,或许会遇到一些课上没有讲过的汇编指令,我们在这里介绍一下其中一个。

endbr64:一种用于防御ROP(Return Oriented Programming)攻击的机制 —— CET(Control-flow Enforcement)的一部分,但大多的CPU现在还没有实装,因此它们执行到这条指令时会直接略过。本次实验并不涉及相关的内容,你也可以直接略过。如果对二进制漏洞攻防感兴趣,大家也可以关注信息网络安全协会的相关活动。

如果遇到其他没见过的汇编指令,我们建议自己查询互联网。如果你有钻研的精神,欢迎你试着查询Intel的官方手册 英特尔® 64 位和 IA-32 架构开发人员手册,真正理解这个指令的细节。在查询这种奇厚无比的手册时,请一定记得利用好目录以及搜索功能。

五、参考资料 & 推荐资料