深入理解计算机系统|Bomb Lab

Phase_1

汇编代码
在这里插入图片描述

思路分析
在这里插入图片描述

  • 由此两行可知程序将一个地址放入了%esi并调用了 strings_not_equal 函数,应该是将输入的内容与 %esi 中地址存储的内容比较,若不相等则爆炸。
  • 用examine命令查看地址中的内容
    在这里插入图片描述
  • 得到一个字符串应该就是拆弹密码。
  • 退出gdb并验证
    在这里插入图片描述
  • 拆弹成功。

Phase_2

汇编代码
在这里插入图片描述

思路分析

  • 一开始先保护寄存器值,然后将栈的长度增加0x28用于存储。

  • 之后通过callq调用read_six_number函数,应该是用于读取6个数字,之前增长的栈空间应该是用来存这个有6个元素的数组,而数组首地址被放在了%rsi。

  • 用gdb获取read_siz_number函数的代码
    在这里插入图片描述

  • 可以得知read_six_number函数是通过sscanf函数得到输入,由
    在这里插入图片描述
    可以得知sscanf所需要的格式化字符串所在的地址,查看得
    在这里插入图片描述
    是六个有符号整数。

  • 设这个数组为a[6],sscanf还需要6参数即数组中每个元素的地址。

  • 根据调用规定,前4个元素的地址应该用寄存器%rdx,%rcx,%r8,%r9来传递,后2个通过栈来传递。在代码中对应寻找可以看到:
    &a[0]
    在这里插入图片描述
    &a[1]
    在这里插入图片描述
    &a[2] 和 &a[3]
    在这里插入图片描述
    &a[4]
    在这里插入图片描述
    &a[5]
    在这里插入图片描述

  • 回到phase_2函数
    在这里插入图片描述
    我们已经知道%rsp里存放着数组首元素,所以此处将数组第一个元素和1比较,不同就爆炸,相同就跳转到0x400f30。

  • 这里使一开始就压入栈内的两个寄存器指向a[1]和a[6](a[5]后的一个int大小位置),由之后的跳转指令猜测是为了循环准备。
    在这里插入图片描述

  • 跳转到0x400f17进入循环体:
    在这里插入图片描述
    mov -0x4(%rbx),%eax即为使%eax指向%rbx所代表项的前一项,之后将前一项乘以2,再与当前项比较,不同就引爆炸弹,相同则%rbx指向下一个元素,直到末尾(此时a[6]的作用就显现了)。

  • 已知数组第一个元素一定是1,得到拆弹密码:1 2 4 8 16 32

  • 退出gdb并检验:
    在这里插入图片描述
    成功。

Phase_3

汇编代码
在这里插入图片描述在这里插入图片描述

思路分析

  • 首先从寄存器%esi中取出sscanf的格式化字符串
    在这里插入图片描述
    得到输入类型是2个有符号整数,定为n1,n2。之后判断sscanf的返回值(即读取字符个数),小于1则引爆炸弹。

  • 接着判断n1,大于7则跳转到0x400fad引爆炸弹。
    在这里插入图片描述

  • 这里是拆弹核心重点,由此我们得到一张跳转表
    在这里插入图片描述
    n1的范围为0~7,写出伪代码

switch (n1)
case 0: goto 0x0000000000400f7c  if(n2!=0xcf)  boom
case 1: goto 0x0000000000400fb9  if(n2!=0x137)  boom
case 2: goto 0x0000000000400f83  if(n2!=0x2c3)  boom
case 3: goto 0x0000000000400f8a  if(n2!=0x100)  boom
case 4: goto 0x0000000000400f91  if(n2!=0x185)  boom
case 5: goto 0x0000000000400f98  if(n2!=0xce)  boom
case 6: goto 0x0000000000400f9f  if(n2!=0x2aa)  boom
case 7: goto 0x0000000000400fa6  if(n2!=0x147)  boom
  • 拆弹密码即为:
    (0,207)、(1,311)、(2,707)、(3,256)、(4,389)、(5,206)、(6,682)、(7,327)
  • 退出gdb并检验:
    在这里插入图片描述
    成功。

Phase_4

汇编代码
在这里插入图片描述

