计算机组成原理 / 反汇编实验(2)拆弹实验

目录

    • 实验概述
      • 2.2 实验内容及过程
        • 2.2.1 阶段1 字符串比较
        • 2.2.2 阶段2 循环
        • 2.2.3 阶段3 条件/分支
        • 2.2.4 阶段4 递归调用和栈
        • 2.2.5 阶段5 指针
        • 2.2.6 阶段6 链表/指针/结构
        • 2.2.7 阶段7 隐藏阶段

实验概述

  • 实验目的:增强对程序的机器级表示、汇编语言、调试器和逆向工程等方面原理与技能的掌握。
  • 实验目标:需要拆除尽可能多的炸弹。
  • 实验要求:使用gdb调试器和objdump来反汇编炸弹的可执行文件,并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,进而设法“推断”出拆除炸弹所需的目标字符串。
  • 实验语言:c。
  • 实验环境:linux(root用户名为vicai)

2.2 实验内容及过程

一个“binary bombs”(二进制炸弹,下文将简称为炸弹)是一个Linux可执行C程序,包含了6个阶段(phase1~phase6)。炸弹运行的每个阶段要求你输入一个特定的字符串,若你的输入符合程序预期的输入,该阶段的炸弹就被“拆除”,否则炸弹“爆炸”并打印输出 "BOOM!!!"字样。实验的目标是拆除尽可能多的炸弹层次。

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

  • 阶段1:字符串比较
  • 阶段2:循环
  • 阶段3:条件/分支
  • 阶段4:递归调用和栈
  • 阶段5:指针
  • 阶段6:链表/指针/结构
  • 另外还有一个隐藏阶段,但只有当你在第4阶段的解之后附加一特定字符串后才会出现。
  • 为了完成二进制炸弹拆除任务,你需要使用gdb调试器和objdump来反汇编炸弹的可执行文件,并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,进而设法“推断”出拆除炸弹所需的目标字符串。这可能需要你在每一阶段的开始代码前和引爆炸弹的函数前设置断点,以便于调试。

通过gdb调试bomb程序:
计算机组成原理 / 反汇编实验(2)拆弹实验_第1张图片
通过执行以下指令,可以反汇编bomb,并将结果保存到bomb.asm文件中,然后通过gedit或more可以方便地察看汇编代码:
在这里插入图片描述
计算机组成原理 / 反汇编实验(2)拆弹实验_第2张图片

2.2.1 阶段1 字符串比较

  1. 任务描述:通过phase_1的反汇编代码找出要输入的字符串;
  2. 实验设计:利用gdb结合断点来动态地分析;
  3. 实验过程:
    观察phase_1的反汇编代码,如图2.1.1所示:
    计算机组成原理 / 反汇编实验(2)拆弹实验_第3张图片
    通过分析,发现在调用strings_not_equal对比字符串之前,有两个压栈的指令,其中一个是将函数入参送入栈中,还有一个地址送入了栈,函数入参应该就是输入的字符串所在地址,猜测另一个地址就是正确字符串的首址,于是在phase_1处下个断点,然后运行,随意输入一个字符,触发断点,再用x命令查看字符串。如图2.1.2所示:
    计算机组成原理 / 反汇编实验(2)拆弹实验_第4张图片
    故猜测“Public speaking is very easy.”就是所需字符串。重新执行该程序,直接输入该字符串,观察结果。如图2.1.3所示:
    计算机组成原理 / 反汇编实验(2)拆弹实验_第5张图片
  4. 实验结果:如图2.1.3所示,阶段一拆弹成功!
    阶段一很简单,找出字符串首地址就可以了,没有什么大的难点。
    为了提高调试效率,可以将解析的结果保存到文件中,在调试时以参数方式提供给程序运行,方法如下:
    第一步:建立一个参数文件,比如“b.txt”文件,然后用vim或gedit编辑该文件,写入“Public speaking is very easy.”:
    计算机组成原理 / 反汇编实验(2)拆弹实验_第6张图片
    计算机组成原理 / 反汇编实验(2)拆弹实验_第7张图片

