文章目录
-
- Lab 总结博客链接
- 前引
- Secret Phase
-
- 1、发现彩蛋出处
- 2、找寻Secret Phase 入口
- 3、剖析Phase Defused 寻觅进入彩蛋方法
- 4、终到Secret Phase 初入探索
- 5、跳转Fun7 初涉摸探一二
- 6、Fun7 解决问题的最终思考
- 7、验收成果 终斩开迷雾解开谜题
- 结束语
Lab 总结博客链接
CSAPP Lab入门系统安装摸索 + Lab 博客链接
前引
作为彩蛋Phase 自然而然肯定难度是不低的 因为完成这个彩蛋的前提 是需要我们完成
Phase 1
-Phase 6
的 如果没有完成的话 可以点一下上面的链接看一下Phase 1 - Phase 6
的解析
好了 那就不多说了 把这篇彩蛋解析写了就去吃午饭了 ^^ 我们下面走起
Secret Phase
1、发现彩蛋出处
在Phase 1 - Phase 6解析末尾
我写了怎么发现的这个彩蛋 到这里我就再写一下吧
Wow, they got it! But isn't something... missing? Perhaps * something they overlooked? Mua ha ha ha ha!
哇,他们成功了!但是不是有什么东西错过了?也许他们忽略了一些东西吧?哈哈哈
看到上面的我心生疑惑 哪里错过了什么 明明6个Phase
都完成了啊 怎么回事呢? 于是我带着疑问 又重新的巡视了一下bomb.asm
结果就发现了下面这个
竟然真的有个
Secret_Phase
应该是彩蛋Phase吧
本着什么事情要不就不做 要不就做好的原则 那我们就正式对最后一个拆弹任务的发起我们的总攻吧
2、找寻Secret Phase 入口
在发现了之后 我刚开始还是十分疑惑的 我们明明只让输入了6串字符串 我们哪里会进入Secret Phase
呢 那从哪里进去呢 此时我大胆猜测 肯定是有函数调用了Secret_Phase
我们在bomb.asm
文件中搜索调用了secret_phase
的函数即可
在一番搜索下 我们找到了调用
彩蛋phase
的函数 叫做phase_defused
是不是感觉哪里有点熟悉呢 此时我们再去bomb.c
去看看
原来竟然每个
phase
完成后 每次都调用了这个phase_defused
那彩蛋phase
入口就被我们找到了 那我们接下来要对phase_defused
进行解析了
3、剖析Phase Defused 寻觅进入彩蛋方法
好了 下面这部分就进入对函数的解析了 原函数在这部分的最底下 建议大家先自己去看看这部分能不能解决 实在解决不了再来看看解析
其实这部分 我还是分析了很久的 因为当时试了好久都没有思路 就一直进入不了 每次输入完就直接退出 显示6个
phase
已经完成 要不就是没有反应 - -
下面就直接给出解析了
1、0x4015d8
num_input_strings
应该是表示 我们已经输入了多少串字符串了 如果不等于6串的话 直接跳转到最下方 则我们的secret phase
是无法进入的 则先决条件是 我们需要完成phase 1 - 6
在完成6个任务后才能进入彩蛋任务
2、紧接着 我们再注意
0x4015fa
调用了sscanf函数
如果返回值eax
不等于3的话 我们一样就来到了函数的最下方 我们的callq secret phase
是在0x401630
的 所以必须要让sscanf
函数的返回值为3 所以我们先需要先对sscanf
函数有所了解
sscanf函数用法详解
在了解完sscanf
原理后 我们了解了 每次读取都是对指定字符串 作格式化读取 我们先看看在调用前的mov
进寄存器的参数 有两条mov
的语句 根据原理 应该能够猜测出 一个是指定的字符串 一个格式化读取字符串 那我们就先看看
在此之前 我们先输入6个之前我们完成的字符串 并于0x4015fa
断点
最后得到格式化字符串%d %d %s
指定字符串7 0
这部分其实我还是卡了好一会 因为我始终没搞懂哪里来的
7 0
知道看到了最后一行input_strings + 240
提示了我 之后我再比对了一下 好家伙 我们输入的第四个phase
的答案不就是7 0
吗 要使eax == 3
意思是我们必须要在7 0
后面再输入一串字符串 后面那串字符串要下面来比对
3、得到关键信息后 我们就知道 过掉这部分就需要在第四个
phase
答案后加入一串字符串 我们接着往下走0x40160e
调用了strings_not_equal
很显然 意思就是要把我们输入的字符串与其输入的esi指针
字符串进行比较 相等则可以继续往下走 我们稍微看一下 这部分过了 除了两个puts
不影响执行徐磊 下面就直接到了secret phase
了
我们已经是老手了 老方法print (char*)
得到字符串
401604: be 22 26 40 00 mov $0x402622,%esi
是我们的重点关注目标get it!
DrEvil
4、到了最后的验证时间 我们在第四个
Phase 4
后面加上DrEvil
对我的第四个Phase
就是输入7 0 DrEvil
看看效果make it!
进入了!
这里就是源代码了 就不多加注释了 因为上面已经解释的够清楚了 大家对照着看一定能看得懂 后面任务还多着呢 我们接着往下走
00000000004015c4 <phase_defused>:4015c4: 48 83 ec 78 sub $0x78,%rsp4015c8: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax4015cf: 00 00 4015d1: 48 89 44 24 68 mov %rax,0x68(%rsp)4015d6: 31 c0 xor %eax,%eax4015d8: 83 3d 81 21 20 00 06 cmpl $0x6,0x202181(%rip) # 603760 <num_input_strings>4015df: 75 5e jne 40163f <phase_defused+0x7b>4015e1: 4c 8d 44 24 10 lea 0x10(%rsp),%r84015e6: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx4015eb: 48 8d 54 24 08 lea 0x8(%rsp),%rdx4015f0: be 19 26 40 00 mov $0x402619,%esi4015f5: bf 70 38 60 00 mov $0x603870,%edi4015fa: e8 f1 f5 ff ff callq 400bf0 <__isoc99_sscanf@plt>4015ff: 83 f8 03 cmp $0x3,%eax401602: 75 31 jne 401635 <phase_defused+0x71>401604: be 22 26 40 00 mov $0x402622,%esi401609: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi40160e: e8 25 fd ff ff callq 401338 <strings_not_equal>401613: 85 c0 test %eax,%eax401615: 75 1e jne 401635 <phase_defused+0x71>401617: bf f8 24 40 00 mov $0x4024f8,%edi40161c: e8 ef f4 ff ff callq 400b10 <puts@plt>401621: bf 20 25 40 00 mov $0x402520,%edi401626: e8 e5 f4 ff ff callq 400b10 <puts@plt>40162b: b8 00 00 00 00 mov $0x0,%eax401630: e8 0d fc ff ff callq 401242 <secret_phase>401635: bf 58 25 40 00 mov $0x402558,%edi40163a: e8 d1 f4 ff ff callq 400b10 <puts@plt>40163f: 48 8b 44 24 68 mov 0x68(%rsp),%rax401644: 64 48 33 04 25 28 00 xor %fs:0x28,%rax40164b: 00 00 40164d: 74 05 je 401654 <phase_defused+0x90>40164f: e8 dc f4 ff ff callq 400b30 <__stack_chk_fail@plt>401654: 48 83 c4 78 add $0x78,%rsp401658: c3 retq
4、终到Secret Phase 初入探索
终于到了Secret Phase
我们不妨先来看看它的原貌吧 除了最后方便指令读取的nop
其他我都保留了 那我们一起来看看究竟长什么样子
0000000000401242 <secret_phase>:401242: 53 push %rbx401243: e8 56 02 00 00 callq 40149e <read_line>401248: ba 0a 00 00 00 mov $0xa,%edx40124d: be 00 00 00 00 mov $0x0,%esi401252: 48 89 c7 mov %rax,%rdi401255: e8 76 f9 ff ff callq 400bd0 <strtol@plt>40125a: 48 89 c3 mov %rax,%rbx40125d: 8d 40 ff lea -0x1(%rax),%eax401260: 3d e8 03 00 00 cmp $0x3e8,%eax401265: 76 05 jbe 40126c <secret_phase+0x2a>401267: e8 ce 01 00 00 callq 40143a <explode_bomb>40126c: 89 de mov %ebx,%esi40126e: bf f0 30 60 00 mov $0x6030f0,%edi401273: e8 8c ff ff ff callq 401204 <fun7>401278: 83 f8 02 cmp $0x2,%eax40127b: 74 05 je 401282 <secret_phase+0x40>40127d: e8 b8 01 00 00 callq 40143a <explode_bomb>401282: bf 38 24 40 00 mov $0x402438,%edi401287: e8 84 f8 ff ff callq 400b10 <puts@plt>40128c: e8 33 03 00 00 callq 4015c4 <phase_defused>401291: 5b pop %rbx401292: c3 retq
1、很明显 在0x401255调用了
strtol
字符串转换为整形的函数 前面调用了read_line
就是读取字符串的 在0x40125a
到0x401273 调用func函数
中间有个explode
的问题 就是如果num - 1 > 1000(0x3e8)
则会引爆炸弹 所以我们输入的数字 必须是小于等于1001
则是主要这个函数的限制条件
2、在此之后 调用了fun7
函数 根据上下文可得 应该是最后一个函数 我们要在func
函数中探索最后的秘密0x401278的
cmp $2,%eax
意思是 我们输入的数字num
esi = num(<= 1001)
被带进func
作为参数 要求最后出来的时候返回值等于0x2
如果等于 我们拆弹就成功啦!
好!那我们继续跳往fun7
看看究竟是怎么样的
5、跳转Fun7 初涉摸探一二
对了 上面第四部分带到Fun7
的参数 除了esi = numx
还有edi = 0x6030f0
别小看edi
这个是之后我们解决问题的金钥匙 我们不妨先看看最后谜团的函数究竟是怎么样的
看到这么短的函数时 我刚开始还以为是彩蛋送温暖 哈哈哈 想着最后彩蛋phase
应该比较简单 结果最后完成的时候 才发现花的时间跟Phase 6
差不多 真的还是非常有难度的 函数如下
0000000000401204 <fun7>:401204: 48 83 ec 08 sub $0x8,%rsp401208: 48 85 ff test %rdi,%rdi40120b: 74 2b je 401238 <fun7+0x34>40120d: 8b 17 mov (%rdi),%edx40120f: 39 f2 cmp %esi,%edx401211: 7e 0d jle 401220 <fun7+0x1c>401213: 48 8b 7f 08 mov 0x8(%rdi),%rdi401217: e8 e8 ff ff ff callq 401204 <fun7>40121c: 01 c0 add %eax,%eax40121e: eb 1d jmp 40123d <fun7+0x39>401220: b8 00 00 00 00 mov $0x0,%eax401225: 39 f2 x/1 cmp %esi,%edx401227: 74 14 je 40123d <fun7+0x39>401229: 48 8b 7f 10 mov 0x10(%rdi),%rdi40122d: e8 d2 ff ff ff callq 401204 <fun7>401232: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax401236: eb 05 jmp 40123d <fun7+0x39>401238: b8 ff ff ff ff mov $0xffffffff,%eax40123d: 48 83 c4 08 add $0x8,%rsp401241: c3 retq
前引:作为已经破除六路的拆弹手 我们对于类似莫名其妙的数字 肯定都有着不一般的嗅觉 可以大胆推测 是一个地址 那我们不妨来看看是不是我们想的那样 对地址
0x6030f0
开探
果然不出所料 就是一个跟phase 6
差不多的结构体 只不过感觉更复杂了点6304xxx
应该也是指针(phase 6经验所得
) 而且意外发现phase 6
的指针数组竟然就在下方 哈哈 这样布局 我猜测是 老美那边的老师防止我们一次性弄比较大的看内存 看到了secret phase
需要用的结构体了 让我们产生疑惑 哈哈 真的挺细的
在看到上方的结构体后 或许是因为没有找到入口才来看解析 或许是没有什么思路才来看 我的解析写到这里 或多或少能够带给你一些思路和启发了 引路到这里 如果你已经有点感觉了 此时可以把博客关掉 大胆去做 去尝试了
如果在鏖战一番 但收获很少的话 那就继续往下面看吧 我也不得不承认 确实这里难度不亚于Phase 6
秉着把博客写好的原则 我还是写细致一点吧
下方直接是思路解析
1、这里其实就是一个带着两个指针的结构体 前面的七个结构体的两个指针都是带有值的 指向其他的结构体 最后的8个结构体指针是不带有值的 仅仅有头部数据
2、我们最后要带着eax = 2
离开 就必须要先随便带一个值进去尝试 我们的值要求是<= 1001
不妨我们就带着1001
进去看看程序是怎么运行的
博客上面不是很方便写 我就直接写了esi = 1001
的运行流程 如下
从上面可以看出来 是运行了递归 而且例如上面的
36 -> 50 -> 107 -> 1001
每次地址的转换 都根据着我们数据与我们的esi = num
来比较 如果小于或者等于的话 我们就转换到add + 0x10
的地址继续进行比较 直到最后的数据相等时 其实这个结论不好得出来 因为我大概自己列举了5-6条路线 并且在最后的cmp $2,%eax
进行了 断点 根据我算出来的eax
与得到的eax
来比较才得出来的结论
下面就直接说这部分在干什么 并给出结论了
1、不断地进行递归 如果我们目前的数x
大于esi(即我们输入进来的数)
则地址转向(rdi + 0x8)
否则转向(rdi + 0x10)
2、不仅仅与地址转向有关 还与递归回来后的eax
处理方式不同
如果我们目前的数x
大于esi
转向的是(rdi + 0x8)
我们递归回来处理方式为add %eax,%eax(即eax = eax*2)
再return eax
如果我们目前的数小于等于的esi
话 转向(rdi + 0x10
)我们递归回来处理方式为lea $1(%eax,%eax,1) (即 eax = eax*2 + 1)
再return eax
3、递归结束的调试 要不就是rdi = 0
了 即地址已经转到了0x0
则eax = 0xffffffff(按照非unsigned类型来说就是-1)
显然我们不能让我们的rdi = 0
rdi != 0
的条件就是 我们输入的数必须要是那15个结点
其中一个头部数据 当等于的时候 从底层返回的eax
会为 0 然后按照递归处理方式 来处理eax
递归结束 找到其对应的数据了 则对应(mov $0,%eax)
) 会return 0x0
从这里开始 返回ret
开始递归后的处理
6、Fun7 解决问题的最终思考
上面写了那么多 最后这点 也是最后我的思路过程 解题过程也该来写出来了 上面我在草稿纸上写的路径太过单一 没有覆盖的很好 比如eax = eax*2
的那一种都没有覆盖进去 大家可以自己去试一下其他路径
这里贴一下结构体指针图 方便查阅
下面就是最后的思路分析了
1、我们最后要求
eax
等于2
我这里做了很多的假设 也尝试了很多条路径 有很多的结果 因为递归处理eax
返回方式不同 所以结果不同 因为我们要求eax = 2
则我们第一次开始递归 那一次的决策 绝不可能是eax = eax*2 + 1
因为这样的话 无论如何返回的数都是奇数 那么只有可能是eax = eax*2
那么说明我们设置的esi
必须要求是小于第一次递归的那个数的 也就是36
因为36>x
才能让它eax = eax*2
2、那么我们逆着推 最后的eax = 2
最后一层乘以了2 那么倒数第二层是不是应该值为1
那么1
怎么来呢 很简单 我们想一下 最后递归结束后eax是不是等于0
从0
开始往上乘 那么从0
开始经过的第一层 如果处理过程是eax * 2 + 1
是不是就有了1
了 !那么1
经过这最后一层eax = eax*2
是不是就得到2
了
3、我们把上面部分分析总结一下 我们从底部向上分析应该是需要3
层
1、最底部得到0return 0
2、向上经过一层eax = eax*2 + 1
得到1return 1
3、再向上经过一层eax = eax*2
得到2return 2
那么我们就按照这个思路走 顺着来推
1、首先是来到了36
首先36
需要大于我们的数x
eax = eax * 2
那么地址应该是rdi + 0x8
指针值为6304016
x/1 6304016
得到值为0x603110
我们转到地址为0x603110
看看 头部数据为8
那我们来到8
这里继续看
2、来到了8
我们想要数据eax = eax*2 + 1
则8
需要小于等于 我们就看看0x603110 + 0x16
的指针里面的数据是多少x/1 6304080
得到地址0x603150
头部数据为22
3、最后我们得到了数据22
当我们输入22
的时候 指针来到了22
的地方 因为相等 则eax = 0
return 0
倒数第二层eax = eax*2 + 1
return 1
倒数第三层eax = eax*2
则最后成功return 2
其实我们的得到了数据 我们可以想一下难道只有
22了吗
肯定不是的 因为22
还有两个指针 且不是空指针 如果22
大于那个最终的数 是不是就eax *= eax
刚好我试了一下 最后那个rdi + 0x8
的数 是20
则路径变成了
最底部eax = 0
return 0
倒数第二层eax = eax*2
return 0
倒数第三层eax = eax*2 +1
return 1
倒数第四层eax = eax*2
return 2
那我们就得到了两个数据 都是可以解开炸弹的 20
和22
我们不妨到下面去试一下
7、验收成果 终斩开迷雾解开谜题
那我们就挨个挨个输入吧
输入20
验证成功
输入22
验证成功
结束语
写完博客不知不觉 已经下午3点了 呜 午饭还没有吃 4点钟就有体育课了
花这么多时间做这个Lab
真的感觉还是非常好的 这种与题目博弈 思考 最终只依靠自己而解决问题的感觉太好了 而且是能够真真切切的感觉到自己能力的提升
尽管现在很饿 而且中间有一段时间有点不耐烦了 但是还是坚持下来写完了 我觉得在自己年轻的时候 多记录一点自己写过的东西 也许也能够在一些地方留下自己的足迹吧
好了 各位 IT人也要去吃午饭了 那各位有缘下一篇博客再见啦~ ヾ( ̄▽ ̄)ByeBye