网络知识 娱乐 HUST网络攻防实践|6_物联网设备固件安全实验|实验二 基于 MPU 的物联网设备攻击缓解技术

HUST网络攻防实践|6_物联网设备固件安全实验|实验二 基于 MPU 的物联网设备攻击缓解技术

文章目录

  • 实验二 基于 MPU 的物联网设备攻击缓解技术
    • 实验要求
    • 实验准备
      • Keil安装及使用
      • 分析给出的`.c`/`.h`/`.lib`文件的内容和作用
    • 子任务1 解除代码段不可写入保护将指定代码段改为可写
      • 逆向分析 AttackTest 函数
        • 违反 MPU 区域保护的指令
        • 违反的权限
      • 解除 AttackTest 函数违反规则的新 MPU 区域重配置代码
      • 设置的区域说明
        • CTRL
        • RNR
        • RBSR
        • RASR
    • 子任务 2 解除特定外设区域保护将指定外设区域改为可读写
      • 逆向分析 AttackTest 函数
        • 违反 MPU 区域保护的指令
        • 违反的权限
      • 解除 attackTest 函数违反规则的新 MPU 区域重配置代码
      • 设置的区域说明

写在最前:注意下载最新版lib(6月2日更新版),并结合自己的lib分析。分析过程我写得有点长,是为了帮助大家找到自己的地址、并理解它。实际上,需要写进报告的内容只有子任务2那么点

实验二比较麻烦,附加内容我其实已经写进博客了,如果想写的话,请自行整理吧。实验三很简单,实验二比较麻烦而且比较长,考虑到大家应该还要写一下报告,我就分开先发实验二了。

实验二 基于 MPU 的物联网设备攻击缓解技术

实验要求

实验的 lib 库代码中开启了多项 MPU 区域保护

请根据不同的子任务重配置 MPU 区域解除相应的区域保护

  • 子任务 1 解除代码段不可写入保护将指定代码段改为可写
  • 子任务 2 解除特定外设区域保护将指定外设区域改为可读写

注:请通过逆向分析出触发 Memfault 的指令,从而在满足任务要求的同时尽可能用最 少的 MPU 区域配置、最小的 MPU 区域范围,最小的特权级。

特权级排序:rwx(可读可写可执行)> rw>rx>r>无权限

注意:任务2要用架构mps2-an386

实验准备

Keil安装及使用

安装破解过程:

  1. 安装Keil5:一路Next就行,名称邮箱全部任意填,安装驱动什么的一律选是,安装路径最好不要包含中文,或者直接默认
  2. 破解Keil5:以管理员权限打开刚刚安装的Keil5,关掉弹出来的让安装包的窗口,然后点击File-License Management,复制CID到破解软件中。破解软件填写CID、设置TargetARM之后,点击Generate生成LIC,粘贴到Keil5LIC输入框中。点击Add LIC就行。
  3. 安装ARM CMSDK_CM4_FP:老师发的Keil5默认是没有我们要的ARM CMSDK_CM4_FP。不过老师还发了两个.pack文件,双击安装就行。一路Next安装路径要和Keil5的安装目录相对应

新建项目并添加文件:

Project-New uVersion Project ,然后配置直接按实验指导书就行,后面也直接按指导书。添加文件可以直接拖进去。编译就是Project-build

注意:如果之前装过其他版本的Keil,并新建了项目,记得删掉项目目录中的Listings、Objects、RTE,否则它会自动优先从这些目录下读取配置,版本就不对。

如果配置项完全正确,和指导书完全一致,直接运行时就会0 Error。

可执行文件会生成在Objects/下,运行如下指令运行:

~/qemu-7.0.0/build/qemu-system-arm -M mps2-an386 -cpu cortex-m4 -m 16M -nographic -d in_asm,nochain -kernel ~/exp6/task2/task2.axf -D log.txt

分析给出的.c/.h/.lib文件的内容和作用

先简单地分析一下task2.c的内容。

main函数:
① 定义无符号整型变量a,赋值为学号末4位;
② 调用prvSetupHardware(),硬件初始化;
③ 调用xTaskCreate,创建任务,任务内容为vTaskStart,任务名称为"Task2",权限为特权级;
④ 调用StartFreeRTOS(a)
⑤ 使用for(;;)让程序不退出。

vTaskStart的任务中,调用了AttackTest()

在逆向分析过后,可以进一步确定:在StartFreeRTOS(a)中包含对任务序列的启动。当启动了Task2后,将会调用AttackTest()。如果AttackTest没有触发MemMange_Handler异常,在一定的条件下,就会顺利打印输出flag;当AttackTest触发异常后,该任务就会结束。

MemMange_Handler异常有触发延时,所以旧的lib总是在异常还没被触发时,就已经打印了flag,所以老师更新了新版lib文件。

通过查看task2.h,可以看到AttackTestprvSetupHardwareStartFreeRTOS的函数声明,而这些函数的具体实现,正是在task2x_xx.lib文件中。

当项目构建完成之后,所有的文件的内容都会被链接到可执行文件task2.axf中。

子任务1 解除代码段不可写入保护将指定代码段改为可写