思路分析

  • 首先通过明码地址分析输入的类型
    在这里插入图片描述
    两个有符号整数,仍设为n1(0x8(%rsp)),n2(0xc(%rsp))。

  • 分析代码可以看到
    在这里插入图片描述
    即输入字符个数不为2,炸弹爆炸,n1大于14,炸弹爆炸。当n1小于等于14时跳转到0x4013a。

  • 再通过观察代码可知
    在这里插入图片描述
    若n2不为0,炸弹爆炸,因此n2一定为0。在0x4013a,将传入func4的两个参数设置为n1,14和0。

  • 下面分析func4
    在这里插入图片描述

  • 由func4内部调用了func4可知,这是一个递归。

  • 为了直观,定义%edi为a,%esi为b,%edx为c,%eax为x,%ecx为y。
    在这里插入图片描述
    x=c-b
    在这里插入图片描述
    x=(x>>31+x)>>1
    在这里插入图片描述
    y=(x>>31+x) >> 1 + b
    在这里插入图片描述
    比较a和y,如小于等于就跳转到0x400ff2,%rax设置为0,否则改变参数重新调用func4。
    在这里插入图片描述
    此处再比较,若大于等于(即等于)则跳转去0x401007,函数结尾,否则改变参数重新调用func4。

  • 可以逆向得到如下C语言代码
    在这里插入图片描述

  • 回到phase_4函数中分析剩余代码
    在这里插入图片描述
    可知当func4的返回值为0时拆弹成功,否则炸弹引爆。

  • 通过编写如下程序可得到答案
    在这里插入图片描述
    得到输出结果为
    在这里插入图片描述
    则拆弹密码为(0,0)、(1,0)、(3,0)、(7,0)

  • 退出gdb,任取一组密钥测试
    在这里插入图片描述
    在这里插入图片描述
    成功。

Phase_5

汇编代码
在这里插入图片描述
在这里插入图片描述

思路分析

  • 此处表明密钥一定是一个长度为6的字符串,否则炸弹爆炸。
    在这里插入图片描述

  • 跳转到4010d2初
    在这里插入图片描述
    此处将%eax的值设置为0,再跳转到0x40108b。

  • 0x40108b处有一个循环体,重点关注
    在这里插入图片描述
    最后一行的利用掩码,只取%edx字符数组的低4位。

  • 利用刚才得到的字符低4位数值作为索引来访问一个首地址为0x4024b0的数组。利用gdb查看该数组内容
    在这里插入图片描述

  • 后面的内容明示前面那个单词“不能用复制来解决炸弹”,即不是密钥。
    在这里插入图片描述

  • 仍然是访问数组,数组首地址为%rdx+0x10(应该和上一个存在映射关系)。
    在这里插入图片描述

  • 循环结束之后把数组第(0x16-0x10)个元素置为0,可知该数组有6个元素。
    在这里插入图片描述

  • 在phase_1见过的strings_not_equal函数,在这里用来比较刚才的数组和储存在0x4025e处的数组。用gdb查看得到:
    在这里插入图片描述
    此时很明了,我们输入的6个字符的低4位作为索引去访问0x4024b0的数组,最后要得到flyers。

  • 数组对照如下:
    m a d u i e r s n f o t v b y l
    1 2 3 4 5 6 7 8 9 a b c d e f g
    即下标值为9,15,14,5,6,7,转换为16进制即为0x09,0x0f,0x0e,0x05,0x06,0x07。

    • 低4位为0x09的字符有:’)’、‘9’、‘I’、‘Y’、‘i’、‘y’
    • 低4位为0x0f的字符有:/’、‘?’、‘O’、‘_’、‘o’
    • 低4位为0x0e的字符有:.’、‘>’、‘N’、‘^’、n‘、‘~’
    • 低4位为0x05的字符有:’%’、‘5’、‘E’、‘U’、‘e’、‘u‘
    • 低4为为0x06的字符有:‘&’、‘6’、‘F’、‘V’、‘f’、‘v’
    • 低4为为0x07的字符有:‘’’、‘7’、‘G’、‘W’、‘g’、‘w’
  • 以上六种中每种任选一个组合即为拆弹密钥。

  • 退出gdb,任取一组密钥测试
    在这里插入图片描述

Phase_6

