Java字節碼修改神器HiBeaver:黑掉你的SDK

前言

有時候咱們在Java開發過程當中可能有這樣的需求:須要研究或者修改工程依賴的Jar包中的一些邏輯,查看代碼運行中Jar包代碼內部的取值狀況(好比了解SDK與其服務器通訊的請求報文加密前的狀況)。git

這個需求相似於Hook。github

可是每每這些依賴的Jar包中的代碼已經被混淆過,刪去了本地變量表和代碼行號等debug信息,因此沒法直接斷點調試,其內部邏輯和運行狀況也幾乎沒法觸及,研究更難如下手。這時候,通常的辦法有二:1.將Jar反解爲Java源碼,以module方式引入,即可自由修改調試;2.修改字節碼或者打包後的smali代碼,實現想要的邏輯後再從新打包。這兩種方法中,前者每每十分繁雜,尤爲在混淆後邏輯變得極其複雜,幾乎不可能完成;後者也很麻煩,工序較多,修改爲本也比較高。apache

插件:HiBeaver

Gradle編譯插件hibeaver結合Java AOP編程中對於大名鼎鼎的ASM.jar的應用,和Android gradle 插件提供的最新的Transform API,在Apk編譯環節中、class打包成dex以前,插入了中間環節,依據開發者的配置調用ASM API對項目所依賴的jar進行相應的修改,從而能夠比較高效地實現上面的Hook需求。
源碼地址:https://github.com/BryanSharp/hibeaver編程

(如今hiBeaver已經發布了1.2.7版本,支持輕量級AOP框架設計。)服務器

惟一須要注意的是,運用好這個插件須要有必定的Java彙編指令基礎,並瞭解基本的ASM3的使用方法:後者仍是很簡單的,而前者,關於Java彙編指令基礎這塊,對於事先不瞭解的同窗,接觸起來有必定難度,可是學一學這個其實很是有益處,對於理解Java的運行有很大的幫助。
閒話少說,先看看如何快速實踐一把!關鍵看療效!網絡

關於彙編指令的資料能夠參閱本人的文章:大話+圖說:Java彙編指令——只爲讓你懂app

實戰演練

咱們就先來嘗試用這個Hook掉小米推送的SDK。框架

首先,在須要的工程的根項目gradle配置中加入如下內容:
圖片描述ide

如圖所示,該插件上傳到了jcenter中,只需引入classpath:工具

classpath 'com.bryansharp:HiBeaver:1.2.7'

這裏須要注意的是,目前該插件僅支持Android gradle編譯插件2.0及以上的版本。
而後,在你的App項目gradle配置底部或任意位置加入以下代碼:

apply plugin: 'hiBeaver'
hiBeaver {
    //turn this on to make it print help content, default value is true
    showHelp = true
    //this flag will decide whether the log of the modifying process be printed or not, default value is false
    keepQuiet = false
    //this is a kit feature of the plugin, set it true to see the time consume of this build
    watchTimeConsume = false
    //this is the most important part
    modifyMatchMaps = [:]
}

而後,從新編譯一下項目,會先去jitpack下載這個插件,開始編譯後能夠看到Android Studio的右下角的Gradle Console中,多輸出瞭如下內容:
圖片描述

若是你看到了和我同樣的內容,那說明初步配置成功。
能夠看到,使用插件後會輸出一段友好的幫助內容,仍是中英文的,告訴咱們能夠直接拷貝做爲初始配置,這個幫助輸出也是能夠關閉的。
下面咱們正式開始嘗試Hook小米推送SDK,首先,找出其業務邏輯中的一個節點。
首先,引入小米推送,這個過程不贅述了,blablabla,引入成功!
衆所周知,使用小米推送須要先在代碼中調用以下:

MiPushClient.registerPush(this, APP_ID, APP_KEY);

這個代碼應該會調起本地長鏈接的創建、註冊服務器等流程。假如咱們出於學習的目的,想研究其中的流程,試舉一例,先從查看其反編譯的代碼開始,找一個切入的節點,以下:
首先進入查看MiPushClient.registerPush這個方法:
圖片描述
圖片描述

在initialize的方法中,找到一段邏輯以下:

圖片描述

進入a方法,來到了這個類:com.xiaomi.mipush.sdk.u中,發現:

圖片描述

下面若是咱們想看看運行時前兩個方法傳入參數的值,就能夠開始Hook了。該如何作呢?這個方法體內打Log輸出全部的值嗎?那樣太麻煩了。咱們能夠這樣作:
首先在咱們項目的源碼裏新建一個靜態方法,包含兩個參數,以下圖:

圖片描述

其後,咱們只要在a方法中加入一段代碼,調用咱們的靜態方法,並傳入咱們想查看的兩個參數便可。
這就有賴於咱們的hibeaver插件了,具體如何作呢?
咱們能夠先看看以前的幫助內容:

圖片描述

裏面有提到一個the most important par,最重要的部分。沒錯,這個插件的核心就在於配置這個類型爲Map<String, List<Map<String, Object>>>的傳入量。
首先咱們配置以下:

圖片描述

而後從新編譯,發現輸出log以下:

圖片描述

這樣就輸出這個u類的全部方法信息,用於後面進行配置。
再來看看剛剛的方法a:
圖片描述

是一個泛型方法,衆所周知泛型只存在於編碼階段,編譯後是沒有泛型的,其實傳入的參數的實際類型爲org.apache.thrift.a,最終找到其方法描述應該爲:

(Lorg/apache/thrift/a;Lcom/xiaomi/xmpush/thrift/a;ZLcom/xiaomi/xmpush/thrift/r;)V

進一步配置:

圖片描述

而後從新編譯,console輸出新增revist部分,以下:

圖片描述

最後,咱們增長以下代碼,在其中植入咱們的代碼,調用剛剛的靜態方法,並把對應值傳遞過來:
終極配置:

圖片描述

以上代碼就不作詳細解釋了,相信有基礎的都能明白,而後編譯查看輸出:

圖片描述

下面咱們debug一下,看看是否能夠成功在registerPush的運行流程中調用到咱們的方法:

圖片描述
圖片描述
上面能夠看到,不管是debug仍是log輸出均可以抓到想要的參數了。
由於小米推送是商業產品,這裏不便於探索太多內容,可是經過hibeaver這個插件能夠比較方便的進行相似的研究。

總結

hibeaver所體現的技術,並無特別大的價值,僅僅做爲工具來說比較方便易用,有助於學習研究Jar中的邏輯,和學習應用Java彙編碼。除此以外,還有幾個應用場景:1.修改引用SDK中的一些bug或者提升其效率;2.得到必要的SDK的一些關鍵調用時機,經過hook創建回調;3.欺騙SDK、關閉或減小SDK中不受控制的網絡傳輸。不一而足,仍是頗有趣、頗有想象空間的。
目前存在的問題,以下,這個除了偶爾同步報錯以外沒有影響,編譯正常:

圖片描述

還有,若是僅僅修改了gradle文件,不會觸發更新,須要在代碼上也進行任意修改方生效。

關於項目

hibeaver徹底開源,你們能夠自行查看其中代碼,有大量的中文註釋,對於學習gradle插件開發大有裨益。
github開源項目地址:https://github.com/BryanSharp/hibeaver

相關文章
相關標籤/搜索