本任务中,需要将task2.ctask2.htask2a_89.lib添加到Project中,注意要下载最新版的.lib文件。

逆向分析 AttackTest 函数

首先,不加修改地直接编译项目,得到task2.axf,以便逆向分析AttackTest函数。编译结果如下图所示:

在这里插入图片描述

在IDA Pro中打开文件task2.axf,选择的选项和上一个实验一致。

根据之前对三个文件的分析,我们能很轻易地找到AttackTest函数在逆向中的位置,顺着点开它,它的内容如下图所示:
在这里插入图片描述

F5反汇编得到的结果如下图所示:

在这里插入图片描述

接下来,先保证能正确进入输出flag的条件分支内。

《ARM Cortex-M3与Cortex权威指南》(后简称为权威指南),第11章写了与MPU区域设置相关的寄存器,其中0xE000ED94就是寄存器CTRL。根据静态地址和寄存器的对应关系,预定义如下:

#define MACRO_CTRL 0xE000ED94
#define MACRO_RNR 0xE000ED98
#define MACRO_RBAR 0xE000ED9C
#define MACRO_RASR 0xE000EDA0

然后,在vTaskStart里添加如下几行代码,以便通过指针形式来访问寄存器,并打印所有原始区域:

volatile unsigned int * pCTRL=(volatile unsigned int *)MACRO_CTRL;
volatile unsigned int * pRNR=(volatile unsigned int *)MACRO_RNR;
volatile unsigned int * pRBAR=(volatile unsigned int *)MACRO_RBAR;
volatile unsigned int * pRASR=(volatile unsigned int *)MACRO_RASR;
for(int i=0;i<8;i++){
	*pRNR=i;
	printf("%d,%d,%08x,%08xn",*pRNR,*pCTRL,*pRBAR,*pRASR);
}

寄存器有自己的结构,这里先定义结构体、再定义结构体指针会更合适一些。但是,由于实验任务简单,并没有太多需要修改的内容,所以我干脆直接用无符号整数赋值了。

为保证至少有一个MPU是使能的,再在vTaskStart里的循环体后添加一行代码:

*pCTRL=5;

然后Rebuild并直接运行一下,看看在哪里触发了异常,运行后的结果如下图:

在这里插入图片描述

根据老师的说法,MemMange_Handler这个异常会有延时,所以违反 MPU 区域保护的指令是触发异常的部分上面几行代码,而不是紧随其后的其他代码。即:

