CSAPP实验——逆向工程拆除“二进制炸弹”程序

计算机系统基础的实验课,引用了国外的实验,非常好

一、实验目的:

逆向工程拆除“二进制炸弹”程序

增强对程序机器级表示、汇编语言、调试器和逆向工程等 理解。

一个“Binary Bombs”(二进制炸弹,简称炸弹)是一个 Linux可执行C程序,包含phase1~phase6共6个阶段。

炸弹运行各阶段要求输入一个字符串,若输入符合程序预 期,该阶段炸弹被“拆除”,否则“爆炸”。

 你需要拆除尽可能多的炸弹

二、实验要求

拆弹装备:

 熟练使用gdb调试器和objdump;

 单步跟踪调试每一阶段的机器代码;

 理解汇编语言代码的行为或作用;

 “推断”拆除炸弹所需的目标字符串。

 在各阶段的开始代码前和引爆炸弹函数前设置断点,便于调试。

每个炸弹阶段考察机器级语言程序不同方面,难度递增

 阶段1:字符串比较

 阶段2:循环

 阶段3:条件/分支:含switch语句

 阶段4:递归调用和栈

 阶段5:指针

 阶段6:链表/指针/结构

 隐藏阶段,第4阶段之后附加特定字符串后出现

三、实验内容(所修改函数代码,功能以及重要代码的解释):

1.phase_1
8048b30:	55                   	push   %ebp
 8048b31:	89 e5                	mov    %esp,%ebp
 8048b33:	83 ec 10             	sub    $0x10,%esp
 8048b36:	68 44 a0 04 08       	push   $0x804a044
 8048b3b:	ff 75 08             	pushl  0x8(%ebp)
 8048b3e:	e8 9a 04 00 00       	call   8048fdd <strings_not_equal>
 8048b43:	83 c4 10             	add    $0x10,%esp
 8048b46:	85 c0                	test   %eax,%eax
 8048b48:	74 05                	je     8048b4f <phase_1+0x1f>
 8048b4a:	e8 91 05 00 00       	call   80490e0 <explode_bomb>
 8048b4f:	c9                   	leave  
 8048b50:	c3                   	ret    

观察汇编代码,第6行,可知输入为一个字符串,4 5行压入参数时有两个参数,用gdb参看0x804a044中的内容在这里插入图片描述
可知,call的函数判断我们输入的参数与该字符串是否相等,返回值会存入eax中 ,test指令与,若两字符串相等,返回0,跳过炸弹

第一题答案为“Border relations with Canada have never been better.”

2.phase_2
8048b51:	55                   	push   %ebp
 8048b52:	89 e5                	mov    %esp,%ebp
 8048b54:	53                   	push   %ebx
 8048b55:	83 ec 2c             	sub    $0x2c,%esp//下移11个字节
 8048b58:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048b5e:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048b61:	31 c0                	xor    %eax,%eax
 8048b63:	8d 45 dc             	lea    -0x24(%ebp),%eax
 8048b66:	50                   	push   %eax
 8048b67:	ff 75 08             	pushl  0x8(%ebp)
 8048b6a:	e8 99 05 00 00       	call   8049108 <read_six_numbers>//读入6个输入
 8048b6f:	83 c4 10             	add    $0x10,%esp//栈顶指针下移
 8048b72:	83 7d dc 00          	cmpl   $0x0,-0x24(%ebp)//将-0x24(%ebx)的内容与0比较(将第一个输入与0比较)
 8048b76:	79 05                	jns    8048b7d <phase_2+0x2c>//若数据比0大,跳转
 8048b78:	e8 63 05 00 00       	call   80490e0 <explode_bomb>
 8048b7d:	bb 01 00 00 00       	mov    $0x1,%ebx //令ebx存1
 8048b82:	89 d8                	mov    %ebx,%eax //eax存ebx的内容(存i)
 8048b84:	03 44 9d d8          	add    -0x28(%ebp,%ebx,4),%eax //eax存自己加上ebp移位11个字节后的内容(i+第i-1个输入)
 8048b88:	39 44 9d dc          	cmp    %eax,-0x24(%ebp,%ebx,4) //比较此时eax结果与ebp移位10个字节后的内容(第i个输入与i+i-1输入相等)
 8048b8c:	74 05                	je     8048b93 <phase_2+0x42> //若eax与比较数相等则跳转
 8048b8e:	e8 4d 05 00 00       	call   80490e0 <explode_bomb>
 8048b93:	83 c3 01             	add    $0x1,%ebx //令ebx内容+1
 8048b96:	83 fb 06             	cmp    $0x6,%ebx //比较6与1
 8048b99:	75 e7                	jne    8048b82 <phase_2+0x31> //不相等跳转循环,循环 6次后ebx与6相等
 8048b9b:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048b9e:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048ba5:	74 05                	je     8048bac <phase_2+0x5b>
 8048ba7:	e8 e4 fb ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048bac:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048baf:	c9                   	leave  
 8048bb0:	c3                   	ret    

