2024Spring> HNU-计算机系统-实验3-Bomblab-导引+验收

【更新】

【4.24更新】去年录制的视频讲解,后期声音偏小,然后部分地方可能没讲太清楚或者有小谬误,整体流程应该还是讲清楚的。如果实在没有看懂的同学可以参考一下。

HNU-CS炸弹实验……蹦蹦炸弹!_哔哩哔哩_bilibili

【4.27更新】验收结束,可以把我自己写的实验报告放出来了。

HNU-计算机系统-实验3-BombLab_甘晴void-CSDN博客

【4.27更新】验收感悟,《BombLab实验验收评分参考》

验收的感悟,以及我预先准备的可供询问的问题(其实基本没用上)

前言

BombLab一定要花时间完成哦,对于期末卷面的提升和计算机系统的理解都非常重要。

导引

①文件目录概览

助教下发一个文件包,打开之后是这样的几个文件。

这几个文件解释如下

  • bomb:可执行文件,无法打开,我们主要研究的对象
  • bomb.c文件
  • bomb-quiet:暂时不需要关注
  • README文件

②反汇编

对bomb进行反汇编:在终端中输入以下指令

objdump -d bomb >m.txt

这一步是使用objdump对bomb可执行文件反汇编,并保存在txt文件中。

在这一步之后,文件夹中打开m.txt即可查看汇编代码。

③实验流程-概览

bomb可执行文件会读取你的输入,并与它的内部存储进行匹配。

该可执行文件内部有多段,如果你的输入能够匹配的话,程序就可以继续执行。直到这多段全部完成。这就是最终的成功。否则,如果中间任意一步出现问题,就会爆炸(即结束程序,终端输出bomb)。如下,我输入错误答案1使之引爆。

▲bomb可执行文件大致的执行逻辑可以在bomb.c中看到(bomb.c就是删去了各部分验证函数之后的主函数)

以phase_4为例,bomb读取你的输入,并与内部进行比对,如果正确就继续,否则调用bomb表示该步骤破译失败。

最终在你成功破译所有问题之后,效果大概是这样:

 ④实验流程-破译

以较为简单的phase_1为例:

汇编代码中,有一段这里调用了explode_bomb,而这是导致程序终止执行的原因,想办法让这句汇编代码不被执行。当然比较容易想到的是跟跳转结合起来,不跳转到这里或者跳转到别的地方即可。再注意到这里的<string_not_equal>似乎是某个字符串比较,可以想到bomb从我这里读入的密码是不是与字符串有关,从而继续研究下去……

⑤实验流程-验证

使用./bomb开始执行炸弹文件,然后逐个输入每题的密码,可以看到是否正确的反馈。

如这里反馈“phase 1 defused” 表示第一个通过了。

逐个这样验证下去直到最终结果。

⑥实验技巧-逐步调试 

使用gdb进行逐步调试,可以提升效率(有时想看如何跳转,有时不想重新开始)

下面开始逐步调试:

gdb -q bomb

若没有gdb,就安装gdb

成功安装后就可以逐步调试

gdb调试是基础,这里不再赘述。

⑦实验技巧-保存答案与快速验证

我不想每次都使用./bomb然后逐个输入答案,这样太烦了。

有没有什么办法能够快速验证我前几步是否正确?

可以将答案写在ans.txt中,一行写一道题目的答案即可。

然后使用./bomb ans.txt来运行,bomb会逐个读入文件中的每一行作为每一题的答案。

问题汇总与解决

 ①可执行文件运行,如果出现permission denied的情况

解决方法:使用chmod 777 bomb,改变可执行文件的权限即可。

②明明有该可执行文件,但无法被执行,显示no such file or directory

原因:试图在64位系统上与运行32位程序

解决方法:可参考如下

Linux执行可执行文件提示No such file or directory的解决办法_fixrtc: no such file or directory-CSDN博客

★期望