2.2.2 阶段2 循环

  1. 任务描述:通过phase_2的反汇编代码推断第二阶段要输入的数据;
  2. 实验设计:利用gdb结合断点来动态分析;
  3. 实验过程:
    观察phase_2的前一部分反汇编代码,如图2.2.1所示:
    计算机组成原理 / 反汇编实验(2)拆弹实验_第8张图片
    a) 由1处可发现输入的应该是6个数字,而且通过将字符串拆分为6个数,存入ebp -0x18的位置上,可能是通过数组存储;
    b) 在2处马上就比较了第一个数:cmpl $0x1, -0x18(%ebp),说明输入的第一个数如果不是1,那么炸弹就将爆炸;
    c) 紧接着进入了一个循环,关键语句为cmp %eax,(%esi,%ebx,4)。其中,%eax可根据前一条语句计算得出值为12=2,而(%esi,%ebx,4)中存储的是后面输入中的5个数其中的一个。比较前后两者,如果不满足“第n个数的值=第n-1个数的值n”就爆炸;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第9张图片
    由图2.2.3可知,%ebx寄存器的值就是用来计数的,inc之后表示当前为第n个数,当n大于5时就退出循环。而每次循环都会进行以上的判断,当循环结束时,如果都符合要求则本关通过。推测可能的代码如下:
1.	if (arr[0] != 1)  explode_bomb();  
2.	for (int i = 1; i <= 5; i++) {  
3.	    if ((i+1) * arr[i-1] != arr[i])  
4.	        explode_bomb();  
5.	}  

由此可以得到答案为1 2 6 24 120 720,验证结果如下:
计算机组成原理 / 反汇编实验(2)拆弹实验_第10张图片

2.2.3 阶段3 条件/分支

  1. 任务描述:通过phase_3的反汇编代码推断第三阶段要输入的数据;
  2. 实验设计:利用gdb结合断点来动态地分析;
  3. 实验过程:
    观察phase_3的前一部分反汇编代码,由图2.3.1可知内部含有sscanf函数,猜测是格式字符串,而sscanf输入了3个参数,但格式未知。
    计算机组成原理 / 反汇编实验(2)拆弹实验_第11张图片
    由图2.3.2可知,通过gdb调试得到sscanf输入数据格式为:整数,字符,整数。
    计算机组成原理 / 反汇编实验(2)拆弹实验_第12张图片
    进一步查看汇编代码,发现第一个数要小于等于7,而紧接着跳转到0x80497e8这个地址。
    计算机组成原理 / 反汇编实验(2)拆弹实验_第13张图片
    可是后面的代码中又都可以跳转到这个位置,猜测可能是switch语句,如果是则需要查找跳转表。
    计算机组成原理 / 反汇编实验(2)拆弹实验_第14张图片
    检测内存值,发现全部为phase_3内的地址,验证了switch语句的猜想。
    计算机组成原理 / 反汇编实验(2)拆弹实验_第15张图片
    既然要求第一个数小于等于7,那么先用0进行测试。由图2.3.5可知。eax为0时就跳转到0x8497e8指向的位置(若为1则跳转到0x8048c00,以此类推),同时还可以看到0x80497eb存储的内容为0x8048be0。
    由图2.3.1已知ebp-0x4中存储的是第3个参数,由图2.3.6可看到其值需要为0x309=777D。同时还设置了bl=0x71,用je跳转到了0x8048c8f。
    计算机组成原理 / 反汇编实验(2)拆弹实验_第16张图片
    来到0x8048c8f,发现是将bl的值与ebp-0x5(由图2.3.1已知存储着第2个参数)进行比较,字符值为0x71,换算成字符为’q’。
    在这里插入图片描述
    因此答案为0 q 777(根据第一个数字不同,还可以是1 b 214等等)最终结果如下:
    计算机组成原理 / 反汇编实验(2)拆弹实验_第17张图片计算机组成原理 / 反汇编实验(2)拆弹实验_第18张图片

