二进制拆弹一共设有七个关卡:
- phase_1:字符串比较
- phase_2:循环
- phase_3:switch
- phase_4:递归
- phase_5:指针
- phase_6:链表/指针/结构
- secret_phase:隐藏关
Phase1-6通关截图
准备工作
- 我的电脑是windows的,所以需要在虚拟机上安装VMware,并且安装gdb(百度寻找教程)。
- 使用objdump反汇编bomb可执行程序,得到bomb的汇编代码asm.txt文件:objdump -d bomb > asm.txt
- 使用gdb调试bomb程序,进入调试状态,如图1所示:gdb bomb
-
附一个汇编中的跳转指令说明网址:https://blog.csdn.net/do2jiang/article/details/5262327
phase_1
08048b90 <phase_1>:
8048b90: 83 ec 1c sub $0x1c,%esp
8048b93: c7 44 24 04 e4 a1 04 movl $0x804a1e4,0x4(%esp) //取0x804a1e4的内容,到%esp+0x4
8048b9a: 08
8048b9b: 8b 44 24 20 mov 0x20(%esp),%eax
8048b9f: 89 04 24 mov %eax,(%esp) //把用户的输入放入%esp
8048ba2: e8 03 05 00 00 call 80490aa <strings_not_equal>
8048ba7: 85 c0 test %eax,%eax //判断eax是否为0
8048ba9: 74 05 je 8048bb0 <phase_1+0x20> //为0跳转
8048bab: e8 05 06 00 00 call 80491b5 <explode_bomb> //不为0爆炸
8048bb0: 83 c4 1c add $0x1c,%esp
8048bb3: c3 ret
程序解读:
- 取内存0x804a1e4处的内容
- 取用户输入的内容
- 比较两者的值,相同则进入下一关,不相同则爆炸
获取x804a1e4处的内容: p (char *) 0x804a1e4
可以看到,第一关的密码为:Houses will begat jobs, jobs will begat houses.
phase_2
08048bb4 <phase_2>:
//程序第一部分
8048bb4: 53 push %ebx
8048bb5: 83 ec 38 sub $0x38,%esp
8048bb8: 8d 44 24 18 lea 0x18(%esp),%eax
8048bbc: 89 44 24 04 mov %eax,0x4(%esp)
8048bc0: 8b 44 24 40 mov 0x40(%esp),%eax
8048bc4: 89 04 24 mov %eax,(%esp)
8048bc7: e8 10 06 00 00 call 80491dc <read_six_numbers> //读入6个数字
8048bcc: 83 7c 24 18 00 cmpl $0x0,0x18(%esp) //判断第一个数字是否为0
8048bd1: 79 22 jns 8048bf5 <phase_2+0x41> //如果不为0,则跳转到8048bf5处
8048bd3: e8 dd 05 00 00 call 80491b5 <explode_bomb> //如果为0,爆炸!!!
8048bd8: eb 1b jmp 8048bf5 <phase_2+0x41>
//程序第二部分,此处是一个循环
8048bda: 89 d8 mov %ebx,%eax //%ebx的值赋值给%eax
8048bdc: 03 44 9c 14 add 0x14(%esp,%ebx,4),%eax //取了0x14+%esp+4*%ebx的值加上%eax的值,存到%eax中
8048be0: 39 44 9c 18 cmp %eax,0x18(%esp,%ebx,4)
8048be4: 74 05 je 8048beb <phase_2+0x37>
8048be6: e8 ca 05 00 00 call 80491b5 <explode_bomb>
8048beb: 83 c3 01 add $0x1,%ebx
8048bee: 83 fb 06 cmp $0x6,%ebx
8048bf1: 75 e7 jne 8048bda <phase_2+0x26>
//程序第三部分
8048bf3: eb 07 jmp 8048bfc <phase_2+0x48>
8048bf5: bb 01 00 00 00 mov $0x1,%ebx //第一个数字不为0时跳转到这里来啦!!! %ebx里面存的是一个循环变量
8048bfa: eb de jmp 8048bda <phase_2+0x26>
8048bfc: 83 c4 38 add $0x38,%esp
8048bff: 5b pop %ebx
8048c00: c3 ret
程序分析:
- 通过对程序第一部分的分析,可以了解到是要输入6个数字,且第一个数字不能为0;
- 第一个数字不为0时,跳转到8048bf5处。此处设置了一个新的变量,并且初始化为1。然后跳转到8048bda
- 0x14(%esp,%ebx,4)是比例变址寻址方式,地址是 0x14+%esp+4*%ebx
- 然后比较%eax和0x18(%esp,%ebx,4)的操作数的值,如果相等的话,%eax的值++(循环变量++),如果不为6,则继续循环,如果循环体中没有explode_bomb的话,则会循环5次。
- 循环体如果没有explode_bomb,则第二关激活成功教程。所以,关键是循环体8048be0处比较的是何处的值。实际比较的是后一个数字(0x18(%esp,%ebx,4))是否等于 当前数字+循环变量(%eax)。
- 如果我们第一个输入的数字是1,则第二关的密码应该为 1 2(1+1) 4(2+2) 7(4+3) 1(7+4) 16(11+5) 即 1 2 4 7 11 16,答案不唯一,满足这个规律即可,但是第一个数字不能为0.
phase_3
08048c01 <phase_3>:
8048c01: 83 ec 3c sub $0x3c,%esp
//输入
8048c04: 8d 44 24 2c lea 0x2c(%esp),%eax
8048c08: 89 44 24 10 mov %eax,0x10(%esp)
8048c0c: 8d 44 24 27 lea 0x27(%esp),%eax
8048c10: 89 44 24 0c mov %eax,0xc(%esp)
8048c14: 8d 44 24 28 lea 0x28(%esp),%eax
8048c18: 89 44 24 08 mov %eax,0x8(%esp)
8048c1c: c7 44 24 04 3a a2 04 movl $0x804a23a,0x4(%esp) //取出入内容,这里可以看出入的数值类型
8048c23: 08
8048c24: 8b 44 24 40 mov 0x40(%esp),%eax
8048c28: 89 04 24 mov %eax,(%esp)
8048c2b: e8 30 fc ff ff call 8048860 <__isoc99_sscanf@plt>
//输入的第一个数字要大于2,小于等于7,否则爆炸
8048c30: 83 f8 02 cmp $0x2,%eax
8048c33: 7f 05 jg 8048c3a <phase_3+0x39>
8048c35: e8 7b 05 00 00 call 80491b5 <explode_bomb>
8048c3a: 83 7c 24 28 07 cmpl $0x7,0x28(%esp) //后者的值是输入的第一个数字,假设输入的第一个数字是5
8048c3f: 0f 87 fc 00 00 00 ja 8048d41 <phase_3+0x140>
8048c45: 8b 44 24 28 mov 0x28(%esp),%eax
//根据输入的数值进行跳转,这里可以通过调试,把所有可能的跳转地址都输出
8048c49: ff 24 85 60 a2 04 08 jmp *0x804a260(,%eax,4)
8048c50: b8 76 00 00 00 mov $0x76,%eax
8048c55: 81 7c 24 2c f1 00 00 cmpl $0xf1,0x2c(%esp)
8048c5c: 00
8048c5d: 0f 84 e8 00 00 00 je 8048d4b <phase_3+0x14a>
8048c63: e8 4d 05 00 00 call 80491b5 <explode_bomb>
8048c68: b8 76 00 00 00 mov $0x76,%eax
8048c6d: e9 d9 00 00 00 jmp 8048d4b <phase_3+0x14a>
8048c72: b8 69 00 00 00 mov $0x69,%eax
8048c77: 81 7c 24 2c aa 01 00 cmpl $0x1aa,0x2c(%esp)
8048c7e: 00
8048c7f: 0f 84 c6 00 00 00 je 8048d4b <phase_3+0x14a>
8048c85: e8 2b 05 00 00 call 80491b5 <explode_bomb>
8048c8a: b8 69 00 00 00 mov $0x69,%eax
8048c8f: e9 b7 00 00 00 jmp 8048d4b <phase_3+0x14a>
8048c94: b8 67 00 00 00 mov $0x67,%eax
8048c99: 81 7c 24 2c 15 03 00 cmpl $0x315,0x2c(%esp)
8048ca0: 00
8048ca1: 0f 84 a4 00 00 00 je 8048d4b <phase_3+0x14a>
8048ca7: e8 09 05 00 00 call 80491b5 <explode_bomb>
8048cac: b8 67 00 00 00 mov $0x67,%eax
8048cb1: e9 95 00 00 00 jmp 8048d4b <phase_3+0x14a>
8048cb6: b8 62 00 00 00 mov $0x62,%eax
8048cbb: 81 7c 24 2c fd 01 00 cmpl $0x1fd,0x2c(%esp)
8048cc2: 00
8048cc3: 0f 84 82 00 00 00 je 8048d4b <phase_3+0x14a>
8048cc9: e8 e7 04 00 00 call 80491b5 <explode_bomb>
8048cce: b8 62 00 00 00 mov $0x62,%eax
8048cd3: eb 76 jmp 8048d4b <phase_3+0x14a>
8048cd5: b8 74 00 00 00 mov $0x74,%eax
8048cda: 81 7c 24 2c 7b 02 00 cmpl $0x27b,0x2c(%esp)
8048ce1: 00
8048ce2: 74 67 je 8048d4b <phase_3+0x14a>
8048ce4: e8 cc 04 00 00 call 80491b5 <explode_bomb>
8048ce9: b8 74 00 00 00 mov $0x74,%eax
8048cee: eb 5b jmp 8048d4b <phase_3+0x14a>
8048cf0: b8 79 00 00 00 mov $0x79,%eax //第一数字是5的时候,跳转到这里来啦!!!!
8048cf5: 81 7c 24 2c 3a 01 00 cmpl $0x13a,0x2c(%esp) //判断第二个数字的值是不是 0x13a,即十进制的314
8048cfc: 00
8048cfd: 74 4c je 8048d4b <phase_3+0x14a>
8048cff: e8 b1 04 00 00 call 80491b5 <explode_bomb>
8048d04: b8 79 00 00 00 mov $0x79,%eax
8048d09: eb 40 jmp 8048d4b <phase_3+0x14a>
8048d0b: b8 70 00 00 00 mov $0x70,%eax
8048d10: 81 7c 24 2c cf 02 00 cmpl $0x2cf,0x2c(%esp)
8048d17: 00
8048d18: 74 31 je 8048d4b <phase_3+0x14a>
8048d1a: e8 96 04 00 00 call 80491b5 <explode_bomb>
8048d1f: b8 70 00 00 00 mov $0x70,%eax
8048d24: eb 25 jmp 8048d4b <phase_3+0x14a>
8048d26: b8 71 00 00 00 mov $0x71,%eax
8048d2b: 81 7c 24 2c 7b 01 00 cmpl $0x17b,0x2c(%esp)
8048d32: 00
8048d33: 74 16 je 8048d4b <phase_3+0x14a>
8048d35: e8 7b 04 00 00 call 80491b5 <explode_bomb>
8048d3a: b8 71 00 00 00 mov $0x71,%eax
8048d3f: eb 0a jmp 8048d4b <phase_3+0x14a>
8048d41: e8 6f 04 00 00 call 80491b5 <explode_bomb>
//如果第二个数字是314,就会跳转到这里来啦, 判断输入的字符是不是 0x65,即字母y
8048d46: b8 65 00 00 00 mov $0x65,%eax
8048d4b: 3a 44 24 27 cmp 0x27(%esp),%al
8048d4f: 74 05 je 8048d56 <phase_3+0x155>
8048d51: e8 5f 04 00 00 call 80491b5 <explode_bomb>
8048d56: 83 c4 3c add $0x3c,%esp
8048d59: c3 ret
程序分析:
- 查看输入的数据类型,可以看到输入为 整型数值、字符、整型数值
(gdb) p (char*)0x804a23a
$16 = 0x804a23a "%d %c %d"
- 查看所有可能的跳转地址,假设输入的第一个数是5,则应该跳转到的地址是 p/x *(0x804a274)的结果,即跳转到 0x8048cf0
- 然后顺着地址跳转就行了,我通过的密码是:5 y 314 解不唯一
phase_4
08048d5a <func4>:
8048d5a: 56 push %esi
8048d5b: 53 push %ebx
8048d5c: 83 ec 14 sub $0x14,%esp
8048d5f: 8b 54 24 20 mov 0x20(%esp),%edx
8048d63: 8b 44 24 24 mov 0x24(%esp),%eax
8048d67: 8b 5c 24 28 mov 0x28(%esp),%ebx
8048d6b: 89 d9 mov %ebx,%ecx
8048d6d: 29 c1 sub %eax(0),%ecx(14)
8048d6f: 89 ce mov %ecx,%esi
8048d71: c1 ee 1f shr $0x1f,%esi
8048d74: 01 f1 add %esi,%ecx
8048d76: d1 f9 sar %ecx
8048d78: 01 c1 add %eax,%ecx
8048d7a: 39 d1 cmp %edx,%ecx
8048d7c: 7e 17 jle 8048d95 <func4+0x3b> //不大于则转移
8048d7e: 83 e9 01 sub $0x1,%ecx
8048d81: 89 4c 24 08 mov %ecx,0x8(%esp)
8048d85: 89 44 24 04 mov %eax,0x4(%esp)
8048d89: 89 14 24 mov %edx,(%esp)
8048d8c: e8 c9 ff ff ff call 8048d5a <func4>
8048d91: 01 c0 add %eax,%eax
8048d93: eb 20 jmp 8048db5 <func4+0x5b>
8048d95: b8 00 00 00 00 mov $0x0,%eax
8048d9a: 39 d1 cmp %edx,%ecx
8048d9c: 7d 17 jge 8048db5 <func4+0x5b>//大于或等于则转移
8048d9e: 89 5c 24 08 mov %ebx,0x8(%esp)
8048da2: 83 c1 01 add $0x1,%ecx
8048da5: 89 4c 24 04 mov %ecx,0x4(%esp)
8048da9: 89 14 24 mov %edx,(%esp)
8048dac: e8 a9 ff ff ff call 8048d5a <func4>
8048db1: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax
8048db5: 83 c4 14 add $0x14,%esp
8048db8: 5b pop %ebx
8048db9: 5e pop %esi
8048dba: c3 ret
08048dbb <phase_4>:
8048dbb: 83 ec 2c sub $0x2c,%esp
//输入两个值
8048dbe: 8d 44 24 1c lea 0x1c(%esp),%eax
8048dc2: 89 44 24 0c mov %eax,0xc(%esp)
8048dc6: 8d 44 24 18 lea 0x18(%esp),%eax
8048dca: 89 44 24 08 mov %eax,0x8(%esp)
8048dce: c7 44 24 04 cf a3 04 movl $0x804a3cf,0x4(%esp)
8048dd5: 08
8048dd6: 8b 44 24 30 mov 0x30(%esp),%eax
8048dda: 89 04 24 mov %eax,(%esp)
//调用函数
8048ddd: e8 7e fa ff ff call 8048860 <__isoc99_sscanf@plt>
//比较返回值是否为2,不是2则爆炸
8048de2: 83 f8 02 cmp $0x2,%eax
8048de5: 75 07 jne 8048dee <phase_4+0x33>
//比较输入的第一个数是否小于等于14,如果大于15则爆炸
8048de7: 83 7c 24 18 0e cmpl $0xe,0x18(%esp)
8048dec: 76 05 jbe 8048df3 <phase_4+0x38>
8048dee: e8 c2 03 00 00 call 80491b5 <explode_bomb>
//定义了两个变量,值分别为14 0
8048df3: c7 44 24 08 0e 00 00 movl $0xe,0x8(%esp)
8048dfa: 00
8048dfb: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp)
8048e02: 00
8048e03: 8b 44 24 18 mov 0x18(%esp),%eax
8048e07: 89 04 24 mov %eax,(%esp)
//传入参数,调用函数fun4,传入的参数的顺序,从左到右为:输入的第一个数 0 14
8048e0a: e8 4b ff ff ff call 8048d5a <func4>
//判断返回的值是否为7,不是7则爆炸
8048e0f: 83 f8 07 cmp $0x7,%eax
8048e12: 75 07 jne 8048e1b <phase_4+0x60>
//判断输入的第二个参数是否为7,不是则爆炸,所以可以确定第二个参数值为7
8048e14: 83 7c 24 1c 07 cmpl $0x7,0x1c(%esp)
8048e19: 74 05 je 8048e20 <phase_4+0x65>
8048e1b: e8 95 03 00 00 call 80491b5 <explode_bomb>
8048e20: 83 c4 2c add $0x2c,%esp
8048e23: c3 ret
第四关是递归,核心是要写出递归函数。
我得到的递归函数是:
int func4(int a1, int a2, int a3)
{
int v3; // ecx
int result; // eax
v3 = a2 + (a3 - a2) / 2;
if ( v3 > a1 )
return 2 * func4(a1, a2, v3 - 1);
result = 0;
if ( v3 < a1 )
result = 2 * func4(a1, v3 + 1, a3) + 1;
return result;
}
- 第一次调用fun4的时候,传入的第二个参数值是0,第三个参数值是14
- 分析phase_4,可以知道只有fun4的返回值是7的时候,才能不爆炸
- 我们输入的第一个数不大于14,所以尝试求解,可以看到,当输入14的时候,返回值为7,所以第四关的密码是 14 7
phase_5
08048e24 <phase_5>:
8048e24: 53 push %ebx
8048e25: 83 ec 18 sub $0x18,%esp
8048e28: 8b 5c 24 20 mov 0x20(%esp),%ebx
8048e2c: 89 1c 24 mov %ebx,(%esp)
8048e2f: e8 57 02 00 00 call 804908b <string_length>
//输入字符串的长度不等于6,则爆炸
//所以输入是长度为6的字符串
8048e34: 83 f8 06 cmp $0x6,%eax
8048e37: 74 05 je 8048e3e <phase_5+0x1a>
8048e39: e8 77 03 00 00 call 80491b5 <explode_bomb>
8048e3e: ba 00 00 00 00 mov $0x0,%edx
8048e43: b8 00 00 00 00 mov $0x0,%eax
//这里是一个循环
8048e48: 0f b6 0c 03 movzbl (%ebx,%eax,1),%ecx
//取低四位
8048e4c: 83 e1 0f and $0xf,%ecx //这里是关键!!!
//相加
8048e4f: 03 14 8d 80 a2 04 08 add 0x804a280(,%ecx,4),%edx //这里是关键!!!
8048e56: 83 c0 01 add $0x1,%eax
8048e59: 83 f8 06 cmp $0x6,%eax
8048e5c: 75 ea jne 8048e48 <phase_5+0x24>
//判断%edx中的操作数是不是0x2b,即十进制的43
8048e5e: 83 fa 2b cmp $0x2b,%edx
8048e61: 74 05 je 8048e68 <phase_5+0x44>
8048e63: e8 4d 03 00 00 call 80491b5 <explode_bomb>
8048e68: 83 c4 18 add $0x18,%esp
8048e6b: 5b pop %ebx
8048e6c: c3 ret
程序分析:
- 输入一个长度为6的字符串
- 经过一个循环操作
- 判断某个变量的值是不是43,如果是43则通关
- 查看一下0x804a280(,%ecx,4)的内容,%ecx依次取0 1 2 3 4 ….
- 43 = 10+10+10+10+1+2 = 0xa + 0xa + 0xa + 0xa + 0x1 + 0x2
- 0xa对应的%ecx中的值为1(0x804a280+4*1=0x804a284),所以只要 输入的字符的二进制表示&0x0f == 1 即可,例如a的ASCII码的二进制表示是01100001,01100001&0f =1
- 0x1对应的%ecx中的值为3(0x804a280+4*3=0x804a28c), 01100011 & 0x0f 对应的十进制是3,01100011对应的字符是c
- 0x2对应的%ecx中的值为0(0x804a280+4*0=0x804a280), 01110000 & 0x0f 对应的十进制是0,01110000对应的字符是p
- 所以密码之一是:aaaacp(答案不唯一)
phase_6
08048e6d <phase_6>:
8048e6d: 56 push %esi
8048e6e: 53 push %ebx
8048e6f: 83 ec 44 sub $0x44,%esp
8048e72: 8d 44 24 10 lea 0x10(%esp),%eax
8048e76: 89 44 24 04 mov %eax,0x4(%esp)
8048e7a: 8b 44 24 50 mov 0x50(%esp),%eax
8048e7e: 89 04 24 mov %eax,(%esp)
8048e81: e8 56 03 00 00 call 80491dc <read_six_numbers>
8048e86: be 00 00 00 00 mov $0x0,%esi
//这是第一个循环,第一个循环里面还嵌入着一个小循环 这里的条件是数组中所有的元素都小于等于6,且不能存在两个数相同。所以输入的数字是1-6的排列组合之一
8048e8b: 8b 44 b4 10 mov 0x10(%esp,%esi,4),%eax
8048e8f: 83 e8 01 sub $0x1,%eax
8048e92: 83 f8 05 cmp $0x5,%eax
8048e95: 76 05 jbe 8048e9c <phase_6+0x2f> //%eax-1 <= 5则继续,否则爆炸
8048e97: e8 19 03 00 00 call 80491b5 <explode_bomb> //爆炸
8048e9c: 83 c6 01 add $0x1,%esi //计数器加1,如果不等于6则循环
8048e9f: 83 fe 06 cmp $0x6,%esi
8048ea2: 75 07 jne 8048eab <phase_6+0x3e>
8048ea4: bb 00 00 00 00 mov $0x0,%ebx
8048ea9: eb 38 jmp 8048ee3 <phase_6+0x76> //此处是第一个大循环的出口
8048eab: 89 f3 mov %esi,%ebx
8048ead: 8b 44 9c 10 mov 0x10(%esp,%ebx,4),%eax //取数组中的下一个值 小循环开始
8048eb1: 39 44 b4 0c cmp %eax,0xc(%esp,%esi,4) //进行了一次比较,打印看看值是多少
8048eb5: 75 05 jne 8048ebc <phase_6+0x4f>
8048eb7: e8 f9 02 00 00 call 80491b5 <explode_bomb>
8048ebc: 83 c3 01 add $0x1,%ebx
8048ebf: 83 fb 05 cmp $0x5,%ebx
8048ec2: 7e e9 jle 8048ead <phase_6+0x40> //小于等于则跳转 小循环结束
8048ec4: eb c5 jmp 8048e8b <phase_6+0x1e>
//这是第二个循环
8048ec6: 8b 52 08 mov 0x8(%edx),%edx
8048ec9: 83 c0 01 add $0x1,%eax
8048ecc: 39 c8 cmp %ecx,%eax
8048ece: 75 f6 jne 8048ec6 <phase_6+0x59>
8048ed0: eb 05 jmp 8048ed7 <phase_6+0x6a>
8048ed2: ba 3c c1 04 08 mov $0x804c13c,%edx //这里存的是一个地址值
8048ed7: 89 54 b4 28 mov %edx,0x28(%esp,%esi,4) //把地址传给了一个结点
8048edb: 83 c3 01 add $0x1,%ebx //++
8048ede: 83 fb 06 cmp $0x6,%ebx
8048ee1: 74 17 je 8048efa <phase_6+0x8d> //
8048ee3: 89 de mov %ebx,%esi //从此处进入第二个循环,%ebx寄存器内的初始值是0
8048ee5: 8b 4c 9c 10 mov 0x10(%esp,%ebx,4),%ecx //取数组中的第一个元素
8048ee9: 83 f9 01 cmp $0x1,%ecx
8048eec: 7e e4 jle 8048ed2 <phase_6+0x65> //小于等于1则跳转
8048eee: b8 01 00 00 00 mov $0x1,%eax
8048ef3: ba 3c c1 04 08 mov $0x804c13c,%edx
8048ef8: eb cc jmp 8048ec6 <phase_6+0x59>
8048efa: 8b 5c 24 28 mov 0x28(%esp),%ebx
8048efe: 8d 44 24 2c lea 0x2c(%esp),%eax
8048f02: 8d 74 24 40 lea 0x40(%esp),%esi
8048f06: 89 d9 mov %ebx,%ecx
8048f08: 8b 10 mov (%eax),%edx //间接寻址
8048f0a: 89 51 08 mov %edx,0x8(%ecx)
8048f0d: 83 c0 04 add $0x4,%eax
8048f10: 39 f0 cmp %esi,%eax
8048f12: 74 04 je 8048f18 <phase_6+0xab>
8048f14: 89 d1 mov %edx,%ecx
8048f16: eb f0 jmp 8048f08 <phase_6+0x9b>
8048f18: c7 42 08 00 00 00 00 movl $0x0,0x8(%edx)
//这是第四个循环,前一个节点中的值要比后一个结点中的值大,从大到小排序
8048f1f: be 05 00 00 00 mov $0x5,%esi
8048f24: 8b 43 08 mov 0x8(%ebx),%eax
8048f27: 8b 00 mov (%eax),%eax
8048f29: 39 03 cmp %eax,(%ebx)
8048f2b: 7d 05 jge 8048f32 <phase_6+0xc5>
8048f2d: e8 83 02 00 00 call 80491b5 <explode_bomb>
8048f32: 8b 5b 08 mov 0x8(%ebx),%ebx
8048f35: 83 ee 01 sub $0x1,%esi
8048f38: 75 ea jne 8048f24 <phase_6+0xb7>
8048f3a: 83 c4 44 add $0x44,%esp
8048f3d: 5b pop %ebx
8048f3e: 5e pop %esi
8048f3f: c3 ret
程序分析:如下如,各节点存储的值分别为:0x4d<1> 0x11a<2> 0x223<3> 0x3d9<4> 0x106<5> 0x202<6> ,然后由大到小排序:4 3 6 2 5 1
secret_phase
这一关是隐藏关卡,我在asm.txt(反汇编得到的文件)中搜索 secret_phase,发现只有phase_defused函数中使用到了secret_phase函数,所以推测phase_defused是隐藏关卡的入口。
问:phase_defused函数什么时候会被调用???
答:每次密码输对的时候会调用phase_defused函数。所以说,如果调用了phase_defused函数,就意味着通过了一个普通关卡(不包括隐藏关卡)
0x1 分析phase_defused函数
08049326 <phase_defused>:
8049326: 81 ec 8c 00 00 00 sub $0x8c,%esp
804932c: 65 a1 14 00 00 00 mov %gs:0x14,%eax
8049332: 89 44 24 7c mov %eax,0x7c(%esp)
8049336: 31 c0 xor %eax,%eax
//判断 0x804c3c8 中的值是否为6,不是6则跳转,跳转后,phase_defused函数会顺序执行完毕,无法进入隐藏关卡
8049338: 83 3d c8 c3 04 08 06 cmpl $0x6,0x804c3c8
804933f: 75 72 jne 80493b3 <phase_defused+0x8d>
//调用 <__isoc99_sscanf@plt>
8049341: 8d 44 24 2c lea 0x2c(%esp),%eax
8049345: 89 44 24 10 mov %eax,0x10(%esp)
8049349: 8d 44 24 28 lea 0x28(%esp),%eax
804934d: 89 44 24 0c mov %eax,0xc(%esp)
8049351: 8d 44 24 24 lea 0x24(%esp),%eax
8049355: 89 44 24 08 mov %eax,0x8(%esp)
8049359: c7 44 24 04 29 a4 04 movl $0x804a429,0x4(%esp) //"%d %d %s"
8049360: 08
8049361: c7 04 24 d0 c4 04 08 movl $0x804c4d0,(%esp)
8049368: e8 f3 f4 ff ff call 8048860 <__isoc99_sscanf@plt>
//判断<__isoc99_sscanf@plt>的返回值是否为3,不是3则跳转
804936d: 83 f8 03 cmp $0x3,%eax //sscanf返回参数,表示输入的参数个数
8049370: 75 35 jne 80493a7 <phase_defused+0x81>
8049372: c7 44 24 04 32 a4 04 movl $0x804a432,0x4(%esp) //DrEvil
8049379: 08
804937a: 8d 44 24 2c lea 0x2c(%esp),%eax
804937e: 89 04 24 mov %eax,(%esp)
8049381: e8 24 fd ff ff call 80490aa <strings_not_equal>
8049386: 85 c0 test %eax,%eax
8049388: 75 1d jne 80493a7 <phase_defused+0x81>
804938a: c7 04 24 f8 a2 04 08 movl $0x804a2f8,(%esp) //0x804a2f8中的值:Curses, you've found the secret phase!"
8049391: e8 5a f4 ff ff call 80487f0 <puts@plt>
8049396: c7 04 24 20 a3 04 08 movl $0x804a320,(%esp) //But finding it and solving it are quite different...
804939d: e8 4e f4 ff ff call 80487f0 <puts@plt>
80493a2: e8 ea fb ff ff call 8048f91 <secret_phase> //调用secret_phase函数
80493a7: c7 04 24 58 a3 04 08 movl $0x804a358,(%esp) //Congratulations! You've defused the bomb!
80493ae: e8 3d f4 ff ff call 80487f0 <puts@plt>
80493b3: 8b 44 24 7c mov 0x7c(%esp),%eax
80493b7: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
80493be: 74 05 je 80493c5 <phase_defused+0x9f>
80493c0: e8 fb f3 ff ff call 80487c0 <__stack_chk_fail@plt>
80493c5: 81 c4 8c 00 00 00 add $0x8c,%esp
80493cb: c3 ret
80493cc: 66 90 xchg %ax,%ax
80493ce: 66 90 xchg %ax,%ax
程序分析:
- 8049338: 83 3d c8 c3 04 08 06 cmpl $0x6,0x804c3c8 函数首先判断0x804c3c8中的值是不是6,如果不是6的话则跳转,不会进入隐藏关卡。我们可以在0x8049338处设置断点(b *0x8049338),每次查看0x804c3c8中的值(p *(int *)0x804c3c8)。查看值的时候发现,每通过一个普通关卡,0x804c3c8中的值加1,例如第三关的密码正确,就会调用phase_defused函数,此时0x804c3c8中的值为3。所以当0x804c3c8中的值为6的时候,才有可能进入隐藏关卡。
- 当通过了前6关以后,0x804c3c8中的值为6,此时函数不会跳转,会继续执行。
- 8049359: c7 44 24 04 29 a4 04 movl $0x804a429,0x4(%esp) ,输出0x804a429中的内容,发现是%d %d %s。
- 8049361: c7 04 24 d0 c4 04 08 movl $0x804c4d0,(%esp),输出0x804c4d0中的内容,发现是14 7,是第四关输入的内容。但是上一步我们分析了输入是%d %d %s,这样的话还缺少一个%s,也就是说,隐藏关卡的调用可能需要在第四关多输入一个字符串。
- 继续分析,我们发现sscanf函数调用完后,对其返回值进行了判断:804936d: 83 f8 03 cmp $0x3,%eax,即判断 输入的参数是不是有3个,如果不是的话,就会跳转,无法继续执行。这也进一步验证了我们上一步的猜测。
- 那,输入的字符串是啥呢?类似第一关,我们在phase_defused中发现了<strings_not_equal>,所以前面应该有个程序直接设定好的字符串,找一找,果然8049372: c7 44 24 04 32 a4 04 movl $0x804a432,0x4(%esp) ,输出0x804a432的内容,发现是DrEvil,这不就是一个字符串么!!
- 如果第六关通过、第四关的输入是14 7 DrEvil,那么我们就能进入隐藏关卡了!!
- 继续分析secret_phase函数吧!!
0x2 分析secret_phase函数
08048f91 <secret_phase>:
8048f91: 53 push %ebx
8048f92: 83 ec 18 sub $0x18,%esp
8048f95: e8 92 02 00 00 call 804922c <read_line>
8048f9a: c7 44 24 08 0a 00 00 movl $0xa,0x8(%esp)
8048fa1: 00
8048fa2: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp)
8048fa9: 00
8048faa: 89 04 24 mov %eax,(%esp)
8048fad: e8 1e f9 ff ff call 80488d0 <strtol@plt>
8048fb2: 89 c3 mov %eax,%ebx
8048fb4: 8d 40 ff lea -0x1(%eax),%eax
8048fb7: 3d e8 03 00 00 cmp $0x3e8,%eax
8048fbc: 76 05 jbe 8048fc3 <secret_phase+0x32>
8048fbe: e8 f2 01 00 00 call 80491b5 <explode_bomb>
8048fc3: 89 5c 24 04 mov %ebx,0x4(%esp) //我们输入的数字
8048fc7: c7 04 24 88 c0 04 08 movl $0x804c088,(%esp) //程序设定的数组
8048fce: e8 6d ff ff ff call 8048f40 <fun7>
8048fd3: 83 f8 01 cmp $0x1,%eax //查看fun7返回值是否等于1,不等于则引爆
8048fd6: 74 05 je 8048fdd <secret_phase+0x4c>
8048fd8: e8 d8 01 00 00 call 80491b5 <explode_bomb>
8048fdd: c7 04 24 14 a2 04 08 movl $0x804a214,(%esp)
8048fe4: e8 07 f8 ff ff call 80487f0 <puts@plt>
8048fe9: e8 38 03 00 00 call 8049326 <phase_defused>
8048fee: 83 c4 18 add $0x18,%esp
8048ff1: 5b pop %ebx
8048ff2: c3 ret
8048ff3: 66 90 xchg %ax,%ax
8048ff5: 66 90 xchg %ax,%ax
8048ff7: 66 90 xchg %ax,%ax
8048ff9: 66 90 xchg %ax,%ax
8048ffb: 66 90 xchg %ax,%ax
8048ffd: 66 90 xchg %ax,%ax
8048fff: 90 nop
程序分析
- 初步分析,应该是需要我们输入一个数字(假设是x),然后判断x是不是小于0x3e9,如果大于的话,就抱歉地爆炸了,如果小于的话,调用函数fun7,函数fun7的参数是一个指针(或者理解为数组的首地址,程序设定好的:0x804c088)和一个数字(输入的x),也就是说fun7的是这样的:fun7(int *a,int x)。
- fun7调用完后,判断fun7的返回值,如果不是1,则爆炸,如果是1,恭喜,你过关了!!!
- 最后,我们来愉快地分析一波fun7吧,开森!!
0x2 分析fun7函数
08048f40 <fun7>:
8048f40: 53 push %ebx
8048f41: 83 ec 18 sub $0x18,%esp
8048f44: 8b 54 24 20 mov 0x20(%esp),%edx
8048f48: 8b 4c 24 24 mov 0x24(%esp),%ecx
8048f4c: 85 d2 test %edx,%edx
8048f4e: 74 37 je 8048f87 <fun7+0x47>
8048f50: 8b 1a mov (%edx),%ebx
8048f52: 39 cb cmp %ecx,%ebx
8048f54: 7e 13 jle 8048f69 <fun7+0x29>
8048f56: 89 4c 24 04 mov %ecx,0x4(%esp)
8048f5a: 8b 42 04 mov 0x4(%edx),%eax
8048f5d: 89 04 24 mov %eax,(%esp)
8048f60: e8 db ff ff ff call 8048f40 <fun7>
8048f65: 01 c0 add %eax,%eax //,2*%eax ,eax中存储的是函数的返回值
8048f67: eb 23 jmp 8048f8c <fun7+0x4c>
8048f69: b8 00 00 00 00 mov $0x0,%eax
8048f6e: 39 cb cmp %ecx,%ebx
8048f70: 74 1a je 8048f8c <fun7+0x4c>
8048f72: 89 4c 24 04 mov %ecx,0x4(%esp)
8048f76: 8b 42 08 mov 0x8(%edx),%eax
8048f79: 89 04 24 mov %eax,(%esp)
8048f7c: e8 bf ff ff ff call 8048f40 <fun7>
8048f81: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax
8048f85: eb 05 jmp 8048f8c <fun7+0x4c>
8048f87: b8 ff ff ff ff mov $0xffffffff,%eax
8048f8c: 83 c4 18 add $0x18,%esp
8048f8f: 5b pop %ebx
8048f90: c3 ret
程序分析:
- 初步分析,这是一个简单的递归函数
- 根据递归函数,写出伪代码,然后进行程序分析
- 你看了可能会有个问题:指针a1里面是啥,里面是啥,里面是啥???a[1]、a[2]里面居然还是个地址???我们来倒推一波吧!
- 分析secret_phase函数,我们知道只有fun7的返回值是1,才能过关。如果fun7的返回值是1,则意味着在第一次递归的时候,我们进入的分支是if(*a1 != a2)的分支,且2 * fun7(a1[2], a2) 的结果是0,即 fun7(a1[2], a2) 是0。同时我们还能知道,第一次递归的时候a2 > *a1.
- fun7(a1[2], a2) 的返回值是0,意味着啥呢?意味着 *a[2] = a2,嘻嘻,所以现在的关键是研究一波第一次递归传入的指针。
- 第一次递归,传入的指针,实际是个地址,的值是0x804c088,查看其附近的值。
- 从上图,得到输入的值应该是32H,即十进制的50,至于为啥,仔细看前几步的分析呀!
- 至此,二进制炸弹拆完了!!!
今天的文章二进制拆弹(20181023-20181026)分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/25550.html