0x000002c2:  f44f 40e0  mov.w    r0, #0x7000
0x000002c6:  490c       ldr      r1, [pc, #0x30]
0x000002c8:  6001       str      r1, [r0]
0x000002ca:  f8c0 1080  str.w    r1, [r0, #0x80]
0x000002ce:  e7ec       b        #0x2aa

对应的AttackTest在IDA Pro中的汇编代码如下:

LDR指令的作用:将存储器地址所指地址处连续的4个字节的数据传送到目的寄存器中。

MOV.W	R0, #0x7000		; 将0x7000赋值给R0寄存器
LDR		  R1, =0x12345678    ; 将0x12345678赋值给R1寄存器
STR		  R1, [R0]		       ; 将R1的连续4字节的数据传送到R0寄存器指向的地址中
STR.W	  R1, [R0,#0x80]      ; 将R1的连续4字节的数据传送到(R0寄存器指向的地址+0x80)中
B		  Judge			   ; 跳转执行Judge

显然,访问了#0x7000#0x7080这两个地址,并且都需要写权限。

分析老师设置的所有的MPU的RASR的AP属性,可以看出,区域0、1、3都是只读的,其他区域对特权级都是可读可写的,区域6和7暂时没有设置。AP域编码对应的访问权限如下图所示(摘自权威指南):

在这里插入图片描述

而结合基址RBAR和RASR中的区域大小属性,可以看出,#0x7000#0x7080这两个地址都在区域0和1内,而它们都是不可写的。

不过,除了这几行指令,保不齐后面还有别的访问不了的呢?继续往后看。

打印flag函数的条件分支体如下:

在这里插入图片描述

其中打印flag的条件分支体对应的代码解释如下:

1	ldr R0, #0x20000038	; 将0x20000038赋值给R0寄存器
2	ldr R1, [R0]		 ; 将R0寄存器指向的地址上的值读出并赋值给R1
3	adds R1, R1, #2		 ; R1 = R1 + 2,影响进位标志位C的加操作
4	str R1, [R0]		 ; 将R1的连续4字节的数据传送到R0寄存器指向的地址中
5	adr R0, aFlagU		; 把格式化字符串的地址赋值给R0
6	b.w __2printf	       ; 调用__2printf

总之,Judge中还涉及了内存地址0xE000ED94的读权限和0x20000038的读写权限。好在,通过MPU的信息可以看出,0x20000038虽然在区域2内,但是区域2可读写。0xE000ED94则不被约束。

因此这两个地址并不是违反MPU保护的地址。

违反 MPU 区域保护的指令

综合以上所有分析,可知,违反MPU区域保护的指令是AttackTest函数中的:

STR		  R1, [R0]		      ; 将R1的连续4字节的数据传送到R0寄存器指向的地址中
STR.W	  R1, [R0,#0x80]      ; 将R1的连续4字节的数据传送到(R0寄存器指向的地址+0x80)中

违反的权限

0x70000x7080这两个地址的写权限。

解除 AttackTest 函数违反规则的新 MPU 区域重配置代码

总的来说,我添加的解除AttackTest函数违反规则的新 MPU 区域重配置代码如下:

在这里插入图片描述

运行并得到flag的结果如下图:

在这里插入图片描述

设置的区域说明

CTRL

CTRL=5。表示使能。

RNR

RNR=6,表示区域6。

为了不覆盖老师的设置,我选择区域6来配置新的MPU区域。

RBSR

RBSR设置为0x7000

RBSR的结构如下图(摘自权威指南):

在这里插入图片描述

上表的误导性非常强,我起初以为ADDR是独立出来的,比如当地址是0x7000、区域是1时,对应ADDR部分是0x7000,对应RBAR是0x70001。好在一位同学及时点醒了我,往后看一下权威指南,会发现地址部分是直接赋值的,根本没有考虑最后四位数。

经过实践,可以得到结论如下:

一方面,当倒数第五位数VALID是0的时候,REGION位的设置不会影响修改的MPU区域编号寄存器,程序会使用MPU区域编号寄存器选择的区域,因此最后四位数可以是任意值

另一方面,地址必须是REGION域大小的整数倍。首先,我们需要知道每一位地址能索引8比特的数据。对于大小为32B即(32×8)bits的数据,理论上需要用 l o g 2 ( 32 ) log_2(32) log2(32)位、也就是5位地址去表示,而每4位二进制数对应一个16进制数。因此,使用(00~1f)即可表示32B的数据,而每32B的数据之间的间隔,应当是0x20。
REGION域大小最小为32B,地址是N×0x20,N为整数。因此,非零地址最小是0x20。即使直接令RBAR=0x20,都不会影响到倒数5位二进制数的设置。(相当于,倒数5位本来是保留位,只是为了方便,所以多定义了VALID域和REGION域)
地址对应容量大小参考博客:【嵌入式基础】为什么0x100是256个字节、0x400是1KB、0x800是2KB、0x1000是4KB?。

总之,RBSR=0x7000,意思就是MPU的基址是0x7000

RASR

RASR=0x0102EE0F,对应属性如下:

  • AP = 001,对应只支持特权级读/写;
  • TEX S C B = 000 0 1 0,对应ROM, Flash(可编程存储器);
  • SRD = 11101110,对应子区域0和4启用,其他禁用;
  • 区域大小 = 00111,对应区域大小256B;
  • 使能 = 1,使这块MPU能用。

接下来解释一下我设置的AP、SRD、区域大小、类型为什么是最小的:

  • AP

    AP编码与访问权限的对应关系如下图(摘自权威指南):
    在这里插入图片描述

    对比上表可以看出,满足我们特权级任务、读写权限要求的最小访问权限就是001

  • 区域大小

    区域大小的编码如下图(摘自权威指南):
    在这里插入图片描述

    想要取最小的,那就取32B,也就是b00100。可惜我需要访问0x70000x7080这两个地址,只取128B都不行,因为取128B、基址是0x7000的时候,MPU对应的区域只有0x7000~0x707F。所以取256B,对应b00111

  • SRD

    当区域大小达到256B的时候,这个MPU足够大,能够设置8个子区域了。

    因此,我还得考虑子区域禁止属性SRD的设置。(我问过别人,别人的lib只访问了一个地址,或者访问的地址跨度没这么大。因此,我这居然是属于特殊情况)

    我的子区域0和子区域4是必须启用的,其他都可以禁用。

    所以设置为11101110

  • 类型

    只是简单介绍一下大类,不对TEX、S、C、B做详细介绍。

    本实验涉及到的TEX、S、C、B的设置如下图(摘自权威指南):
    在这里插入图片描述
    显然,设置成ROM,Flash是权限最小的。

子任务 2 解除特定外设区域保护将指定外设区域改为可读写

在子任务1的分析基础上,不需要太多解释。
再加上预设的MPU没变,所以基本上只要改改地址范围即可。
实验结果如下。

逆向分析 AttackTest 函数

在这里插入图片描述

违反 MPU 区域保护的指令

AttackTest中的STR R0, [R1]

违反的权限

0x40010000的写权限。

解除 attackTest 函数违反规则的新 MPU 区域重配置代码

代码,以及相比子任务1的关键修改:

在这里插入图片描述

运行结果:
在这里插入图片描述

设置的区域说明

RNR=6。

CTRL=5。

RBSR设置为0x40010000

RASR=0x01020009,对应属性如下:

  • AP = 001,对应只支持特权级读/写;
  • TEX S C B = 000 0 1 0,对应ROM, Flash(可编程存储器);
  • SRD = 00000000,区域大小未达到256B,因此该属性不需要配置;
  • 区域大小 = 00100,对应区域大小32B;
  • 使能 = 1,使这块MPU能用。