二进制炸弹

文章详细介绍了作者通过学习gdb和汇编知识,逐步解决一个名为“二进制炸弹”的课程大作业的过程。作业包含7个普通阶段和1个隐藏阶段,涉及浮点数运算、数字规律、数组和链表操作等内容。作者通过分析汇编代码、设置断点和使用调试工具,逐步揭示每个阶段的解决策略,强调了ida和专业资源在解决复杂问题时的帮助作用。
摘要由CSDN通过智能技术生成

二进制炸弹总结

最近在学习gdb的使用和部分汇编指令的意义,在查找资料的过程中发现南京大学的mooc质量很高,于是跟着老师们学完了整个mooc,对gdb和汇编指令有了一个大概的了解。最后磕磕绊绊地完成了很有意思的课程大作业:二进制炸弹,下面对整个大作业进行一个总结。

本次作业的环境为:WSL2 20.04.5 LTS,gcc 9.4.0, gdb 9.2。作业的下载地址为https://cs.nju.edu.cn/sufeng/course/mooc/0809NJU064_bomblab.tar。二进制炸弹一共有7个普通阶段和1个隐藏阶段。

phase_0

在这里插入图片描述

phase_0 是所有阶段里面最简单的,从反汇编代码中也能看出来,主要是判断我们的输入和地址0x804a198中保存的字符串是否一样,直接使用x/s 0x804a198即可查看应该输入的字符串。然后运行我们的程序,即可看到成功通过phase_0 的提示

在这里插入图片描述

在这里插入图片描述

phase_1

phase_1 的主要考察内容是浮点数的表示,使用b phase_1将断点打在phase_1处,然后使用si在phase_1 内单步执行指令然后查看结果。
在这里插入图片描述

这里通过分析汇编指令可以知道,这段代码的意思是先将0x1a4eccf6整个16进制数送到$ebp-0xc的位置,然后将其中的值送到浮点寄存器栈顶,再从浮点寄存器栈顶把值存到$epb-0x18的位置。接下来使用sscanf从命令行读入,这里有一个明码地址,由于对sscanf的调用比较了解,因此这里猜测,这个明码地址应该是输入格式,使用x/s 0x804a1bd进行查看,果不其然,这里让我们从命令行输入两个整数,接下来程序判断输入的是不是两个数,如果不是则炸弹爆炸。
在这里插入图片描述

了解了上面汇编的意思,下面的代码就很简单了。先将这个浮点数的低32位与第一个输入判断,如果不相等炸弹直接爆炸,否则继续将高32位与第二个输入对比,若都成功则通过phase_1。这里如果不想手动计算,可以有取巧的方法,直接将断点打在0x80494e3,然后查看寄存器edx的值,然后第一个输入即为这个值,接下来如法炮制,将断点打在0x80494f2即可获得第二个输入值,过程如下所示:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

当然如果手算也可以,只是会比较麻烦(这里的28是转换成1.x的形式需要移位28位)。
在这里插入图片描述

接下来将我们得到的值输入查看结果,成功通过phase_1。
在这里插入图片描述

phase_2

phase_2主要是让输入一串数字,数字之间满足一定的规律。老规矩还是分析汇编代码,然后使用gdb调试。可以看到在phase_2中是需要调用read_n_numbers这个函数的,这样的话就先进入read_n_numbers,看看它是如何实现的。ps:这里直接说吧,在我的程序中是需要输入8个数,具体可以看汇编代码,这里就不再分析。这里直接进入read_n_numbers函数查看输入,可以看到内部调用strtok将从标准输入读取数据进行分割,然后使用sscanf读取数据。
在这里插入图片描述

在这里插入图片描述

此外这里还有两个明码地址,我们打印一下,发现读入的应该是8个整数,且整数之间需要用, 隔开。
在这里插入图片描述

继续查看汇编代码,使用gdb将断点打在0x8049532处,因为这里有一个比较$0x8a,%eax。重新运行程序,设置输入为8, 7, 6, 5, 4, 3, 2, 1,这样有顺序方便后续检查,可以看到当前eax的值刚好为我们输入的第一个值,因此可以得到结论,第一个输入的值应该是0x81即138。
在这里插入图片描述

