二进制炸弹bomblab-隐藏关-详细解析

观前提示:本文并非速通指南,偏向于如何一步步思考与解题,也许会分析得有些拖沓。且本文为系列文章,每一篇会分析一关的破解思路,前文提到的知识点后面不会赘述,分析过的类似的汇编代码段也不会再分析,除非是没见过的结构,所以越往后分析得越少

本文共2400余字。

前言

​ 如果这些解析对你有帮助,那么我会非常荣幸和开心!

​ 如果我的解析存在错误,非常抱歉误导了你!请在评论区提出!我也是一个正在不断学习的学生。

​ 谢谢你!

破解前的分析

当我们来到这里,已经解完了6个关卡。在第一篇文章我们有说过,main函数里一共调用了6个关卡函数。炸弹提示语好像也告诉我们拆除了炸弹:
在这里插入图片描述
但是回到我们拥有的main函数C语言代码,可以看到设下炸弹的人留下了这么一段话:
在这里插入图片描述

“But isn’t something… missing? Perhaps something they overlooked? Mua ha ha ha ha! ”

但是是不是有什么东西… 缺失了? 也许是他们忽略了什么!*得意地怪笑

可见,炸弹还没有完全破解。

回顾一下,我们一路走来几乎是把所有关卡函数和它的子函数都分析了个遍。所以,仅剩的phase_defused函数就是最后的疑点。

正式破解

隐藏关卡触发方法

扫视一下phase_defused函数果然可以发现它有可能会触发隐藏关:

0x08049249 <+115>:	call   0x8048e92 <secret_phase>

那就来分析一下这个函数:

   0x080491d6 <+0>:	sub    $0x6c,%esp
   0x080491d9 <+3>:	mov    %gs:0x14,%eax
   0x080491df <+9>:	mov    %eax,0x5c(%esp)
   0x080491e3 <+13>:	xor    %eax,%eax
   ;栈保护操作,之前的文章讲过
   
   0x080491e5 <+15>:	cmpl   $0x6,0x804c3cc
   0x080491ec <+22>:	jne    0x8049261 <phase_defused+139>
   ;………………略去中间代码……………………
   0x08049261 <+139>:	mov    0x5c(%esp),%eax
   0x08049265 <+143>:	xor    %gs:0x14,%eax
   0x0804926c <+150>:	je     0x8049273 <phase_defused+157>
   0x0804926e <+152>:	call   0x8048790 <__stack_chk_fail@plt>
   0x08049273 <+157>:	add    $0x6c,%esp
   0x08049276 <+160>:	ret  

可以发现:
在这里插入图片描述
只有输入的字符串个数等于6才会触发隐藏关,也就是要先破解完第六关才会往下走,否则会跳过隐藏函数。我上面的截图是第一关调用的phase_defused函数,所以是1。经过调试,来到第六次调用,于是成功往下运行,不跳过:

 ;略去入栈操作
   0x08049200 <+42>:	push   $0x804a1c9
   0x08049205 <+47>:	push   $0x804c4d0
   0x0804920a <+52>:	call   0x8048810 <__isoc99_sscanf@plt>
   0x0804920f <+57>:	add    $0x20,%esp
   0x08049212 <+60>:	cmp    $0x3,%eax
   0x08049215 <+63>:	jne    0x8049251 <phase_defused+123>

在这里插入图片描述
发现是把第四关的通关答案再次拿出来解析,不过这次按"%d %d %s"解析,而不是之前第四关里的 "%d %d "。如果参数个数不等于3,又会导致跳过。

假设我们没有跳转,程序接着往下走:

   0x08049217 <+65>:	sub    $0x8,%esp
   0x0804921a <+68>:	push   $0x804a1d2
   0x0804921f <+73>:	lea    0x18(%esp),%eax
   0x08049223 <+77>:	push   %eax
   0x08049224 <+78>:	call   0x8048f86 <strings_not_equal>
   0x08049229 <+83>:	add    $0x10,%esp
   0x0804922c <+86>:	test   %eax,%eax
   0x0804922e <+88>:	jne    0x8049251 <phase_defused+123>

