• HUST网络攻防实践|6_物联网设备固件安全实验|实验二 基于 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
    
    • 1

    分析给出的.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
    
    • 1
    • 2
    • 3
    • 4

    然后,在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,%08x\n",*pRNR,*pCTRL,*pRBAR,*pRASR);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

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

    *pCTRL=5;
    
    • 1

    然后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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    对应的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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    显然,访问了#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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    总之,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)中
    
    • 1
    • 2

    违反的权限

    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能用。
  • 相关阅读:
    2-2Linux下文件操作常用命令
    Winform DataGridView 跳转到指定数据行
    【华为机试真题】字符串匹配
    Python的安装及其python程序生成exe可执行程序
    ADS127L11采集板系统噪声评估
    华为P系列“砍了”,三角美学系列全新登场
    浏览器跨域请求
    【python基础】函数-参数形式
    ptp协议相关术语
    数码管的动态显示(三)
  • 原文地址:https://blog.csdn.net/qq_46106285/article/details/125455204