后续可以按照这个方法直接一个一个往下试,这样就可以得到所有的数据,最终为138, 137, 134, 129, 122, 113, 102, 89。不过这样做太低效了,我们可以继续分析汇编代码,后面可以发现其实这些输入的数据之间满足一定的规律,这里对主要的代码使用红框和绿框进行了标注。假如我们第一个数据输入正确的话,那么会在$ebp-0xc的地方存入1,即当前读入数据的个数,然后跳转至0x8049584,比较这个$ebp-0xc与7的大小,如果小于等于7则会进行一个循环,循环的主体即为红框和绿框的内容。这个循环到底是什么意思呢,其实写成C代码会很容易看出来

for (int i = 1; i <= 7; i++) {
    if (array[i] != array[i - 1] - 2 * i + 1) {
        explode_bomb();
    }
}

i就是eax寄存器中的内容,在红框内将数据备份在ebx,然后在绿框中计算-2*i的值,再和ecx寄存器中的内容相加,最后再加1。ecx寄存器的值在红框中给出了计算过程,即为$ebp+4*$eax-0x2c地址中保存的值,经检查当eax寄存器的值为0时,则edx的值为138,
在这里插入图片描述

由此可以推断出,从$ebp-0x2c起始存了8个数,然后继续分析下面的逻辑就很简单了,eax-1是为了方便下面获得首地址,然后将上一个值保存在ecx寄存器中。
在这里插入图片描述

接下来将我们获取得到的数据输入到程序中,很轻松就过了phase_2。
在这里插入图片描述

phase_3

来到phase_3 我们首先又遇到了熟悉的sscanf输入和明码地址,先查看一下保存的内容,发现又是让输入两个数。
在这里插入图片描述

在这里插入图片描述

继续查看汇编代码,碰到第一个需要注意的地方,这里要求我们的第一个输入减7之后不能比9小,结合下面代码不难看出这是一个switch-case结构,数一下跳转数量,发现应该有9个case分支,因此这里可以确定下来第一个输入应该为16。在正确输入第一个数之后,会跳转至0x8049652, 然后比较输入的第二个数和$ebp-0xc位置的数 。
在这里插入图片描述
在这里插入图片描述

这里我们输入16, 666,设置666方便后面排查错误。将断点打到0x8049652,然后单步执行再查看这个地方的数据,发现输入应该为973,重新运行程序并输入16 973可以看到,phase_3 已成功通过。

在这里插入图片描述

在这里插入图片描述

phase_4

phase_4 应该是所有关卡里面最难的一个了,因为设计递归,所以调试起来会非常痛苦。先来看汇编代码
在这里插入图片描述

熟悉的sscanf,熟悉的输入两个数。我一开始看错了,直接输入了4, 30,结果还真给它通过了。。。然后就没管,继续做后面的,但是等做到secret_phase的时候回头一看心态当场爆炸。这里提前爆料一下,secret_phase是跟phase_4相关的,这里只要再多输入一个密码,就能进入隐藏关卡。误打误撞答案正确了,但是过程是什么样的还是不清楚,这里还是要老老实实地进行调试。

在这里插入图片描述

将断点打在0x8049734,这里push了三个值,说明func4函数有三个形参。第一个是我们输入的第一个数,第二个是4,第三个是48。然后进入func4继续查看该函数的功能,这一段的主要内容就是判断我们的第一个输入和(48-4)/2+4哪个大,主要是用移位操作来完成。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

接下来我实在分析不动了,直接上ida,通过反汇编可以得到下面的代码:

int __cdecl func4(int a1, int a2, int a3)
{
  int v4; // [esp+Ch] [ebp-Ch]

  v4 = (a3 - a2) / 2 + a2;
  if ( v4 > a1 )
    return func4(a1, a2, v4 - 1) + (v4 >> 1);
  if ( v4 >= a1 )
    return (a3 - a2) / 2 + a2;
  return func4(a1, v4 + 1, a3) + 2 * v4;
}

signed int __cdecl phase_4(int a1)
{
  signed int result; // eax
  int v2; // [esp+0h] [ebp-18h]
  int v3; // [esp+4h] [ebp-14h]
  int v4; // [esp+8h] [ebp-10h]
  int v5; // [esp+Ch] [ebp-Ch]

  v5 = __isoc99_sscanf(a1, "%d %d", &v3, &v2);
  if ( v5 == 2 && v3 > 3 && v3 <= 48 )
  {
    v4 = func4(v3, 4, 48);
    if ( v4 == v2 )
    {
      result = 1;
    }
    else
    {
      explode_bomb();
      result = 0;
    }
  }
  else
  {
    explode_bomb();
    result = 0;
  }
  return result;
}

