http://csapp.cs.cmu.edu/public/labs.html 下了个32位的bomb文件。。
使用
-objdump -d bomb
得到反汇编的代码
= =一大坨的完全被吓到了有木有
然后看到有main函数还有6个phase,于是抓住重点开始研究
call <phase_x> 之后如果ret出来再调用<phase_defused>就表示拆蛋。。!弹成功了
不然就会爆炸。
所以我们深入到每个phase_x里面研究。
先来看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 8048b31: 50 push %eax 8048b32: e8 f9 04 00 00 call 8049030 <strings_not_equal> ;这个函数改变了%eax的值 8048b37: 83 c4 10 add $0x10,%esp 8048b3a: 85 c0 test %eax,%eax 8048b3c: 74 05 je 8048b43 <phase_1+0x23> ;如果eax不为0,那么字符串正确 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>函数,之后再判断寄存器%eax是否为0,如果为0的话,phase_1正常ret,不然就调用explode_bomb引爆炸弹
所以关键就是strings_not_equal函数,部分代码见下面:
08049030 <strings_not_equal>: 8049030: 55 push %ebp 8049031: 89 e5 mov %esp,%ebp 8049033: 83 ec 0c sub $0xc,%esp 8049036: 57 push %edi 8049037: 56 push %esi 8049038: 53 push %ebx 8049039: 8b 75 08 mov 0x8(%ebp),%esi 804903c: 8b 7d 0c mov 0xc(%ebp),%edi 804903f: 83 c4 f4 add $0xfffffff4,%esp
8049039: 8b 75 08 mov 0x8(%ebp),%esi 804903c: 8b 7d 0c mov 0xc(%ebp),%edi
这两行可以看出strings_not_equal有两个参数,那么是哪两个呢?我们回到phase_1里面,看到再调用之前的两个进栈命令
进栈的分别是%eax和$0x80497c0,猜测比较两个字符串的函数的参数必然是两个指向char类型的指针,所以继续猜想eax是我们输入的字符串,而0x80497c0就是系统给定的字符串了,然后从整个文件里搜0x80497c0.发现什么都没有= =怎么办好zhaoji!!!。。。。
不过我们好像忘了什么东西!!gdb!!以前调试C的时候用过,发现不方便就再也不用了!没想到它在逆向工程的时候这么逆天!
好了我们 gdb comb
设断点:b *0x8048b2c
查看字符串内容:p (char*) 0x80497c0
出现了!
“Public speaking is very easy.” ,nice 第一关pass
接着看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 ;phase_2的参数 8048b53: 83 c4 f8 add $0xfffffff8,%esp 8048b56: 8d 45 e8 lea -0x18(%ebp),%eax 8048b59: 50 push %eax ; 8048b5a: 52 push %edx ;read_six_numbers的参数 8048b5b: e8 78 04 00 00 call 8048fd8 <read_six_numbers> 8048b60: 83 c4 10 add $0x10,%esp 8048b63: 83 7d e8 01 cmpl $0x1,-0x18(%ebp) ;第一个数get 1 8048b67: 74 05 je 8048b6e <phase_2+0x26> 8048b69: e8 8e 09 00 00 call 80494fc <explode_bomb> 8048b6e: bb 01 00 00 00 mov $0x1,%ebx ebx为1 8048b73: 8d 75 e8 lea -0x18(%ebp),%esi esi为第一个数的地址 8048b76: 8d 43 01 lea 0x1(%ebx),%eax eax为2,eax为3 8048b79: 0f af 44 9e fc imul -0x4(%esi,%ebx,4),%eax 第一个数*eax=2,第二个数*eax=6 8048b7e: 39 04 9e cmp %eax,(%esi,%ebx,4) ;第二个数get 2,get 6,阶乘233333 8048b81: 74 05 je 8048b88 <phase_2+0x40>
8048b83: e8 74 09 00 00 call 80494fc <explode_bomb> 8048b88: 43 inc %ebx ebx为2 8048b89: 83 fb 05 cmp $0x5,%ebx 2<5 8048b8c: 7e e8 jle 8048b76 <phase_2+0x2e> 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
好了我们来进行分析:
8048b56: 8d 45 e8 lea -0x18(%ebp),%eax
注意这一行,这是给局部变量留出来的空间0x18=十进制24阿,正好6个int型整数。
所以read_six_number我们猜测是一个有6个整型元素的数组,并读取6个整数到给定位置中的函数
之后我们要进行的就是让程序的整个流程都不接触到explode_bomb函数
分析每个与cmp有关的函数
第一步观察
8048b63: 83 7d e8 01 cmpl $0x1,-0x18(%ebp)
比较1和a[0],我们假定数组名为a,相等可以跳过explode_bomb这一条指令,因此可以确定a[0],继续向下看
很显然这是一个以ebx为迭代量的循环过程
依次判断前一个值*当前迭代下标是否等于后一个值,当迭代量ebx>5的时候就退出循环
否则的话比较A[i]*(ebx+1)是否等于A[i+1],当然ebx是从1开始的
由A[0]=1 推理得到A[6]={1,2,6,24,120,720}
这个 数组就是我们的答案,
做到这里应该已经掌握基本的拆弹套路了,继续看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 8048ba2: 83 c4 f4 add $0xfffffff4,%esp 8048ba5: 8d 45 fc lea -0x4(%ebp),%eax 一个int 8048ba8: 50 push %eax 8048ba9: 8d 45 fb lea -0x5(%ebp),%eax 一个char 8048bac: 50 push %eax 8048bad: 8d 45 f4 lea -0xc(%ebp),%eax 一个int,3个局部变量作为参数 8048bb0: 50 push %eax 8048bb1: 68 de 97 04 08 push $0x80497de 8048bb6: 52 push %edx 8048bb7: e8 a4 fc ff ff call 8048860 <sscanf@plt> 8048bbc: 83 c4 20 add $0x20,%esp 8048bbf: 83 f8 02 cmp $0x2,%eax >2 eax一直为3 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) <=7 第一个数,无符号<=7,只能0,1,2,3,4,5,6,7其中一个 8048bcd: 0f 87 b5 00 00 00 ja 8048c88 <phase_3+0xf0> 8048bd3: 8b 45 f4 mov -0xc(%ebp),%eax 第一个数给eax 8048bd6: ff 24 85 e8 97 04 08 jmp *0x80497e8(,%eax,4) 这个跳到哪里阿。。。0x8048be0+4*eax 8048bdd: 8d 76 00 lea 0x0(%esi),%esi 8048be0: b3 71 mov $0x71,%bl 跳到这个,第一个数get 0, 8048be2: 81 7d fc 09 03 00 00 cmpl $0x309,-0x4(%ebp) 第三个数get 0x309= 777 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 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 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 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 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 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 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 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 第二个数get = 0x71 = 113 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
观察0x80497de位置的字符串,发现是这样的"%d %c %d",猜测我们的答案肯定是一个整数一个char类型,接着一个整数
分析发现这段代码会根据%eax,即第一个参数的值而跳转到不同的地方,是不是和switch语句很相似呢?
不过这道题好像有多解。。我取了第一个数为0,然后按代码跳转依次得到第二个参数和第三个参数的值。。
为此在断网的情况查看ascii码我居然顺手拿起了谭浩强的C语言。。。
第三题答案
0 q 777
phase_4
08048ce0 <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 8048ce9: 83 c4 fc add $0xfffffffc,%esp 8048cec: 8d 45 fc lea -0x4(%ebp),%eax 8048cef: 50 push %eax 8048cf0: 68 08 98 04 08 push $0x8049808 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 eax要等于1 8048d01: 75 06 jne 8048d09 <phase_4+0x29> 8048d03: 83 7d fc 00 cmpl $0x0,-0x4(%ebp) 第一个数大于0 8048d07: 7f 05 jg 8048d0e <phase_4+0x2e> 8048d09: e8 ee 07 00 00 call 80494fc <explode_bomb> 8048d0e: 83 c4 f4 add $0xfffffff4,%esp 8048d11: 8b 45 fc mov -0x4(%ebp),%eax 第一个数给eax 8048d14: 50 push %eax eax进栈 8048d15: e8 86 ff ff ff call 8048ca0 <func4> 8048d1a: 83 c4 10 add $0x10,%esp 8048d1d: 83 f8 37 cmp $0x37,%eax f(9)=0x37 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 8048d2a: c3 ret 8048d2b: 90 nop
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 第一个数给ebx 8048cab: 83 fb 01 cmp $0x1,%ebx ebx<=1 返回 8048cae: 7e 20 jle 8048cd0 <func4+0x30> 8048cb0: 83 c4 f4 add $0xfffffff4,%esp 8048cb3: 8d 43 ff lea -0x1(%ebx),%eax eax=ebx-1 8048cb6: 50 push %eax 8048cb7: e8 e4 ff ff ff call 8048ca0 <func4> 8048cbc: 89 c6 mov %eax,%esi 8048cbe: 83 c4 f4 add $0xfffffff4,%esp 8048cc1: 8d 43 fe lea -0x2(%ebx),%eax 8048cc4: 50 push %eax 8048cc5: e8 d6 ff ff ff call 8048ca0 <func4> 8048cca: 01 f0 add %esi,%eax f(n-1)+f(n-2) 8048ccc: eb 07 jmp 8048cd5 <func4+0x35> 8048cce: 89 f6 mov %esi,%esi 8048cd0: b8 01 00 00 00 mov $0x1,%eax f(0)=f(1)=1; 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,%esi
第四个炸弹的关键就是这个func4函数,发现函数中有两次调用了自身,显然是一个递归的过程,每次递归都会把寄存器%eax的值多减少一个1然后push作为参数,返回的时候将返回值存储在%eax中再将两次调用的合相加,因此得到递归公式f(n)=f(n-1)+f(n-2)
最后求f(多少)=0x37,慢慢推到 多少=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 8048d37: 83 c4 f4 add $0xfffffff4,%esp 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 <phase_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 "isrveawhobpnutfg\260\001" 8048d57: 8a 04 1a mov (%edx,%ebx,1),%al ebx是字符数组的首地址,取每个字符char c=a[i] 8048d5a: 24 0f and $0xf,%al c=c&0xf 8048d5c: 0f be c0 movsbl %al,%eax 8048d5f: 8a 04 30 mov (%eax,%esi,1),%al pp[c]=b[a[0]&0xf] *pp="giants" 8048d62: 88 04 0a mov %al,(%edx,%ecx,1) 8048d65: 42 inc %edx 8048d66: 83 fa 05 cmp $0x5,%edx for(i=0;i<=5;i++) 8048d69: 7e ec jle 8048d57 <phase_5+0x2b> 8048d6b: c6 45 fe 00 movb $0x0,-0x2(%ebp) 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> 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
<string_length>规定了字符串长度为6
又出现了strings_not_equal..所以我们找进栈的是什么,发现了一个固定的值0x804980b
p (char *) 0x804980b 之后发现是"giants"字符串
上面还有一个0x804b220, "isrveawhobpnutfg\260\001" ,一开始还以为搞错了。弄下去后发现这个字符串非常有用有木有!
又看到了一个以%edx为迭代量的循环,依旧假设我们的字符串为a[i]的形式,迭代中%cl保存了我们输入的字符串中的一个char类型,分析下看到我们的a[i]&0xf变成了那个奇怪的字符串的下标。。。。这个下标形成的新的字符串再去和“giants”比较,相同才能过关,然后按顺序在那个奇怪的字符串中找对应的g,i,a,n,t,s这几个字母。得到以下等式
a[0]&0xf=15 a[1]&0xf=0 a[2]&0xf=5 a[3]&0xf=11 a[4]&0xf=13 a[5]&0xf=1,然后a[0-5]就查ascii表随便取吧
我取了O@EKMA这6个字符。
继续继续
phase_6以及secret_phase
去你妹的!!一个早上+一个下午楞是没看懂 不看了!!