11行分析知6个输入数字,根据17和24行可判断是一个6次的循环,可知如果要安全通过这6次循环,需要在第19行时满足某个条件,根据上下文分析,条件为第i个数与第i-1个数与i相加进行比较,则我们需要输入的6个数满足这样一个数学公式 a i = a i − 1 + i a_i=a_{i-1}+i ai=ai1+i

又根据第13行确定第一个输入为0 则可退出我们的输入为 0 1 3 6 10 15

3.phase_3
8048bb1:	55                   	push   %ebp
 8048bb2:	89 e5                	mov    %esp,%ebp //使ebp指向新的栈底
 8048bb4:	83 ec 18             	sub    $0x18,%esp //分配栈空间,6个字节
 8048bb7:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax//赋值eax
 8048bbd:	89 45 f4             	mov    %eax,-0xc(%ebp)//令第3个字节赋值 
 8048bc0:	31 c0                	xor    %eax,%eax
 8048bc2:	8d 45 f0             	lea    -0x10(%ebp),%eax
 8048bc5:	50                   	push   %eax
 8048bc6:	8d 45 ec             	lea    -0x14(%ebp),%eax
 8048bc9:	50                   	push   %eax
 8048bca:	68 2f a2 04 08       	push   $0x804a22f
 8048bcf:	ff 75 08             	pushl  0x8(%ebp)
 8048bd2:	e8 39 fc ff ff       	call   8048810 <__isoc99_sscanf@plt>//eax存返回值 
 8048bd7:	83 c4 10             	add    $0x10,%esp//栈顶上移4字节
 8048bda:	83 f8 01             	cmp    $0x1,%eax//eax参数与1比较
 8048bdd:	7f 05                	jg     8048be4 <phase_3+0x33>//eax大于1跳
 8048bdf:	e8 fc 04 00 00       	call   80490e0 <explode_bomb>
 8048be4:	83 7d ec 07          	cmpl   $0x7,-0x14(%ebp)//比较参数与7
 8048be8:	77 3b                	ja     8048c25 <phase_3+0x74>//参数比7大时跳到爆炸(要比7小)
 8048bea:	8b 45 ec             	mov    -0x14(%ebp),%eax//eax赋值参数 
 8048bed:	ff 24 85 c0 a0 04 08 	jmp    *0x804a0c0(,%eax,4)//从0x804a0c0取eax个字节的数然后加上当前的地址跳转,比1大,输2即可,输2跳到bfb
 8048bf4:	b8 92 01 00 00       	mov    $0x192,%eax //eax赋值0x192=402
 8048bf9:	eb 3b                	jmp    8048c36 <phase_3+0x85>//跳
 8048bfb:	b8 ff 02 00 00       	mov    $0x2ff,%eax//eax=767
 8048c00:	eb 34                	jmp    8048c36 <phase_3+0x85>//跳
 8048c02:	b8 9b 00 00 00       	mov    $0x9b,%eax
 8048c07:	eb 2d                	jmp    8048c36 <phase_3+0x85>//跳
 8048c09:	b8 c7 02 00 00       	mov    $0x2c7,%eax
 8048c0e:	eb 26                	jmp    8048c36 <phase_3+0x85>//跳 
 8048c10:	b8 f9 00 00 00       	mov    $0xf9,%eax
 8048c15:	eb 1f                	jmp    8048c36 <phase_3+0x85>//跳
 8048c17:	b8 41 02 00 00       	mov    $0x241,%eax
 8048c1c:	eb 18                	jmp    8048c36 <phase_3+0x85>//跳
 8048c1e:	b8 0c 02 00 00       	mov    $0x20c,%eax
 8048c23:	eb 11                	jmp    8048c36 <phase_3+0x85>//跳
 8048c25:	e8 b6 04 00 00       	call   80490e0 <explode_bomb>
 8048c2a:	b8 00 00 00 00       	mov    $0x0,%eax
 8048c2f:	eb 05                	jmp    8048c36 <phase_3+0x85>
 8048c31:	b8 5a 03 00 00       	mov    $0x35a,%eax
 8048c36:	3b 45 f0             	cmp    -0x10(%ebp),%eax //比较eax和第 4个参数
 8048c39:	74 05                	je     8048c40 <phase_3+0x8f> //比较是否相等,相等跳转通关
 8048c3b:	e8 a0 04 00 00       	call   80490e0 <explode_bomb>
 8048c40:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048c43:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048c4a:	74 05                	je     8048c51 <phase_3+0xa0>
 8048c4c:	e8 3f fb ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048c51:	c9                   	leave  
 8048c52:	c3                   	ret    

第三题是条件分支判断,先看第11行,压入了一个地址,我们看看这个地址对应的东西是什么
在这里插入图片描述