汇编代码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思路分析

  • 将五个寄存器入栈,然后开辟栈空间,0x50字节不小。
    在这里插入图片描述

  • 熟悉的read_six_numbers函数,断定在此处定义了一个数组a[6],数组首地址就是当前栈顶(%rsp)
    在这里插入图片描述

  • 取出%rbp指向的数组元素再与6比较,大于6则炸弹爆炸,否则跳转到0x41128。
    在这里插入图片描述

  • 此处%r12d加1,它在程序开头被压入栈,可知它是一个计数器,然后判断它是否等于6,若相等则跳转至0x401153,此时跳出了循环。
    在这里插入图片描述

  • 若小于,则将%r12d的值赋给%ebx,之后又是一个循环,这个循环的将后续的数组元素和a[%r12d]比较,若相等则爆炸,即数组中各元素大小不相等。
    在这里插入图片描述
    之后把%ebx加一,并比较和5的大小关系,小于等于则跳转,继续循环(内循环)。
    这个双层循环我们得知数组a的6个元素值小于等于6,且彼此不相等。

  • 也是之前见过的形式,%rax指向首地址,%rsi指向尾地址后面的区块,这是一个循环,在循环体中用7减去数组a的每个元素。在这里插入图片描述

  • 明显看到这段代码仍然是一个二层循环。仔细发现里面有一个基址为%rsp+0x20,步长为8的内存区域。 在这里插入图片描述

  • 还发现一个明码地址0x6032d0,调用gdb查看其中内容在这里插入图片描述
    果然是链表,此时猜测基址为%rsp+0x20,步长为8的内存区域是一个数组,元素类型为struct Node*,定为nodes[6]。

  • 这里先是访问链表首结点entry,后访问next。
    在这里插入图片描述

  • 这里有一个循环,作用是给nodes数组赋值。将数组a的值和链表节点顺序对应起来,即a[%rsi]的值为nodes[%rsi]指向的Node节点的顺序
    在这里插入图片描述

  • 还是一个循环,它从nodes[0]开始遍历链表,并且每个结点的entry值是递增的。在这里插入图片描述
    通过回顾之前的链表结点信息,可以得到此时的数组a的值为3 4 5 6 1 2。
    从前面的代码我们已经得知数组a的6个元素就是我们的输入,并且原数组减去7才得到新的数组。
    于是得到原始数组,即拆弹密钥为4 3 2 1 6 5

  • 退出gdb,测试得
    在这里插入图片描述
    成功!

Secret_phase

汇编代码
在这里插入图片描述

思路分析

  • 隐藏关是因为在浏览汇编代码文本的时候发现的
    在这里插入图片描述

  • 首先来探索进入条件

  • 再次查看文本,发现第六关结束后会执行phase_defused的函数,即关卡结束。
    在这里插入图片描述

  • 调用gdb查看内容:
    在这里插入图片描述
    在这里插入图片描述

  • 里面有很多明码地址,一 一查看。
    在这里插入图片描述
    第一个是sscanf函数所需的格式化字符串,表示需要输入两个整型和一个字符串,之前的关卡只有第四关和第三关的密钥是两个整型,所以猜测应该是在密钥后再输入字符串“DrEvil”以进入隐藏关卡。

  • 检验得知猜测成立。
    在这里插入图片描述

  • 分析secret_phase的汇编代码

  • 调用read_line读取我们输入的一行内容,返回值为%rax。之后给寄存器赋值作为传入strtol函数,看名字猜测这个函数将我们输入的内容转换长整型数。
    在这里插入图片描述

  • 判断(输入的值-1)和0x3e8(即1000)的大小关系,小于等于则跳转,否则炸弹爆炸。
    在这里插入图片描述

  • 更新了两个寄存器的值作为参数传入fun7,第一个是我们输入的字符n2,另一个n1保存在一个明码地址里。
    在这里插入图片描述

  • 调用gdb查看内容
    在这里插入图片描述
    在这里插入图片描述

  • 查看fun7的汇编代码
    在这里插入图片描述

  • 首先对%rdi做一个判断,等于0则直接返回-1。

  • 不等于0就将%rdi的值放入%edx,再将其与我们输入的数字进行比较,若小于等于则跳转到0x401220,将%eax置为0,再做一次相同的比较,若相等则跳转至0x40123d,函数返回0。

  • 若第二次比较不相等(即输入的数小于%edx),则继续执行,取%rdi中存放的数据结构里的下一个位(步长为16),

  • 之前猜想它只是一个0x24看来太天真,重新查看,得到
    在这里插入图片描述

  • 查询资料得知这是一个二叉树结构,每个节点第1个8字节存放数据,第2个8字节存放左子树地址,第3个8字节存放右子树位置。

  • 将%rdi移到右子树位置,再调用fun7,返回后令%eax=2*%rax+1
    在这里插入图片描述

  • 若第一次比较大于,则令%rdi移到左子树位置,再调用fun7,返回后令%eax=2* %eax。下面跳至返回处。
    在这里插入图片描述

  • 综上,edi指向一个树的节点,令edi节点的值与我们读入的值进行比较。

    • 如果两者相等:返回0
    • 如果前者大于后者:%rdi移至左子树,返回2* %rax
    • 如果后者大于前者:%rdi移至右子树,返回2* %rax+1
  • 返回到secret_phase函数
    在这里插入图片描述

  • fun7的返回值必须等于2,不然爆炸。

  • 那么我们需要返回2,应该在最后一次调用返回0,倒数第二次调用返回2*%rax+1,第一次调用返2*%rax。换句话说,这个数应该在第三层,比父节点大且比根结节小。观察二叉树,唯一的拆弹密钥是:0x16(22)。

  • 退出gdb,验证得
    在这里插入图片描述
    成功!

小结

这个实验耗时很长,但是很有意思,在第三章学的内容都有体现,比如各个存储器的具体作用,各种栈操作和寄存器操作,条件控制和条件传送实现的条件分支,数据对齐等等都有所体现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值