2.2.4 阶段4 递归调用和栈

  1. 任务描述:通过phase_4以及func4的反汇编代码推断第四阶段要输入的数据;
  2. 实验设计:利用gdb结合断点来动态地分析;
  3. 实验过程:

首先还是观察phase_4的反汇编代码,由图2.4.1可知依然是sscanf;
计算机组成原理 / 反汇编实验(2)拆弹实验_第19张图片
再次进行调试,由图2.4.2可知输入是一个整数,且由图2.4.1可知该参数必须大于0;
在这里插入图片描述
由图2.4.3可知,数据传入到了func4函数中,且返回值必须为0x37=55D,此时需要进一步探明func4函数得到输入参数;
计算机组成原理 / 反汇编实验(2)拆弹实验_第20张图片
由图2.4.4不难发现,func4存在自身调用的情况,推测是递归结构;
计算机组成原理 / 反汇编实验(2)拆弹实验_第21张图片
由此推测伪代码如下:

1.	int func4 (int n) {  
2.	    if (n <= 1)  
3.	        return 1;  
4.	    else  
5.	        return func4(n-1) + func4(n-2);  
6.	}

通过计算得出n=9时,返回值即为0x37=55D,验证结果如下:
计算机组成原理 / 反汇编实验(2)拆弹实验_第22张图片

2.2.5 阶段5 指针

  1. 任务描述:通过phase_5的反汇编代码推断第五阶段要输入的数据;
  2. 实验设计:利用gdb结合断点来动态地分析;
  3. 实验过程:
    首先由图2.5.1可知,字符串长度必须为6;
    在这里插入图片描述
    用户输入字符串后,首地址存储到ebx中,而esi为模式串首地址;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第23张图片
    由图2.5.1可以看出,用户输入字符串后,低4位的值作为索引,找到模式串中该索引对应的字符,放入新数组中。其流程图如图2.5.3所示:
    在这里插入图片描述
    根据图2.5.4的调试结果,已知模式串与匹配串如下:
1.	pat[]="isrveawhobpnutfg\260\001";  // 模式串
2.	str[]="giants";  // 匹配串

计算机组成原理 / 反汇编实验(2)拆弹实验_第24张图片
用户输入后需要pat[ecx[i]]=str[i],其中i in (0,5),因此
ecx[6]={15,0,5,11,13,1},但用户是从键盘输入的字符,这些值在ASCii对应的字符无法从键盘输入。
而每个字符8位,低4位不能改变,因此高四位可以任意加1,于是可能的一种结果为ecx[6]={ 79,80,69,75,77,65},对应字符OPEKMA,验证结果如图2.5.5所示;

2.2.6 阶段6 链表/指针/结构

  1. 任务描述:通过phase_6的反汇编代码推断第五阶段要输入的数据;
  2. 实验设计:利用gdb结合断点来动态地分析;
  3. 实验过程:
    由图2.6.1可以看出,依然是首先读入6个数字,存放在ebp-0x18的地方,推测可能是数组的首地址;
    在这里插入图片描述
    观察图2.6.2中代码结构,发现是与阶段2相同的循环,根据汇编代码,尝试写出其伪码如下:
1.	for (int i = 0; i<=5; i++) {  
2.	    if (arr[i] >= 6)  
3.	        explode_bomb();  
4.	    for (int j = i+1; j <= 5; j++) {  
5.	        if (arr[i] == arr[j])  
6.	            explode_bomb();  
7.	    }  
8.	}

