[QCM6125][Android13] 关闭dm-verity后OTA升级兼容性校验的应对策略
1. 从一次失败的OTA升级说起关闭dm-verity后的连锁反应最近在折腾一块基于高通QCM6125平台的开发板系统是Android 13。为了让设备获得更高的灵活性比如能直接remount /分区进行一些调试和修改我按照老习惯把dm-verity给关掉了。dm-verity是Android从5.0开始引入的一个安全机制简单理解就是给系统分区比如system、vendor装了个“防盗门”和“监控摄像头”。每次启动和运行时它都会校验分区数据的完整性防止被恶意篡改。关掉它就像拆了防盗门进出/system分区修改文件就自由多了。操作本身不复杂通常就是改一下avbtool生成vbmeta镜像时的默认flags把默认值从0改成2。这个2就是AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED标志位告诉系统“这个分区的哈希树校验我不用了”。改完代码重新编译bootloader和vbmeta镜像刷机一气呵成。重启后果然可以愉快地执行mount -o rw,remount /了再也没看到那个烦人的Permission denied。正当我以为大功告成准备把修改后的固件打包成正式版本并通过OTA推送给其他测试设备时问题来了。无论是生成的全量升级包还是差分包在另一台同型号设备上尝试升级时都在 recovery 模式下卡住了。查看/cache/recovery/last_log一眼就看到了刺眼的错误E:Failed to verify package compatibility (result 1): Runtime info and framework compatibility matrix are incompatible: Vbmeta version 0.0 does not match framework matrix 1.0这个报错直指核心vbmeta的版本号与框架兼容性矩阵framework compatibility matrix不匹配。OTA升级流程中有一个关键的校验环节就是要确保当前设备运行时的状态Runtime Info与升级包所声明的兼容性要求Framework Compatibility Matrix是一致的。而vbmeta的版本号正是这个运行时信息里的一个重要组成部分。我关闭dm-verity的操作无意中动到了vbmeta的某些元数据导致系统识别到的vbmeta版本号变成了0.0而OTA包里的兼容性矩阵却要求它是1.0。版本对不上校验自然就失败了升级流程被强行中止。这就像你拿着新版身份证vbmeta version 0.0去办理一项明确要求旧版身份证version 1.0才能办的业务OTA升级柜台系统一看格式不对直接就给拒了。所以我们面临一个看似矛盾的需求既要保持dm-verity关闭带来的系统可写便利性又要让系统在OTA升级时能“瞒天过海”通过这个严格的版本兼容性校验。这不仅仅是改一个标志位那么简单需要我们深入理解Android 13的OTA校验链条找到那个关键的“开关”。2. 深入核心理解Android 13的OTA兼容性校验链条要解决问题不能只盯着报错的那一行日志。我们得把整个OTA升级过程中关于兼容性校验的链条理清楚。在Android 8.0以后Project Treble引入了VINTFVendor Interface的概念目的是让Android框架Framework和供应商实现Vendor Implementation比如芯片厂商提供的HAL能独立更新。为了实现这个目标系统需要一套机制来确认当前设备的环境是否满足一个升级包或新系统的要求。这就是compatibility matrix兼容性矩阵和runtime info运行时信息的用武之地。你可以把兼容性矩阵想象成一份“产品说明书”由升级包或目标系统镜像提供里面详细写着“要安装我你的设备必须满足以下条件内核版本至少是XXXVNDK版本是YYYvbmeta版本是ZZZ……” 而运行时信息就是你当前设备状态的“体检报告”由设备在升级前实时生成并上报。当用户发起OTA升级时Recovery系统或A/B分区的更新引擎会做这么几件事解析OTA升级包ZIP格式提取出里面的compatibility.zip。从compatibility.zip里找到framework-compatibility-matrix.xml文件这就是框架兼容性矩阵。同时系统会调用libvintf库中的相关函数收集当前设备的运行时信息生成一份runtime-info.xml。将这两份“文件”进行比对校验。校验的逻辑实现在system/libvintf/下的源码中比如RuntimeInfo.cpp。我们的报错就发生在第四步。具体到代码是在RuntimeInfo::checkCompatibility()这个函数里。它发现从运行时信息中提取的vbmeta version是0.0而从框架矩阵中提取的vbmeta version是1.0。字符串简单比较0.0!1.0于是校验失败返回false。那么vbmeta version 到底从哪里来为什么关闭dm-verity会影响它这就要追溯到vbmeta镜像本身了。vbmeta是Android Verified Boot 2.0 (AVB) 的核心它是一个小型的、被签名保护的镜像包含了启动链中其他分区boot, system, vendor等的哈希描述符、公钥以及一些属性描述符Property Descriptor。其中就有一个可选的属性用来设置vbmeta version。在默认的AVB配置和生成流程中这个版本号可能被设置比如为1.0也可能默认为空或0。当我们使用avbtool修改flags来禁用哈希树即关闭dm-verity时如果操作不当或者使用的命令参数组合没有显式处理版本属性可能会导致生成的vbmeta镜像中丢失或重置了这个版本信息。系统在读取时可能就得到了一个默认的0.0值。另一方面在构建系统镜像用于制作OTA包时构建系统会根据配置可能是BoardConfig.mk或类似文件将一个预期的vbmeta版本号打包进框架兼容性矩阵中。这个版本号通常被硬编码为1.0或其他非零值。于是矛盾就产生了设备运行时报告“我是0.0”OTA包要求“必须是1.0”两者对不上。所以解决方案的思路就清晰了无非两条路要么让设备的运行时信息“变成”1.0要么让框架兼容性矩阵“接受”0.0。从工程实践的安全和可控角度出发修改设备端客户端的校验逻辑使其在特定条件下即我们主动关闭dm-verity时绕过或通过这个检查是更常见和稳妥的做法。3. 实战策略一修改运行时校验逻辑源码级方案这是最直接、最根本的解决方案也就是修改libvintf中负责校验的源代码。我们来看原始文章中给出的补丁它修改的是QSSI.13/system/libvintf/RuntimeInfo.cpp文件// 修改前 if (runtimeAvb ! matAvb) { std::stringstream ss; ss Vbmeta version runtimeAvb does not match framework matrix matAvb; *error ss.str(); } return false; // 校验失败返回false // 修改后 if (runtimeAvb ! matAvb) { std::stringstream ss; ss Vbmeta version runtimeAvb does not match framework matrix matAvb; *error ss.str(); } return true; // 即使不匹配也强制返回true让校验通过这个改动的意图非常明确在发现vbmeta版本不匹配时虽然仍然记录错误信息到error字符串方便调试查看但函数最终返回true强行让兼容性检查通过。具体操作步骤定位源码在你的Android 13源代码树下找到system/libvintf/RuntimeInfo.cpp文件。对于QCM6125平台路径可能因厂商定制而略有不同但通常在{你的源码根目录}/system/libvintf/下。分析上下文打开文件搜索checkCompatibility函数再进一步搜索vbmeta或Vbmeta version关键词找到报错所在的代码块。确保你找到的正是比较runtimeAvb和matAvb的那个if语句。应用修改将return false;改为return true;。我强烈建议在修改处添加一行注释说明修改原因例如// [QCM6125] Modified: Force return true to bypass vbmeta version check when dm-verity is disabled. return true;这对于后续的代码维护和团队协作非常重要。重新编译修改完成后需要重新编译libvintf模块及其依赖。最干净的方式是source build/envsetup.sh lunch your_target-eng # 选择你的QCM6125目标例如 qssi_userdebug mma -j8 system/libvintf或者如果你不确定影响范围可以编译整个系统镜像make -j8更新系统将新编译出的system.img和可能相关的vendor.img刷入设备。由于你之前已经关闭了dm-verity现在可以直接刷机测试。这个方案的优缺点优点一劳永逸修改一次源码之后所有基于此代码构建的版本都具备绕过校验的能力。目标准确只影响OTA兼容性校验这一个环节不影响AVB验证启动等其他安全机制。可控性强你知道问题在哪也知道是怎么解决的。缺点需要源码你必须拥有Android系统的源代码并且有编译环境。影响官方流程这修改了AOSP的标准行为可能会在将来合并官方更新时产生冲突需要手动解决。潜在风险完全绕过版本检查如果未来OTA包真的引入了需要特定vbmeta版本才能支持的重要特性可能会导致问题。不过在关闭dm-verity这种深度定制场景下这个风险通常可以接受。一个更优雅的改进思路与其简单粗暴地永远返回true我们可以考虑增加一个条件判断。例如检查设备的一个特定属性如ro.boot.veritymode或一个我们自定义的ro.vbmeta.bypass.version.check只有当该属性表明我们处于关闭dm-verity的调试模式时才跳过检查。这样可以在生产模式和调试模式之间取得更好的平衡。不过这需要更深入的代码修改来传递和解析这个属性。4. 实战策略二调整vbmeta镜像的生成参数如果我们不希望动系统框架层的校验代码另一个思路是“治本”——确保生成的vbmeta镜像本身就携带正确的、能被兼容性矩阵接受的版本号。这样运行时信息报告的就是1.0自然就能通过校验。这需要我们仔细查看构建系统中生成vbmeta.img的步骤。通常这个过程在BoardConfig.mk或设备特定的AndroidBoard.mk中通过BOARD_AVB_*等变量配置最终由build/make/core/Makefile中的规则调用avbtool完成。关键步骤查找avbtool调用命令在编译日志中搜索avbtool make_vbmeta_image或者查看out/目录下对应版本的build-*.ninja文件找到生成vbmeta镜像的具体命令。分析参数标准的命令可能如下avbtool make_vbmeta_image \ --key path/to/private_key.pem \ --algorithm SHA256_RSA4096 \ --flag 2 \ # 这就是我们设置 HASHTREE_DISABLED 的地方 --chain_partition boot:1:path/to/boot_chain.cert \ --chain_partition system:2:path/to/system_chain.cert \ --prop com.android.build.system.fingerprint:$(BUILD_FINGERPRINT) \ --prop com.android.build.system.os_version:$(PLATFORM_VERSION) \ --output vbmeta.img注意这里缺少了设置版本的--prop。添加版本属性avbtool支持通过--prop或--prop_from_file参数向vbmeta镜像添加任意属性。我们需要添加一个名为com.android.build.vbmeta.version的属性这是AOSP中常用的命名约定并将其值设为1.0。修改构建配置在生成vbmeta的命令中加入--prop com.android.build.vbmeta.version:1.0修改构建系统你需要找到你设备配置中定义BOARD_AVB_VBMETA_SYSTEM_*等参数的地方并确保在构建脚本中这个--prop参数被正确添加。这可能涉及到修改device/目录下你设备对应的BoardConfig.mk文件或者修改avbtool的调用包装脚本。重新生成并验证清理旧的vbmeta镜像重新编译系统。刷机后可以通过以下命令验证vbmeta镜像中的属性avbtool info_image --image vbmeta.img在输出信息中你应该能看到类似Prop: com.android.build.vbmeta.version - 1.0的条目。同步框架矩阵确保你的系统构建配置中框架兼容性矩阵所期望的vbmeta版本也是1.0。这通常在framework-compatibility-matrix.xml文件中定义构建系统会自动将其打包。只要两边一致校验就能通过。这个方案的优缺点优点符合标准通过正确设置属性来解决问题更符合AVB的设计初衷。无需修改框架代码保持了AOSPlibvintf的原始逻辑未来合并更新更容易。信息明确vbmeta镜像自身携带了清晰的版本信息。缺点配置复杂需要深入理解设备的构建配置和avbtool的使用定位和修改正确的文件可能有难度。可能影响签名修改vbmeta的生成参数意味着需要重新签名vbmeta镜像你必须妥善保管好对应的私钥。平台差异不同厂商如高通的基线代码可能对vbmeta的生成有自己的一套封装需要找到其定制点进行修改。对于QCM6125平台高通很可能在device/qcom/的某个目录下提供了基础的AVB配置。你需要仔细搜索BOARD_AVB_VBMETA_VERSION或类似的变量如果不存在可能需要仿照其他属性添加的方式将其集成进去。5. 进阶考量与避坑指南解决了基本的校验问题并不意味着就可以高枕无忧了。在实际的开发和维护中还有一些进阶的细节和潜在的“坑”需要注意。5.1 差分升级包Delta OTA的特殊处理全量包完整镜像校验通过差分包可能还会出问题。差分包升级时除了进行兼容性校验还会检查设备的当前状态是否与差分包构建时所基于的“源版本”完全一致。任何微小的差异都可能导致差分应用失败。当你关闭dm-verity后系统分区的内容虽然没变但分区的元数据metadata和签名信息已经改变了。制作差分包的工具如imgdiff或ota_from_target_files脚本内部逻辑在计算差异时可能会把这些元数据差异也算进去导致生成的差分包非常大或者根本不可用。应对策略在生成用于制作差分包的目标文件target_files.zip时确保用于制作“源版本”和“新版本”的构建环境、配置特别是AVB相关配置是完全一致的除了你特意修改的dm-verity标志位。最好能建立一个清晰的记录标明哪个版本的vbmeta是关闭了dm-verity的。对于重要的OTA更新建议同时提供全量包和差分包让用户可以根据实际情况选择。在测试阶段务必对差分包升级流程进行充分验证。5.2 与AVB其他功能的兼容性dm-verity只是AVB功能的一部分。关闭它时要留意是否会影响AVB的其他特性例如启动回滚保护Rollback ProtectionAVB通过存储在vbmeta或特定分区中的防回滚索引rollback index来防止设备降级到旧的不安全版本。关闭dm-verity通常不影响这个索引的存储和校验但修改vbmeta时需确保不意外重置它。链式分区Chained Partitions如果你的设备使用了AVB的链式分区验证--chain_partition修改vbmeta的签名密钥或描述符格式时需要确保整个信任链仍然是完整和有效的。设备状态Device State在 recovery 或 fastboot 模式下avbctl工具可以用来查询和修改AVB状态。关闭dm-verity后avbctl get-verity应该返回disabled。了解这些状态有助于调试。5.3 生产环境与调试环境的平衡在开发板或工程样机上我们可以比较自由地关闭安全特性。但如果设备最终要面向消费者就需要慎重考虑。用户版本User Build vs 工程版本Eng Build通常只在eng或userdebug版本上关闭dm-verity。对于最终的user版本应重新启用dm-verity以保证用户设备的安全。这意味着你需要维护两套编译配置或者通过某种机制如刷写不同的vbmeta镜像在出厂前切换状态。OTA服务器的兼容性如果你有自己的OTA服务器需要确保服务器端的逻辑能够正确处理你定制后的设备状态报告。有些服务器可能会严格检查vbmeta版本等字段。降级操作一旦设备运行在关闭了dm-verity的版本上再想通过OTA降级到一个开启dm-verity的旧版本可能会因为校验逻辑的逆向变化而失败。这种情况下的降级通常需要借助fastboot强制刷机。5.4 调试与验证技巧提取运行时信息在设备上可以通过dumpsys命令获取详细的兼容性信息adb shell dumpsys android.hardware.vintf.IVintf/default查看输出中关于vbmeta版本的部分。手动校验测试你可以在设备上模拟OTA升级的校验步骤使用lshal和vintf工具进行手动检查。查看完整日志OTA升级失败时除了last_log还要查看recovery.log以及系统日志 (logcat)里面可能包含更底层的错误信息。使用测试密钥在开发阶段务必使用测试密钥对vbmeta等镜像进行签名避免泄露生产密钥。同时确保设备刷写的是测试密钥签名的bootloader否则无法通过验证。处理这类底层系统修改最需要的就是耐心和细致的测试。每次修改后从编译、刷机、启动到OTA升级的完整流程最好都能走一遍。建立一个简单的测试清单记录关键检查点能有效避免反复踩坑。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2411473.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!