这个逻辑我在第一篇文章的后半部分提过,不在此赘述。在这里就是比较我们第四关输入的通关字符串的第三个字符串参数是否和0x804a1d2处的字符串相等,不相等又会导致跳过。
查看0x804a1d2处的字符串:
在这里插入图片描述
假设我们没有跳转,程序接着往下走,就即将到隐藏关了:

   0x08049230 <+90>:	sub    $0xc,%esp
   0x08049233 <+93>:	push   $0x804a098
   ;入栈操作
   0x08049238 <+98>:	call   0x80487c0 <puts@plt>
   0x0804923d <+103>:	movl   $0x804a0c0,(%esp)
   0x08049244 <+110>:	call   0x80487c0 <puts@plt>
   ;在进入隐藏关之前,调用puts函数,输出2句字符串
   0x08049249 <+115>:	call   0x8048e92 <secret_phase>

输出的2句字符串:
在这里插入图片描述

该死的!你找到了隐藏关卡!

但是找到它和解决它是完全不同的……

看到了语气有趣的困难预告。

到这里已经知道触发隐藏关的条件了:

  • 先要破解完6个普通关卡
  • 第四关的通关字符串要额外加一个字符串参数 “DrEvil”

分析隐藏关卡

   0x08048e92 <+0>:		push   %ebx
   0x08048e93 <+1>:		sub    $0x8,%esp
   0x08048e96 <+4>:		call   0x80490dd <read_line>
   0x08048e9b <+9>:		sub    $0x4,%esp
   0x08048e9e <+12>:	push   $0xa
   0x08048ea0 <+14>:	push   $0x0
   0x08048ea2 <+16>:	push   %eax
   0x08048ea3 <+17>:	call   0x8048880 <strtol@plt>

strtol(String to Long)函数是C语言标准库中的一个函数,用于将字符串转换为长整型数(long int类型),并返回该数值。它的函数原型在头文件 <stdlib.h> 中声明:

long int strtol(const char *nptr, char **endptr, int base);
  • nptr:要转换的字符串的指针。
  • endptr:一个指向字符指针的指针,用于存储转换停止的位置。如果不想获取这个信息,可以传入 NULL
  • base:进制数,指定待转换字符串的进制。

在这里就是把我们的输入转成10(0xa)进制。

   0x08048ea8 <+22>:	mov    %eax,%ebx
   0x08048eaa <+24>:	lea    -0x1(%eax),%eax
   0x08048ead <+27>:	add    $0x10,%esp
   0x08048eb0 <+30>:	cmp    $0x3e8,%eax
   0x08048eb5 <+35>:	jbe    0x8048ebc <secret_phase+42>
   0x08048eb7 <+37>:	call   0x804907d <explode_bomb>
   ;输入的数字大于0x3e8 (即1000) 就炸
   
   0x08048ebc <+42>:	sub    $0x8,%esp
   0x08048ebf <+45>:	push   %ebx
   0x08048ec0 <+46>:	push   $0x804c088
   ;把ebx和0x804c088处的数据作为参数传入fun7 ebx是我的输入 另一个是题目定好的变量n1
   0x08048ec5 <+51>:	call   0x8048e41 <fun7>

在这里插入图片描述
这里发现了一个变量名n1,回顾一路解题的过程中,我们一共遇到过2次这种情况:

  • 一次是第五关有个叫arrary的变量,这个告诉我们要用数组这个数据结构来理解内存里的数据。
  • 一次是第六关有个叫node1的变量,这个告诉我们要用链表这个数据结构来理解内存里的数据。

