尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Android /system 目录只读?一文解决 adb remount 与 dm-verity 难题

Android /system 目录只读?一文解决 adb remount 与 dm-verity 难题 如果你也遇到过这样的场景想在 /system 目录里放个字体、改个 hosts、替换系统应用结果明明已经 root 了执行 adb push 却提示 “Read-only file system”或者 adb remount 直接来一句 “Permission denied”不要怀疑人生这基本是每个做 Android 定制的人都会撞上的墙。这篇文章就围绕“/system 目录无法写入”这件事把背后的原因、标准的 adb remount 操作流程、以及我实际踩过的坑全部摊开讲。不管是做 ROM 移植、想精简系统应用还是单纯想把某个配置文件塞进系统目录这里的思路都适用。文中的命令是我在真实设备上验证过的但不同机型、不同 Android 版本会有细微差异我会在对应位置提醒你。1. 为什么 /system 这么难写入1.1 只读挂载不是 Windows 的“只读属性”很多从 Windows 或普通 Linux 桌面转过来的朋友一开始会把 /system 无法写入理解成“权限不够”。其实权限只是表面现象真正的关键在挂载方式。Android 是 Linux 内核挂载一个文件系统时可以指定 roread-only或 rwread-write。/system 分区在系统启动时通常以 ro 方式挂载这是编译时由 fstab 决定的。内核层面只要挂载标志是 ro就算你 root 了直接往里写文件也会被拒绝。这就像你有一把钥匙能打开保险柜的门但保险柜在交付时被设计成只能看不能摸——钥匙解不开“设计上的锁”。要绕过这层限制不能只靠 root 权限去写文件得先重新改变挂载状态把只读改成可读写。1.2 老设备好办新设备绕了一大圈在 Android 7 以前的设备上/system 是一个实实在在的独立分区想写入的话root 之后执行adb shell mount -o rw,remount /system通常就能搞定。这也是网上大量教程里最常见的一行命令。到了 Android 8/9/system 被合并到 system-as-root 结构里/system 不再是独立的挂载点而是根文件系统的一部分。Android 10 之后又引入了动态分区Logical Partitions/system 被放在 super 分区里由 dm-linear 映射出来。这带来的直接后果是原来那行 mount -o rw,remount /system 在新设备上经常报错因为系统里可能根本没有一个叫 /system 的真实块设备或者 /system 已经挂载成了根目录的一部分只能对 / 整体做 remount。很多人卡在这一步还在反复确认是不是自己 root 得不够彻底。1.3 dm-verity 才是真正的大 BOSS比挂载方式更麻烦的是 verity 校验。Android 从 4.4 开始引入 dm-verity到 6.0 之后在出厂设备上普遍启用。它的作用是在读取文件块时校验哈希一旦你改了 /system 里的文件校验值对不上系统可能直接拒绝启动或者把设备置于只读模式。所以光是 remount 成功还不够如果 verity 还在重启之后大概率迎来的不是修改生效而是卡在开机界面或者进入 recovery 提示系统损坏。这就解释了为什么官方工具链里专门给了 adb disable-verity 这个命令——它不是可选项而是改完系统文件之后必须处理的一环。注意理解这个机制比背命令重要。后面很多莫名其妙的失败其实都是因为 dm-verity 在“暗中守护”System 目录。2. 动手前的准备2.1 确认 root 方案adbd 能不能以 root 身份运行和普通 App 能不能拿到 root 是完全两码事。很多设备安装 Magisk 之后App 可以弹窗授权但 adb shell 进去执行 su 还是会被拦一下因为 Magisk 默认只对配置过的应用开放 su 权限。adb root 更是需要在 userdebug 或 eng 版本上才默认可用正式版系统user build通常会出现adbd cannot run as root in production builds遇到这个提示说明设备本身不允许 adb 直接提权。此时你有几个选择换用 userdebug 版本的系统适合做 ROM 定制的人保持 Magisk 方案进 adb shell 后手动 su再自己执行 mount 系列命令使用 KernelSU 或 APatch 这类方案时同理需要先在 shell 里 su 到 root。我自己的习惯是如果只是临时改文件优先用 Magisk 的 su mount如果是长期开发调试直接刷 userdebug 版本省心得多。2.2 手机端开关进入开发者选项打开 USB 调试。注意部分机型还有“USB 调试安全设置”或“监控 ADB 安装应用”之类的开关如果找不到 adb root 或者 remount 后写入失败优先检查这里。另外建议关闭“启动时锁定”这类安全策略否则 adb 操作可能会被系统安全模块拦截表现为命令没有报错但文件没写进去。听起来很玄学但我确实遇到过。2.3 电脑端环境常规做法是下载 Android SDK Platform-Tools把里面的 adb 和 fastboot 解压到一个目录然后在这个目录打开终端。Windows 用户记得把目录加入 PATH或者每次都用完整路径。你提到的 Android Studio、Android SDK 官网其实指向的都是同一套 platform-tools版本够新就行。不要直接在 adb shell 里敲 remount那不是同一个东西。后面我会专门讲这个坑。3. 核心方案adb remount 的正确用法3.1 老版本的一行流在 Android 7 及之前的设备上标准流程一般是adb root adb remount然后就可以 adb push 文件进 /system 了。adb remount 这个命令是 adbd 内部实现的一个辅助功能它会尝试把 /system 重新以 rw 方式挂载同时关掉相关的安全检查。但我在实际操作中发现这一行流在新设备上经常失效。原因就是前面说的动态分区和 verity。如果 adb remount 提示remount failed或者只输出一句adb: error: failed to remount partition: Operation not permitted别急着放弃往下走。3.2 新设备的完整流程我自己在 Android 12/13 上验证过最稳的一套流程是adb root adb disable-verity adb reboot adb wait-for-device adb root adb remount第一轮 adb root 是为了获得 root 权限disable-verity 会关闭 dm-verity 校验重启是为了让 disable-verity 生效。注意disable-verity 之后设备重启可能会多等一会儿甚至第一次启动会有一瞬间黑屏不用慌等 adb 重新连上。第二次 adb root 之后再执行 adb remount这时候大部分设备会输出remount succeeded甚至有时会直接提示 system 已经是 rw 状态。这一步如果失败我再提供手工兜底方案。3.3 手工 mount 兜底当 adb remount 不认账或者设备上压根没有可用的 userdebug 环境但你已经通过 Magisk/KernelSU 拿到了 root可以在 adb shell 里这样操作adb shell su mount -o rw,remount /注意是 remount /不是 remount /system。在 system-as-root 和动态分区设备上/system 只是 / 下的一个目录真正的挂载对象是整个根文件系统。执行完后可以用mount | grep / 确认 ro/rw 状态。我曾经在一台 Android 11 设备上看到 /system 的挂载项后面写着 ro但实际 / 已经是 rw文件也能写进去。原因就是 /system 没有独立挂载点它继承 / 的状态。如果你的 / 本身就是 rw但 /system 下的文件还是提示只读那就试试su mount -o rw,remount /system这个操作在部分设备上仍然有效因为某些系统还是把 /system 作为独立挂载项列出来了。两个命令都试过哪个不报错就用哪个。3.4 push 文件的正确姿势挂载成 rw 之后写文件有两种方式在电脑端用 adb push 直接推在 adb shell 里用 cp、mv、echo 等命令直接写。我建议优先用 adb push因为它走的是 adbd 的文件传输通道对文件权限和属主处理得比较规整。手动 cp 出来的文件经常出现属主变成 shell 或者权限不对导致系统服务读不了。命令示例adb push hosts /system/etc/hosts adb shell chmod 644 /system/etc/hosts adb shell chown root:root /system/etc/hosts改完别忘了一个关键动作确认 SELinux 上下文。很多新设备即使文件权限和属主都对了SELinux 还是会把新写入的文件拦下来。简单做法是复制同目录下原始文件的上下文adb shell ls -Z /system/etc/hosts把输出的 context 记下来然后adb shell chcon 原始context /system/etc/hosts这一步容易被忽略但恰恰是很多“改完后系统不生效”的元凶。4. 一整套可复现的操作记录4.1 我用的环境我拿来做测试的设备是一台 Android 12 的机器已解锁 bootloader系统为 Magisk root 的 user 版本。电脑是 Windows 11Android SDK Platform-Tools 版本为 34.0.4。整套命令都在这个环境下跑过。4.2 完整命令序列打开终端先进入 platform-tools 目录adb devices确认设备已经识别。如果列表里有 unauthorized就去手机上点允许 USB 调试。然后开始adb root adb disable-verity adb reboot adb wait-for-device adb root adb remount执行完这组命令正常情况下 adb 会提示remount succeeded接着验证挂载状态adb shell mount | grep / 我这边看到的输出是类似/dev/block/dm-xxx / ext4 rw,seclabel,relatime 0 0注意 rw 已经出现。接下来直接推送文件adb push test.txt /system/etc/test.txt没有报错后看看文件是否真的写入adb shell ls -l /system/etc/test.txt adb shell cat /system/etc/test.txt确认内容没问题然后重启adb reboot重启后再次查看文件仍然存在说明写入成功且没有被 verity 还原。4.3 修改完成后是否要恢复校验如果你只是临时调试重启后 /system 会自动恢复为 ro 状态不用额外处理。如果之前执行过 disable-verity这台设备的 verity 已经永久关闭恢复出厂后才会重新启用。想让系统恢复完整性校验可以执行adb enable-verity adb reboot但这里有个前提你的 /system 文件必须和原始签名一致。如果你已经改过系统文件enable-verity 后开机大概率失败因为校验值对不上。所以我的建议是日常开发保持 disable-verity 即可只有交付设备或者想还原到“官方状态”时才考虑重新打开。不要为了追求安全而把设备搞到无法开机。4.4 如果 remount 还是失败还是有少部分设备即使走完上面流程依然报 Permission denied。这时候我用过的最有效的兜底方案是adb root adb shell su mount -o rw,remount /然后不重启直接在 adb shell 里用 cp 命令施工cp /sdcard/test.txt /system/etc/test.txt之所以用 cp 而不是 adb push是因为 adb push 在某些机型上仍然受到 adbd 的安全策略限制而 shell 内 cp 只要当前 shell 是 root就相对自由。这个现象有点反直觉但确实存在。5. 常见问题与排查技巧实录5.1 报错速查表报错信息可能原因处理办法adbd cannot run as root in production builds当前系统是 user 版本不允许 adb root用 Magisk su 手动提权或刷 userdebugPermission deniedbootloader 未解锁 / 未 root先解锁、装 MagiskRead-only file system挂载状态还是 ro执行 disable-verity remount /remount failedverity 未关闭或动态分区adb disable-verity 后重启再试Device or resource busy分区被系统进程占用进入 shell 后 stop 再 remount完成后再 start/system/bin/sh: remount: not found在 adb shell 里执行了 remount 命令回电脑端执行 adb remount这里额外说一句adbd cannot run as root in production builds 是很多人遇到的第一个坎。如果你不想刷 userdebug就用 Magisk 的思路adb shell 后输入 su看到 # 提示符就说明你已经提权成功后面所有 mount 操作都在这个 shell 里执行。5.2 关于 Android Studio 和 adb 的小提醒有些朋友习惯在 Android Studio 里直接操作设备实际上 Android Studio 内部也是调用 SDK 的 adb 和 platform-tools。如果你在 Android Studio 里改文件失败不代表设备有问题很多时候是它指向的 adb 版本和系统不匹配。比如新版 adb 在 Windows 上需要对应的驱动否则会出现adb: error: failed to copy ... Read-only file system这种报错有时并不是真只读而是驱动或者 adb server 版本导致的传输异常。可以先执行 adb kill-server再 adb start-server排除旧连接干扰。5.3 改完 hosts 不生效很多朋友改 /system/etc/hosts 是想实现域名重定向但改完发现浏览器仍走旧解析。原因大多是 Android 的 DNS 缓存或者某些 App 内置了 DNS-over-HTTPS根本不读 /system/etc/hosts。这个属于“文件写入成功但业务不生效”的范畴不是 /system 写入问题的锅。你可以先确认文件内容已经更新再考虑关掉 App 的私有 DNS 或者清缓存验证。5.4 写入后系统卡在开机界面这是我遇到过最头疼的情况。卡 boot 的原因通常是你在 /system 里删了关键文件或者替换的应用签名不一致又或者 SELinux context 没设置对。兜底方法是在 fastboot 模式重新刷系统镜像而不是硬等。fastboot flash system system.img刷完再 fastboot reboot。如果你手头没有原厂镜像基本只能找售后或者线刷工具。所以我的习惯是改任何文件之前先用 adb pull 备份一份原文件放到电脑上。改坏了至少能原样推回去。5.5 不要把“系统目录问题”和“存储权限问题”混为一谈热搜里经常出现 content://com.baidu.searchbox.fileprovider 之类的内容这是 App 通过 FileProvider 访问外部存储的 URI跟 /system 写入完全不是一回事。/system 写入是系统级操作需要 root而外置存储访问只需要 App 申请存储权限普通用户就能在文件管理器里操作。还有 Windows 上的 “system volume information” 目录无法删除、 “你需要来自 System 的权限才能对此文件夹进行更改” 这类问题也经常被人和 Android /system 搞混。它们本质上都是操作系统的权限保护机制但解决路径完全不同。看问题的时候先分清是 Windows 系统还是 Android 系统别被关键词带偏。做了这么多年 Android 定制我的体会是/system 写入这类问题90% 的卡点不在“命令不够神奇”而在“没有先搞懂自己设备属于哪一代分区方案”。老教程里的 mount -o rw,remount /system 不是错的只是新设备不吃这一套了。你在动手前先把系统版本和分区结构确认清楚后面每一步都会顺利很多。最后再分享一个小技巧改完系统文件之后不要急着一次性重启验证所有东西先 adb shell sync 把缓存落盘再检查一遍文件属主、权限和 SELinux 上下文确认无误后再重启。这套检查顺序帮我省下过无数次往返刷机的痛苦。希望这篇文章也能帮你少走一些弯路。
返回列表