CSAPP: bomb lab
来源:互联网 发布:软件销售 编辑:程序博客网 时间:2024/05/17 22:26
有兴趣做这个lab的可以到这里下载, 里面包含原文档和bomb二进制文件和我的解答及过程:
http://download.csdn.net/detail/xuzhezhaozhao/5288867
或
https://github.com/xuzhezhaozhao/CSAPP-Labs
花了两天时间把CSAPP中的bomb实验做了一下,果然是名不虚传,感觉学到不少东西,对于汇编和GDB熟悉不少!
实验内容很简单,就是输入6个特定的字符串, 输错一个就失败. 而要得到这6个字符串, 则要反汇编bomb文件, 通过汇编代码找到这6个字符串.
比较变态的是里面除了这6个字符串外, 还有一个secret phase, 就是隐藏的字符串, 必须输入特定的字符串才能得到!
不多废话, 开始实验!
首先用objdump得到bomb反汇编代码:
# objdump -d bomb > bomb.s
打开bomb.s文件一看, 有1000多行, 咋一看头都大了, 再仔细一看, 发现里面有函数名的注释, 于是就能在其中看到有6个函数,
phase_1, phase_2, phase_3, phase_4, phase_5, phase_6, 很明确了, 下面就是研究这6个函数了.
- phase_1:
08048b20 <phase_1>: 8048b20:55 push %ebp 8048b21:89 e5 mov %esp,%ebp 8048b23:83 ec 08 sub $0x8,%esp 8048b26:8b 45 08 mov 0x8(%ebp),%eax 8048b29:83 c4 f8 add $0xfffffff8,%esp 8048b2c:68 c0 97 04 08 push $0x80497c0 <---------- 参数1, 标准字符串, 也就是答案 8048b31:50 push %eax <--------- 参数2, 输入的字符串 8048b32:e8 f9 04 00 00 call 8049030 <strings_not_equal> <------- 调用字符串比较函数, 用栈来传递参数 8048b37:83 c4 10 add $0x10,%esp 8048b3a:85 c0 test %eax,%eax <---------- 由返回值判断标准字符串与你输入的字符串是否相等 8048b3c:74 05 je 8048b43 <phase_1+0x23> 8048b3e:e8 b9 09 00 00 call 80494fc <explode_bomb> <-------- 爆炸函数 8048b43:89 ec mov %ebp,%esp 8048b45:5d pop %ebp 8048b46:c3 ret 8048b47:90 nop
08048b20 <phase_1>: 8048b20:55 push %ebp 8048b21:89 e5 mov %esp,%ebp 8048b23:83 ec 08 sub $0x8,%esp 8048b26:8b 45 08 mov 0x8(%ebp),%eax 8048b29:83 c4 f8 add $0xfffffff8,%esp 8048b2c:68 c0 97 04 08 push $0x80497c0 <---------- 参数1, 标准字符串, 也就是答案 8048b31:50 push %eax <--------- 参数2, 输入的字符串 8048b32:e8 f9 04 00 00 call 8049030 <strings_not_equal> <------- 调用字符串比较函数, 用栈来传递参数 8048b37:83 c4 10 add $0x10,%esp 8048b3a:85 c0 test %eax,%eax <---------- 由返回值判断标准字符串与你输入的字符串是否相等 8048b3c:74 05 je 8048b43 <phase_1+0x23> 8048b3e:e8 b9 09 00 00 call 80494fc <explode_bomb> <-------- 爆炸函数 8048b43:89 ec mov %ebp,%esp 8048b45:5d pop %ebp 8048b46:c3 ret 8048b47:90 nop
这个比较简单, 仔细分析就发现这个函数只是调用了一个strings_not_equal来判断输入的字符串是否与标准字符串相等, 不等就爆炸.由上面的分析知标准字符串保存在
0x80497c0位置处. 用GDB调试, 设置好断点, 用 x/s 0x80497c0命令查看字符串的值, 得到 "Public speaking is very easy." 这个字符串, 这就是第一个字符串了!
solution: Public speaking is very easy.
- phase_2:
08048b48 <phase_2>: 8048b48:55 push %ebp 8048b49:89 e5 mov %esp,%ebp 8048b4b:83 ec 20 sub $0x20,%esp 8048b4e:56 push %esi 8048b4f:53 push %ebx 8048b50:8b 55 08 mov 0x8(%ebp),%edx <---------- 由栈传递输入的字符串的首地址 8048b53:83 c4 f8 add $0xfffffff8,%esp 8048b56:8d 45 e8 lea -0x18(%ebp),%eax 8048b59:50 push %eax 8048b5a:52 push %edx 8048b5b:e8 78 04 00 00 call 8048fd8 <read_six_numbers> <----- 调用函数, 很明显, 从函数名就可以看出来这是读取6个数字 8048b60:83 c4 10 add $0x10,%esp +------------- 所以输入的字符串应该是6个数字的形式 8048b63:83 7d e8 01 cmpl $0x1,-0x18(%ebp) <-------------- ($ebp)-0x18 所指向的地址中保存第一个数 a[0] 8048b67:74 05 je 8048b6e <phase_2+0x26> <-------------- if == , goto .next, 说明第一个数一定是1 8048b69:e8 8e 09 00 00 call 80494fc <explode_bomb> 8048b6e:bb 01 00 00 00 mov $0x1,%ebx 8048b73:8d 75 e8 lea -0x18(%ebp),%esi .loop 8048b76:8d 43 01 lea 0x1(%ebx),%eax 8048b79:0f af 44 9e fc imul -0x4(%esi,%ebx,4),%eax 8048b7e:39 04 9e cmp %eax,(%esi,%ebx,4) <-------------- (esi+4*ebx) : eax 8048b81:74 05 je 8048b88 <phase_2+0x40> <-------------- if ==, goto next2 8048b83:e8 74 09 00 00 call 80494fc <explode_bomb> .next2 8048b88:43 inc %ebx 8048b89:83 fb 05 cmp $0x5,%ebx <------------- ebx : 5 8048b8c:7e e8 jle 8048b76 <phase_2+0x2e> <------------- if <=, goto .loop 8048b8e:8d 65 d8 lea -0x28(%ebp),%esp 8048b91:5b pop %ebx 8048b92:5e pop %esi 8048b93:89 ec mov %ebp,%esp 8048b95:5d pop %ebp 8048b96:c3 ret 8048b97:90 nop
用GDB单步调试可得到0x804b6d0 保存"%d %d %d %d %d %d"字符串, 0xffffd6a0 中保存的是输入的6个数字的起始位置, 6个数字满足关系,a[n] * (n + 2) = a[n + 1] n=0...5, 且a[0] = 1. 这一关主要就考察循环.solution: 1 2 6 24 120 720
08048b48 <phase_2>: 8048b48:55 push %ebp 8048b49:89 e5 mov %esp,%ebp 8048b4b:83 ec 20 sub $0x20,%esp 8048b4e:56 push %esi 8048b4f:53 push %ebx 8048b50:8b 55 08 mov 0x8(%ebp),%edx <---------- 由栈传递输入的字符串的首地址 8048b53:83 c4 f8 add $0xfffffff8,%esp 8048b56:8d 45 e8 lea -0x18(%ebp),%eax 8048b59:50 push %eax 8048b5a:52 push %edx 8048b5b:e8 78 04 00 00 call 8048fd8 <read_six_numbers> <----- 调用函数, 很明显, 从函数名就可以看出来这是读取6个数字 8048b60:83 c4 10 add $0x10,%esp +------------- 所以输入的字符串应该是6个数字的形式 8048b63:83 7d e8 01 cmpl $0x1,-0x18(%ebp) <-------------- ($ebp)-0x18 所指向的地址中保存第一个数 a[0] 8048b67:74 05 je 8048b6e <phase_2+0x26> <-------------- if == , goto .next, 说明第一个数一定是1 8048b69:e8 8e 09 00 00 call 80494fc <explode_bomb> 8048b6e:bb 01 00 00 00 mov $0x1,%ebx 8048b73:8d 75 e8 lea -0x18(%ebp),%esi .loop 8048b76:8d 43 01 lea 0x1(%ebx),%eax 8048b79:0f af 44 9e fc imul -0x4(%esi,%ebx,4),%eax 8048b7e:39 04 9e cmp %eax,(%esi,%ebx,4) <-------------- (esi+4*ebx) : eax 8048b81:74 05 je 8048b88 <phase_2+0x40> <-------------- if ==, goto next2 8048b83:e8 74 09 00 00 call 80494fc <explode_bomb> .next2 8048b88:43 inc %ebx 8048b89:83 fb 05 cmp $0x5,%ebx <------------- ebx : 5 8048b8c:7e e8 jle 8048b76 <phase_2+0x2e> <------------- if <=, goto .loop 8048b8e:8d 65 d8 lea -0x28(%ebp),%esp 8048b91:5b pop %ebx 8048b92:5e pop %esi 8048b93:89 ec mov %ebp,%esp 8048b95:5d pop %ebp 8048b96:c3 ret 8048b97:90 nop
- phase_3
08048b98 <phase_3>: 8048b98:55 push %ebp 8048b99:89 e5 mov %esp,%ebp 8048b9b:83 ec 14 sub $0x14,%esp 8048b9e:53 push %ebx 8048b9f:8b 55 08 mov 0x8(%ebp),%edx <------ get &phase_3, 输入字符串首地址 8048ba2:83 c4 f4 add $0xfffffff4,%esp <----- esp-12 8048ba5:8d 45 fc lea -0x4(%ebp),%eax <----- eax = ebp - 4 8048ba8:50 push %eax 8048ba9:8d 45 fb lea -0x5(%ebp),%eax 8048bac:50 push %eax 8048bad:8d 45 f4 lea -0xc(%ebp),%eax 8048bb0:50 push %eax 8048bb1:68 de 97 04 08 push $0x80497de <------- 保存sscanf函数的输入格式参数 "%d %c %d" 8048bb6:52 push %edx <------ 输入的字符串, 形式为 "1个整数 1个字符 1个整数" 8048bb7:e8 a4 fc ff ff call 8048860 <sscanf@plt> 8048bbc:83 c4 20 add $0x20,%esp <------ esp+32 8048bbf:83 f8 02 cmp $0x2,%eax <---- %eax是函数sscanf返回值, 是读取的参数个数 8048bc2:7f 05 jg 8048bc9 <phase_3+0x31> 8048bc4:e8 33 09 00 00 call 80494fc <explode_bomb> 8048bc9:83 7d f4 07 cmpl $0x7,-0xc(%ebp) <----- 第一个数字a[0]与7比较, 大于7就跪了, 所以第一个数可以为0-7 8048bcd:0f 87 b5 00 00 00 ja 8048c88 <phase_3+0xf0> 8048bd3:8b 45 f4 mov -0xc(%ebp),%eax <------- eax 中保存第一个数字a[0] 8048bd6:ff 24 85 e8 97 04 08 jmp *0x80497e8(,%eax,4) <------ 直接跳转指令, 是一条switch语句, 8048bdd:8d 76 00 lea 0x0(%esi),%esi +----- 跳转地址用了一张table保存, $eax中的值作为偏移量 8048be0:b3 71 mov $0x71,%bl <--------- case 0: $bl保存的是第二个字符 8048be2:81 7d fc 09 03 00 00 cmpl $0x309,-0x4(%ebp) <-------- ($ebp)-0x4 中保存第三个数字 8048be9:0f 84 a0 00 00 00 je 8048c8f <phase_3+0xf7> 8048bef:e8 08 09 00 00 call 80494fc <explode_bomb> 8048bf4:e9 96 00 00 00 jmp 8048c8f <phase_3+0xf7> 8048bf9:8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi 8048c00:b3 62 mov $0x62,%bl <-------- case 1: 8048c02:81 7d fc d6 00 00 00 cmpl $0xd6,-0x4(%ebp) 8048c09:0f 84 80 00 00 00 je 8048c8f <phase_3+0xf7> 8048c0f:e8 e8 08 00 00 call 80494fc <explode_bomb> 8048c14:eb 79 jmp 8048c8f <phase_3+0xf7> 8048c16:b3 62 mov $0x62,%bl <------ case 2: 8048c18:81 7d fc f3 02 00 00 cmpl $0x2f3,-0x4(%ebp) 8048c1f:74 6e je 8048c8f <phase_3+0xf7> 8048c21:e8 d6 08 00 00 call 80494fc <explode_bomb> 8048c26:eb 67 jmp 8048c8f <phase_3+0xf7> 8048c28:b3 6b mov $0x6b,%bl <------ case 3: 8048c2a:81 7d fc fb 00 00 00 cmpl $0xfb,-0x4(%ebp) 8048c31:74 5c je 8048c8f <phase_3+0xf7> 8048c33:e8 c4 08 00 00 call 80494fc <explode_bomb> 8048c38:eb 55 jmp 8048c8f <phase_3+0xf7> 8048c3a:8d b6 00 00 00 00 lea 0x0(%esi),%esi 8048c40:b3 6f mov $0x6f,%bl <------- case 4: 8048c42:81 7d fc a0 00 00 00 cmpl $0xa0,-0x4(%ebp) 8048c49:74 44 je 8048c8f <phase_3+0xf7> 8048c4b:e8 ac 08 00 00 call 80494fc <explode_bomb> 8048c50:eb 3d jmp 8048c8f <phase_3+0xf7> 8048c52:b3 74 mov $0x74,%bl <------- case 5: 8048c54:81 7d fc ca 01 00 00 cmpl $0x1ca,-0x4(%ebp) 8048c5b:74 32 je 8048c8f <phase_3+0xf7> 8048c5d:e8 9a 08 00 00 call 80494fc <explode_bomb> 8048c62:eb 2b jmp 8048c8f <phase_3+0xf7> 8048c64:b3 76 mov $0x76,%bl <------- case 6: 8048c66:81 7d fc 0c 03 00 00 cmpl $0x30c,-0x4(%ebp) 8048c6d:74 20 je 8048c8f <phase_3+0xf7> 8048c6f:e8 88 08 00 00 call 80494fc <explode_bomb> 8048c74:eb 19 jmp 8048c8f <phase_3+0xf7> 8048c76:b3 62 mov $0x62,%bl <------- case 7: 8048c78:81 7d fc 0c 02 00 00 cmpl $0x20c,-0x4(%ebp) 8048c7f:74 0e je 8048c8f <phase_3+0xf7> 8048c81:e8 76 08 00 00 call 80494fc <explode_bomb> 8048c86:eb 07 jmp 8048c8f <phase_3+0xf7> 8048c88:b3 78 mov $0x78,%bl 8048c8a:e8 6d 08 00 00 call 80494fc <explode_bomb> 8048c8f:3a 5d fb cmp -0x5(%ebp),%bl 8048c92:74 05 je 8048c99 <phase_3+0x101> 8048c94:e8 63 08 00 00 call 80494fc <explode_bomb> 8048c99:8b 5d e8 mov -0x18(%ebp),%ebx 8048c9c:89 ec mov %ebp,%esp 8048c9e:5d pop %ebp 8048c9f:c3 ret
jmp *0x80497e8(,%eax,4) 这句对应为switch 语句,指针的值为 0x80497e8+4*$eax, %eax中保存的是第一个数字, 0-7, $bl 第二个字符,
($ebp)-0x4 第三个数字. 这一关主要考察的是switch语句.
0-7所有的情况.
solution:
0 q 777
1 b 214
2 b 755
3 k 251
4 o 160
5 t 458
6 v 780
7 b 524
solution:
0 q 777
1 b 214
2 b 755
3 k 251
4 o 160
5 t 458
6 v 780
7 b 524
- phase_4
08048ca0 <func4>: 8048ca0:55 push %ebp 8048ca1:89 e5 mov %esp,%ebp 8048ca3:83 ec 10 sub $0x10,%esp 8048ca6:56 push %esi 8048ca7:53 push %ebx 8048ca8:8b 5d 08 mov 0x8(%ebp),%ebx <---------- 得到递归函数参数var 8048cab:83 fb 01 cmp $0x1,%ebx <---------- 递归结束状态,var<= 1 8048cae:7e 20 jle 8048cd0 <func4+0x30> <---------- goto done 8048cb0:83 c4 f4 add $0xfffffff4,%esp 8048cb3:8d 43 ff lea -0x1(%ebx),%eax <--------- result = var - 1 8048cb6:50 push %eax <--------- 将var - 1作为下一次递归的参数 8048cb7:e8 e4 ff ff ff call 8048ca0 <func4> 8048cbc:89 c6 mov %eax,%esi <---------- %esi中保存返回值 8048cbe:83 c4 f4 add $0xfffffff4,%esp 8048cc1:8d 43 fe lea -0x2(%ebx),%eax <--------- var - 2 8048cc4:50 push %eax <--------- 再将var - 2 作为参数递归调用 8048cc5:e8 d6 ff ff ff call 8048ca0 <func4> 8048cca:01 f0 add %esi,%eax <--------- result = func4(var-1) + func4(var-2) 8048ccc:eb 07 jmp 8048cd5 <func4+0x35> <-------- goto over 8048cce:89 f6 mov %esi,%esi
.done 8048cd0:b8 01 00 00 00 mov $0x1,%eax <--------- return 1
.over 8048cd5:8d 65 e8 lea -0x18(%ebp),%esp 8048cd8:5b pop %ebx 8048cd9:5e pop %esi 8048cda:89 ec mov %ebp,%esp 8048cdc:5d pop %ebp 8048cdd:c3 ret 8048cde:89 f6 mov %esi,%esi08048ce0 <phase_4>: 8048ce0:55 push %ebp 8048ce1:89 e5 mov %esp,%ebp 8048ce3:83 ec 18 sub $0x18,%esp 8048ce6:8b 55 08 mov 0x8(%ebp),%edx <------ get &phase_4 8048ce9:83 c4 fc add $0xfffffffc,%esp <----- esp - 4 8048cec:8d 45 fc lea -0x4(%ebp),%eax 8048cef:50 push %eax 8048cf0:68 08 98 04 08 push $0x8049808 <--------- 指向 "%d" 8048cf5:52 push %edx 8048cf6:e8 65 fb ff ff call 8048860 <sscanf@plt> 8048cfb:83 c4 10 add $0x10,%esp 8048cfe:83 f8 01 cmp $0x1,%eax <----- 返回值为读入的参数个数, 应该读入一个整数, 可知 8048d01:75 06 jne 8048d09 <phase_4+0x29> 8048d03:83 7d fc 00 cmpl $0x0,-0x4(%ebp) <-------- ($ebp)-4 指向读入的整数 8048d07:7f 05 jg 8048d0e <phase_4+0x2e> <------- 读入的数要大于0 8048d09:e8 ee 07 00 00 call 80494fc <explode_bomb> 8048d0e:83 c4 f4 add $0xfffffff4,%esp ----- esp - 12 8048d11:8b 45 fc mov -0x4(%ebp),%eax - <-------- get input num 8048d14:50 push %eax 8048d15:e8 86 ff ff ff call 8048ca0 <func4> 8048d1a:83 c4 10 add $0x10,%esp 8048d1d:83 f8 37 cmp $0x37,%eax <------- func4的返回值应该为0x37(55) 8048d20:74 05 je 8048d27 <phase_4+0x47> 8048d22:e8 d5 07 00 00 call 80494fc <explode_bomb> 8048d27:89 ec mov %ebp,%esp 8048d29:5d pop %ebp 8048d2b:90 nop这一关主要考察递归.
分析可知递归函数func4的返回值应该等于0x37 (55)
int func4(int num)
{
if (num <= 1) {
return 1;
} else {
return func(num-1)+func(num-2);
}
}
求得num = 9
solution: 9
int func4(int num)
{
if (num <= 1) {
return 1;
} else {
return func(num-1)+func(num-2);
}
}
求得num = 9
solution: 9
- phase_5
08048d2c <phase_5>: 8048d2c:55 push %ebp 8048d2d:89 e5 mov %esp,%ebp 8048d2f:83 ec 10 sub $0x10,%esp 8048d32:56 push %esi 8048d33:53 push %ebx 8048d34:8b 5d 08 mov 0x8(%ebp),%ebx <------ get &phase_5 8048d37:83 c4 f4 add $0xfffffff4,%esp <----- esp - 12 8048d3a:53 push %ebx 8048d3b:e8 d8 02 00 00 call 8049018 <string_length> 8048d40:83 c4 10 add $0x10,%esp 8048d43:83 f8 06 cmp $0x6,%eax <----- 说明输入的字符串长度为6 8048d46:74 05 je 8048d4d <phfase_5+0x21> 8048d48:e8 af 07 00 00 call 80494fc <explode_bomb> 8048d4d:31 d2 xor %edx,%edx ---- %edx = 0 8048d4f:8d 4d f8 lea -0x8(%ebp),%ecx 8048d52:be 20 b2 04 08 mov $0x804b220,%esi <----这是一张table的起始地址 8048d57:8a 04 1a mov (%edx,%ebx,1),%al <---- loop %al = (%edx+%ebx),遍历6个字符 8048d5a:24 0f and $0xf,%al <----- %al = %al & 0xf, 将字符高4位置0 8048d5c:0f be c0 movsbl %al,%eax <------ 将al扩展为eax, 除低4位,高位都是0, 即只保留了字符的低4位的值 8048d5f:8a 04 30 mov (%eax,%esi,1),%al <----- %al = (字符低4位 + %esi), 在table中查找 8048d62:88 04 0a mov %al,(%edx,%ecx,1) <-----将查找到的table中的字符放到(%ebp-8)为起始的位置 8048d65:42 inc %edx 8048d66:83 fa 05 cmp $0x5,%edx <------- %edx : 5 8048d69:7e ec jle 8048d57 <phase_5+0x2b> <------- if <= goto loop 8048d6b:c6 45 fe 00 movb $0x0,-0x2(%ebp) <----- 字符存在(%ebp-8)-(%ebp-1)中,(%ebp-2)存放'\0' 8048d6f:83 c4 f8 add $0xfffffff8,%esp 8048d72:68 0b 98 04 08 push $0x804980b <---------- 字符串 "giants"起始地址 8048d77:8d 45 f8 lea -0x8(%ebp),%eax <----- 要比较的字符串起始地址 8048d7a:50 push %eax 8048d7b:e8 b0 02 00 00 call 8049030 <strings_not_equal> 8048d80:83 c4 10 add $0x10,%esp 8048d83:85 c0 test %eax,%eax 8048d85:74 05 je 8048d8c <phase_5+0x60> <---- strings_not_equal返回值要为0 8048d87:e8 70 07 00 00 call 80494fc <explode_bomb> 8048d8c:8d 65 e8 lea -0x18(%ebp),%esp 8048d8f:5b pop %ebx 8048d90:5e pop %esi 8048d91:89 ec mov %ebp,%esp 8048d93:5d pop %ebp 8048d94:c3 ret 8048d95:8d 76 00 lea 0x0(%esi),%esi
这一关考察指针. 分析知程序设置了一个table, 地址 0x804b220, 表中有16个字符, "isrveawhobpnutfg", 输入的字符串为6个字符,6个字符的低4位值对应为偏移量(0-f),由偏移从表中得到新的字符串应该为"giants","giants"存放位置为0x804980b.
则得到偏移量应该为 f 0 5 b d 1
则字符串有多种情况,只要满足低4位为 f 0 5 b d 1 的顺序就可以
solution;
如: OPUKMQ
/ %+-Q
- phase_6
8048d98 <phase_6>: 8048d98:55 push %ebp 8048d99:89 e5 mov %esp,%ebp 8048d9b:83 ec 4c sub $0x4c,%esp 8048d9e:57 push %edi 8048d9f:56 push %esi 8048da0:53 push %ebx 8048da1:8b 55 08 mov 0x8(%ebp),%edx ----------- get &phase_6 8048da4:c7 45 cc 6c b2 04 08 movl $0x804b26c,-0x34(%ebp) 8048dab:83 c4 f8 add $0xfffffff8,%esp ------------- esp - 8 8048dae:8d 45 e8 lea -0x18(%ebp),%eax ----------- 8048db1:50 push %eax ----------- **($eax) = "GRADE_BOMB" 8048db2:52 push %edx ----------- &phase_6 8048db3:e8 20 02 00 00 call 8048fd8 <read_six_numbers> <--- 调用read_six_numbers之后,6个数(a0-a5)将依次放在 $ebp-24 - $ebp-4 的位置 8048db8:31 ff xor %edi,%edi <---- set $edi = 0 8048dba:83 c4 10 add $0x10,%esp 8048dbd:8d 76 00 lea 0x0(%esi),%esi <----此时 $esi == 0 8048dc0:8d 45 e8 lea -0x18(%ebp),%eax <-----.loop *($eax) = a[0] 8048dc3:8b 04 b8 mov (%eax,%edi,4),%eax <---- get a[$edi] 8048dc6:48 dec %eax <--- get a[$edi] - 1 8048dc7:83 f8 05 cmp $0x5,%eax <------ (a[$edi]-1) : 5 , 无符号数比较 8048dca:76 05 jbe 8048dd1 <phase_6+0x39> ----------- if <=, goto .next, 注意是无符号比较,每个数都要小于或等于6且大于0, 不能等于0 8048dcc:e8 2b 07 00 00 call 80494fc <explode_bomb> 8048dd1:8d 5f 01 lea 0x1(%edi),%ebx <--- .next, get 数的序号数,(1-6) 8048dd4:83 fb 05 cmp $0x5,%ebx 8048dd7:7f 23 jg 8048dfc <phase_6+0x64> <--- if >, goto .next2 8048dd9:8d 04 bd 00 00 00 00 lea 0x0(,%edi,4),%eax <---- get 4*$edi 8048de0:89 45 c8 mov %eax,-0x38(%ebp) <----- set ($ebp-56) = 4*$edi 8048de3:8d 75 e8 lea -0x18(%ebp),%esi <----set $esi = &a[0] 8048de6:8b 55 c8 mov -0x38(%ebp),%edx <---- set $edx = 4*$edi ----- .next3 8048de9:8b 04 32 mov (%edx,%esi,1),%eax <--- set $eax = (4*$edi+&a[0]) = a[$edi] 8048dec:3b 04 9e cmp (%esi,%ebx,4),%eax <---- a[$edi] : a[$ebx] 8048def:75 05 jne 8048df6 <phase_6+0x5e> <--- if !=, goto .right 8048df1:e8 06 07 00 00 call 80494fc <explode_bomb> 8048df6:43 inc %ebx -------.right 8048df7:83 fb 05 cmp $0x5,%ebx 8048dfa:7e ea jle 8048de6 <phase_6+0x4e> <---- if <=, goto .next3 8048dfc:47 inc %edi -------------- .next2 8048dfd:83 ff 05 cmp $0x5,%edi ---------- %edi : 5 8048e00:7e be jle 8048dc0 <phase_6+0x28> <------ if <=, goto .loop -------------------以上是判断任意两个数是否相等-------------------------------------------------- 8048e02:31 ff xor %edi,%edi 8048e04:8d 4d e8 lea -0x18(%ebp),%ecx <---- set $ecx = &a[0] 8048e07:8d 45 d0 lea -0x30(%ebp),%eax <---- get 0xffffd678, 8048e0a:89 45 c4 mov %eax,-0x3c(%ebp) <------ set ($ebp-0x3c) = 0xffffd678, 8048e0d:8d 76 00 lea 0x0(%esi),%esi <--- $esi == 0xffffd60 .loop2 8048e10:8b 75 cc mov -0x34(%ebp),%esi <---- .loop2 set $esi = 0x804b26c 8048e13:bb 01 00 00 00 mov $0x1,%ebx <----- set $ebx = 1 8048e18:8d 04 bd 00 00 00 00 lea 0x0(,%edi,4),%eax <----- set $eax = 4*$edi 8048e1f:89 c2 mov %eax,%edx <----- set $edx = 4*$edi 8048e21:3b 1c 08 cmp (%eax,%ecx,1),%ebx <------ 1 : a[$edi] 8048e24:7d 12 jge 8048e38 <phase_6+0xa0> <---- if >=, goto .next4 8048e26:8b 04 0a mov (%edx,%ecx,1),%eax <---- set $eax = a[$edi] 8048e29:8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi .next5 8048e30:8b 76 08 mov 0x8(%esi),%esi --- .next5 ------------ set $esi 8048e33:43 inc %ebx 8048e34:39 c3 cmp %eax,%ebx ----- $ebx : a[$edi] 8048e36:7c f8 jl 8048e30 <phase_6+0x98> --- if <, goto .next5 .next4 8048e38:8b 55 c4 mov -0x3c(%ebp),%edx ----- .next4 set %edx = 0xffffd678------- goto.next4 8048e3b:89 34 ba mov %esi,(%edx,%edi,4) --------- $edx ---------,以上循环是用来根据输入的数字生成链表---------------- 8048e3e:47 inc %edi 8048e3f:83 ff 05 cmp $0x5,%edi 8048e42:7e cc jle 8048e10 <phase_6+0x78> if <=, goto .loop2 8048e44:8b 75 d0 mov -0x30(%ebp),%esi <-------- set $esi = 第一个指针的值 p[0] 8048e47:89 75 cc mov %esi,-0x34(%ebp) <-------- $epb-0x34中保存 p[0] 8048e4a:bf 01 00 00 00 mov $0x1,%edi <---------- set $edi=1 8048e4f:8d 55 d0 lea -0x30(%ebp),%edx <------ set $edx = $ebp-0x30, 则 $edx = &p[0] .next6 8048e52:8b 04 ba mov (%edx,%edi,4),%eax <---- .next6 , set $eax = p[$edi] 8048e55:89 46 08 mov %eax,0x8(%esi) <------ set = p[$edi] 8048e58:89 c6 mov %eax,%esi <--------- set $esi = p[$edi] 8048e5a:47 inc %edi 8048e5b:83 ff 05 cmp $0x5,%edi 8048e5e:7e f2 jle 8048e52 <phase_6+0xba> if <=, goto .next6 8048e60:c7 46 08 00 00 00 00 movl $0x0,0x8(%esi) 8048e67:8b 75 cc mov -0x34(%ebp),%esi <------- set $esi = p[0] 8048e6a:31 ff xor %edi,%edi <----------- set $edi = 0 8048e6c:8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi .next8 <-----------------$esi 为第一个数字对应的指针p[0] 8048e70:8b 56 08 mov 0x8(%esi),%edx <---- .next8, set %edx = p[i+1] 8048e73:8b 06 mov (%esi),%eax <---------------get *p[i] 8048e75:3b 02 cmp (%edx),%eax *p[i] : *p[i+1] 8048e77:7d 05 jge 8048e7e <phase_6+0xe6> <---- if >=, goto .next7 8048e79:e8 7e 06 00 00 call 80494fc <explode_bomb> .nxxt7 8048e7e:8b 76 08 mov 0x8(%esi),%esi <----- .next7, set %esi指向下一个数字 8048e81:47 inc %edi 8048e82:83 ff 04 cmp $0x4,%edi 8048e85:7e e9 jle 8048e70 <phase_6+0xd8> <---- if <=, goto .next8 8048e87:8d 65 a8 lea -0x58(%ebp),%esp 8048e8a:5b pop %ebx 8048e8b:5e pop %esi 8048e8c:5f pop %edi 8048e8d:89 ec mov %ebp,%esp 8048e8f:5d pop %ebp 8048e90:c3 ret 8048e91:8d 76 00 lea 0x0(%esi),%esi
这个感觉难度最大,一堆循环,感觉人都绕晕了, 搞了好久才发现原来这是在对一个链表进行操作, 理清了这一点就好多了.0x804b26c是一个已经有了的链表的第一个结点的位置,其数据域为0x000000fd,
程序要做的是根据输入的6个不同大小的数字(1-6)来重新排列这个链表使其按降序排列.
$ebp-0x3c保存的是初始链表的头指针,链表的数据元素是个结构体,
struct LElement {
unsigned int data;
struct LElement *next;
}
%esi 寄存器是用来传递指针的,把握住它的值这个程序就好读一些.
%eiz 是个伪寄存器,其值永远为0.
输入的数字与对应的指针如下:
1 -> 0x804b26c -> data = fd
2 -> 0x804b260 -> data = 2d5
3 -> 0x804b254 -> data = 12d
4 -> 0x804b248 -> data = 3e5
5 -> 0x804b23c -> data = d4
6 -> 0x804b230 ->data = 1b0
按输入的顺序生成链表
6个指针:
0xffffd678: p[0]
0xffffd67c: p[1]
0xffffd680: p[2]
0xffffd684: p[3]
0xffffd688: p[4]
0xffffd68c: p[5]
由最后的循环来看,是判断链表是否为降序排列
有 *p[0] > *p[1] > *p[2] ... > *p[5]
则有 3e5 > 2d5 > 1b0 > 12d > fd > d4
对应数字顺序为: 4 2 6 3 1 5
solution: 4 2 6 3 1 5
$ebp-0x3c保存的是初始链表的头指针,链表的数据元素是个结构体,
struct LElement {
unsigned int data;
struct LElement *next;
}
%esi 寄存器是用来传递指针的,把握住它的值这个程序就好读一些.
%eiz 是个伪寄存器,其值永远为0.
输入的数字与对应的指针如下:
1 -> 0x804b26c -> data = fd
2 -> 0x804b260 -> data = 2d5
3 -> 0x804b254 -> data = 12d
4 -> 0x804b248 -> data = 3e5
5 -> 0x804b23c -> data = d4
6 -> 0x804b230 ->data = 1b0
按输入的顺序生成链表
6个指针:
0xffffd678: p[0]
0xffffd67c: p[1]
0xffffd680: p[2]
0xffffd684: p[3]
0xffffd688: p[4]
0xffffd68c: p[5]
由最后的循环来看,是判断链表是否为降序排列
有 *p[0] > *p[1] > *p[2] ... > *p[5]
则有 3e5 > 2d5 > 1b0 > 12d > fd > d4
对应数字顺序为: 4 2 6 3 1 5
solution: 4 2 6 3 1 5
- secret_phase
8048ee8 <secret_phase>: 8048ee8:55 push %ebp 8048ee9:89 e5 mov %esp,%ebp 8048eeb:83 ec 14 sub $0x14,%esp 8048eee:53 push %ebx 8048eef:e8 08 03 00 00 call 80491fc <read_line> 8048ef4:6a 00 push $0x0 8048ef6:6a 0a push $0xa 8048ef8:6a 00 push $0x0 8048efa:50 push %eax ----------- get &secret_string 8048efb:e8 f0 f8 ff ff call 80487f0 <__strtol_internal@plt> ------- 将字符串转换成整数value 8048f00:83 c4 10 add $0x10,%esp 8048f03:89 c3 mov %eax,%ebx 8048f05:8d 43 ff lea -0x1(%ebx),%eax ----- $eax = value-1 8048f08:3d e8 03 00 00 cmp $0x3e8,%eax ---------- value-1 : 0x3e8 (1000) 8048f0d:76 05 jbe 8048f14 <secret_phase+0x2c> ----- if <=, 无符号比较, OK 8048f0f:e8 e8 05 00 00 call 80494fc <explode_bomb> 8048f14:83 c4 f8 add $0xfffffff8,%esp ---------- esp-8 8048f17:53 push %ebx ------- push value 8048f18:68 20 b3 04 08 push $0x804b320 -------- push 0x24 8048f1d:e8 72 ff ff ff call 8048e94 <fun7> ------ 调用fun7 8048f22:83 c4 10 add $0x10,%esp 8048f25:83 f8 07 cmp $0x7,%eax ----------- 比较返回值 : 0x7 8048f28:74 05 je 8048f2f <secret_phase+0x47> ---------if ==, OK 8048f2a:e8 cd 05 00 00 call 80494fc <explode_bomb> 8048f2f:83 c4 f4 add $0xfffffff4,%esp 8048f32:68 20 98 04 08 push $0x8049820 8048f37:e8 d4 f8 ff ff call 8048810 <printf@plt> 8048f3c:e8 eb 05 00 00 call 804952c <phase_defused> 8048f41:8b 5d e8 mov -0x18(%ebp),%ebx 8048f44:89 ec mov %ebp,%esp 8048f46:5d pop %ebp 8048f47:c3 ret
这个比较难, 因为按正常情况输入了正确的6个字符串之后, 这个程序就结束了. 你没有办法输入第7个字符串, 要想输入第7个secret字符串, 还得好好研究一下,
搜索整个汇编代码, 发现只在phase_defuse函数中调用过secret_phase函数, 所以显然我们就要先分析这个函数.
0804952c <phase_defused>: 804952c:55 push %ebp 804952d:89 e5 mov %esp,%ebp 804952f:83 ec 64 sub $0x64,%esp 8049532:53 push %ebx 8049533:83 3d 80 b4 04 08 06 cmpl $0x6,0x804b480 <------ 0x804b480的值每执行一次phase_defused函数就会增1 804953a:75 63 jne 804959f <phase_defused+0x73> 804953c:8d 5d b0 lea -0x50(%ebp),%ebx 804953f:53 push %ebx 8049540:8d 45 ac lea -0x54(%ebp),%eax 8049543:50 push %eax 8049544:68 03 9d 04 08 push $0x8049d03 <---------- "%d %s" 8049549:68 70 b7 04 08 push $0x804b770 <--------- 应该是要读取的字符串, 这个字符串应该包含一个数字和一个字符串 804954e:e8 0d f3 ff ff call 8048860 <sscanf@plt> 8049553:83 c4 10 add $0x10,%esp 8049556:83 f8 02 cmp $0x2,%eax <------- sscanf 要读入两个参数 8049559:75 37 jne 8049592 <phase_defused+0x66> 804955b:83 c4 f8 add $0xfffffff8,%esp 804955e:68 09 9d 04 08 push $0x8049d09 <-------- "austinpowers"首地址 8049563:53 push %ebx 8049564:e8 c7 fa ff ff call 8049030 <strings_not_equal> 8049569:83 c4 10 add $0x10,%esp 804956c:85 c0 test %eax,%eax 804956e:75 22 jne 8049592 <phase_defused+0x66> 8049570:83 c4 f4 add $0xfffffff4,%esp 8049573:68 20 9d 04 08 push $0x8049d20 8049578:e8 93 f2 ff ff call 8048810 <printf@plt> 804957d:83 c4 f4 add $0xfffffff4,%esp 8049580:68 60 9d 04 08 push $0x8049d60 8049585:e8 86 f2 ff ff call 8048810 <printf@plt> 804958a:83 c4 20 add $0x20,%esp 804958d:e8 56 f9 ff ff call 8048ee8 <secret_phase> 8049592:83 c4 f4 add $0xfffffff4,%esp 8049595:68 a0 9d 04 08 push $0x8049da0 804959a:e8 71 f2 ff ff call 8048810 <printf@plt> 804959f:8b 5d 98 mov -0x68(%ebp),%ebx 80495a2:89 ec mov %ebp,%esp 80495a4:5d pop %ebp 80495a5:c3 ret
在phase_defused函数中有cmpl $0x6, 0x804b480 语句,搜索0x804b480发现这个内存的值在每读入一条字符串后就会加1,则显然secret_phase是在第6条字符串之后输入.又发现cmp $0x2, %eax,语句, 此时的%eax是sscanf读取的参数个数,
sscanf读取的参数格式保存在之前push的0x8049d03位置处,为"%d %s", 待读取的参数存放在最后push的0x804b770内存处,
问题就在这里了,用x/s 0x804b770看时发现这个内存位置的字符串为"9",明显不对,从后面的调用
strings_not_equal来看,还要读入的字符保存在0x8049d09中,应该为"austinpowers".下面
的问题就是要把这个字符串存放在0x804b774位置处,非常tricky的就是当我们逐个断点观察每个
read_line函数的时候,发现读入第4条字符串9的位置正好是0x804b770,于是我们要在第4条字符串9后
加上字符串"austinpowers",这样就可以正确执行到secret_phase函数调用位置处了.真是辛苦啊!
strings_not_equal来看,还要读入的字符保存在0x8049d09中,应该为"austinpowers".下面
的问题就是要把这个字符串存放在0x804b774位置处,非常tricky的就是当我们逐个断点观察每个
read_line函数的时候,发现读入第4条字符串9的位置正好是0x804b770,于是我们要在第4条字符串9后
加上字符串"austinpowers",这样就可以正确执行到secret_phase函数调用位置处了.真是辛苦啊!
下面是解析secret_phase, 不难分析到函数是先将字符串转换成整数,并且其值<=1001再调用func7函数,
根据后面的分析,返回值要为0x07, 则可得参数value的值为1001.
typedef struct BiTree {
int data;
BiTree *lchild;
BiTree *rchild;
}BiTree;
/* p指向树的头结点, 树结构如下 */
36
8 50
6 22 45107
20 35 40 47 991001
int func7(int value, BiTree *p)
{
if (NULL == p) {
return -1;
} else if (value < p->data) {
return 2 * func7(value, p->lchild);
} else {
if (value == p->data) {
return 0;
} else {
return 2 * func(value, p->rchild) + 1;
}
}
}
根据后面的分析,返回值要为0x07, 则可得参数value的值为1001.
typedef struct BiTree {
int data;
BiTree *lchild;
BiTree *rchild;
}BiTree;
/* p指向树的头结点, 树结构如下 */
36
8 50
6 22 45107
20 35 40 47 991001
int func7(int value, BiTree *p)
{
if (NULL == p) {
return -1;
} else if (value < p->data) {
return 2 * func7(value, p->lchild);
} else {
if (value == p->data) {
return 0;
} else {
return 2 * func(value, p->rchild) + 1;
}
}
}
08048e94 <fun7>: 8048e94:55 push %ebp 8048e95:89 e5 mov %esp,%ebp 8048e97:83 ec 08 sub $0x8,%esp 8048e9a:8b 55 08 mov 0x8(%ebp),%edx <---- get 参数 setret_phase中传的参数地址,指向 36 8048e9d:8b 45 0c mov 0xc(%ebp),%eax <----- get 参数 setret_phase中传的参数 value 8048ea0:85 d2 test %edx,%edx 8048ea2:75 0c jne 8048eb0 <fun7+0x1c> <-------- if $edx != 0, goto .next 8048ea4:b8 ff ff ff ff mov $0xffffffff,%eax <----- set $eax = -1 8048ea9:eb 37 jmp 8048ee2 <fun7+0x4e> <------ goto .done 8048eab:90 nop 8048eac:8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi .next 8048eb0:3b 02 cmp (%edx),%eax <--- $eax : *$edx 8048eb2:7d 11 jge 8048ec5 <fun7+0x31> <----- if >=, goto .next2 8048eb4:83 c4 f8 add $0xfffffff8,%esp <----- esp - 8 8048eb7:50 push %eax 8048eb8:8b 42 04 mov 0x4(%edx),%eax 8048ebb:50 push %eax 8048ebc:e8 d3 ff ff ff call 8048e94 <fun7> 8048ec1:01 c0 add %eax,%eax 8048ec3:eb 1d jmp 8048ee2 <fun7+0x4e> <---- goto .done .next2 8048ec5:3b 02 cmp (%edx),%eax <----- $eax : *$edx 8048ec7:74 17 je 8048ee0 <fun7+0x4c> <---- if ==, goto .next3 8048ec9:83 c4 f8 add $0xfffffff8,%esp 8048ecc:50 push %eax 8048ecd:8b 42 08 mov 0x8(%edx),%eax 8048ed0:50 push %eax 8048ed1:e8 be ff ff ff call 8048e94 <fun7> 8048ed6:01 c0 add %eax,%eax 8048ed8:40 inc %eax 8048ed9:eb 07 jmp 8048ee2 <fun7+0x4e> ---- goto .done 8048edb:90 nop 8048edc:8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi .next3 8048ee0:31 c0 xor %eax,%eax ---------- set $eax = 0 .done 8048ee2:89 ec mov %ebp,%esp 8048ee4:5d pop %ebp 8048ee5:c3 ret 8048ee6:89 f6 mov %esi,%esi
solution: 1001
到这里bomb都解除了. 得到的7个字符串(包括隐藏字符串)应该为:
Public speaking is very easy.
1 2 6 24 120 720
7 b 524
9 austinpowers
/ %+-Q
4 2 6 3 1 5
1001
1 2 6 24 120 720
7 b 524
9 austinpowers
/ %+-Q
4 2 6 3 1 5
1001
- 总结:
做了这个实验之后对GDB调试工具的使用熟悉了很多, 发现它确实很好很强大, 这里总结几个常用的命令.
si 单指令执行
display/i $pc 执行si命令时打印汇编代码
空行 重复上一条命令
b 断点, b *0x8048a45 在指定内存位置停止, 也可以设在函数上, 按TAB能自动补全
n 单语句执行
c 继续执行
finish 结束当前函数
bt 查看函数堆栈
shell 执行shell命令
display/i $pc 执行si命令时打印汇编代码
空行 重复上一条命令
b 断点, b *0x8048a45 在指定内存位置停止, 也可以设在函数上, 按TAB能自动补全
n 单语句执行
c 继续执行
finish 结束当前函数
bt 查看函数堆栈
shell 执行shell命令
x/xw Addr: 查看4个字节的内存, 16进制显示
x/s Addr: 查看以该地址开头的字符串
p/x $eax: 查看寄存器
做的过程中使用vim查看汇编代码, 很方便, 设置书签的功能和CTRL+O, CTRL+I在位置间跳转的功能常用到, 很不错.
做的过程虽然辛苦, 但收获也多多, 加油!
- CSAPP: bomb lab
- CSAPP Bomb Lab
- CSAPP: Bomb Lab(1)
- CSAPP: Bomb Lab(2)
- CSAPP: Bomb Lab(3)
- CSAPP: Bomb Lab(4)
- CSAPP实验2:Bomb Lab
- csapp bomb lab:csapp lab2 炸弹实验
- CSAPP lab binary bomb 二进制炸弹
- <csapp> bomb lab (《深入理解计算机系统》lab2)
- Bomb lab
- CSAPP LAB---Proxy lab
- csapp lab2 bomb
- CSAPP: buffer lab
- CSAPP: shell lab
- CSAPP: malloc lab
- CSAPP LAB---MALLOC实验
- CSAPP LAB---shlab-handout
- or1200移植实时系统Raw-OS(二)
- Python:新浪微博API初试
- e-人事管理系统-组织管理-应用流程
- ubuntu_shell开关机,重启
- 我就是一个 飞秋爱好者
- CSAPP: bomb lab
- 迭代器的使用
- C#数据类型详细的介绍
- MyEclipse_8.5_GA 安装Freemarker插件
- hdu1711(ac自动机思想)
- fstab挂载硬盘及分区
- Time类中的运算符重载
- 一锅乱炖【数论】
- Commit Monitor–svn监控工具