☰
adb remount 完全指南:突破 /system 只读限制与 dm-verity 校验
2026/10/2 3:46:13 网站建设 项目流程

玩Android搞系统级开发的,几乎没人没被/system目录教育过。好不容易打开了USB调试,手机连上电脑,一句adb push想把文件丢进 /system,结果屏幕上弹出一行冷冰冰的Read-only file system或Permission denied,瞬间没脾气。这个问题我前前后后折腾过很多次,从早期的Android 4.x一路玩到Android 13、14,结论是:要在/system里稳定写入文件,核心就是adb remount,但它背后牵扯到分区挂载方式、dm-verity校验、固件构建类型、SELinux策略好几个层面,哪一环没打通都白搭。这篇文章围绕“在/system目录中写文件”这个需求,把原理、实操、备选方案和报错排查一次讲透,适合正在折腾ROM、做系统定制、或者单纯想删掉预装垃圾软件的同学参考。

1. 问题根源:/system 为什么“顽固”拒绝写入

1.1 它一出生就是只读分区,不是“普通文件夹”

先说一个很多新手容易忽略的基本点:/system 在Android设备里根本不是“系统里的一个普通文件夹”,它对应一块独立的分区。手机开机时,Bootloader加载内核,内核再拉起init进程,init解析fstab(文件系统挂载表)配置,把system、vendor、odm这些分区分别挂载到根目录下的对应路径。关键是,system分区的默认挂载参数里就带着ro,也就是只读。

这个ro不是写一句代码刻意锁死,而是Android系统架构的设计逻辑:内核本身和核心系统库都在这个分区里,如果不小心被写坏,手机直接无法开机。所以从Android诞生之初,system、vendor这类分区就默认以只读方式挂载,从引导阶段就决定了它们“不可写”。

你可以用下面这个命令看一下实际挂载参数:

adb shell mount | grep system

正常情况下你会看到类似这样的输出:

/dev/block/dm-0 /system ext4 ro,seclabel,relatime,firstpl ......

注意中间那个ro,它代表当前挂载状态是只读。就算是root用户,直接在/data、/storage目录里能用mv、rm、echo > file自由操作,到了/system同样没戏,因为文件系统层面的挂载状态就不允许写入。想写入就得先“取消只读”,也就是把分区重新挂载成rw(可读写),这正是adb remount要干的事。

1.2 dm-verity 与 AVB:修改 /system 后更严格的“惩罚机制”

如果只是ro挂载,其实问题并不麻烦,root后重新mount为可写就行。真正让Android系统变得难改的是Android 4.4以后引入的 dm-verity,以及Android 7.0以后全面使用的 AVB(Android Verified Boot,验证启动)。

dm-verity 是一个内核层的块设备校验机制,可以理解成给/system分区的每个数据块提前计算了一棵哈希树,开机后内核读取每个block时都拿哈希值对照。只要分区里有任何一个字节被第三方改动过,下一位读取时校验值就会对不上,设备就会触发保护机制:拒绝继续读取、重启进入recovery、开机循环,甚至直接显示“dm-verity verification failed”,卡在logo页不动。

AVB则是更上层的完整验证链,它把Linux内核、ramdisk、system镜像等所有关键组件的哈希摘要捆绑在一起,写入独立的vbmeta分区。Bootloader在启动时会逐级验证,任何一个环节的哈希与vbmeta里记录的不一致,都会拒绝启动或者进入错误状态。

现在你应该明白了:直接对/system分区修改后,不仅要面对ro挂载限制,还要绕过内核层的校验机制。这也是为什么有时候你已经用root权限把分区强制挂载为rw、文件也推进去了,但重启后要么修改消失,要么手机直接卡死。不是操作无效,而是你没有先“关闭验证”。

1.3 shell 用户与 Root:权限链条根本没打通

还有个前置问题常常被忽略——adb shell 进去之后,你的身份并不是 root。

Android的adbd守护进程默认以shell用户运行,shell用户虽然比普通App权限大一点,能读很多系统配置,但对mount、重启adbd这类操作依然没有权限。mount这个动作需要CAP_SYS_ADMIN能力,shell用户根本不具备。

所以要在/system里写文件,完整的权限链条是:adb shell 获得 root 身份 → 关闭 dm-verity/AVB 校验 → 将 system 分区重新挂载为 rw → 执行写入。只有root权限而没有remount,写入失败;有remount但没关校验,重启翻车;两者都做了但SELinux策略拦截,应用也读不到,后面会逐个讲到。

另外,手机固件的构建类型直接决定了你是否能拿root。Android固件一般分三种:

构建类型允许 adb root允许 adb remount日常使用
user否否绝大多数零售机
userdebug是是开发者测试机、部分刷机包
eng是是工程机、内部开发板