结合第13行可推测该程序利用scanf读取两个整数输入,然后根据第19行可知输入的第一个参数要比7小,然后根据21行可知,程序回跳到下面的某个指令,具体跳到那条与eax寄存器的值有关,也就是我们输入的第一个参数,观察发现下方指令都有

jmp 8048c36 <phase_3+0x85>//跳

可推测这是一个带break的switch语句的汇编,跳到下方后会比较eax与我们第二个参数的大小,相等则进入下一关,这里的答案可以有很多组,我将第一个参数设为2,参看跳到第24行给eax寄存器赋的值,发现为767

答案为 2 767 成功

4.phase_4
8048c9e:	55                   	push   %ebp
 8048c9f:	89 e5                	mov    %esp,%ebp
 8048ca1:	83 ec 18             	sub    $0x18,%esp //创建新栈区
 8048ca4:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048caa:	89 45 f4             	mov    %eax,-0xc(%ebp)//把eax内容赋给第三个参数
 8048cad:	31 c0                	xor    %eax,%eax//异或,eax赋值0
 8048caf:	8d 45 ec             	lea    -0x14(%ebp),%eax //参数2
 8048cb2:	50                   	push   %eax //压入eax到栈中
 8048cb3:	8d 45 f0             	lea    -0x10(%ebp),%eax //参数 1
 8048cb6:	50                   	push   %eax //压入新的eax值
 8048cb7:	68 2f a2 04 08       	push   $0x804a22f //压入该个地址,输入两个数 
 8048cbc:	ff 75 08             	pushl  0x8(%ebp) //把ebp向上数第二个字节
 8048cbf:	e8 4c fb ff ff       	call   8048810 <__isoc99_sscanf@plt> //调用函数,eax存返回值
 8048cc4:	83 c4 10             	add    $0x10,%esp //栈顶下移4个字节
 8048cc7:	83 f8 02             	cmp    $0x2,%eax //比较2
 8048cca:	75 0b                	jne    8048cd7 <phase_4+0x39> //不等于2爆炸
 8048ccc:	8b 45 ec             	mov    -0x14(%ebp),%eax//赋值 (密钥)参数2
 8048ccf:	83 e8 02             	sub    $0x2,%eax//eax减二
 8048cd2:	83 f8 02             	cmp    $0x2,%eax//比较2与eax,
 8048cd5:	76 05                	jbe    8048cdc <phase_4+0x3e>//eax-2小于等于2跳到下方
 8048cd7:	e8 04 04 00 00       	call   80490e0 <explode_bomb>//否则爆炸
 8048cdc:	83 ec 08             	sub    $0x8,%esp //栈顶下移14
 8048cdf:	ff 75 ec             	pushl  -0x14(%ebp) //压入参数1
 8048ce2:	6a 07                	push   $0x7 //压栈立即数 
 8048ce4:	e8 6a ff ff ff       	call   8048c53 <func4> //跳,进入递归函数
 8048ce9:	83 c4 10             	add    $0x10,%esp//栈顶上移
 8048cec:	3b 45 f0             	cmp    -0x10(%ebp),%eax//比较参数1与eax
 8048cef:	74 05                	je     8048cf6 <phase_4+0x58>//eax相等,进入下一关 
 8048cf1:	e8 ea 03 00 00       	call   80490e0 <explode_bomb>//爆炸
 8048cf6:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048cf9:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048d00:	74 05                	je     8048d07 <phase_4+0x69>
 8048d02:	e8 89 fa ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048d07:	c9                   	leave  
 8048d08:	c3                   	ret    

第4题是递归,首先发现第11行是这个地址,跳过去看看是什么东西

在这里插入图片描述
发现和上一个题一模一样,同样是输入两个整数,观察17到20行 可知要求我们输入的第二个参数-2之后的值要小于等于2才行,有根据第21行是有符号数的比较,可知输入可为2 3 4

第25行是一个函数,顺利通关的条件是我们输入的参数1与这个函数的返回值相同,我们看看第25行的函数是啥

