Systemless root 是個蠻有趣的概念,在 Android 4.x 以前的時代 root 手機不外乎就是在 /system 下塞 su 與管理 app,到了 5.x 之後引入了 daemon 並且繞接 app_process 取得權限,之後加入的檔案越來越多 (比如 supolicy)。不過在 Android 6.0 強制 Block-Based OTA 之後,在 /system 植入 root 似乎不是一個很好的 idea,於是有了 systemless root 的出現,藉由修改 boot.img 的方式搭配 mount 指令與調整 SELinux 的 policy 達到 root 手機的目的,而收到 OTA 升級通知後,也只要刷回 boot.img 與 recovery.img (optional) 即可無痛升級,之後再進入 twrp 把 root 刷回來即可。

但事實上 systemless root 在某些場合無法發揮上述的優勢:

  1. 在新的 (Android 6.0) 裝置上幾乎需要解鎖 bootloader 才能載入修改過的 boot.img,而大部分手機廠在解鎖 bootloader 後取消 OTA 升級的推送,只能透過刷入整包原廠 ROM 的方式更新,於是 systemless 這件事情變得無關緊要。
  2. 大部分的人在使用 root app 時還是會修改到 /system 的內容,比如把 app 丟到 /system/app 或者 /system/priv-app 目錄下,或者編輯 /system/etc/ 裡面的檔案,如此一來就算可以收到 OTA 通知,依舊會在升級時出現失敗的狀況。

第一點無解,所以來看看第二點可以怎麼處理,可以分為幾部分:

  • 新增 app (在 /system/app 或者 /system/priv-app 下新增 apk or lib)
  • 新增檔案 (通常是 /system/etc,也有的是 /system/framework )
  • 編輯檔案 (通常是 /system/etc 與 build.prop)
  • 刪除檔案 (通常是 /system/app 或者 /system/priv-app 或 /system/etc ..)

基本的概念就是使用 bind mount 將修改過的檔案『疊』在原始檔案上,對上層的使用者來說,只會看到修改過的內容,因此可以利用別的分割區放置修改的檔案,再使用下面的指令將修改過的檔案『疊』上去。

mount -o bind modified_file original_file

新增檔案的部份稍微複雜些,假設我們想在 /system/etc 下新增檔案 dict.db,如果使用 bind mount 把 dict.db 所在目錄『疊』上去,那麼你會在 /system/etc 下只看到 dict.db 這個檔案。比較單純的解決方法為:

  1. 所有在 /system/etc 下的檔案與目錄透過 touch 跟 mkdir -p 指令在 dict.db 所在目錄裡產生一份,symbolic link 可以直接使用 cp -a 指令複製。
  2. 另外在系統其他目錄掛載一份 system 分割區。
  3. 使用 bind mount 將 /system/etc 替換為 dict.db 所在目錄,並且將所有空檔案透過 bind mount 指令連結到前面掛載 system 分割區目錄下的檔案。

如果新增的檔案不多,其實只要把 /system/etc 這一層下的目錄與檔案連結過去即可,不用將整個 /system/etc 目錄樹下所有的檔案一一連結。

知道怎麼新增檔案後,刪除檔案的作法大同小異,差別只在於要刪除的檔案不做連結即可。

最後來講一下 /system/app 與 /system/priv-app 的部份,『理論上』可以使用前面提到的方法,但問題在於有些 app 在開機時會執行,而 bind mount 需要花一些時間,如果沒法在做 bind mount 時卡住開機程序,進入系統桌面便有機會產生 force close 的對話框。我們可以修改 /init.rc 實作卡住的機制,但如果只是單純加檔案到 /system/app,有另一種更簡單的方式。

Android 的 PackageManagerService 除了處理 /system/app 與 /system/priv-app 以外,也會掃描 /system/vendor/app 下的 apk,而到了 5.x 之後另外新增 /oem/app,因此我們可以先把要新增的 apk 放在某個目錄下,再以 bind mount 連結到上述其中一個路徑。這件事情透過腳本可以讓 SuperSU 在開機時執行,我把程式碼丟在這:

https://gist.github.com/shakalaca/bde818d1fbc4b1343d1d44d954c9ddd5

把 01mount_verndor_app 放在 /su/su.d 目錄下,把要新增的 apk 放在 /su/app 目錄中 (比如 /su/app/omg/omg.apk) 最後把權限設定好,重新開機後就會自動掛載。

不過看到這邊一定會想問,那怎麼不順便處理 /system 下其他的檔案與目錄呢?有的!但還在開發中,之後會再寫一篇說明,有興趣的可以先來這邊嘗鮮:

https://gist.github.com/shakalaca/b170c7afcce6ddfb47e06293a36fa8eb

Happy hacking !

Comments

comments powered by Disqus