零售手机出厂基本都是user版本,这也是为什么很多人按照教程敲adb remount会收到adbd cannot run as root in production builds的报错——不是命令敲错,是固件本身被“阉割”了这两个能力。

2. adb remount 的标准操作:从环境准备到一把成功

2.1 动手前的准备工作和前置条件

先说结论,adb remount能一把成功,通常要满足下面几个条件:

  • 固件是userdebug或eng版本,或者已经通过Magisk等方式拿到了root权限;
  • 系统没有启用dm-verity,或已经执行过adb disable-verity;
  • Bootloader已解锁(部分设备不解锁根本无法修改分区);
  • 使用的adb工具版本比较新,最好是最新版的platform-tools。

我实际测试中,Google官方的Pixel系列手机开启“允许OEM解锁”后刷入userdebug镜像,是目前最顺手的操作环境。老式的国产机因为厂商深度定制,解锁流程各不相同,反而坑最多。

下面这部分操作都以“能进系统、能开USB调试”为前提。需要的文件都准备好之后,按顺序执行,每一步都有明确的验证方法。

2.2 标准命令序列(抄作业版)

先把手机连上电脑,在终端里确认设备状态:

adb devices

看到device状态而不是unauthorized或offline,继续下一步。如果显示unauthorized,记得看手机屏幕,确定弹窗“允许USB调试”要点击确认。

然后执行下面这一条,目的是让adbd以root身份运行:

adb root

这一步正常会返回adbd is already running as root或者restarting adbd as root。如果返回的是adbd cannot run as root in production builds,说明你的固件是user版,直接跳到第3章的备选方案。

接着关闭dm-verity:

adb disable-verity

这个命令的输出通常会提示需要重启才能生效。不用怀疑,直接重启:

adb reboot

等手机重新进入桌面,再次执行adb root获取root权限。然后执行最关键的一步:

adb remount

此时如果一切正常,你会看到一堆分区被重新挂载为rw,包括/system、/vendor等。验证方式很简单:

adb shell mount | grep system

这次再看到system分区,挂载参数应该变成了rw。到这里,/system分区就已经完全可写了。

2.3 写入验证:push 一个真实文件看看

为了确认真的很能写,我一般会做一个小测试:在本地写一个内容为空的文本文件,然后push到/system目录里。

echo "test ok" > test.txt adb push test.txt /system/test.txt

如果输出显示文件传输成功,接着查一下:

adb shell cat /system/test.txt

能读到test ok,说明整个链路已经打通。之后你就可以按需求操作:删预装应用(adb shell rm /system/app/某app/某app.apk)、替换系统字体、修改build.prop配置,或者把编译好的系统应用直接放到/system/app或/system/priv-app下面。

注意:执行adb remount后,所有修改是对当前系统分区直接生效的,换言之你对/system的写入会真实落盘。一旦改错地方,或者删除某个关键系统组件,系统可能无法正常进桌面。强烈建议动手前做一次完整备份,后面第5章会细说。

3. 当 adb remount 失效:三种进阶方案与原理拆解

3.1 方案A:解锁Bootloader并关闭AVB验证后重试

很多新手机出厂带了Bootloader锁和AVB链条,即使刷了userdebug包,adb disable-verity也不一定成功。这种情况下,需要先到“开发者选项 -> OEM解锁”里打开解锁开关,然后关机进入Bootloader界面操作。

常见的解锁命令如下(具体命令以品牌为准):

fastboot flashing unlock # 或老设备: fastboot oem unlock

解锁过程会清空所有数据,所以一定提前备份。解锁完成后,如果你的设备有独立的vbmeta分区,建议直接把它刷成一个关闭验证的版本:

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

这条命令会把vbmeta里的验证标志位改掉,告诉Bootloader“不要校验系统分区的完整性”。之后再重新进入系统,adb root和adb remount就能正常执行了。

3.2 方案B:离线修改 system 镜像再重新刷入

如果你的设备是user版零售机,既不能adb root,也不方便解锁,那还有一条“笨但有效”的路:把固件里的system.img提取出来,在电脑本地挂载、修改、重新打包,然后刷回去。

思路是这样的:先从官方固件里抽出system.img(也可能是super.img,包含system、product、vendor等动态分区的总镜像),用工具转换成可读写的ext4镜像。Linux下可以这样操作:

# 将system.img以rw方式挂载到本地目录 sudo mount -o loop system.img /mnt/system_partition # 修改 /mnt/system_partition 里的文件 sudo vim /mnt/system_partition/build.prop # 卸载 sudo umount /mnt/system_partition