08048c53 <func4>: //递归函数 
 8048c53:	55                   	push   %ebp
 8048c54:	89 e5                	mov    %esp,%ebp//存上一个栈顶
 8048c56:	57                   	push   %edi//寄存器d
 8048c57:	56                   	push   %esi//寄存器s
 8048c58:	53                   	push   %ebx//寄存器b
 8048c59:	83 ec 0c             	sub    $0xc,%esp //移动栈顶3字节
 8048c5c:	8b 5d 08             	mov    0x8(%ebp),%ebx //b= 7
 8048c5f:	8b 7d 0c             	mov    0xc(%ebp),%edi //d=参数2
 8048c62:	85 db                	test   %ebx,%ebx //比较
 8048c64:	7e 2b                	jle    8048c91 <func4+0x3e> //参数 1小于等于0跳转
 8048c66:	89 f8                	mov    %edi,%eax //eax=参数2
 8048c68:	83 fb 01             	cmp    $0x1,%ebx //比较1和参数1
 8048c6b:	74 29                	je     8048c96 <func4+0x43> //相等跳转
 8048c6d:	83 ec 08             	sub    $0x8,%esp //栈顶减2个字节
 8048c70:	57                   	push   %edi //入栈
 8048c71:	8d 43 ff             	lea    -0x1(%ebx),%eax //取地址
 8048c74:	50                   	push   %eax
 8048c75:	e8 d9 ff ff ff       	call   8048c53 <func4> //递归
 8048c7a:	83 c4 08             	add    $0x8,%esp //上移2字节 
 8048c7d:	8d 34 07             	lea    (%edi,%eax,1),%esi 
 8048c80:	57                   	push   %edi
 8048c81:	83 eb 02             	sub    $0x2,%ebx
 8048c84:	53                   	push   %ebx
 8048c85:	e8 c9 ff ff ff       	call   8048c53 <func4>
 8048c8a:	83 c4 10             	add    $0x10,%esp
 8048c8d:	01 f0                	add    %esi,%eax
 8048c8f:	eb 05                	jmp    8048c96 <func4+0x43>
 8048c91:	b8 00 00 00 00       	mov    $0x0,%eax
 8048c96:	8d 65 f4             	lea    -0xc(%ebp),%esp
 8048c99:	5b                   	pop    %ebx
 8048c9a:	5e                   	pop    %esi
 8048c9b:	5f                   	pop    %edi
 8048c9c:	5d                   	pop    %ebp
 8048c9d:	c3                   	ret    

发现是一个递归函数,这个递归函数自己看是在是太难受了,试着将这个递归函数用C语言写出来,人工翻译后的结果如下

int func4(int x,int y)
{
    int b=x;
    int d=y;
    if(b<=0)
    {
        return 0;
    }
    else
    {
        int a=d;
        if(b==1) {
            return a;
        }
        else
        {
            a=b-1;
            int k=func4(a,d);
            int s=d+k;
            b=b-2;
            int m=func4(b,d);
            m+=s;
            return m;
        }
        }
    }

我们由fun4的汇编指令的第8行和第4关汇编的第24行可知输入参数中参数1固定为7,于是向我们的C语言代码中输入7,4得到结果为132在这里插入图片描述
答案不止一组,对应第2个参数为2 3 4均有一组答案,我们这里输入132 4 成功通关

5.phase_5的求解
08048d09 <phase_5>:
 8048d09:	55                   	push   %ebp 
 8048d0a:	89 e5                	mov    %esp,%ebp
 8048d0c:	83 ec 18             	sub    $0x18,%esp
 8048d0f:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048d15:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048d18:	31 c0                	xor    %eax,%eax
 8048d1a:	8d 45 f0             	lea    -0x10(%ebp),%eax //参数2
 8048d1d:	50                   	push   %eax
 8048d1e:	8d 45 ec             	lea    -0x14(%ebp),%eax //参数1
 8048d21:	50                   	push   %eax
 8048d22:	68 2f a2 04 08       	push   $0x804a22f//输入两个整数
 8048d27:	ff 75 08             	pushl  0x8(%ebp)
 8048d2a:	e8 e1 fa ff ff       	call   8048810 <__isoc99_sscanf@plt>
 8048d2f:	83 c4 10             	add    $0x10,%esp
 8048d32:	83 f8 01             	cmp    $0x1,%eax //比较1与eax
 8048d35:	7f 05                	jg     8048d3c <phase_5+0x33> //eax比1大跳转
 8048d37:	e8 a4 03 00 00       	call   80490e0 <explode_bomb>//否则爆炸
 8048d3c:	8b 45 ec             	mov    -0x14(%ebp),%eax //赋值参数1
 8048d3f:	83 e0 0f             	and    $0xf,%eax//低四位与
 8048d42:	89 45 ec             	mov    %eax,-0x14(%ebp) //参数2改变
 8048d45:	83 f8 0f             	cmp    $0xf,%eax //比较eax与15
 8048d48:	74 2c                	je     8048d76 <phase_5+0x6d>//相等爆炸
 8048d4a:	b9 00 00 00 00       	mov    $0x0,%ecx//c=0
 8048d4f:	ba 00 00 00 00       	mov    $0x0,%edx//d=0
 8048d54:	83 c2 01             	add    $0x1,%edx //d+1
 8048d57:	8b 04 85 e0 a0 04 08 	mov    0x804a0e0(,%eax,4),%eax//第一次为5
 8048d5e:	01 c1                	add    %eax,%ecx //c+=a
 8048d60:	83 f8 0f             	cmp    $0xf,%eax //比较15与eax
 8048d63:	75 ef                	jne    8048d54 <phase_5+0x4b>//不相等跳回
 8048d65:	c7 45 ec 0f 00 00 00 	movl   $0xf,-0x14(%ebp) //参数2改成15
 8048d6c:	83 fa 0f             	cmp    $0xf,%edx//比较d与15
 8048d6f:	75 05                	jne    8048d76 <phase_5+0x6d>//不相等爆炸
 8048d71:	3b 4d f0             	cmp    -0x10(%ebp),%ecx//参数1与c
 8048d74:	74 05                	je     8048d7b <phase_5+0x72>相等通过
 8048d76:	e8 65 03 00 00       	call   80490e0 <explode_bomb>
 8048d7b:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048d7e:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048d85:	74 05                	je     8048d8c <phase_5+0x83>
 8048d87:	e8 04 fa ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048d8c:	c9                   	leave  
 8048d8d:	c3                   	ret    

