这个 Lab 应该比上一个 Lab 简单不少, Lab 的说明文档也差不多把基本的过关要点都提到了,所以这里主要介绍一下几个相关工具的使用,帮助大家进一步熟悉 Linux 命令行   ;-) 首先从主页上下载一个压缩包,最简单的方法是直接在命令行使用 wget 工具 wget http://10.132.143.100/lab2007/buflab-handout.tar

顺便提一下 wget 的功能很强大,除了可以在后台运行,它还支持 ftp https http 等协议,提供整个网站下载的功能(类似于 Windows 下的 WebZIP )

接着使用 tar 命令解压这个文件

tar xvf  buflab-handout.tar

其中, x 参数表示解压工作, v 表示显示解压所得的文件列表, f 及其后面跟着的文件 名指定了待解压文件

这样在当前目录下就多出了三个文件: bufbomb, makecookie, sendstring

接下来使用 makecookie 得到自己学号对应的 cookie 号,用法是 ./makecookie 学号

这个 cookie 号在后面的几个关卡中都有可能会用到,同时 http://10.132.143.234/bufbombstatus.html 中可以通过自己的 cookie 号随时获得自己现在的过关情况。

和上一个 Lab 一样,这里介绍一下怎么过 Level 0 。

这个热身关和两个函数有关, test() 和 smoke() ,正常情况下 smoke 不会被任何函数调用,我们要做的就是输入一串特定的字符,使得 smoke 被调用。另外这个关卡简单之处在于 smoke 被调用后就直接退出程序,我们在破坏了栈的数据后不需要恢复原样。

注意到 test 中调用了 getbuf 函数,联系到 CS:APP Section 3.13 ,看来只要把 getbuf 的返回地址改成 smoke 的地址就行了。

召唤 gdb 监视 bufbomb 的运行,设置断点在 getbuf ,使用 run -t 学号 运行,程序停在 getbuf 的第三行,使用 disas 查看当前函数 (getbuf) 的汇编代码

0x08048ab3 :  lea    0xffffffe8(%ebp),%eax
0x08048ab6 :  sub    $0x24,%esp
0x08048ab9 :  push   %eax
0x08048aba :  call   0x8048bbc
0x08048abf :  mov    $0x1,%eax

看来这里的 %eax 保存了字符串的首字节地址,从第一句可以算出,它与 %ebp 指向的地址相差了 24 。

也就是说我们输入 24 个字符后,接下来输入的 4 个字符会覆盖掉原来的帧指针,再之后的 4 个字符就会覆盖函数的返回地址。把这个地址覆盖成 smoke 的就行,用 disas smoke 得到 smoke 的地址为 0x08048964 。

知道了这些后,我们就来构造这条特殊的字符串,新建一个文本文件(当然也可以在 sendstring 中键盘输入,不过调试起来比较麻烦),输入

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 64 89 04 08

最后四个字节就是 smoke 的地址,注意是 little endian 约定。假设保存文件为 ans.txt

但这只是文本格式的指令,需要把它转换为二进制值, sendstring 工具可以帮助我们生成二进制串:cat ans.txt | ./sendstring > raw.txt

这行命令用到了 shell 的管道和重定向功能,它的作用是把 ans.txt 的内容放到 sendstring 中,把 sendstring 生成的内容放到 raw.txt 中。这样在 raw.txt 中包含了最后的二进制代码。

回到 gdb ,使用 run -t 学号 < raw.txt 运行程序,程序停在 getbuf 中的断点后,使用几个 ni 命令往下运行,等调用完 Gets 函数后(一般是在 0x08048abf ),查看当前的帧指针附近的信息:

(gdb) p/x *(int*) ebp@2
$ 1 = {0x0, 0x8048964}

看来我们已经成功得把返回地址改掉了,使用 c(ontinue) 继续运行,就可以看到成功信息了。

后面几关(除了最后一个)和这个热身关差不多,只是返回地址需要指向 buf 数组,运行嵌在其中的代码再返回原来的地址,要获得嵌入的二进制代码,可以先写一个包含汇编指令的 .s 文件,使用 gcc -c 把这个文件编译成 relocatable object ,再用 objdump -d 得到这段二进制代码。大致思路 pdf 上已经写得很详细了,这里不再赘述。

关于最后一关, CS:APP 书上有一个类似的题目 (3.38, P236) ,我记得我当时做的时候栈帧地址也是会跳动的,只是没有 Level 4 那个那么明显,有兴趣的同学可以试一下。

程序的下载地址: http://10.132.141.124/study/ics/bufbomb.c

其他几个提示

输入的文本格式的二进制代码计算字节数比较麻烦,可以每 10 个字节一行,写完后使用 vim 的 shift+j 快捷键把这几行都拼起来。

本地过关后记得查看网站确定一下。

gdb 的 run 命令会自动根据上一次的参数运行程序,所以第一次输入 run -t 学号 < raw.txt 后,以后只要简单的使用 r 就可以了。

建议开两个 PieTTY 窗口,一个 gdb 调试,一个修改、生成二进制文本,很方便。