修改完后,用make_ext4fs或其他工具重新打包成镜像:

make_ext4fs -l 2G -a system new_system.img /mnt/system_partition

然后刷入设备:

fastboot flash system new_system.img

这种方式对老式A-only设备比较友好,但Android 10以后动态分区(Dynamic Partitions)和A/B分区结构让镜像处理变得复杂得多。一般需要先用脚本解析super.img,把system分区提取并打包,再塞回super.img里。操作复杂度和出错概率都明显上升,不太适合新手,建议优先考虑方案A或方案C。

3.3 方案C:用 Magisk 的“挂载覆盖”思路绕开写 /system

如果你只是想删预装应用、加模块,不想动/system底层,还有一条曲线路径:Magisk模块。

Magisk的核心理念不是直接改/system分区,而是在系统启动早期用“嫁接”的方式,把模块目录里的内容通过magisk服务挂载到/system对应路径,看起来像是改了,实际上原分区没有动。它的优点非常明显:安全、可控、卸载模块就恢复原状,不影响系统完整性校验。

比如想给/system/app里加一个应用,只需要按照模块标准创建一个目录结构:

模块名/ ├── META-INF/ ├── system/ │ └── app/ │ └── 我的系统应用/ │ └── 我的系统应用.apk

打包成zip后,在Magisk App里刷入,重启后就生效了。这种方式完全不需要remount,也不需要关闭验证,是目前最稳妥的日常“伪造系统改动”方案。

我个人体会:如果只是为了“把文件塞进/system”这个最终目的,方案C的通用性和安全性其实比硬改分区高一个量级。但如果你是在做ROM移植、系统编译产物测试这种需要真实改动分区内容的开发工作,adbd remount依然不可替代。

4. 高频报错与排查手册:我踩过的坑,按图索骥

4.1 adbd cannot run as root in production builds

症状:adb root直接拒绝,提示该错误。

原因:当前固件是user版本,没有开放adbd的root运行权限。这是厂商出厂固件最常见的表现,不是你操作错误。

解决:优先方案是找对应的userdebug/eng版本固件刷入;或者使用Magisk给设备root后,通过Magisk授权终端工具以root身份执行mount命令。模拟器和官方开发板直接切换userdebug镜像即可。

4.2 /system 分区 remount 失败,报 Permission denied 或 Operation not permitted

症状:敲adb remount后返回类似remount of the /super block failed: Permission denied,或者更早的版本提示/system not in fstab。

原因:这个报错通常是两个原因叠加导致的:一是adbd没有真正的root权限(有时adb root成功但实际受限);二是Android 10+动态分区结构下,system分区位于super内部,remount逻辑变复杂,操作前必须关闭dm-verity;三是少数情况是SELinux的neverallow规则拦截。

解决:按顺序检查三步:确认adb root是否返回成功;执行adb disable-verity后重启;再用管理员前台重新执行adb remount。如果还不行,用adb shell setenforce 0临时关掉SELinux再试,注意这只是排查手段,重启后会恢复enforcing。

4.3 修改 /system 后重启卡死,显示 dm-verity verification failed

症状:改完/system分区,重启后卡在开机画面,屏幕提示校验失败,反复重启无法进桌面。

原因:没有关闭dm-verity就修改了分区,内核检测到数据被改动,触发保护机制。这种问题在Android 6以上的设备上很常见,尤其是只用了第三方root工具,没有执行disable-verity的场景。

解决:如果还能进Bootloader,可以在fastboot模式下执行vbmeta清理或直接刷回原版system镜像。最常见的操作是:

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

然后重新刷入原始system镜像,把分区恢复到干净状态再操作。

4.4 device not found / unauthorized / offline

症状:敲adb devices找不到设备,或者显示unauthorized/offline。

原因:这类问题多数不是/system写入导致的,而是连接链路问题,但实际操作中几乎每一步都绕不开它。原因可能是手机弹窗没点允许、USB线不支持数据传输、驱动没装好,或电脑上adb端口被占用。

解决:先拔插USB线,看手机端是否弹出“允许USB调试”弹窗;不行就换一条确认支持数据传输的线;再不行执行adb kill-server && adb start-server重启服务,同时检查后台是否有其他手机助手软件占用5037端口。记住,unauthorized和offline是两个不同问题,前者是授权,后者是驱动或连接质量。

4.5 写入成功但重启后修改丢失

症状:push文件时提示成功,mount也是rw,但重启之后所有内容都没了,像没发生过一样。

原因:这种情况在两类设备上最常见:一是动态分区设备,部分机型在开机时会对super分区做覆盖式初始化,直接把修改抹掉;二是开启了AVB的设备,虽然临时关闭了校验,但下一次启动时又被某个启动项强制覆盖。