希望你能:

  • (重要)独立完成该实验
  • 熟练掌握汇编代码的阅读能力
  • 总结归纳对于阅读汇编的小技巧,小心得
  • 从该实验中收获乐趣

验收感悟

本人担任22级某班的课程验收助教。故参与了验收并做了一些工作。

本次验收的感受如下:整体较好,但有些失望。主要的原因还是在课程安排可能太紧凑了,同学来不及把时间投在这个实验上。他们在这一周中还有OS的实验甚至还有期中考试,确实有点赶了。这样导致我认为CS最精彩的实验没有得到认真对待,真的有点可惜。有些同学甚至没来得及完成所有关卡,也有的同学完成了但没来得及深入思考,我觉得都有点遗憾。能深入思考的同学不是很多,最终给出2个A+,几个方面都无可挑剔,尤其总结较好。

如果有时间,还是推荐能再好好地看一遍这个实验。但是,估计没有课程压力,很少有人能再做一遍这个实验吧。对于我本人来说,这个实验是汇编入门(真的入门了嘛?)第一课,也许还没入门,但是带给我的震撼和学习汇编的兴趣是无以复加的。

附:

《BombLab实验验收评分参考》

by 甘晴void

【注】该份验收参考由本人制定,只在我负责验收的班级采用。其它班级是否使用我并不清楚。另:实际操作有调整。

【另】实际评价已经提交且无法更改。该份参考不以任何方式影响已经成立的评价。

主要标准:独立完成,认真思考

评分参考:

  • 能通关能讲清楚【】

  • 能通关但无法讲清楚【】

  • 未能全部通关【】

  • 若未能全部通关123456,但有深入思考,不超过【】

  • 若未解secret phase,不超过【】,根据讲解可能降级

  • 【实际操作有调整,根据展示情况有上下浮动】

注意:检查Bomb是否本人学号(实际上我没检查,又忘记了)

实操评价:请同学手动演示输出结果页面,以及部分行的gdb调试(这个可以穿插在解不同phase内,比如查看一下某个变量)【主要考察是否熟练,bomblab对实操要求很高,若不熟练,怀疑是否独立完成】

下面是部分参考试题,可供随机挑选,难度各异,自己判断。

主要旨在根据回答判断是否独立完成,以及是否认真思考。

(部分同学可能不一样,但是大体相似,具体分析,逻辑自洽即可)

注意:每个关卡bomb的触发逻辑建议问,这个能反应其对该部分的总体感知,如果这个回答的支支吾吾,怀疑是否独立完成或是否认真思考。

总体概览

  • 这个实验在干什么,如何算完成这个实验?

  • 有什么有趣的地方?

  • 有什么gdb操作以及Linux使用上的总结?

  • 你学到了什么?(基本能看出做了什么工作)

phase1 字符串比较

  • bomb如何被触发(phase1只有一个触发点)

  • test指令的含义

  • strings_not_equal函数返回的是什么,它有几个参数,分别是什么?

  • 开头sub $0x1c,%esp与结尾add $0x1c,%esp是干什么的

phase2 循环

  • bomb如何被触发?(phase2中值不对时,以及read_six_numbers函数内部也有被触发的可能)

  • 开头push %esi,push %ebx的作用,什么是调用者保存寄存器,有哪些?(%eax,%edx,%ecx)什么是被调用者保存寄存器?有哪些?(%ebx,%esi,%edi)【这些仅在32位机上成立】

  • read_six_numbers函数内部的逻辑是什么样的?(没什么同学关注到)

  • 循环控制变量被保存在哪个寄存器内(esi%)

phase3 分支语句与跳转表

  • bomb如何被触发?

  • jmp *0x804a2c0(,%eax,4)是什么意思?(跳转表的基础知识,基本都答上来了)

  • 关注<__isoc99_sscanf@plt>函数,这个函数的参数是什么,在本题中都是怎么样的,你能用c语言表示吗?其第一个参数是干什么的?(函数参数是什么,以及存放的位置)

  • switch-case语句是用跳转表实现的,请描述如何实现(课堂内容),具体在本题中有几个可能的跳转值?对于某一个跳转值,会跳转到哪里?

  • 本题应该是多解的,你有没有把所有解都解出来?(不强求,但可知探究精神)

