计算机系统ARM64拆除炸弹

一、内容简介

1.1 拆弹实验

        拆弹实验是csapp课程的一个经典实验,实验共有六关(phase_1 ... phase_6),还有一个隐藏关卡(secret_phase)。该实验与x86架构下的拆弹实验的内容上基本没有差别,所以以下内容是基于本人已经有过bomb实验的经验而写。

1.2 ARM64

        这次实验是面向基于鲲鹏的华为TaiShan 服务器的拆弹实验。这是与之前的计算机系统拆弹实验的不同之处,也是这次实验的难点。在ARMv8处理器下,我们对"bomb"进行反汇编,得到的是ARM64汇编代码。由于没有正式学习过ARM64汇编,所以这个实验的一个重点就是通过查阅、学习ARM64汇编的各种指令,然后“拆弹”。

二、实验过程

2.1 phase_1

        phase_1的拆弹密码的格式是一个字符串,当然,目前这只是一个经验的推断,我们通过objdump得到的汇编可以验证我们的推断是正确的。

        adrp是一个取址指令。图中的两句指令,将地址0x4023b0加载到寄存器X1中,之后调用了过程<strings_not_equal>,功能是判断输入的字符串与答案是否匹配。出于好奇以及推理,我们想要查看0x4023b0这个地址存储的是什么,于是:

        验证答案:

        phase_1拆解成功。

2.2 phase_2

        在这一阶段,我们要理解的核心就是如下图的“循环”。在这一循环中,sxtw指令是我们未曾在x86下见过的,他在这里的使用已经在图中说明。

   

        w19寄存器存储的是一个“循环变量”,或者说“循环计数器”。每次循环+1,w2与w19之间的关系是:w2存储的值始终为w19存储的值+1。w1存储的是:“将w19 signed-extended之后,左移两位,并将其作为偏移量,与x0所存储的地址相加,并将得到的地址所存储的数据加载w1”。w0的存储类似。

        从过程<read_six_numbers>可知,这一阶段的拆弹密码是六个数字。我们不妨理解输入的六个数字存储在一个数组中: int a[6].且sizeof(int) = 4,我们就不难理解这一循环的对应的逻辑关系应该是a[i+1] = a[i] + (i+1).(0 <= i <= 5)所以这一过程的答案并不唯一,满足这个关系,且在int类型的范围内的正数,均可“拆弹”,比如:0 1 3 6 10 15; 1 2 4 7 11 16; ……

 

        phase_2拆解成功。

2.3 phase_3

        根据反汇编文件这段代码可以推断,这个阶段应该对于根据不同的输入,会有不同的拆弹密码。对应的也就是switch开关语句。

        Arm64架构下的汇编并不像x86汇编有明显的跳转表,所以在这里,我们不妨输入第一个数字为4,跟踪观察其在后续过程的变化:

        最后这里取出输入的第二个数字存储在w1,并与跳转了一通的w0存储的数字比较,如果不相等,炸弹爆炸。所以推测w1存储的值此时应该与w0存储的值相等,为0。于是,我们推理出这个阶段的一个拆弹密码:4 0

        验证答案:

        phase_3拆解成功 。

2.4 phase_4

