AI手机网,短视频直播 硬改改机 一键新机 群控软件 刷机定制

 找回密码
 立即注册
搜索
查看: 2233|回复: 8

Magisk学习之刷入vbmeta.img及关闭avb2.0校验​

[复制链接]
发表于 2021-8-11 13:42:39 | 显示全部楼层 |阅读模式
最近刷了很多机型的Magisk,经历了N次变砖和修复,也有了一些经验,Magisk提供了三种方式刷入Magisk,分别是:
  • 具有root权限情况下直接manager中直接安装
  • 在能获取到boot.img的情况下,patch boot.img然后fastboot刷入
  • 在三方recovery中直接刷入magisk.zip
这三种安装方式教程很多,主要介绍一下直接安装或者patch boot.img刷完magisk变砖之后遇到的分区验证导致变砖问题以及如何解决。

从刷入Magisk的方式都需要操作boot.img也可以看出来,magisk的核心原理就是修改和替换了负责安卓系统启动的boot.img中的Ramdisk部分,该映像包括了一系列初始化init进程和启动时使用的rc文件。

如果直接能拿到Root权限,可以考虑直接安装或者选择并修补boot.img的方式刷入,两种方式的本质相同,均是获取到boot.img后进行patch,只不过实现方式不同,一种依赖Fastboot进行刷入,一种则是Magisk Manager中进行刷入。

获取boot.img的方式有两种:
  • 从官方的ROM中提取,不同ROM的打包方式不同,例如一加是payload.bin,有专门的脚本提取,而google原生就是一个zip包解压即可,再例如三星可能是AP开头的tar文件。
  • 如果有root权限,直接从设备中提取,/dev/block/by-name下根据软连接找到boot.img,然后使用dd命令将img写出。如果是A/B分区,一般采用boot_a.img,但为了防止变砖后无法还原,建议用这种方式将recovery.img boot.img vbmeta.img都备份一份。
  1. ls -l /dev/block/by-name/ | grep boot
  2. dd if=/dev/block/sdc6 of=/sdcard/boot.img
复制代码
如果在刷入Magisk时变砖无法开机或着无限重启,通常只需在fastboot中将正常的boot.img重新刷回即可,也是为什么要提前备份boot.img等的原因。
avb2.0验证导致手机无法启动
在部分机型中,可能由于vbmeta.img的验证导致设备无法启动,验证启动(Verified Boot)是Android一个重要的安全功能,主要是为了访问启动镜像被篡改,提高系统的抗攻击能力,简单描述做法就是在启动过程中增加一条校验链,即 ROM code 校验 BootLoader,确保 BootLoader 的合法性和完整性,BootLoader 则需要校验 boot image,确保 Kernel 启动所需 image 的合法性和完整性,而 Kernel 则负责校验 System 分区和 vendor 分区。

由于 ROM code 和 BootLoader 通常都是由设备厂商 OEM 提供,而各家实际做法和研发能力不尽相同,为了让设备厂商更方便的引入 Verified boot 功能,Google 在 Android O上推出了一个统一的验证启动框架 Android verified boot 2.0,好处是既保证了基于该框架开发的verified boot 功能能够满足 CDD 要求,也保留了各家 OEM 定制启动校验流程的弹性。
Fastboot解决方案
简单的说,就是部分厂商机型可能由于avb2.0验证boot.img是否被修改,导致刷入magisk或者三方Recovery后陷入假变砖无限重启的情况,此时将备份的vbmeta.img重新刷入并关闭验证即可:

fastboot --disable-verity --disable-verification flash vbmeta vbmeta.img

这种方式简单易用,适合个人玩家,但如果是用在生产中,成千台设备需要批量刷入magisk时,这种方式太过人工,因此还可以通过定制Magisk代码的方式来一键刷入Magisk。
Magisk源代码定制解决方案
来看一下Fastboot的原理,查阅上一步中FastBoot的源码可以看到:
  1. // There's a 32-bit big endian |flags| field at offset 120 where
  2.     // bit 0 corresponds to disable-verity and bit 1 corresponds to
  3.     // disable-verification.
  4.     //
  5.     // See external/avb/libavb/avb_vbmeta_image.h for the layout of
  6.     // the VBMeta struct.
  7.     uint64_t flags_offset = 123 + vbmeta_offset;
  8.     if (g_disable_verity) {
  9.         data[flags_offset] |= 0x01;
  10.     }
  11.     if (g_disable_verification) {
  12.         data[flags_offset] |= 0x02;
  13.     }
复制代码
实际上只是对vbmeta.img中120偏移处进行了两个bit位的处理,即vbmeta.img中是否关闭验证有两个flag可以控制,我们只需依葫芦画瓢:
  • 从手机提取原始vbmeta.img(参考Magisk直接安装时寻找boot.img的操作)
  • 修改120偏移处的flag (参考Magisk对三星AP文件中的vbmeta.img修复操作)
  • 重新刷回系统中