发现第12行有个地址,去看看

在这里插入图片描述

发现还是输入两个整数。观察27到33行的代码这部分代码是我们通关的核心代码

跳出循环的条件是eax寄存器中的值为为15,而跳出之后我们观察32行33行,要求条件为edx中的值为15,edx从0开始每次循环+1,所以总结下来就是,经过15次循环之后,eax的值若恰好为15,则能够安全跳出,而eax中的值是我们输入的参数1,ecx存放的是每次循环时eax数的加和。观察第30行,可知eax每次在这地方发生值的变化,查看一下其中内容
根据对应的跳转规律我们画出循环的表
在这里插入图片描述
发现从5开始,跳15次,恰能跳到15 ecx中累加的数为115

则答案为 5 115顺利通过

6.phase_6
08048d8e <phase_6>:
 8048d8e:	55                   	push   %ebp
 8048d8f:	89 e5                	mov    %esp,%ebp
 8048d91:	56                   	push   %esi
 8048d92:	53                   	push   %ebx
 8048d93:	83 ec 48             	sub    $0x48,%esp //
 8048d96:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax //
 8048d9c:	89 45 f4             	mov    %eax,-0xc(%ebp) 
 8048d9f:	31 c0                	xor    %eax,%eax
 8048da1:	8d 45 c4             	lea    -0x3c(%ebp),%eax
 8048da4:	50                   	push   %eax //参数入栈 
 8048da5:	ff 75 08             	pushl  0x8(%ebp)//
 8048da8:	e8 5b 03 00 00       	call   8049108 <read_six_numbers> //读6个数
 8048dad:	83 c4 10             	add    $0x10,%esp //
 8048db0:	be 00 00 00 00       	mov    $0x0,%esi //s=0
 8048db5:	8b 44 b5 c4          	mov    -0x3c(%ebp,%esi,4),%eax //移位,获取某个输入
 8048db9:	83 e8 01             	sub    $0x1,%eax //减1
 8048dbc:	83 f8 05             	cmp    $0x5,%eax //比较5和a
 8048dbf:	76 05                	jbe    8048dc6 <phase_6+0x38> //小于等于5不爆炸
 8048dc1:	e8 1a 03 00 00       	call   80490e0 <explode_bomb>
 8048dc6:	83 c6 01             	add    $0x1,%esi //s+1
 8048dc9:	83 fe 06             	cmp    $0x6,%esi //比较s和6
 8048dcc:	74 1b                	je     8048de9 <phase_6+0x5b> //相等跳
 8048dce:	89 f3                	mov    %esi,%ebx //b=s
 8048dd0:	8b 44 9d c4          	mov    -0x3c(%ebp,%ebx,4),%eax //eax赋值
 8048dd4:	39 44 b5 c0          	cmp    %eax,-0x40(%ebp,%esi,4) //比较上一个参数
 8048dd8:	75 05                	jne    8048ddf <phase_6+0x51> //不相等,否则爆炸
 8048dda:	e8 01 03 00 00       	call   80490e0 <explode_bomb>
 8048ddf:	83 c3 01             	add    $0x1,%ebx //b=b+1
 8048de2:	83 fb 05             	cmp    $0x5,%ebx //比较b和5
 8048de5:	7e e9                	jle    8048dd0 <phase_6+0x42>  //小于等于则跳回
 8048de7:	eb cc                	jmp    8048db5 <phase_6+0x27> //跳回输入部分
 8048de9:	8d 45 c4             	lea    -0x3c(%ebp),%eax //eax赋值参数
 8048dec:	8d 5d dc             	lea    -0x24(%ebp),%ebx //ebx赋值参数
 8048def:	b9 07 00 00 00       	mov    $0x7,%ecx //c=7
 8048df4:	89 ca                	mov    %ecx,%edx //d=7
 8048df6:	2b 10                	sub    (%eax),%edx //d=d-eax
 8048df8:	89 10                	mov    %edx,(%eax) //指针
 8048dfa:	83 c0 04             	add    $0x4,%eax //加4
 8048dfd:	39 c3                	cmp    %eax,%ebx //比较ebx和eax的
 8048dff:	75 f3                	jne    8048df4 <phase_6+0x66> //不相等跳回
 8048e01:	bb 00 00 00 00       	mov    $0x0,%ebx //ebx赋值0
 8048e06:	eb 16                	jmp    8048e1e <phase_6+0x90> //跳转
 8048e08:	8b 52 08             	mov    0x8(%edx),%edx //移位,链表下一个
 8048e0b:	83 c0 01             	add    $0x1,%eax //a=a+1
 8048e0e:	39 c8                	cmp    %ecx,%eax//比较ac
 8048e10:	75 f6                	jne    8048e08 <phase_6+0x7a>//不相等回跳
 8048e12:	89 54 b5 dc          	mov    %edx,-0x24(%ebp,%esi,4)//修改链表中值
 8048e16:	83 c3 01             	add    $0x1,%ebx //b=b+1
 8048e19:	83 fb 06             	cmp    $0x6,%ebx //b与6比较
 8048e1c:	74 17                	je     8048e35 <phase_6+0xa7>//相等跳出循环,6次
 8048e1e:	89 de                	mov    %ebx,%esi//s=b
 8048e20:	8b 4c 9d c4          	mov    -0x3c(%ebp,%ebx,4),%ecx//c=第b个输入参数
 8048e24:	b8 01 00 00 00       	mov    $0x1,%eax//a=1
 8048e29:	ba 3c c1 04 08       	mov    $0x804c13c,%edx//传地址,edx为链表
 8048e2e:	83 f9 01             	cmp    $0x1,%ecx//c与1
 8048e31:	7f d5                	jg     8048e08 <phase_6+0x7a>//大于回跳
 8048e33:	eb dd                	jmp    8048e12 <phase_6+0x84>//回跳
 8048e35:	8b 5d dc             	mov    -0x24(%ebp),%ebx//跳出循环
 8048e38:	8d 45 dc             	lea    -0x24(%ebp),%eax//加载参数
 8048e3b:	8d 75 f0             	lea    -0x10(%ebp),%esi//加载参数
 8048e3e:	89 d9                	mov    %ebx,%ecx//c=b
 8048e40:	8b 50 04             	mov    0x4(%eax),%edx//修改链表起始部分
 8048e43:	89 51 08             	mov    %edx,0x8(%ecx)
 8048e46:	83 c0 04             	add    $0x4,%eax//a=4
 8048e49:	89 d1                	mov    %edx,%ecx//c=d
 8048e4b:	39 c6                	cmp    %eax,%esi//s=i
 8048e4d:	75 f1                	jne    8048e40 <phase_6+0xb2>//不相等跳转 
 8048e4f:	c7 42 08 00 00 00 00 	movl   $0x0,0x8(%edx)
 8048e56:	be 05 00 00 00       	mov    $0x5,%esi//s=5
 8048e5b:	8b 43 08             	mov    0x8(%ebx),%eax//eax赋值
 8048e5e:	8b 00                	mov    (%eax),%eax //地址赋值
 8048e60:	39 03                	cmp    %eax,(%ebx) //地址比较,比较i与i-1个节点
 8048e62:	7d 05                	jge    8048e69 <phase_6+0xdb> //大于等于否则爆炸
 8048e64:	e8 77 02 00 00       	call   80490e0 <explode_bomb>
 8048e69:	8b 5b 08             	mov    0x8(%ebx),%ebx//b移位。移到下一个
 8048e6c:	83 ee 01             	sub    $0x1,%esi //s=s-1
 8048e6f:	75 ea                	jne    8048e5b <phase_6+0xcd> 查看zf是否为1,否则循环
 8048e71:	8b 45 f4             	mov    -0xc(%ebp),%eax //移位
 8048e74:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048e7b:	74 05                	je     8048e82 <phase_6+0xf4> //相等通过
 8048e7d:	e8 0e f9 ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048e82:	8d 65 f8             	lea    -0x8(%ebp),%esp
 8048e85:	5b                   	pop    %ebx
 8048e86:	5e                   	pop    %esi
 8048e87:	5d                   	pop    %ebp
 8048e88:	c3                   	ret    