解决:这个没有通用办法,只能通过方案B的重新刷入镜像方式,把修改固化到镜像里。另外,改完后不要马上重启,多验证一下文件权限和内容,再决定下一步操作。

4.6 常见问题速查表

报错信息直接原因优先解决方法
Read-only file system分区以ro挂载adb remount重新挂载rw
Permission denied身份不是root或被SELinux拦截adb root;必要时setenforce 0
Operation not permitted内核能力受限检查是否关闭dm-verity,确认固件为userdebug/eng
dm-verity verification failed修改后未关闭校验fastboot下刷入vbmeta并指定禁用校验
adbd cannot run as root固件类型为user换userdebug/eng固件或用Magisk
No space left on device分区空间不足删除无用内容或重新打包更大的镜像
chown: Operation not permitted部分路径受SELinux标签保护设置正确上下文后重试,不要硬改根路径权限

5. 实操经验与避坑指南:真实环境里的几点心得

5.1 动手前一定做好三件事:备份、记录、找好救砖包

在/system里做任何修改之前,数据备份是第一优先,但很多人都会忽略备份“分区当前状态”。我建议除了备份照片、联系人等个人数据,还要用adb shell ls -al /dev/block/by-name/记录分区表信息,搞清楚system、vbmeta、boot、recovery等分区分别对应哪个block节点。

同时,必须下载好当前版本的原厂固件并保存到本地。原因很简单:/system写入失败最坏的结果是设备变砖,这时候手里有原厂镜像,还能通过fastboot模式刷回来;没下载固件就只能到处找教程,浪费时间不说还容易刷错版本。我自己刷坏过一次,后面所有折腾前都会把“救砖包”准备好,这条真心建议每个小白都遵守。

5.2 分区空间问题:修改前先看看剩余有多大

/system分区的空间是固定大小,不像/data分区能自动扩展。很多老机型固件已经把system塞得满满当当,执行adb push时容易遇到No space left on device。

解决方案分三种:一是删除系统里无关紧要的文件(比如预装壁纸、不用的铃声)腾出空间;二是如果只是替换文件,确保新文件体积不超过原文件体积;三是用方案B重新打包分区镜像,把分区容量调大后刷入。个人推荐优先用“替换”代替“新增”,能少踩很多坑。

5.3 文件权限和SELinux上下文是最后一个大坑

很多人把文件push进/system以后,发现启动时应用根本读不到这个文件,报权限错误。原因通常是文件权限不对或SELinux安全上下文不匹配。

以系统应用为例,正常的system应用APK权限是644(即root拥有读写权限,其他人可读),属主是root。如果push进去的是普通用户文件,应用无法正常解析。另外,Android每一步文件访问都会被SELinux策略检查,文件的安全上下文必须与其所在目录匹配,否则即使权限正确,框架层也可能拒绝读取。

手动修复常用方法:

adb shell chmod 644 /system/app/xxx/xxx.apk adb shell chown root:root /system/app/xxx/xxx.apk # 但更稳妥的是直接引用同目录下同类文件的上下文 adb shell ls -Z /system/app/

对比同目录下正常文件的安全上下文,然后同样设置到新文件上。这里没有一条命令能解决所有机型的上下文问题,最好直接参考你设备上同类文件的标签。

5.4 模拟器与开发板:最容易“一把过”的练手环境

如果你是第一次接触/system写入,强烈建议先用官方模拟器或者开发板练手,而不要直接拿主力手机测试。Google官方模拟器的Google APIs镜像默认就是userdebug/eng类型,adb root和adb remount几乎不会报错,改坏了直接冷启动一个快照就能恢复原状。

模拟器环境适合练熟整套命令流程,理解分区概念和校验机制后,再迁移到真机实战。很多人在真机上翻车,不是因为操作复杂,而是因为对基础概念不熟悉,出了问题也不知道从哪排查。在模拟器上把disable-verity、remount、push、权限修改这些动作练几遍,后面折腾真机会顺手很多。

5.5 再补充一个很实用的小技巧

修改/system/build.prop时,先复制一份原文件到 /data 或 /sdcard 备份,然后再编辑。这个文件是系统开机最关键的配置文件之一,里面任何一个语法错误都可能导致系统无法进入桌面。

adb shell cp /system/build.prop /data/local/tmp/build.prop.bak adb pull /system/build.prop # 本地修改后再push回去 adb push build.prop /system/build.prop adb shell chmod 644 /system/build.prop adb shell chown root:root /system/build.prop

改完以后暂时别急着大动干戈,先重启一次,能正常进系统,再做后续操作。这个习惯我保留了很多年,能避免至少一半的“改完就变砖”惨案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询