Dear Assembly(1)

By on

苦逼的暑假开始了。

lcb 大神 的推荐下,毫不犹豫地在六月份就选了 Coursera 上的 CSAPP(Computer Systems: A Programmer’s Perspective)。一方面正好自己操作系统也学得很烂,所以趁这个暑假充实一下自己的暑假生活;另一方面,这 CSAPP 的课机会难得,不容错过啊。

这篇文章的话,主要还是讲一下 Lab 2 的拆炸弹作业。俗话说:

DDL 是第一生产力!

赶在 DDL 前两天,晚上花了 5 个小时终于做完了!

本文分上下两篇,主要介绍一下 Assembly、gdb 的使用,还有拆炸弹的解题过程/w\


Phase_1

反编译 phase_1 的代码 disassemble phase_1,得到:

除去 1,7,8 行,关注剩下的 2 到 6 行。

程序调用了 strings_not_equal() 函数,比较输入字符串与 0x401af8 指向的字符串是否相等。使用 x /sb 0x401af8 查看 0x401af8 指向的字符串,就得到了第一个答案:

Science isn’t about why, it’s about why not?

phase_1 结束!

Phase_2

进入 phase_2,disassemble phase_2 得到:

第七行调用 read_six_numbers 函数,disassemble read_six_numbers

read_six_numbers 调用了 sscanf,格式字符串由地址为 0x401eb2 中的格式解析。查看 0x401eb2 地址中的格式字符串,使用 x /sb 0x401eb2

0x401eb2: “%d %d %d %d %d %d”

回到 phase_2 中,我们要知道读完六个数之后做了什么,继续看第 8 行开始的代码。第 8 行让 rbp = rsp 并且注意到第⑨行中 r13 寄存器保存了 rsp + 12 (即 rbp + 12 )的地址,以及第 12、13 行的 eax 取出了 rbp + 12 的数并且用 eaxrbp 两个寄存器之间的书比较是否相等。之后的第 17 行,rbp = rbp + 4,让指针往后走一个 int 的大小。看到这里也就知道了 phase_2 的含义:

输入数组 a[6] 后,比较是否是一个长度为 3 的循环数组。即是否满足 a[0] = a[3]a[1] = a[4]a[2] = a[5]

输入符合条件的六个数即可~难度也不是很大(「・ω・)「

Phase_3

开始进入比较有挑战性的 phase_3,同样使用 disassenmble phase_3

比之前更长了,不是么?

观察一下函数的特征,尤其是 14 - 27行,Switch/Case 的跳转表,非常的明显!

查看 sscanf 的格式字符串,x /sb 0x401ebe

0x401ebe: “%d %d”

输入两个数,第一个数用于 Switch/Case 分支判断,第二个数字则用于和 eax 的比较。注意到 11 行的 cmpl(每次都是你!)判断了 rsp + 12 中的数是否大于 7,也就是输入的第一个数是否大于 7(default 分支):如果大于就引爆了炸弹,否则就进入不同的 case 。通过计算不同的组合我们可以很轻松的得到这道题的不同的 7 个解(一行一个解):

0 535
1 926
2 214
3 339
4 119
5 352
6 919
7 412

当屏幕显示 Phase 3 cleared! 的时候,我们已经解决了一半的问题了!

Phase_4

Move on, 进入到 phase_4,disassemble phase_4

第 3 行查看 sscanf 的格式字符串,x /sb 0x401ec1

0x401ec1: “%d”

即输入一个数。将这个输入的数放入 edi 中调用了函数 func4func4 的代码如下:

7 - 13 行的主要说明了递归函数目的 f(x) = f(x-1) + f(x-2),边际条件在第 5 和 6 行 f(1) = 1(Fibbonacci 数列)。

回到原函数,14 行的 cmp 使用了返回值 eax0x37 = 55 比较,题目意图也很明显了:n 等于几时,有 f(n) = 55。答案就是:

⑨(这么写当然是错的)
9

Phase_5

同样地先观察格式字符串,x /sb 0x401ebe

0x401ebe: “%d %d”

格式输入正确后跳转到第 10 行执行函数,这里一行一行解释。

第 10 行,eax 存入地址为 rsp + 12 中的数,也就是第二个参数。11 - 12 行用这个数和 0xf 做了与操作,取出了最后两位并重新保存到 rsp + 12 中。13 行判断了这个数是不是 0xf,若是就引爆了炸弹,否则接下来进入循环。15 - 16 行的两个计数器 ecxedx 清零。

17 到 22 行由 jne 判断出这是一个循环。17 行的作用让 edx = edx + 1,马上 18 行 cltqeax 进行符号扩展,在 19 行加载 rax * 4 + 0x401ba0 这个地址中的数到 eax 中。20 行 ecx 作为累加器加上 eax 中的数。21 行依旧判断 eax 这个数是不是 0xf,不是则进行循环。

比较难理解的是 19 行 eax = *(rax * 4 + 0x401ba0) 即取出了起始地址为 0x401ba0 的数组中序号为 eax 的数放入 eax 中。根据 21 行判断数组大小,用 x /16wd 0x401ba0 查看一下 0x401ba0 开始的数组:

0x401ba0 array.3014: 10 2 14 7
0x401bb0 array.3014+16: 8 12 15 11
0x401bc0 array.3014+32: 0 4 1 13
0x401bd0 array.3014+48: 3 9 6 5

整理一下:

10 2 14 7 8 12 15 11 0 4 1 13 3 9 6 5

跳出循环,第 24 行,判断 edx 即函数的循环次数是不是 0xc = 12;第 26 行判断了第二个参数是否等于 ecx 中的数。phase_5 也就被我们转化成了一道数组倒推问题。计算后得到答案:

7 93

至此,作业要求的 5 个函数已经完成!(撒花)


What’s Next

  • Phase 6
  • Secret Phase
  • Gdb Guide