第6关是关于链表的题目,从第13行可以看出是需要输入6个数,观察到第55行有个地址,我们进去看看
在这里插入图片描述
观察发现对应了一个链表地址,每个链表有3个参数,第一个为当前节点的值,第二个为在链表中的序号,第三个为指向的下一个链表的地址,观察第35行到40行这是对我们输入的6个数进行翻转,用7进行反转,而刚好有6个节点**,可以推测输入的这6个数是对链表的某种操作**,观察59到80行,可知通关条件是链表中节点顺序与储存的值按从大到小的顺序排列。从38行到56行是对链表进行重组,根据我们给出的6个参数。于是将链表画出进行重排
在这里插入图片描述
由于输入的数字会进行反转,所以按从小到大的顺序输入

答案为 1 4 3 2 6 5 顺利通过

7·Secret_phase

在汇编代码中还有一个隐藏关卡,这个隐藏关卡存放在phase_defused()函数中,在 bomb.c程序中,每次通过一关就会调用该方法一次

我们观察phase_defused可以得出

08049245 <phase_defused>:
 8049245:	55                   	push   %ebp
 8049246:	89 e5                	mov    %esp,%ebp
 8049248:	83 ec 68             	sub    $0x68,%esp
 804924b:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8049251:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8049254:	31 c0                	xor    %eax,%eax
 8049256:	83 3d cc c3 04 08 06 	cmpl   $0x6,0x804c3cc
 804925d:	75 6f                	jne    80492ce <phase_defused+0x89>
 804925f:	83 ec 0c             	sub    $0xc,%esp
 8049262:	8d 45 a4             	lea    -0x5c(%ebp),%eax
 8049265:	50                   	push   %eax
 8049266:	8d 45 a0             	lea    -0x60(%ebp),%eax
 8049269:	50                   	push   %eax
 804926a:	8d 45 9c             	lea    -0x64(%ebp),%eax
 804926d:	50                   	push   %eax
 804926e:	68 89 a2 04 08       	push   $0x804a289
 8049273:	68 d0 c4 04 08       	push   $0x804c4d0
 8049278:	e8 93 f5 ff ff       	call   8048810 <__isoc99_sscanf@plt>
 804927d:	83 c4 20             	add    $0x20,%esp
 8049280:	83 f8 03             	cmp    $0x3,%eax
 8049283:	75 39                	jne    80492be <phase_defused+0x79>
 8049285:	83 ec 08             	sub    $0x8,%esp
 8049288:	68 92 a2 04 08       	push   $0x804a292
 804928d:	8d 45 a4             	lea    -0x5c(%ebp),%eax
 8049290:	50                   	push   %eax
 8049291:	e8 47 fd ff ff       	call   8048fdd <strings_not_equal>
 8049296:	83 c4 10             	add    $0x10,%esp
 8049299:	85 c0                	test   %eax,%eax
 804929b:	75 21                	jne    80492be <phase_defused+0x79>
 804929d:	83 ec 0c             	sub    $0xc,%esp
 80492a0:	68 58 a1 04 08       	push   $0x804a158
 80492a5:	e8 16 f5 ff ff       	call   80487c0 <puts@plt>
 80492aa:	c7 04 24 80 a1 04 08 	movl   $0x804a180,(%esp)
 80492b1:	e8 0a f5 ff ff       	call   80487c0 <puts@plt>
 80492b6:	e8 20 fc ff ff       	call   8048edb <secret_phase>
 80492bb:	83 c4 10             	add    $0x10,%esp
 80492be:	83 ec 0c             	sub    $0xc,%esp
 80492c1:	68 b8 a1 04 08       	push   $0x804a1b8
 80492c6:	e8 f5 f4 ff ff       	call   80487c0 <puts@plt>
 80492cb:	83 c4 10             	add    $0x10,%esp
 80492ce:	8b 45 f4             	mov    -0xc(%ebp),%eax
 80492d1:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 80492d8:	74 05                	je     80492df <phase_defused+0x9a>
 80492da:	e8 b1 f4 ff ff       	call   8048790 <__stack_chk_fail@plt>
 80492df:	c9                   	leave  
 80492e0:	c3                   	ret    