phase4 递归调用

  • bomb如何被触发?

  • ★该部分重点关注运行时栈的知识点是否掌握(课堂重点),可以让同学在纸上稍微画一下,必须保证掌握。【这个我忘记强调了,建议下次能完整画出来无差错纳入考核,很多同学还是有些问题的,期末重点】

  • func4有几个参数,分别放在哪些地址?(2个,0x4(%esp)和(%esp),不同同学可能不一样)

  • func4函数内部在做什么逻辑?

  • func4函数内开头的mov %ebx,0x10(%esp)和mov %esi,0x14(%esp)和mov %edi,0x18(%esp)这三个在干什么?(被调用者保存寄存器)为什么要这样做?(保护寄存器值)

  • 递归的几个重要特点(算法)?(递归终止条件,递归调用函数参数等)你能用c语言写出func4的逻辑吗?

  • 函数的返回值存放在哪里?(%eax)

  • call,ret,leave有什么区别,分别做什么事情?(重要知识)

  • 本题应该是多解的,你有没有把所有解都解出来?(不强求,但可知探究精神)

phase5 字符串末尾累加

  • bomb如何被触发?

  • movsbl (%ebx,%eax,1),%ecx的含义?

  • 循环控制变量是谁?累加器是谁?

  • 循环体在干什么?(参考:其实循环执行体很简单,就只有如下几句,它想干的事情是这样的。s[i]的末4位与0xf相与,结果以整数形式存储,记作r。(s[i]本应该是8位,这里实际上取了它的末4位,结果共有16种,即0~15)。访问(基地址0x804a2e0+4*r)这个地址,记作target_address,把这个地址上的值累加到寄存器%edx上。这就结束了。)

  • 本题应该是多解的,你有没有把所有解都解出来?(不强求,但可知探究精神)

phase6 链表

  • bomb如何被触发?

  • 这道题想让我认识一个什么结构?(基本都能答出来,不重要,没答出来但做出来也可)

  • 这道题在做一个什么事情?(建立链表,根据链表数据域重组排序)

  • 看一下链表的结构,让同学用x/3x命令查看链表的结点(独立完成该题,这个要求不过分),这三个域分别是什么?(第一个是数据域,存储的是值的大小;第二个是编号域,反应的是节点的编号(实际上用处不大);第三个是next域,是指针类型,指向下一个节点的地址)

secret_phase 二叉检索树(BinarySearchTree)

  • 如何发现隐藏的关卡?这个探究过程很重要。能反映独立完成与认真思考:

    • 怎么发现的隐藏关卡?(这个问题很有趣,很微妙地能发现同学大概是怎么完成实验的)

    • 是否关注了phase_defused函数?该函数的逻辑是什么?(不同同学的逻辑不一样,仅供参考:若前6关都通过才会触发隐藏关卡,否则跳过。触发隐藏关卡之后,首先判定第四关中输入的是不是两个整数与一个字符串。我们知道,第四关的答案是两个整数,但是如果要进入隐藏关卡,这里需要在两个整数之后再输入一个字符串)

    • 思考问题:我前面题目读的数据为什么现在还能回去访问?到底哪个函数起到了从终端读入的作用,其它read函数又都在干什么? <__isoc99_sscanf@plt>这个函数在干什么,read_line函数在干什么?

  • 关于隐藏关卡

    • func7内部逻辑是什么?

    • bomb如何被触发?

    • 你能否画出二叉检索树示意图