那说明n1既不是数组也不是链表,那是什么呢?我们来试试查看从他开始的接下来的变量名,但是,内存空间该地址该怎么变换呢?地址该+4还是+8还是别的特殊位数?目前我们不知道,就先不碰运气枚举查看了,先往下看。

从fun7出来后:

   0x08048eca <+56>:	add    $0x10,%esp
   0x08048ecd <+59>:	cmp    $0x5,%eax
   0x08048ed0 <+62>:	je     0x8048ed7 <secret_phase+69>
   0x08048ed2 <+64>:	call   0x804907d <explode_bomb>
   ;fun7返回值不等于5就爆炸
   0x08048ed7 <+69>:	sub    $0xc,%esp
   0x08048eda <+72>:	push   $0x8049fbc
   0x08048edf <+77>:	call   0x80487c0 <puts@plt>
   0x08048ee4 <+82>:	call   0x80491d6 <phase_defused>
   ;fun7返回值等于5就打印提示语 通关
   0x08048ee9 <+87>:	add    $0x18,%esp
   0x08048eec <+90>:	pop    %ebx
   0x08048eed <+91>:	ret    

在这里插入图片描述
小结一下,目前得到的信息就是隐藏关的通关字符串是一个数字,且不能超过1000。数据结构未知。

分析这一关的数据结构

   0x08048e5b <+26>:	pushl  0x4(%edx)

   0x08048e77 <+54>:	pushl  0x8(%edx)

从这两句可以看出,那个新的数据结构能够按+4和+8两种方式跳转到下面的存储单元。

知道这个就可以查看余下的其他的变量了:

在这里插入图片描述
在这里插入图片描述
发现这个数据结构的命名是层数+在当前层的序号,每个节点存了自己本身的值还有2个指向下层节点的指针。而且一共有四层,每个节点有2个分支。所以这一关的数据结构是二叉树,更确切可以发现是满二叉搜索树
在这里插入图片描述

分析fun7代码

扫一眼就发现又是递归。
在这里插入图片描述

//按汇编代码直译:
int fun7(node *p,int input){
    int result;
    if(p == NULL){
        result = -1;
    }else{
        if( p->val > input){
            return  fun7(p->left,input) * 2 ;
        }
        result = 0;
        if(p->val != input){
            result = fun7(p->right,input) * 2 + 1 ;
        }
    }
    return result;
}

//按汇编代码意译:
int fun7(node *p,int input){
    if(p == NULL){
        return -1 ;
    }
    if( input == p->val ){
        return  0 ;
    }
    
    if( input < p->val ){
        return  fun7( p->left, input ) * 2 ;
    }
    
    if( input > p->val ){
        return  fun7( p->right, input ) * 2 + 1 ;
    }
    
}

之前分析fun7最后应该要返回5,不然就会触发炸弹。那就意味着:

  • 输入的值不能等于36这个根节点,不然直接返回0。
  • 如果过程中来到了叶子节点,并且此时它与输入值不相等,就会导致返回值为-1,就不可能通过(-1)*2 +1or +0 最后达成返回5的结果,所以输入值不能是遍历到达了叶子结点发现找不到,回溯到上一层去另外一个节点,即过程中不可以发生回溯。所以最后一定是找到了,所以输入值是树上的节点。
  • 所以5应该是递去到结点等于输入值,然后返回0, 然后一次次归回0x2+1, 1x2=2, 2x2 +1 =5。

上面是从递去终点开始分析的,现在回过来结合fun7代码按正常顺序:

  • 输入值 > 根 去右孩子
  • 输入值 < 当前节点的值 去左孩子
  • 输入值 > 当前节点的值 去右孩子
  • 输入值 == 当前节点的值 返回

在刚刚画出的二叉树上走一遍即可发现输入值应该是47。

后记

写到这里,这个系列共7篇文章终于完结了。

一路写下来发现有些地方可以讲解得更连贯,但是博客讲解还是有点受限,也许以后会录制视频版?

感谢你看到这里!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值