App虛擬化技術初探

Plugin Technology Background

這裏寫圖片描述

App插件開發技術或App虛擬化技術以及App熱修復技術,最近幾年非常火熱和流行,圖中列舉了兩種主要的需求,第一種需求是很多時候我們想在Android手機上同時登陸多個社交應用,比如QQ或者微信,在Android原生系統中肯定是無法支持的,只有登出一個賬號後然後在換另外一個,因此衍生APP虛擬化技術;另外一種需求是一個大的APK文件我想分批次發佈,防止一次性發佈下載時間過長而損害用戶體驗,因此我把不同的功能以插件的形式發佈,這個插件同樣是APK文件,同樣對於某一種功能的升級或者bug的修復都可以採用這種技術來實現。

What is Android Plugin Technology?

  • Launch an APK file within an Android app.
    在一個APP內部啓動一個APP
  • In the unrooted device.
    不需要root手機
  • “Host App” = Android app.
  • “Plugin” = APK file.
  • No need to install the plugin.
    這裏的無需安裝主要是指對系統來說,並沒有實地安裝,,創建沙箱環境,以用戶的視角來看還是與host app一樣,點擊安裝,然後正常運行

這裏寫圖片描述

現有的插件框架

這裏寫圖片描述

現有的能夠實現上述需求的框架主要有DroidPlugin、VirtualApp、DynamicAPK
典型的應用有Parallel Space,截止2017年下載了達到千萬級別
雖然很多類型的這種框架,但是他們得底層實現機制基本相同

Demystify Plugin Technology

這裏寫圖片描述

插件技術最主要的中原理就是,爲了讓插件中的方法能夠正確的調用。Host APP把所有的插件和系統(Android Framework交互)的方法都在內部Hook並替換掉了。

爲什麼這樣做呢?

因爲,這個是一個插件系統,Activity是虛擬Proxy出來的。四大組件,並不是真正的,即Context拿到不一定是真的。系統也不會有此類的回調。所以,很多單例都需要執行Context才能使用。比如:InputMethodManager.getInstance(Context context);這時,插件中的代碼將不能夠直接運行。當然,還有其他原因。

這裏寫圖片描述

動態加載運行一個Activity的基本機制

爲了進一步的理解這種插件運行機制中的HOOK技術,我們來看一個簡單的實例,即如何動態加載運行一個Apk中的Activity,

既然說到動態加載,我們很容易想到Davilk虛擬機中的類加載器

(1) DexClassLoader可以加載任何路徑的apk/dex/jar/zip
(2) PathClassLoader只能加載緩存在/data/app中的apk,也就是已經安裝到手機中的apk

那麼如何加載一個apk並運行其中的Activity呢?我們很容易想到使用如下代碼實現

這裏寫圖片描述

這樣顯然是不行的
因爲Android中的四大組件都有一個特點就是他們有自己的啓動流程和生命週期,我們使用DexClassLoader加載進來的Activity是不會涉及到任何啓動流程和生命週期的概念,說白了,就是一個普普通通的類,所以啓動肯定會出錯。
那麼如何解決呢?

思路一:替換LoadedApk中mClassLoader

這裏寫圖片描述

由於這個mClassLoader是非static類型的引用,因此我們通過反射的機制無法更改當前運行時數據,那麼該怎麼辦呢?我們再看ActivityThread這個類

這裏寫圖片描述

這個類中有一個靜態變量,我們可以通過反射這個類,來獲取LoadedApk對象,然後更改其mClassLoader

思路二:更新DexPathList數據結構

這裏寫圖片描述

APK在啓動的過程中加載的dex文件都存放在一個叫dexElements的數據結構中,我們通過將插件APK的dex文件解析後的數據結構也放入此數據中,使其成爲host App的一部分,那麼host app在啓動其中的Activity的時候就具有上下文環境了,相應的組件也會有生命週期

這裏寫圖片描述

思路三:使用靜態代理

這裏寫圖片描述

基本思路是使用一個在host app中具有生命週期和執行上下文的Activity代理插件Activity執行其各個生命週期的回調函數

這裏寫圖片描述

之前三種方式共有的缺陷

(1) 需要在宿主應用的Manifest文件中註冊要啓動的Plugin中的組件,權限等信息,只能加載運行特定已知的apk,因此不可能將所有需要以插件方式進行運行的APK中的組件全部進行註冊

思路四:使用hook技術代理實現

我們先看下啓動一個Activity的基本過程,圖中已說明詳細,這裏不再贅述了

這裏寫圖片描述

由於前三種方式的缺陷,第四重方式通過預先在Host APP的Manifest文件中預註冊一些啓動插件app會用到的一些Dummy 組件,也就是圖中的StubActivity,當使用intent啓動一個PluginActivity的時候,使用Hook攔截此消息,將PluginActivity更換爲StubActivity,通過Binder系統調用進行進程間過程調用,ActivityManngerService執行Activity的棧管理、驗證等過程,由於我們使用將PluginActivity更換爲StubActivity,而StubActivity又在Host APP的Manifest文件中預註冊了。所以驗證會通過,然後ActivityManngerService將控制權移交給APP進程(同樣通過進程間的RPC調用),然後APP進程開始執行真正創建Activity的過程,這創建之前又通過hook將StubActivity更換爲PluginActivity,啓動真正要啓動的Activity,因此這樣就可以瞞天過海,欺上瞞下,騙過System_server進程,同時使得啓動的Activity又具有相應的生命週期,整個過程還是比較直觀的。

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章