當前位置:
首頁 > 最新 > 熱修復之 Method Hook 原理分析

熱修復之 Method Hook 原理分析

引言

目前國內大廠均開源了自己的 Android 熱修復框架,阿里的《深入探索 Android 熱修復技術原理》一書全面介紹了熱修復技術的現狀,原理與展望。一方面是阿里係為代表的底層方法替換,另一方面是以騰訊係為代表的類載入方案。前者支持立即生效,但是限制比較多;後者必須冷啟動生效,相對較穩定,修復範圍廣。之前分析過微信的熱修復框架 Tinker 即屬於後者( 《Tinker 接入及源碼分析》)。本篇文章主要分析以 AndFix 為代表的底層方法替換方案,並且實現了《深入探索 Android 熱修復技術原理》中提到得更為優雅,兼容性更好的方法替換新方案(MethodHook)。

開始

方法替換是 AndFix 熱修復方案的關鍵,虛擬機在載入一個類的時候會將類中方法解析成 ArtMethod 結構體,結構體中保存著一些運行時的必要信息以及需要執行的指令指針地址。那麼我們只要在 native 層將原方法的 ArtMethod 結構體替換成新方法的結構體,那麼執行原方法的時候便會執行到新方法的指令,完成了方法替換。

Andfix 中的關鍵代碼如下:

replaceMethod 是 native 方法,查看其實現:

ART 與 Dalvik 虛擬機分別有不同的實現,看 ART 虛擬機下的實現代碼:

由於不同的版本 ArtMethod 結構體參數會不一樣,所以不同的版本有不同的實現,每種實現本地保留一份不同的結構體代碼,我們看 6.0 的版本:

除前兩行代碼,後面都是結構體內容的替換,env->FromReflectedMethod(src) 返回的是 jmethodID ,事實上就是 ArtMethod 結構體的指針地址,所以可以強制類型轉換成 ArtMethod 結構體指針。最終就完成了方法替換。

新方案

上面的實現有很多局限性,由於不同系統版本 ArtMethod 結構體會不一致,所以本地保留了不同版本的 ArtMethod 結構體代碼,每次 Android 有新版本發布均需要做一次兼容,而且如果第三方 ROM 修改了 ArtMethod 結構體,那麼這種方案就會失效。

《深入探索 Android 熱修復技術原理》給出了一種更優雅的實現方案,不關心 ArtMethod 的內部結構,直接通過內存地址替換整個 ArtMethod,我使用文中提供的方案使用極少的代碼實現了一個 Demo 並上傳到了 GitHub 上:https://github.com/pqpo/MethodHook

主要就提供了一個 MethodHook 類完成類方法替換,使用方法如下:

界面如下:

點擊 CLICK 按鈕出現圖一;點擊 HOOK 按鈕,再點擊 CLICK 按鈕出現圖二;點擊 RESTORE 按鈕,再點擊 CLICK 按鈕出現圖一。整個過程完成了方法的替換與恢復。

Java 層核心方法如下:

其中 hook 方法將完成方法替換,restore 方法恢復原來的方法, MapmethodBackup 用於保存備份的方法地址,Key 為備份的方法,Value 為方法備份的內存地址。

主要邏輯位於 native 層,我們知道 System.loadLibrary 載入一個動態庫是,首先會調用其 JNIOnLoad 函數,在 JNIOnLoad 函數中完成一般會完成 native 方法的動態註冊,在本例中還加入了如下代碼,用於計算不同平台 ArtMethod 結構體的大小 :

在 Java MethodHook 類中有下面兩個靜態的空方法:

在 native 層獲取這兩個方法的 jmethodID, 事實上就是 ArtMethod 結構體的指針地址,由於 m1,m2 方法是相鄰的,其在 native 層的 ArtMethod 結構體也是相鄰的,所以它們內存地址的差值就是 ArtMethod 結構體的大小。 接下來看方法替換的邏輯:

這裡的 srcMethodObj,destMethodObj 對應 Java 層的 Method 類, 通過 env -> FromReflectedMethod 獲取方法的 jmethodID, 實際上就獲取了方法位於 native 層 ArtMethod 結構體的指針地址。 一開始已經計算出 ArtMethod 結構體的大小並保存在了 methodHookClassInfo.methodSize 中。再新構造一個 int 數組來備份原方法。使用 memcpy 將原函數 ArtMethod 內存拷貝至備份的數組中,然後使用 memcpy 將目標函數 ArtMethod 結構體拷貝至原函數結構體指針起始位置完成結構體替換。最後將用於備份的 int 數組的指針強制類型轉換為 long 類型返回給 Java 層以供後續恢復之用。

下面是方法恢復的函數:

將上一步保存的 long 類型地址強制轉換成 int 指針,然後獲取原函數 ArtMethod 結構體地址,接著將 int 數組的內容恢復至原函數內存地址處,完成恢複函數,最後釋放備份用的 int 數組。

可以看出這種方法相比較之前的方案優雅不少,不用考慮 ArtMethod 的內部結構,巧妙的計算出不同平台的 ArtMethod 結構體大小,從而不需要做任何適配工作。然而若要真正要將這個技術應用到熱修復框架中還需要考慮很多其他因素,本文拋磚引玉,只講述 Method Hook 的原理,對熱修復知識有興趣的可以閱讀阿里的《深入探索 Android 熱修復技術原理》。文章講到了阿里的熱修復框架 Sophix 結合了方法替換與類載入替換方案值得期待。

參考:

Android熱修復升級探索——追尋極致的代碼熱替換

AndFix

《深入探索 Android 熱修復技術原理》

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 公眾號 的精彩文章:

杭城烤雞NO.1——吳山烤雞

TAG:公眾號 |

您可能感興趣

Prometheus原理和源碼分析
AlphaGo之父DeepMind再出神作,PrediNet原理詳解
LruCache原理分析
Remote Procedure Call基本原理
BGP Confederation原理實踐
Photoshop工作原理
Leap Motion發布AR頭顯Project North Star原理圖
Session和Cookies的基本原理
集群管理工具KafkaAdminClient——原理與示例
Flutter Dart Framework原理簡解
android 結合源碼深入剖析AsyncTask機制原理
Leap Motion發布AR頭顯的原理圖
Spring Cloud斷路器Hystrix原理讀書筆記
opencv+python Hough變換的基本原理
基於Netty的Android系統IM簡單實現原理
ZooKeeper 設計原理
Makefile 及其工作原理
從Paxos到zookeeper分散式一致性原理與實踐-Zookeeper與Paxos
Hook原理
札記CAS、AQS、CountDownLatch、CyclicBarrier的原理