这下差不多明白了,输入了两个数v3和v2,v3要满足大于3小于等于48的条件,然后调用func4(v3, 4, 48),要它的结果等于v2。直接看phase_4的结尾,这里将eax的值和$ebp-0x10进行对比(func4的返回值),如果相等就成功退出pahse_4。

在这里插入图片描述

为了验证我们的结论,先输入一个错误的输入4 32,直接将断点打在0x0804973d,然后查看这个比较的值,发现它等于30,说明我们输入的第二个值确实应该为30。
在这里插入图片描述

在这里插入图片描述

然后重新运行程序,输入4 30,可以看到已成功解决phase_4。
在这里插入图片描述

phase_5

phase_5应该是所有的phase里面最有意思的一个了。phase_5要处理的是一个数组,需要先找到一个入口,然后按照数组的顺序走到f的位置,输入起始位置和路径上数字的总和。老样子依旧是看汇编代码,检查明码地址,发现让输入两个整数。

在这里插入图片描述

继续往下读,发现了我们的输入每次都要和0xf进行与位操作,然后一共要进行7次,每次都将当前的结果加到$ebp-0x10,并且会跳转到下一个节点,最后比较我们的第二个输入和这个值是否相等。

在这里插入图片描述

这里我们直接看从这个地址开始存了哪些东西
在这里插入图片描述

排列一下

10 2 14 7 8 12 15 11 0 4 1  13 3  9  6  5
0  1 2  3 4 5  6  7  8 9 10 11 12 13 14 15

上面已经分析了要7次跳转到15,可以倒着找15 -> 6 -> 14 -> 2 -> 1 -> 10 -> 0 -> 8,所以输入应该是8 48。重新运行程序,输入正确的数对,可以看到结果正确,成功通过phase_5
在这里插入图片描述

phase_6

phase_6主要考察链表结构,查看汇编代码,发现一开始要读入n个数,和phase_2一样,因此不做过多分析,继续阅读代码发现输入的应该是7个数。然后从$ebp-0x34开始依次存入。

在这里插入图片描述

继续阅读源码,我们不难发现链表的起始位置应该在$ebp-0x50处,直接将断点打在0x80498e2,然后开始逐步打印节点的信息。

在这里插入图片描述

在这里插入图片描述

可以看到节点的顺序为6 -> 0 -> 8 -> 5 -> 7 -> 4 -> 2,最后需要按照从大到小的顺序输出,因此最终的输入为2, 7, 6, 4, 1, 5, 3

6 -> 0 -> 8 -> 5 -> 7 -> 4 -> 2
2    7    6    3    1    4    5

在这里插入图片描述

phase_secret

最后这一块是综合考察,比较考验耐心。这里由于老师提前说过,需要在phase_4多输入一个“密码”,不然炸弹会自动defuse,所以我这里直接看phase_defused这里发生了什么。
在这里插入图片描述

发现了一共有三个明码地址,都打印一下,正好是phase_4的输入。

在这里插入图片描述

因此我们得修改一下phase_4阶段的输入,能看到这里已经进入了secret_phase

在这里插入图片描述

继续阅读secret_phase的源码,发现我们输入需要在一定的范围内 ( 0 , 1001 ] (0, 1001] (0,1001]

在这里插入图片描述

然后将输入带入到fun7,然后将结果与1进行比较,之后这里又有一个明码地址,打印一下,发现是结束提示语,说明我们要做的就是让fun7的返回值等于1。

在这里插入图片描述

在这里插入图片描述

进入fun7,发现代码非常长,于是我直接使用ida来看伪代码,要让返回值等于1,那就只能是最后这个条件。最后这个条件又递归调用了fun7一次,现在需要知道的就是a1到底是多少,继续阅读secret_phase的伪代码,发现这里输入了一个n1,直接查看:
在这里插入图片描述
在这里插入图片描述

找到了n1的地址是0x0804c1d0

在这里插入图片描述

使用gdb打印该节点,因为只需要递归一次,所以只用看下一个节点的值即可,可以看到结果为50
在这里插入图片描述

输入50,可以看到隐藏炸弹也被拆除了。

在这里插入图片描述
以上就是整个作业的流程了,断断续续做了一天半才勉强做完,有一说一汇编真的很难,不过如果有像ida这种专业的工具的话,其实做起来就很轻松啦,这里分享一个我下载的绿色版ida的网址:https://www.32r.com/soft/42075.html,一个汇编指令具体意思的网址:https://shell-storm.org/x86doc/,以及常用的gdb调试指令的网址:https://jyywiki.cn/pages/OS/manuals/gdb-cheat-sheet.pdf

  • 19
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值