参考代码逻辑(方便助教跟进逻辑,每个同学不同,仅供参考)

 void bomb()
 {
 }
  
 void phase_1()
 {
     string s_ori = "I am for medical liability at the federal level.";
     string s_input;
     scanf("%s", &s_input);
     if (strings_not_equal(s_input, s_ori))
         bomb();
 }
  
 void phase_2()
 {
     int m[6];
     scanf("%d %d %d %d %d %d", &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]);
     if (m[0] != 0)
         bomb();
     if (m[1] != 1)
         bomb();
     for (int i = 2; i <= 5; i++)
     {
         int temp = m[i - 1] + m[i - 2];
         if (temp != m[i])
             bomb();
     }
 }
  
 void phase_3()
 {
     int n;
     char c;
     int m;
     if (scanf("%d %c %d", n, c, m) <= 2)
         bomb();
     if (n > 7)
         bomb();
     char temp;
     switch (n)
     {
     case 0:
         if (m != 0x80)
             bomb();
         temp = 0x64;
         break;
     case 1:
         if (m != 0x135)
             bomb();
         temp = 0x6f;
         break;
     case 2:
         if (m != 0x348)
             bomb();
         temp = 0x67;
         break;
     case 3:
         if (m != 0x16d)
             bomb();
         temp = 0x6b;
         break;
     case 4:
         if (m != 0x1d7)
             bomb();
         temp = 0x75;
         break;
     case 5:
         if (m != 0x31b)
             bomb();
         temp = 0x72;
         break;
     case 6:
         if (m != 0x204)
             bomb();
         temp = 0x6e;
         break;
     case 7:
         if (m != 0xb0)
             bomb();
         temp = 0x6f;
         break;
     }
     if (temp != c)
         bomb();
 }
  
 int func4(int n, int x)
 {
     if (n == 0)
         return 0;
     if (n == 1)
         return x;
     return (result(n - 1, x) + result(n - 2, x) + x);
 }
  
 void phase_4()
 {
     int check, x;
     if (scanf("%d %d", check, x) != 2)
         bomb();
     if (x <= 1)
         bomb();
     if (x > 4)
         bomb();
     if (func4(9, x) != check)
         bomb();
 }
 void phase_5()
 {
     string s;
     scanf("%s", s);
     if (string_length(s) != 6)
         bomb();
     int total = 0;
     for (int i = 0; i < 6; i++)
     {
         total += m[s[i] & 0xf];
     }
     if (total != 0x38)
         bomb();
 }
  
 void phase_6()
 {
     int a[6];
     scanf("%d %d %d %d %d %d", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]);
     //  第一阶段:合规性检验
     if (a[0] > 6)
         bomb();
     for (int i = 1; i < 6; i++)
     {
         if (a[i] > 6)
             bomb();
         for (int j = i; j < 6; j++)
         {
             if (a[j] == a[i - 1])
                 bomb();
         }
     }
     // 第二阶段:排序
     struct node
     {
         int data;
         int number;
         node *next;
     } struct node head;
     for (int i = 0; i < 6; i++)
     {
         if (a[i] > 1) // 实际上这句可以不要,放这里可以看得更清晰一点
         {
             for (int j = 1; j < a[i]; j++)
             {
                 head = head->next;
             }
             b[i] = &head;
         }
     }
     // 第三阶段:复写下一状态
     *b[0]->next = *b[1];
     *b[1]->next = *b[2];
     *b[2]->next = *b[3];
     *b[3]->next = *b[4];
     *b[4]->next = *b[5];
     // 第四阶段:检验数据域递增性
     struct node *head;
     for (int i = 5; i > 0; i--)
     {
         if (head->data > head->next->data)
             bomb();
     }
 }
 int fun7(int *x, int input)
 {
     if (x == 0)
         return -1;
     if (*x > input)
         return 2 * fun7(x + 4, input); // 在左孩子中寻找
     else
     {
         if (*x == input) // 寻找到了
             return 0;
         else                                   //*x<input
             return 2 * fun7(x + 8, input) + 1; // 在右孩子中寻找
     }
 }
  
 void secret_phase()
 {
     readline();
     long input = strtol(readline(), 0x0, 0xa);
     if (input > 1000)
         bomb();
     if (fun7(0x804c088, input))
         bomb();
 }

  • 28
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值