观察第8行可知启动隐藏关卡的第一个条件是前面6关都通过了,观察24行到27行,可知另外一个启动条件是输入一个字符串,然后进行比较,我们进到比较地址看看
在这里插入图片描述
找到了隐藏的字符串

我们接着往这个地址前面看看有什么,这样可以确定输入在那个部分
在这里插入图片描述

发现输入字符串前还有俩数字,可知我们第3 4 5 关都有这个部分,因此我们在第四关后加入这个字符串,发现成功进入隐藏关卡

08048edb <secret_phase>:
 8048edb:	55                   	push   %ebp
 8048edc:	89 e5                	mov    %esp,%ebp
 8048ede:	53                   	push   %ebx
 8048edf:	83 ec 04             	sub    $0x4,%esp
 8048ee2:	e8 5b 02 00 00       	call   8049142 <read_line>
 8048ee7:	83 ec 04             	sub    $0x4,%esp
 8048eea:	6a 0a                	push   $0xa //压入a
 8048eec:	6a 00                	push   $0x0 //压入0
 8048eee:	50                   	push   %eax //压入eax
 8048eef:	e8 8c f9 ff ff       	call   8048880 <strtol@plt>//返回eax的string转long值
 8048ef4:	89 c3                	mov    %eax,%ebx //转入eax
 8048ef6:	8d 40 ff             	lea    -0x1(%eax),%eax=eax-1 
 8048ef9:	83 c4 10             	add    $0x10,%esp 
 8048efc:	3d e8 03 00 00       	cmp    $0x3e8,%eax //比较eax与立即数 1000
 8048f01:	76 05                	jbe    8048f08 <secret_phase+0x2d> //小于等于跳转
 8048f03:	e8 d8 01 00 00       	call   80490e0 <explode_bomb>
 8048f08:	83 ec 08             	sub    $0x8,%esp
 8048f0b:	53                   	push   %ebx  //参数2 
 8048f0c:	68 88 c0 04 08       	push   $0x804c088//参数1
 8048f11:	e8 73 ff ff ff       	call   8048e89 <fun7>//进入fun7函数
 8048f16:	83 c4 10             	add    $0x10,%esp
 8048f19:	83 f8 05             	cmp    $0x5,%eax//返回值为5通关
 8048f1c:	74 05                	je     8048f23 <secret_phase+0x48>
 8048f1e:	e8 bd 01 00 00       	call   80490e0 <explode_bomb>
 8048f23:	83 ec 0c             	sub    $0xc,%esp
 8048f26:	68 7c a0 04 08       	push   $0x804a07c//
 8048f2b:	e8 90 f8 ff ff       	call   80487c0 <puts@plt>
 8048f30:	e8 10 03 00 00       	call   8049245 <phase_defused>
 8048f35:	83 c4 10             	add    $0x10,%esp
 8048f38:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048f3b:	c9                   	leave  
 8048f3c:	c3                   	ret    

