苦逼的暑假开始了。
 在 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 的数并且用 eax 和 rbp 两个寄存器之间的书比较是否相等。之后的第 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 中调用了函数 func4。func4 的代码如下:
  7 - 13 行的主要说明了递归函数目的 f(x) = f(x-1) + f(x-2),边际条件在第 5 和 6 行 f(1) = 1(Fibbonacci 数列)。
 回到原函数,14 行的 cmp 使用了返回值 eax 和 0x37 = 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 行的两个计数器 ecx 和 edx 清零。
 17 到 22 行由 jne 判断出这是一个循环。17 行的作用让 edx = edx + 1,马上 18 行 cltq 对 eax 进行符号扩展,在 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