csapp - bomb lab
文章目录
- 环境
- 使用方法
- phase 1
- phase 2
- phase 3
- phase 4
- phase 5
- phase 6
- secret phase
- 结果展示
环境
操作系统:ubuntu 12.04(32bit)
gcc版本:4.6.3
实验时间:2019/4/10 - 2019/4/11
bomb lab版本:csapp 原书第2版
@author:姬小野
使用方法
1、 使用命令 objdump -d bomb > bomb.s,获取可执行文件bomb的汇编源码,同时重定向输出到文件bomb.s中。
即可在文件中方便地查看汇编代码
2、 阅读官方文档,得知可以将输入写入到文件中,就不用每次拆炸弹都需要自己从头开始手动输入。
phase 1
汇编代码分析和注释如下
08048b50 <phase_1>:8048b50: 83 ec 1c sub $0x1c,%esp8048b53: c7 44 24 04 bc a1 04 movl $0x804a1bc,0x4(%esp) # 为什么这个奇怪的地址入栈?gdb打印,发现一个字符串8048b5a: 08 8048b5b: 8b 44 24 20 mov 0x20(%esp),%eax8048b5f: 89 04 24 mov %eax,(%esp)8048b62: e8 8d 04 00 00 call 8048ff4 <strings_not_equal> # 判断字符串是否相等8048b67: 85 c0 test %eax,%eax # 和jz配合检查%eax是否为0,为0跳。test,执行与运算。8048b69: 74 05 je 8048b70 <phase_1+0x20> # je就是jz8048b6b: e8 96 05 00 00 call 8049106 <explode_bomb>8048b70: 83 c4 1c add $0x1c,%esp # 返回了,栈指针指回帧指针处8048b73: c3 ret
使用gdb工具打印地址0x804a1bc处的值。
得到如下字符串,猜测是phase1的密码。
(gdb) x/s 0x804a1bc
0x804a1bc: “We have to stand with our North Korean allies.”
phase 2
汇编代码分析和注释如下
08048b74 <phase_2>: 8048b74: 53 push %ebx8048b75: 83 ec 38 sub $0x38,%esp # 为局部变量划分空间。栈向低地址方向增长,所以用sub8048b78: 8d 44 24 18 lea 0x18(%esp),%eax8048b7c: 89 44 24 04 mov %eax,0x4(%esp)8048b80: 8b 44 24 40 mov 0x40(%esp),%eax8048b84: 89 04 24 mov %eax,(%esp)8048b87: e8 af 06 00 00 call 804923b <read_six_numbers> # 读取6个数字8048b8c: 83 7c 24 18 00 cmpl $0x0,0x18(%esp) # 判断第一个数字是否为0,如果不是bomb爆炸。bingo8048b91: 79 05 jns 8048b98 <phase_2+0x24>8048b93: e8 6e 05 00 00 call 8049106 <explode_bomb>8048b98: bb 01 00 00 00 mov $0x1,%ebx # for循环的开始,设置i(%ebx)为18048b9d: 89 d8 mov %ebx,%eax # %eax是一个临时值,对它进行操作后,和下一个数字进行比较。8048b9f: 03 44 9c 14 add 0x14(%esp,%ebx,4),%eax # 上一个数字加到%eax上8048ba3: 39 44 9c 18 cmp %eax,0x18(%esp,%ebx,4) # 比较%eax和当前数字8048ba7: 74 05 je 8048bae <phase_2+0x3a> # 判断与爆炸8048ba9: e8 58 05 00 00 call 8049106 <explode_bomb>8048bae: 83 c3 01 add $0x1,%ebx # 相当于i++8048bb1: 83 fb 06 cmp $0x6,%ebx # 相当于i != 68048bb4: 75 e7 jne 8048b9d <phase_2+0x29>8048bb6: 83 c4 38 add $0x38,%esp # 将栈指针移到帧指针处,意味着函数要返回了。这两句相当于leave8048bb9: 5b pop %ebx8048bba: c3 ret
关键代码(for循环部分)如下
int x = 0, i;
for (i = 1; i != 6; i++) {x += i;if (input[i] != x) bomb();
}
第一个数字是0
第二个数字为0 + 1 = 1
第三个数字为1 + 2 = 3
第四个数字为3 + 3 = 6
第五个数字为6 + 4 = 10
第六个数字为10 + 5 = 15
结果为 0 1 3 6 10 15
phase 3
汇编代码分析与注释如下
08048bbb <phase_3>:8048bbb: 83 ec 2c sub $0x2c,%esp8048bbe: 8d 44 24 1c lea 0x1c(%esp),%eax8048bc2: 89 44 24 0c mov %eax,0xc(%esp)8048bc6: 8d 44 24 18 lea 0x18(%esp),%eax8048bca: 89 44 24 08 mov %eax,0x8(%esp)8048bce: c7 44 24 04 af a3 04 movl $0x804a3af,0x4(%esp) # "%d %d",两个数字?8048bd5: 08 8048bd6: 8b 44 24 30 mov 0x30(%esp),%eax8048bda: 89 04 24 mov %eax,(%esp)8048bdd: e8 8e fc ff ff call 8048870 <__isoc99_sscanf@plt> # 应该是输入8048be2: 83 f8 01 cmp $0x1,%eax # 判断scanf输入的值得数量是否大于18048be5: 7f 05 jg 8048bec <phase_3+0x31> # 大于,到8048bec,否则爆炸8048be7: e8 1a 05 00 00 call 8049106 <explode_bomb>8048bec: 83 7c 24 18 07 cmpl $0x7,0x18(%esp)8048bf1: 77 66 ja 8048c59 <phase_3+0x9e> # 大于u,到8048c59,即爆炸8048bf3: 8b 44 24 18 mov 0x18(%esp),%eax # switch 的判断值8048bf7: ff 24 85 1c a2 04 08 jmp *0x804a21c(,%eax,4) # 跳转表8048bfe: b8 00 00 00 00 mov $0x0,%eax8048c03: eb 05 jmp 8048c0a <phase_3+0x4f> # 到8048c0a8048c05: b8 0d 03 00 00 mov $0x30d,%eax8048c0a: 2d 0a 01 00 00 sub $0x10a,%eax8048c0f: eb 05 jmp 8048c16 <phase_3+0x5b> # 到8048c168048c11: b8 00 00 00 00 mov $0x0,%eax8048c16: 05 4e 01 00 00 add $0x14e,%eax8048c1b: eb 05 jmp 8048c22 <phase_3+0x67> # 到8048c228048c1d: b8 00 00 00 00 mov $0x0,%eax8048c22: 2d 94 00 00 00 sub $0x94,%eax8048c27: eb 05 jmp 8048c2e <phase_3+0x73> # 到8048c2e8048c29: b8 00 00 00 00 mov $0x0,%eax8048c2e: 05 94 00 00 00 add $0x94,%eax8048c33: eb 05 jmp 8048c3a <phase_3+0x7f> # 到8048c3a8048c35: b8 00 00 00 00 mov $0x0,%eax8048c3a: 2d 94 00 00 00 sub $0x94,%eax8048c3f: eb 05 jmp 8048c46 <phase_3+0x8b> # 到8048c468048c41: b8 00 00 00 00 mov $0x0,%eax8048c46: 05 94 00 00 00 add $0x94,%eax8048c4b: eb 05 jmp 8048c52 <phase_3+0x97> # 到8048c528048c4d: b8 00 00 00 00 mov $0x0,%eax8048c52: 2d 94 00 00 00 sub $0x94,%eax8048c57: eb 0a jmp 8048c63 <phase_3+0xa8> # 到8048c638048c59: e8 a8 04 00 00 call 8049106 <explode_bomb>8048c5e: b8 00 00 00 00 mov $0x0,%eax8048c63: 83 7c 24 18 05 cmpl $0x5,0x18(%esp)8048c68: 7f 06 jg 8048c70 <phase_3+0xb5> # 到8048c70,即爆炸8048c6a: 3b 44 24 1c cmp 0x1c(%esp),%eax # 输入的第二个数?8048c6e: 74 05 je 8048c75 <phase_3+0xba> # 到8048c75,成功过关8048c70: e8 91 04 00 00 call 8049106 <explode_bomb>8048c75: 83 c4 2c add $0x2c,%esp8048c78: c3 ret
分析如下:
看这两行,发现地址0x804a3af有点怪,在gdb中打印,发现它的值是"%d %d",说明输入是两个数字
8048bce: c7 44 24 04 af a3 04 movl $0x804a3af,0x4(%esp) # "%d %d",两个数字?
8048bdd: e8 8e fc ff ff call 8048870 <__isoc99_sscanf@plt> # 调用的函数为scanf
判断scanf输入的值的数量,如果小于等于1,则爆炸
8048be2: 83 f8 01 cmp $0x1,%eax # case 值从2开始
8048be5: 7f 05 jg 8048bec <phase_3+0x31> # 大于,到8048bec, 否则爆炸
发现如果输入的第一个参数大于7,那么也爆炸
8048bec: 83 7c 24 18 07 cmpl $0x7,0x18(%esp)
8048bf1: 77 66 ja 8048c59 <phase_3+0x9e> # 大于u,到8048c59,即爆炸
参数1范围在 0 <= arg1 <= 7 内。
接下来就是执行switch的跳转表
8048bf3: 8b 44 24 18 mov 0x18(%esp),%eax # switch 的判断值
8048bf7: ff 24 85 1c a2 04 08 jmp *0x804a21c(,%eax,4) # 跳转表
下面的就是几条case语句了。
分析switch结束之后的语句,发现arg1 > 5的话,爆炸
8048c63: 83 7c 24 18 05 cmpl $0x5,0x18(%esp)
8048c68: 7f 06 jg 8048c70 <phase_3+0xb5> # 到8048c70,即爆炸
所以arg1的范围为[0, 5]。
case一共有8组,case 0 ~ case 7
case 0: a = 0;
case 1: a -= 10a;
case 2: a += 14e;
case 3: a -= 94;
case 4: a += 94;
case 5: a -= 94;
case 6: a += 94;
case 7: a -= 94;
在执行之前,a = 0
经测试,有六组可行的数据。分别是:
0 701
1 -80
2 186
3 -148
4 0
5 -148
phase 4
汇编代码分析与注释如下
08048c79 <func4>:8048c79: 83 ec 1c sub $0x1c,%esp8048c7c: 89 5c 24 10 mov %ebx,0x10(%esp)8048c80: 89 74 24 14 mov %esi,0x14(%esp)8048c84: 89 7c 24 18 mov %edi,0x18(%esp)8048c88: 8b 74 24 20 mov 0x20(%esp),%esi # 68048c8c: 8b 5c 24 24 mov 0x24(%esp),%ebx # 28048c90: 85 f6 test %esi,%esi # 判断是否为0!也是基例!!!8048c92: 7e 2b jle 8048cbf <func4+0x46> # 结果小于等于0则跳转!8048c94: 83 fe 01 cmp $0x1,%esi8048c97: 74 2b je 8048cc4 <func4+0x4b> # 递归基例,等于1!!!8048c99: 89 5c 24 04 mov %ebx,0x4(%esp)8048c9d: 8d 46 ff lea -0x1(%esi),%eax8048ca0: 89 04 24 mov %eax,(%esp)8048ca3: e8 d1 ff ff ff call 8048c79 <func4> # 第一个递归8048ca8: 8d 3c 18 lea (%eax,%ebx,1),%edi8048cab: 89 5c 24 04 mov %ebx,0x4(%esp)8048caf: 83 ee 02 sub $0x2,%esi8048cb2: 89 34 24 mov %esi,(%esp)8048cb5: e8 bf ff ff ff call 8048c79 <func4> # 第二个递归,返回值是%eax?8048cba: 8d 1c 07 lea (%edi,%eax,1),%ebx8048cbd: eb 05 jmp 8048cc4 <func4+0x4b>8048cbf: bb 00 00 00 00 mov $0x0,%ebx8048cc4: 89 d8 mov %ebx,%eax8048cc6: 8b 5c 24 10 mov 0x10(%esp),%ebx8048cca: 8b 74 24 14 mov 0x14(%esp),%esi8048cce: 8b 7c 24 18 mov 0x18(%esp),%edi8048cd2: 83 c4 1c add $0x1c,%esp8048cd5: c3 ret 08048cd6 <phase_4>:8048cd6: 83 ec 2c sub $0x2c,%esp8048cd9: 8d 44 24 18 lea 0x18(%esp),%eax8048cdd: 89 44 24 0c mov %eax,0xc(%esp)8048ce1: 8d 44 24 1c lea 0x1c(%esp),%eax8048ce5: 89 44 24 08 mov %eax,0x8(%esp)8048ce9: c7 44 24 04 af a3 04 movl $0x804a3af,0x4(%esp) # 同样,是"%d %d"8048cf0: 08 8048cf1: 8b 44 24 30 mov 0x30(%esp),%eax8048cf5: 89 04 24 mov %eax,(%esp)8048cf8: e8 73 fb ff ff call 8048870 <__isoc99_sscanf@plt> # scanf,截止目前,和上面完全一样8048cfd: 83 f8 02 cmp $0x2,%eax8048d00: 75 0e jne 8048d10 <phase_4+0x3a> # 判断是否输入两个参数8048d02: 8b 44 24 18 mov 0x18(%esp),%eax # 第一个参数8048d06: 83 f8 01 cmp $0x1,%eax8048d09: 7e 05 jle 8048d10 <phase_4+0x3a> # arg2 >= 28048d0b: 83 f8 04 cmp $0x4,%eax8048d0e: 7e 05 jle 8048d15 <phase_4+0x3f> # arg2 <= 48048d10: e8 f1 03 00 00 call 8049106 <explode_bomb>8048d15: 8b 44 24 18 mov 0x18(%esp),%eax # arg1赋值给%eax8048d19: 89 44 24 04 mov %eax,0x4(%esp) # func的参数28048d1d: c7 04 24 06 00 00 00 movl $0x6,(%esp) # func的参数1,越早的参数越小!!!8048d24: e8 50 ff ff ff call 8048c79 <func4> # 调用func4了8048d29: 3b 44 24 1c cmp 0x1c(%esp),%eax # func4返回的结果(%eax是返回值),判断是否和参数2相等8048d2d: 74 05 je 8048d34 <phase_4+0x5e> # 判断结果,相等就OK,否则爆炸8048d2f: e8 d2 03 00 00 call 8049106 <explode_bomb>8048d34: 83 c4 2c add $0x2c,%esp8048d37: c3 ret
phase 4的过程是输入两个数(y, x),然后调用一个函数func4(x, 6),判断y 是否等于这个函数的返回值。如果不是,则爆炸。
根据下面四行代码可知,输入x 的取值范围为[2, 4]。
8048d06: 83 f8 01 cmp $0x1,%eax8048d09: 7e 05 jle 8048d10 <phase_4+0x3a> # arg2 >= 28048d0b: 83 f8 04 cmp $0x4,%eax8048d0e: 7e 05 jle 8048d15 <phase_4+0x3f> # arg2 <= 4
以x = 2做讨论,下面的语句是执行func(6, 2)。结果是多少呢?
8048d15: 8b 44 24 18 mov 0x18(%esp),%eax # arg1赋值给%eax8048d19: 89 44 24 04 mov %eax,0x4(%esp) # func的参数28048d1d: c7 04 24 06 00 00 00 movl $0x6,(%esp) # func的参数1,越早的参数越小!!!8048d24: e8 50 ff ff ff call 8048c79 <func4> # 调用func4了
分析func函数,发现它是一个递归函数。它有两个递归基例,和两个可能的自身调用。
int func4(int b, int a) {if (b <= 0) return 0;if (b == 1) return a;return func4(a, b - 1) + a + func4(a, b - 2);
}
递归基例:
8048c90: 85 f6 test %esi,%esi # 判断是否为0!也是基例!!!8048c92: 7e 2b jle 8048cbf <func4+0x46> # 结果小于等于0则跳转!8048c94: 83 fe 01 cmp $0x1,%esi8048c97: 74 2b je 8048cc4 <func4+0x4b> # 递归基例,等于1!!!
递归调用
8048ca3: e8 d1 ff ff ff call 8048c79 <func4> # 第一个递归调用
8048cb5: e8 bf ff ff ff call 8048c79 <func4> # 第二个递归调用,返回值是%eax
根据上面的分析不难得出三种解,他们都可以通过关卡4.
40 2
60 3
80 4
phase 5
汇编代码分析与注释如下:
08048d38 <phase_5>:8048d38: 53 push %ebx8048d39: 83 ec 28 sub $0x28,%esp8048d3c: 8b 5c 24 30 mov 0x30(%esp),%ebx8048d40: 65 a1 14 00 00 00 mov %gs:0x14,%eax8048d46: 89 44 24 1c mov %eax,0x1c(%esp)8048d4a: 31 c0 xor %eax,%eax8048d4c: 89 1c 24 mov %ebx,(%esp) # 数组开始?8048d4f: e8 87 02 00 00 call 8048fdb <string_length> # 计算长度,写入到%eax里面了8048d54: 83 f8 06 cmp $0x6,%eax8048d57: 74 05 je 8048d5e <phase_5+0x26> # 字符串长度为68048d59: e8 a8 03 00 00 call 8049106 <explode_bomb>8048d5e: b8 00 00 00 00 mov $0x0,%eax # i = 08048d63: 0f be 14 03 movsbl (%ebx,%eax,1),%edx # 符号扩展双字。%ebx的值是什么?8048d67: 83 e2 0f and $0xf,%edx8048d6a: 0f b6 92 3c a2 04 08 movzbl 0x804a23c(%edx),%edx8048d71: 88 54 04 15 mov %dl,0x15(%esp,%eax,1) # 替换了读入的字符?8048d75: 83 c0 01 add $0x1,%eax # i++8048d78: 83 f8 06 cmp $0x6,%eax8048d7b: 75 e6 jne 8048d63 <phase_5+0x2b> # 循环,0 <= i < 68048d7d: c6 44 24 1b 00 movb $0x0,0x1b(%esp)8048d82: c7 44 24 04 12 a2 04 movl $0x804a212,0x4(%esp) # oilers,刚好6个!8048d89: 08 8048d8a: 8d 44 24 15 lea 0x15(%esp),%eax8048d8e: 89 04 24 mov %eax,(%esp) # 传递字符数组!数组,所以是地址?!8048d91: e8 5e 02 00 00 call 8048ff4 <strings_not_equal>8048d96: 85 c0 test %eax,%eax8048d98: 74 05 je 8048d9f <phase_5+0x67> # 判断字符是否相等,不相等则爆炸8048d9a: e8 67 03 00 00 call 8049106 <explode_bomb>8048d9f: 8b 44 24 1c mov 0x1c(%esp),%eax8048da3: 65 33 05 14 00 00 00 xor %gs:0x14,%eax8048daa: 74 09 je 8048db5 <phase_5+0x7d>8048dac: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi8048db0: e8 1b fa ff ff call 80487d0 <__stack_chk_fail@plt> # 栈检查8048db5: 83 c4 28 add $0x28,%esp8048db8: 5b pop %ebx8048db9: c3 ret
这是一道字符串的题目,提交答案正确的时候还有点小惊讶,没想到自己的第一个猜测就正确了!
首先是判断输入的字符串的长度,限制在6.
8048d4f: e8 87 02 00 00 call 8048fdb <string_length> # 计算长度,写入到%eax里面了
8048d54: 83 f8 06 cmp $0x6,%eax
8048d57: 74 05 je 8048d5e <phase_5+0x26> # 字符串长度为6
接下来的那一大段就是一个i = 0 到 i < 6 的for循环
在循环中使用了我们读取的字符串。这个循环做了什么工作呢?先看看循环体之后的一段,这是一个字符串比较过程。比较的两个字符串是0x15(%esp)和地址0x804a212中的字符串。使用gdb工具,查看0x804a212处的值,发现是oilers。0x15(%esp)是我们输入的字符串,那么这个判断是不是说明我们输入的字符串应该是oilers呢?没这么简单!
8048d8a: 8d 44 24 15 lea 0x15(%esp),%eax8048d8e: 89 04 24 mov %eax,(%esp) # 传递字符数组!数组,所以是地址?!8048d91: e8 5e 02 00 00 call 8048ff4 <strings_not_equal>8048d96: 85 c0 test %eax,%eax8048d98: 74 05 je 8048d9f <phase_5+0x67> # 判断字符是否相等,不相等则爆炸
观察for循环,我发现,0x15(%esp)处的值不再是原生的输入数据了,而是经过了更改的。如何更改?这就是解密的关键过程。
8048d5e: b8 00 00 00 00 mov $0x0,%eax # i = 08048d63: 0f be 14 03 movsbl (%ebx,%eax,1),%edx # 符号扩展双字。%ebx的值是什么?8048d67: 83 e2 0f and $0xf,%edx8048d6a: 0f b6 92 3c a2 04 08 movzbl 0x804a23c(%edx),%edx8048d71: 88 54 04 15 mov %dl,0x15(%esp,%eax,1) # 替换了读入的字符?8048d75: 83 c0 01 add $0x1,%eax # i++8048d78: 83 f8 06 cmp $0x6,%eax8048d7b: 75 e6 jne 8048d63 <phase_5+0x2b> # 循环,0 <= i < 6
假设读入a[6].i = 0 时,对a[0],首先将它与0xf相与,得到的值作为偏移量,和0x804a23c地址相加得到一个新的地址,传递给%edx,然后再把低八位传递给a[0]。也就是说,原先的a[0]是用来生成地址的,生成了地址,这个地址的值在哪里?我们使用gdb查看0x804a23c。
(gdb) x/s 0x804a23c
0x804a23c <array.2954>: "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
发现它是一个字符串(字符数组),它里面0-15位置的字符,就是我们解密的素材了。逆向的过程是,我们要找到oilers,找到每个字符的下表,如o的下标10,i的下标4,l(15),e(5),r(6),s(7)。
这串密码10-4-15-5-6-7是原生字符串和0xf相与之后得到的,那么什么字符串可以得到这串密码呢?一种可能的解是ASCII值为10-4-15-5-6-7的字符串。但是它是多解的,因为与0xf相与截取的是低四位,所以高四位无论是什么都没关系。为了输入方便,我把它们调整到了a-z区间,每个字符加(01100000)2,也就是96. 得到的这组可行解为 jdoefg
。第五关也顺利通过了。
phase 6
汇编代码分析与注释如下
08048dba <phase_6>:8048dba: 56 push %esi8048dbb: 53 push %ebx8048dbc: 83 ec 44 sub $0x44,%esp8048dbf: 8d 44 24 10 lea 0x10(%esp),%eax8048dc3: 89 44 24 04 mov %eax,0x4(%esp)8048dc7: 8b 44 24 50 mov 0x50(%esp),%eax8048dcb: 89 04 24 mov %eax,(%esp)8048dce: e8 68 04 00 00 call 804923b <read_six_numbers> # 同样是六个数字8048dd3: be 00 00 00 00 mov $0x0,%esi # int i = 0。恐怕是while循环8048dd8: 8b 44 b4 10 mov 0x10(%esp,%esi,4),%eax # 第一个数值!8048ddc: 83 e8 01 sub $0x1,%eax # 如果%eax初始值为7,那么减1后会爆炸.应<=6且>=18048ddf: 83 f8 05 cmp $0x5,%eax # 需要小于等于58048de2: 76 05 jbe 8048de9 <phase_6+0x2f> # 小于等于跳转,无符号!8048de4: e8 1d 03 00 00 call 8049106 <explode_bomb>8048de9: 83 c6 01 add $0x1,%esi # i++8048dec: 83 fe 06 cmp $0x6,%esi # i < 68048def: 74 33 je 8048e24 <phase_6+0x6a> # 跳转到很下面,下面循环继续?while循环吧,break8048df1: 89 f3 mov %esi,%ebx8048df3: 8b 44 9c 10 mov 0x10(%esp,%ebx,4),%eax8048df7: 39 44 b4 0c cmp %eax,0xc(%esp,%esi,4) # 上一个数字!因为i++了,然后0xc比0x10少48048dfb: 75 05 jne 8048e02 <phase_6+0x48> # 如果上一个数字和这个数字不相等,跳转,否则爆炸8048dfd: e8 04 03 00 00 call 8049106 <explode_bomb>8048e02: 83 c3 01 add $0x1,%ebx # ++8048e05: 83 fb 05 cmp $0x5,%ebx # 和5比较8048e08: 7e e9 jle 8048df3 <phase_6+0x39> # 小于就进入循环。双重循环do while8048e0a: eb cc jmp 8048dd8 <phase_6+0x1e> # soga,大循环跳转8048e0c: 8b 52 08 mov 0x8(%edx),%edx # 段一,这是一个结构体的指针。数值、编号、下一地址(+8)。 8048e0f: 83 c0 01 add $0x1,%eax # 2,8048e12: 39 c8 cmp %ecx,%eax8048e14: 75 f6 jne 8048e0c <phase_6+0x52>8048e16: 89 54 b4 28 mov %edx,0x28(%esp,%esi,4) # 段二。读入的6个数之后的存储区,存结构体的地址。8048e1a: 83 c3 01 add $0x1,%ebx8048e1d: 83 fb 06 cmp $0x6,%ebx8048e20: 75 07 jne 8048e29 <phase_6+0x6f>8048e22: eb 1c jmp 8048e40 <phase_6+0x86>8048e24: bb 00 00 00 00 mov $0x0,%ebx # 08048e29: 89 de mov %ebx,%esi # 08048e2b: 8b 4c 9c 10 mov 0x10(%esp,%ebx,4),%ecx # a[0]8048e2f: b8 01 00 00 00 mov $0x1,%eax # eax = 18048e34: ba 3c c1 04 08 mov $0x804c13c,%edx 8048e39: 83 f9 01 cmp $0x1,%ecx # 判断a[0] > 1?8048e3c: 7f ce jg 8048e0c <phase_6+0x52> # 如果大于跳转到段一,上面8048e3e: eb d6 jmp 8048e16 <phase_6+0x5c> # 否则跳转到段二,上面8048e40: 8b 5c 24 28 mov 0x28(%esp),%ebx # 第一个数据的地址8048e44: 8b 44 24 2c mov 0x2c(%esp),%eax # 第二个数据的地址8048e48: 89 43 08 mov %eax,0x8(%ebx)8048e4b: 8b 54 24 30 mov 0x30(%esp),%edx8048e4f: 89 50 08 mov %edx,0x8(%eax)8048e52: 8b 44 24 34 mov 0x34(%esp),%eax8048e56: 89 42 08 mov %eax,0x8(%edx)8048e59: 8b 54 24 38 mov 0x38(%esp),%edx8048e5d: 89 50 08 mov %edx,0x8(%eax)8048e60: 8b 44 24 3c mov 0x3c(%esp),%eax8048e64: 89 42 08 mov %eax,0x8(%edx)8048e67: c7 40 08 00 00 00 00 movl $0x0,0x8(%eax)8048e6e: be 05 00 00 00 mov $0x5,%esi # i = 5,循环从5开始到1. # 这个循环判断p->val大于p->next->val8048e73: 8b 43 08 mov 0x8(%ebx),%eax # 下面三条,这时相互影响,类似递归调用啊。8048e76: 8b 10 mov (%eax),%edx8048e78: 39 13 cmp %edx,(%ebx)8048e7a: 7d 05 jge 8048e81 <phase_6+0xc7> # 这是关键了8048e7c: e8 85 02 00 00 call 8049106 <explode_bomb>8048e81: 8b 5b 08 mov 0x8(%ebx),%ebx8048e84: 83 ee 01 sub $0x1,%esi8048e87: 75 ea jne 8048e73 <phase_6+0xb9> # ZF为0时跳转,ZF是最近操作得出的结果为0,ZF=1。也就是esi = 1 - 1 = 0时,ZF = 1,不跳转。所以可以作为一个循环,当 i >= 1时,循环继续。8048e89: 83 c4 44 add $0x44,%esp8048e8c: 5b pop %ebx8048e8d: 5e pop %esi8048e8e: c3 ret
第一大段是一个双重循环用来判断数组a中是否有重复的数据,并且a中元素的值限制在 1 <= a[i] <= 6,因为做了无符号跳转,所以负数不行!那么这些数据就是[1, 6]的一个排列,刚刚好!
第二大段是根据读入的六个数字,将结构体的地址按顺序添加到栈中,根据后面的分析我们可以知道我们的目的是根据输入数据将链表元素重新排列,使得它从大到小排列。
结构体包含三个数据:val、id、下一元素地址。因此它是一个链表。
用GDB分析地址0x804c13c
(gdb) x/18x 0x804c13c
0x804c13c <node1>: 0x00000148 0x00000001 0x0804c148 0x00000218
0x804c14c <node2+4>: 0x00000002 0x0804c154 0x00000259 0x00000003
0x804c15c <node3+8>: 0x0804c160 0x0000012f 0x00000004 0x0804c16c
0x804c16c <node5>: 0x00000152 0x00000005 0x0804c178 0x000002ab
0x804c17c <node6+4>: 0x00000006 0x00000000
第三大段是根据我们添加的结构体的地址的顺序,将链表重新映射。与第二段结合就是,将链表元素按照输入的数据的顺序排列好。比如我们输入6 3 2 5 1 4
,它的意思就是将原先排在第6的元素排在第一个,将原先排在第1的元素排在第5个。第二大段是整理元素地址顺序,第三大段是通过这个顺序修改元素地址。从而实现链表的排序。
第四大段的功能是,从排列后的第一个元素开始,以指向下一元素地址的方式遍历整个链表,同时比较当前元素和下一元素的值。因此它是一个循环量为5的一重循环。且,如果出现当前元素小于下一元素的情况,将会爆照。这里要求排列好的顺序是非递增序的。
因此,在了解了第4大段的功能之后,我们可以逆向地推理出应该输入的六个数据。也就是 6 3 2 5 1 4
。
secret phase
从函数phase_defused的汇编代码中可以发现隐藏关卡的入口。
打印地址0x804a3be的值,发现它是DrEvil(邪恶博士?哈哈)
在关卡四的密码后面加上DrEvil就可以在最后(六关通关后)进入隐藏关卡了。
从下面的条件判断,可初步看到输入数据的范围
接下来调用函数fun7,发现它有两个参数,第一个参数是一个地址(指针),第二个参数是我们输入的数据。
最后执行fun7返回的结果需要是4 。否则就会爆炸。
那么进入fun7里面分析,它有三种情况fun7(int a, int b):
1、a == b 时,return 0
2、a < b 时,return 2fun7((a+8), b) + 1;
3、a > b 时,return 2fun7((a+4), b);
那么它的函数大概是这样的
int fun7(int *a, int b) {if (*a == 0) return -1;if (*a == b) return 0;if (*a < b) {int c = fun7(*(a + 8), b);return 2*c + 1;} else {int c = fun7(*(a + 4), b);return 2*c;}
}
那么,要得到4
4 = 2*2
2 = 1*2
1 = 0*2 + 1
0 = 0
根据那样的结构,最后会得到输入的数据等于0x7,也就是7.
那么secret phase的结果就是7了
结果展示
an
swer 为输入文件。
程序结果
csapp - bomb lab相关推荐
- [精品]CSAPP Bomb Lab 解题报告(七)——隐藏关卡
接上篇[精品]CSAPP Bomb Lab 解题报告(六) gdb常用指令 设置Intel代码格式:set disassembly-flavor intel 查看反汇编代码:disas phase_1 ...
- [精品]CSAPP Bomb Lab 解题报告(六)
接上篇[精品]CSAPP Bomb Lab 解题报告(五) gdb常用指令 设置Intel代码格式:set disassembly-flavor intel 查看反汇编代码:disas phase_1 ...
- [精品]CSAPP Bomb Lab 解题报告(五)
接上篇[精品]CSAPP Bomb Lab 解题报告(四) gdb常用指令 设置Intel代码格式:set disassembly-flavor intel 查看反汇编代码:disas phase_1 ...
- [精品]CSAPP Bomb Lab 解题报告(四)
接上篇[精品]CSAPP Bomb Lab 解题报告(三) gdb常用指令 设置Intel代码格式:set disassembly-flavor intel 查看反汇编代码:disas phase_1 ...
- [精品]CSAPP Bomb Lab 解题报告(三)
接上篇[精品]CSAPP Bomb Lab 解题报告(二) gdb常用指令 设置Intel代码格式:set disassembly-flavor intel 查看反汇编代码:disas phase_1 ...
- [精品]CSAPP Bomb Lab 解题报告(二)
接上篇[精品]CSAPP Bomb Lab 解题报告(一) gdb常用指令 设置Intel代码格式:set disassembly-flavor intel 查看反汇编代码:disas phase_1 ...
- [精品]CSAPP Bomb Lab 解题报告(一)
接上篇堆栈图解CSAPP Bomb Lab实验解析 gdb常用指令 设置Intel代码格式:set disassembly-flavor intel 查看反汇编代码:disas phase_1 查看字 ...
- 堆栈图解CSAPP Bomb Lab实验解析
CSAPP Bomb Lab 实验解析 Bomblab是csapp的第二个配套实验,该实验提供了一个bomb二进制文件和一个bomb.c源文件,我们的目标是运行bomb并按照提示一步步输入字符串,直到 ...
- CSAPP Bomb Lab
CSAPP Bomb Lab bomb lab给了我们一个bomb的可执行文件,以及一个bomb.c的源文件,不过这个文件只是程序的逻辑逻辑框架,无法编译.进入bomb.c可以看到程序的流程是有6个p ...
- CSAPP Bomb Lab记录
记录关于CSAPP 二进制炸弹实验过程 (CSAPP配套教学网站Bomb Lab自学版本,实验地址:http://csapp.cs.cmu.edu/2e/labs.html) (个人体验:对x86汇编 ...
最新文章
- 加快windows上对大文件,以及很多很多小文件进行不同磁盘拷贝的速度——windows上的最快拷贝软件FastCopy
- redis(nosql数据库)
- 三大执业考试爆泄题丑闻 部分助考机构成泄题中介
- CTC blank 理解
- 加仓减仓口诀_加仓减仓口诀
- 六大重要策略与技巧,让我们更有效在领英(LinkedIn)开展线上营销
- “蔗里最甜”开展新型婚育文化宣传活动
- 随机展示一个汉字,可以用来让一二年级孩子识字
- 一个视频发布在三个平台上,可以赚三份收益,自媒体如何发布视频
- Kylin (四) --------- Kylin 4.0 查询引擎
- Java 与 Mysql 时间相差八小时
- 测试人员的工作周报或工作总结怎么编写?
- python:gettext --- 多语种国际化服务
- fedora利用vmlinuz和initrd制作linux启动u盘,打造Fedora 14安装U盘
- python之flask_sqlalchemy的使用及详解
- wm_concat函数用法
- KiCad 6 版本体验记录
- 深度中国——课后随感
- vsFTP简单安装测试
- redis的热key、大key
热门文章
- 为什么我会关心“雪球嘉年华”活动
- 转:没有效率的增长,是在加速自杀
- java计算机毕业设计ssm学生课堂考勤小程序947n4(附源码、数据库)
- 一对一直播系统源码——如何只需三步搭建
- Arduino + ESP32-C3 + TFT(1.8‘ ST7735S)基础平台(实验二)玩具示波器
- 流量战争:中国互联网的新一轮上山下乡运动
- C语言:计算阶乘与计算从1加到100的代码对比:都要用到3个变量,不同之处在于表达式
- matlab牛顿拉夫逊算法,牛顿拉夫逊法程序设计
- CCRC信息安全服务资质--应急处理
- 2017c语言 形成性考核,(2017年电大)《c语言程序设计》形成性考核作业()解答.doc...