观察隐藏关卡,在第21行进入fun7,当返回值为5时能够通关,我们来进入fun7看看

08048e89 <fun7>:
 8048e89:	55                   	push   %ebp
 8048e8a:	89 e5                	mov    %esp,%ebp
 8048e8c:	53                   	push   %ebx
 8048e8d:	83 ec 04             	sub    $0x4,%esp
 8048e90:	8b 55 08             	mov    0x8(%ebp),%edx//获取输入参数1 当前树的节点
 8048e93:	8b 4d 0c             	mov    0xc(%ebp),%ecx//获取输入参数2
 8048e96:	85 d2                	test   %edx,%edx  //edx是否为0
 8048e98:	74 37                	je     8048ed1 <fun7+0x48>//为0跳转
 8048e9a:	8b 1a                	mov    (%edx),%ebx //ebx赋值参数1
 8048e9c:	39 cb                	cmp    %ecx,%ebx //比较参数2和参数1
 8048e9e:	7e 13                	jle    8048eb3 <fun7+0x2a> //根节点小于等于参数2跳转 
 8048ea0:	83 ec 08             	sub    $0x8,%esp //
 8048ea3:	51                   	push   %ecx //参数2入栈 
 8048ea4:	ff 72 04             	pushl  0x4(%edx) //树节点的左子树入栈
 8048ea7:	e8 dd ff ff ff       	call   8048e89 <fun7> //递归
 8048eac:	83 c4 10             	add    $0x10,%esp 
 8048eaf:	01 c0                	add    %eax,%eax //eax*2
 8048eb1:	eb 23                	jmp    8048ed6 <fun7+0x4d> //返回eax
 8048eb3:	b8 00 00 00 00       	mov    $0x0,%eax //a=0 
 8048eb8:	39 cb                	cmp    %ecx,%ebx //比较根节点与参数2
 8048eba:	74 1a                	je     8048ed6 <fun7+0x4d>//相等跳转,返回0
 8048ebc:	83 ec 08             	sub    $0x8,%esp//
 8048ebf:	51                   	push   %ecx //压入第二个参数
 8048ec0:	ff 72 08             	pushl  0x8(%edx) //节点的右子树
 8048ec3:	e8 c1 ff ff ff       	call   8048e89 <fun7>//递归调用
 8048ec8:	83 c4 10             	add    $0x10,%esp
 8048ecb:	8d 44 00 01          	lea    0x1(%eax,%eax,1),%eax//eax=a+a+1
 8048ecf:	eb 05                	jmp    8048ed6 <fun7+0x4d>
 8048ed1:	b8 ff ff ff ff       	mov    $0xffffffff,%eax//返回值为0
 8048ed6:	8b 5d fc             	mov    -0x4(%ebp),%ebx 
 8048ed9:	c9                   	leave  
 8048eda:	c3                   	ret    

哇塞,好复杂的递归,我试着将他换成c语言写出来,但是发现不行,在调用fun7前,也就是在隐藏关时传入的参数是一个地址,而递归的内容全部基于该地址操作,我们看看这个地址里面存了什么东西

有点看不懂,后面同学告诉我这关是个树的结构,准确一点说这棵树是个二叉树,一个位置存了树节点的值,第二个位置存了左子树的地址,第三个位置存了右子树的位置,画出图来

在这里插入图片描述
递归看不懂呀,直接暴力破解,把树节点一个个输进去,最后试出来是47

四、实验截图

答案

在这里插入图片描述

通关截图
在这里插入图片描述

五、实验总结

太强啦,我的同学都把lab3做了,最近没啥时间,就不做lab3了,应该问题不大(手动狗头)

  • 12
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值