Magisk如何在非Fastboot下刷入boot.img
这三步中比较困难的就是第三步,将patch完的vbmeta.img重新刷回系统中,因此这里参考了Magisk在有root权限时patch完boot.img之后直接刷入new_boot.img的操作。
  1. // MagiskInstaller.kt 直接安装的入口
  2. protected fun direct() = findImage() && extractZip() && patchBoot() && flashBoot()

  3. private fun flashBoot(): Boolean {
  4.         if (!"direct_install $installDir $srcBoot".sh().isSuccess)
  5.             return false
  6.         "run_migrations".sh()
  7.         return true
  8.     }

  9. // shell脚本
  10. direct_install() {
  11.   rm -rf $MAGISKBIN/* 2>/dev/null
  12.   mkdir -p $MAGISKBIN 2>/dev/null
  13.   chmod 700 $NVBASE
  14.   cp -af $1/. $MAGISKBIN
  15.   rm -f $MAGISKBIN/new-boot.img
  16.   echo "- Flashing new boot image"
  17.   flash_image $1/new-boot.img $2
  18.   if [ $? -ne 0 ]; then
  19.     echo "! Insufficient partition size"
  20.     return 1
  21.   fi
  22.   rm -rf $1
  23.   return 0
  24. }

  25. # $1参数是patch后的new-boot.img路径,$2参数是设备系统中boot.img路径
  26. flash_image() {
  27.   # Make sure all blocks are writable
  28.   $MAGISKBIN/magisk --unlock-blocks 2>/dev/null
  29.   case "$1" in
  30.     *.gz) CMD1="$MAGISKBIN/magiskboot decompress '$1' - 2>/dev/null";;
  31.     *)    CMD1="cat '$1'";;
  32.   esac
  33.   if $BOOTSIGNED; then
  34.     CMD2="$BOOTSIGNER -sign"
  35.     ui_print "- Sign image with verity keys"
  36.   else
  37.     CMD2="cat -"
  38.   fi
  39.   if [ -b "$2" ]; then
  40.     local img_sz=`stat -c '%s' "$1"`
  41.     local blk_sz=`blockdev --getsize64 "$2"`
  42.     [ $img_sz -gt $blk_sz ] && return 1
  43.     eval $CMD1 | eval $CMD2 | cat - /dev/zero > "$2" 2>/dev/null
  44.   elif [ -c "$2" ]; then
  45.     flash_eraseall "$2" >&2
  46.     eval $CMD1 | eval $CMD2 | nandwrite -p "$2" - >&2
  47.   else
  48.     ui_print "- Not block or char device, storing image"
  49.     eval $CMD1 | eval $CMD2 > "$2" 2>/dev/null
  50.   fi
  51.   return 0
  52. }
复制代码

看起来很复杂,核心写入的步骤其实就是eval $CMD1 | eval $CMD2 | cat - /dev/zero > "$2" 2>/dev/null,通过这行命令就可以实现不在Fastboot中通过shell命令完成boot.img和vbmeta.img的写入(也许也可以类推到其他img映像分区)
实现vbmeta.img的重写
而且vbmeta.img相比于boot.img比较简单,不需要签名、验证等步骤,因此可以简单提炼为
  1. # 先找到vbmeta.img
  2. vbmeta_block_name=`find /dev/block \( -type b -o -type c -o -type l \) -iname vbmeta | head -n 1`
  3. vbmeta_block=`readlink -f $vbmeta_block_name`
  4. # 将patch好的vbmeta.img写入
  5. eval 'cat '/sdcard/patched_vbmeta.img'' | eval 'cat -' | cat - /dev/zero > $vbmeta_block 2>/dev/null
复制代码
再加上patch操作,完整代码为:
  1. fun disableVbMetaImg() {
  2.         try {
  3.             // find vbmeta.img block
  4.             val vbmetaPath = ShellUtils.fastCmd("readlink -f `find /dev/block \\( -type b -o -type c -o -type l \\) -iname vbmeta | head -n 1`")
  5.             if (TextUtils.isEmpty(vbmetaPath)) {
  6.                 Log.w(Const.TAG, "could not find vbmeta.img!")
  7.                 return
  8.             }
  9.             val patchedVbmetaPath = "/sdcard/patched_vbmeta.img"
  10.             if (!Shell.sh("dd if=$vbmetaPath of=$patchedVbmetaPath").exec().isSuccess) {
  11.                 Log.w(Const.TAG, "extract vbmeta.img failed!")
  12.                 return
  13.             }
  14.             val patchedVbMeta = SuFile.open(patchedVbmetaPath).readBytes()
  15.             // There's a 32-bit big endian |flags| field at offset 120 where
  16.             // bit 0 corresponds to disable-verity and bit 1 corresponds to
  17.             // disable-verification.
  18.             ByteBuffer.wrap(patchedVbMeta).putInt(120, 2)
  19.             File(patchedVbmetaPath).writeBytes(patchedVbMeta)
  20.             // rewrite vbmeta.img
  21.             Log.d(Const.TAG, "rewrite vbmeta.img finished: $patchedVbmetaPath to $vbmetaPath")
  22.             Shell.sh("eval 'cat '$patchedVbmetaPath'' | eval 'cat -' | cat - /dev/zero > $vbmetaPath 2>/dev/null").exec()
  23.         } catch (e: Throwable) {
  24.             Log.i(Const.TAG, "disable vb_meta.img avb2.0 error:", e)
  25.         }
  26.     }
复制代码


发表于 2021-8-11 13:43:21 | 显示全部楼层
感谢刷机成功
发表于 2021-8-11 13:44:07 | 显示全部楼层
过来看看!第一次来这个论坛!来学学技术
发表于 2021-8-11 13:44:51 | 显示全部楼层
谢谢分享,非常好用,很牛
发表于 2021-8-11 13:45:31 | 显示全部楼层
盖了帽了,厉害
发表于 2021-8-11 13:46:19 | 显示全部楼层
感谢客服指导
发表于 2021-8-11 13:46:57 | 显示全部楼层
支持-感谢提供
发表于 2021-8-11 13:47:54 | 显示全部楼层
感谢远程协助
发表于 2021-8-11 13:48:23 | 显示全部楼层
这网站可以哦
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

技术交流售后群

QQ|小黑屋|手机版|站点找错-建议|AI手机网 |Sitemap



GMT+8, 2024-5-9 08:07 , Processed in 0.160964 second(s), 27 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表