计算机组成原理 / 反汇编实验(2)拆弹实验_第25张图片
循环结束后,进入了第二部分,由图2.6.3发现esi=ebp-0x34,但此时ebp-0x34的内容未知;
计算机组成原理 / 反汇编实验(2)拆弹实验_第26张图片
于是在文件内进行查找,发现其位于函数开头,值为0x804b26c,结构类似字符串;
计算机组成原理 / 反汇编实验(2)拆弹实验_第27张图片
为了确定我们的猜测,如图2.6.5进行调试,但打印只显示一个,此时推断esi可能是结构体;
计算机组成原理 / 反汇编实验(2)拆弹实验_第28张图片
而之前在第二部分中,曾看到图2.6.6中mov 0x8(%esi),%esi的操作,这非常类似于链表中node=node->next的行为;
在这里插入图片描述
根据图2.6.4已知esi值为0x804b26c,不妨通过调试验证链表的猜想。结果如图2.6.7所示;可见首尾地址相同(已用同一颜色标出),就是链表;
计算机组成原理 / 反汇编实验(2)拆弹实验_第29张图片
由图2.6.8可推测第二部分伪码如下,其功能为:
输入6个数字,如3 4 1 2 5 6,则原来链表中第3个元素排到第一位, 第4个元素排到第二位,第1个元素排到第三位,以此类推……

1.	for (int i = 0; i <= 5; i++) {  
2.	    for (int j = 0; arr[i] > j; j++) {  
3.	        node = node->next;  
4.	    }  
5.	    a[i]= node; // a[i]是一个新数组  
6.	}  

计算机组成原理 / 反汇编实验(2)拆弹实验_第30张图片
第二部分结束,观察剩余代码,发现第三部分功能为“重新串接链表”,如图2.6.9所示,第四部分功能位“检测链表值是否位降序”,如图2.9.10所示;
计算机组成原理 / 反汇编实验(2)拆弹实验_第31张图片
计算机组成原理 / 反汇编实验(2)拆弹实验_第32张图片
此外已由图2.6.7得知,原链表各结点值分别为fd、2d5、12d、3e5、d4、1b0,即253、725、301、997、212、432,因此输入为4 2 6 3 1 5
计算机组成原理 / 反汇编实验(2)拆弹实验_第33张图片
如图2.6.11所示,完成了炸弹的拆除任务!
计算机组成原理 / 反汇编实验(2)拆弹实验_第34张图片

2.2.7 阶段7 隐藏阶段

  1. 任务描述:找出隐藏阶段开启方式并且拆除隐藏阶段的炸弹;
  2. 实验设计:利用gdb结合断点来动态地分析;
  3. 实验过程:
    不难发现,反汇编代码中只有代码,如图2.7.1所示,一共有2个比较,分别是“与常数0x6”和“与字符串”的比较;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第35张图片
    结合隐藏阶段的开启条件,推测与0x6的比较是指在阶段6完成后,才能开启隐藏阶段。为了验证这个猜想,首先需要知道0x804b480中的值,警告调试,如图2.7.2可知初始值为0;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第36张图片
    在第6阶段设下断点,如图2.7.3所示,通过前面5个阶段后0x804b480的值变为了6,说明猜想是正确的;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第37张图片
    再看看老朋友sscanf,由图2.7.1可知0x8049d03中的内容送入了sccanf函数,通过调试得知输入格式为数字+字符串;那么由图2.7.1可知另一个地址0x804b770中可能存放的就是第一个数字;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第38张图片
    在phase_6设置断点(不运行至此0x804b770始终为空),由图2.7.5可知,第一个数字为9,与第4阶段输入内容相同,推测是在第4阶段输入的数据后加上一段字符串以开启隐藏阶段;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第39张图片
    回到图2.7.1,可以看到字符串首地址为0x8049d09,用x命令观察,如图2.7.6所示,因此我们尝试在第4阶段输入“9 austinpowers”;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第40张图片
    如图2.7.7所示,成功开启了隐藏阶段。因此现在我们回到函数,开始着手隐藏阶段的炸弹拆除任务;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第41张图片
    如图2.7.8可观察到输入的字符串被转换成了整数,且整数val减1后与1000进行了比较,尝试输入1001,结果如图2.8.9所示,意外成功拆除了炸弹;
    计算机组成原理 / 反汇编实验(2)拆弹实验_第42张图片
    计算机组成原理 / 反汇编实验(2)拆弹实验_第43张图片

你可能感兴趣的:(汇编)