输入格式:

 调用fun4之前的值:

        理解fun4是拆解这个炸弹的核心,如下图1,我们可以看到每次调用fun4首先对w19内的值进行操作,即w19 = (w1 + w2)/2; 然后将w19与w0中的值进行比较,如果大于,则跳转到(...),如若小于,则跳转到(...)。然后我们来看跳转之后的指令。调用fun4时w1置为0,w2置为14,w19为w1与w2的平均值,如果w19比w0小,为了要使w1与w2的平均值增大,就将w1置为 w19 - 1,然后再递归调用fun4;如果大于,类似,将w2置为w19 + 1,然后递归调用fun4。我们还注意到每次递归调用完fun4,都会有一个累加的指令,也就是将w19与w0的和载入到w19中。当递归调用的最里层fun4结束时,也就是不再继续调用的条件为w19 = w0,当最后一层的调用完成,执行累加指令,然后又结束该层调用,执行累加指令……直到退回第一层递归。那么完成整个fun4的过程回到phase_4之前,又将累加的结果放回到w0中。

        回到phase_4之后,有两次cmp比较指令,都要求相等才不会使炸弹爆炸,之一为w0与0x1b比较,之二为[sp, #24]与0x1b比较。由此可以知道,答案的第二个数字为0x1b,即27,且fun4调用结束的期待的w0的值为27,然后我们回到fun4:27 = 9*3,也就是说如果w0置为9,需要递归调用fun4三次。那么调用三次之后能否满足结束递归调用的条件呢?通过计算每次递归调用的w1与w2的平均值,并按照前面说的重置w1或w2的过程,最终计算发现,如果w0置为9,递归三次之后w19恰好为9,此时,w19 = w0,正好符合我们的期待。于是我们推断该阶段炸弹的密码为:9 27

        验证答案:

         phase_4拆解成功。

2.5 phase_5

输入格式,与上阶段的完全一致:

 在后续的指令中发现了一个可疑地址,查看发现是一个数组:

理解下图这个循环:

  • w1取出sp+28存储的值,并和0xf比较。如果相等,结束这个循环。
  • w2每次加一;ldr指令:将w1 signed-extended,然后左移两位,并将此作为偏移量加上x0的基地址,将得到的地址中的数据加载到w0中。

str指令:将w0的内容存储到栈指针sp+28中。

  • w3:累加,每次都将w0的值累加到w3中。

        前面说到如果w1 = 0xf结束循环,调转到下图的指令。从下图标记的指令可以知道:w1 = 0xf时,w2也要等于0xf,w3要等与输入的第二个数值。

        至此,我们大致可以非形式化的描述这个过程:首先,取栈指针中的值,并据此计算偏移量,存储数组中的值到栈指针中,直到取到的值为0xf。在这个循环中,w2相当于循环计数器,程序期待其最终值为:w2 = 0xf,所以要求循环次数必须为15;w3为记录每次取到数组中的值的累加和。

 

模拟过程,推断密码为:5 115

        验证答案:

        phase_5拆解成功。 

2.6 phase_6

        1.发现调用了<read_six_numbers>过程,说明输入了六个数字

        2.对输入数字进行比较,六个数字均在区间[1, 6],且互不相同,则六个数字就是1,2,3,4,5,6的一种排列组合。

        3.发现取址指令,进行查看,发现一个链表:

         4.通过后续的一步步调试,发现后续是在对链表进行降序排序;

         5.但这个阶段的排序并不简单,因为这里对链表的序号进行了“取补”,即用7-n表示链表的新编号。

         6.所以,如果只是降序排序的话,链表编号应为:3 5 1 2 4 6

         7.“取补”之后则为:4 2 6 5 3 1

        验证答案:

         phase_6拆解成功。

2.7 secret_phase

       

        观察<main>过程,发现每个phase结束都会调用<phase_defused>,但只有当phase_6结束,传给<phase_defused> w0 = 6时,这个过程才起作用。

         如下图,两次取址,可以分析出这里是在分析phase_4 输入的密码,如果输入了”%d %d %s”,那么进入判定隐藏阶段的环节,否则直接结束拆弹环节。此外,通过调用<string_not_equal>之前的地址查询,可知进入隐藏阶段的密码就是DrEvil。

进入隐藏阶段:

有一个可疑地址,然后调用了<fun7>。通过查询发现,这个地址存储的是一个树。

        进入fun7之后,会发现这个过程类似之前的phase4,都是递归调用。只是递归的规约不同。所以我们直接来看secret_phase期待的fun7返回值:w0 = 4.

        返回来模拟递归过程:4 = (((0*2 + 1) *2)*2).所以根据树反向查询输入的值应为7

        验证答案:

         成功进入。

        secret_phase拆解成功。 

        以上只是实验拆解炸弹的过程,并非完整的学习ARM并应用的过程。其中的很多指令是为了应用理解,故没有实现借此实验完整地学习ARM64汇编的目的。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值