Android系统安全 — 4-理解系统签名用dm-verity机制

dm-verity简介

       它是dm(device mapper)的一个target,是一个虚拟块设备,专门用于文件系统的校验。fs在挂载的时候直接指定 dm-verity 设备,也就是fs直接交互的设备是 dm-verity,dm-verity 调用真正的块驱动去读取对应的块,并计算hash值和hash-tree中对应的hash值进行比较,如果相等,则说明块没有被篡改,返回块数据给fs,如果不相等,则说明块被篡改,根据mode是返回EIO,或者直接重启。

        首先通过ioctl去crt(create)一个dm-verity设备,通过传入参数指定这个创建的dm-verity设备的一些特性,传入的参数包括verity-table,当然也可以使用命令直接创建:

Set up a device:
  # dmsetup create vroot --readonly --table \
    "0 2097152 verity 1 /dev/sda1 /dev/sda2 4096 4096 262144 1 sha256 "\
    "4392712ba01368efdf14b05c76f9e4df0d53664630b5d48632ed17a137f39076 "\
    "1234000000000000000000000000000000000000000000000000000000000000"
verity-table的内容如下:

def build_verity_table(block_device, data_blocks, root_hash, salt):
    table = "1 %s %s %s %s %s %s sha256 %s %s"
    table %= (  block_device,
                block_device,
                BLOCK_SIZE,
                BLOCK_SIZE,
                data_blocks,
                data_blocks + (METADATA_SIZE / BLOCK_SIZE),
                root_hash,
                salt)
    return table

        block_device描述了该dm-verity设备对应了那个底层的块设备,第二个block_device指定了hash-tree存在于哪个块设备上,对于我这个项目就是/dev/ubiblock0_0,BLOCK_SIZE描述了多大一个块对应一个hash,一般都4k, data_blocks描述了有多少个4k的块,data_blocks + (METADATA_SIZE / BLOCK_SIZE)表示hash-tree在对应块设备上的偏移,由此来找到hash-tree,root_hash为hash-tree的根hash。

        dm-verity工作在块设备之上,所以这里是/dev/ubiblock0_0,于是就不能再用ubifs 了,因为ubifs工作在卷设备之上,而/dev/ubi0_0是一个字符设备,所以只能采用工作在块设备之上的文件系统,我这里采用了squashfs,因为它比较简单。

dm-verity的工作原理

        squashfs需要读取某个块时,调用dm-verity读取对应的块,dm-verity根据verity-table中block_device,调用block_device读取对应的块,读取到块的内容后dm-verity会算出块的sha256,然后跟verity-hash-tree中相对应的hash值进行比较,如果相等,则说明该块没有被修改过,一切正常.

         dm-verity跟文件系统是无关的,只要文件系统是工作在块设备之上的,所以ubifs是不可以的,工作在块设备之上的文件系统都是可以的,dm-verity是对逻辑块校验hash值,产生hash-tree的时候也是根据文件系统镜像来产生的(然后除ubifs之外,不存在逻辑块的概念,但是可以类似将它看出逻辑块直接等于物理块),至于逻辑块到物理块直接是怎样映射,dm-verity根本就不需要关心。

        因为dm-verity并不需要在挂载前对所有的块进行校验,而是在使用的过程中用到哪个块就校验哪个块的hash值,这样对于像android一个分区几个G来说优势就显得更加明显了。

流程图如下:

 

dm-verity是如何保证安全的?

前面说过每个block都在hash-tree中记录了对应的hash值,这样就能防止别人篡改block的内容了,但是如果黑客把block改了之后,重新计算hash把hash-tree中对应的hash值也改了呢,这样就能神不知鬼不觉了,所以必须要有一种机制防止hash-tree被篡改,hash-tree是这样一种结构,所有的block对应的hash值放在最底层,也就是第0层,如下图:

第1层的hash值由下面一层的hash值计算得到,除了第0层,其他的层hash值都不对应物理上block的hash值,它们存在的意义只是为了构建hash链,防止hash篡改,这样第0层的hash值改变了的话,上层对应的hash值也需要修改,也就是说根hash也需要修改,所以只需要一种机制能保证root-hash不被篡改就行了。

Android中采用的方法是算root-hash的签名,verity-table中保存了root-hash,对verity-table进行签名,它们的存储分布如下:

在Android中,系统进入ramdisk后,由/system/core/fs_mgr/ 负责dm-verity设备的创建,verity-table的校验,这里涉及到的一些知识是:

1.如何知道哪些分区需要校验?

fs_mgr通过读取fstab文件,其中记录了哪些分区需要校验

2.如何知道需要校验的分区中verity-table的位置?

这是用户空间(/system/core/fs_mgr/ )的工作,android的做法是通过读取文件系统的超级块(Superblock,简称SB),里面记录了文件系统的大小,verity-table紧挨着文件系统镜像之后

3.签名的key存放在哪里?

这里指的是public key

verity-table-metadata主要是为了校验verity-table的合法性,android的格式为:

def build_metadata_block(verity_table, signature):
    table_len = len(verity_table)
    block = struct.pack("II256sI", MAGIC_NUMBER, VERSION, signature, table_len)
    block += verity_table
    block = block.ljust(METADATA_SIZE, '\x00')
    return block

verity-table的校验是在用户空间(/system/core/fs_mgr/ )中完成的,校验合法之后会将verity-table传给kernel使用。ublic key存放的位置可以自己决定,如放在ramdisk中或放在OTP中(一般放在OPT中)

深入研究参考

Android 中的dm-verity原理分析_forestcell的博客-CSDN博客_dm verity

注:  这是参考相关文档总结出的精华,若有侵权问题,请立即联系我删除该文档

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值