Xposed模塊開發基本方法記錄


  由於某些課程實驗的要求,需要通過xposed框架對某應用進行hook操作,筆者選用了開源且免費的xposed框架進行實現。雖然網上存在一些利用xposed實現特定功能的文章資源,但大多均將xposed模塊的構建作為一個小節內容一筆帶過,而且介紹的內容隨着考慮的因素、使用的編輯環境不同也大有區別,使得筆者在實際構建過程中往往提心吊膽,出現了錯誤也不知道如何去改正。故而這里特將筆者最近摸索和學習到的簡單xposed構建的知識記錄,以供查閱和參考。

 

Xposed 框架

  想要使用Xposed模塊實現特定的功能,必須依賴Xposed框架,正如 apk 的運行需要Android系統提供支持一樣,Xposed模塊任務的完成也離不開Xposed框架的支持,Xposed框架提供了Xposed模塊運行所必需的環境和功能接口,同時也可以對系統上已安裝的模塊進行禁用、卸載等管理。故而在想通過Xposed模塊完成一定任務之前,必須安裝Xposed框架。

  Xposed框架Installer下載地址:http://repo.xposed.info/module/de.robv.android.xposed.installer,安裝的過程可參考網絡資料。

  注意:由於Xposed框架安裝時需要將系統文件夾/system/bin的部分文件進行替換和備份,故而手機需要具備root權限

 

Xposed 模塊

  下面以Android Studio 3.0.1為例,總結下簡單的Xposed模塊的編寫所需的配置和注意事項。

  項目創建

  Xposed模塊實際上是作為一種較為特殊的apk安裝在系統上,由於Xposed 模塊的功能通常借助於其他應用和系統資源實現,其一般沒有應用界面,故而創建一個沒有活動的項目即可。

  A.在Android Studio中,點擊 file -> new -> new project 進行新項目的創建。指定應用名稱、公司域名、存儲位置和包名等信息,這里需要注意應用名和包名這兩個名字;

  

  B.勾選 Phone and Tablet 選項,選擇該 Xposed 模塊( 實際就是apk)將要運行的 Android 環境所對應的 API。注意這里的API指定的是Android環境與apk之間交互的接口,只有該API與之后apk實際運行的Android環境相匹配,apk才能正確的安裝和運行。如筆者的 Xposed 模塊將要運行在 Android 4.4上,則應該選擇 Android 4.4 對應的API 19.

  

  C.在接下來的界面中,選擇“ add no activity ”即不添加活動( Xposed 模塊不需要活動 ),即可完成項目的創建。

 

  項目配置

  Xposed模塊被視為一種特殊的apk,故而在創建項目之外,還需要針對其進行 Xposed 模塊相關的配置。

  A. 切換目錄結構。點擊界面左上方的豎式 Project,展開項目界面,將項目結構從 Android 切換至 Project 模式,方便之后的編輯;

  

  B. 添加 Xposed API依賴。Xposed 模塊的功能借助 Xposed 框架實現,Xposed 模塊通過對應的 Xposed 框架 API 來使用 Xposed 框架提供的功能。想要使用 Xposed 框架 API,則必須提供對應的庫( 名為 XposedBridge API jar)的路徑等信息。(以下信息來自 Using the Xposed Framework API )

    *針對 Android Studio

    Android Studio 中使用gradle進行項目的構建,故而想要使用 Xposed 框架 API 對應的庫,則需要在 gradle 的配置文件中進行指定。在 應用名(test) -> app -> build.gradle 中加入以下內容:

1      repositories {  //通常新項目的 build.gradle 中不包含有repositories塊,直接在空白處增加1-3行即可
2          jcenter();       }
3  
4      dependencies {  //通常 build.gradle 文件中已包含dependncies塊,所以只需將第6行的內容添加進已有的 dependencies 中即可
5          provided 'de.robv.android.xposed:api:53'
6        provided 'de.robv.android.xposed:api:53:sources'   //該行是可選的,用於下載API相關的文檔等信息
7  }   

    其中,第2行表示將 jcenter 作為代碼倉庫,可在上面引用開源項目,而第 6 行則指定引用項目的項目名和版本信息。

    第6行的聲明需要注意兩點:  

    a) 使用 provided 關鍵字而不是 compile,后者會將引用的 Xposed 框架 API 類打包至生成的 apk 中,而這些類在安裝好的 Xposed 框架中是已經存在的,所以可能會產生沖突,而 provided 關鍵字則僅保留對 API 的引用,實現具體功能的類則由安裝好的 Xposed 框架提供;     

    b)使用系統對應的 Xposed 框架 api 版本,不同的 Xposed 框架 API 在不同的 Android 環境中發揮作用。如Android 4.x 環境則只能選擇 API 53。

    

    *針對 eclipse

    eclipse 環境下無法通過指定 jcenter 中的開源項目來實現對其的引用,所以必須在實際的項目中手動添加所需引用的庫文件 XposedBridge API jar 的路徑,在這里手動下載所需的 API 版本,將其復制到項目的文件夾下( 注意不要放置在項目本身已經有的 lib / libs 文件夾下,原因與Android Studio環境下使用 provided 而不是 compile 一致 ),選中 jar 包 右鍵 -> Build Path -> add to build path 即可。

  

  C. Android Studio 環境下需要禁用 Instant run 。在 File -> Settings -> Build, Execution, Deployment -> Instant Run 中取消其勾選,否則源程序中的類會由一個 stub 應用加載,而不是直接包含在 apk 文件中,而 Xposed 框架目前無法處理這樣的情況。

  D. 修改 AndroidManifest.xml 文件中的屬性。在 應用名(test) -> app -> src -> main 文件夾中找到 AndroidManifest.xml,修改其中的屬性,使得我們最終生成的 apk 能夠被 Xposed 框架識別。請保證最終文件的部分內容為以下格式。

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" >    // App相關的信息

    <meta-data
            android:name="xposedmodule"    //標志該 apk 為一個 Xposed 模塊,供 Xposed 框架識別
            android:value="true" />
    <meta-data
            android:name="xposedminversion"   
            android:value="53" />       //xposed最小版本號,請與(2)步中選用的api版本保持一致
    <meta-data
            android:name="xposeddescription"
            android:value="hook a function" /> //針對該模塊的描述,會在Xposed框架的模塊管理界面中顯示出來,便於模塊管理

    </application>

  完成上述步驟后,則可將項目文件打包成 apk ( Android Studio 下打包 apk 的簡單步驟可以參見這里),並在手機中安裝。安裝成功后,在 Xposed 框架的模塊管理界面會出現該模塊對應的管理項,證明對 Xposed 模塊的配置是成功的。勾選對應的模塊,並重啟設備,重啟之后該模塊則開始發揮功能。當然目前我們並沒有實現任何功能。

  

  簡單示例

  在完成 Xposed 框架的配置后可在該項目中進行框架功能的編寫。在 應用名(test) -> app -> src -> main -> java 目錄下,可以看到 Android Studio 已經自動根據我們在創建項目時指定的包名生成了一個包( package )的條目,如筆者項目中的包名為 xposed.test(見創建項目時的界面截圖),選中該包右鍵 -> new -> java  ->class ,創建自己的一個 class ,如 test.class 。用戶可在該 class 中實現簡單的xposed hook功能。

  ( 以下部分不涉及具體的實現原理,僅提供函數的簡單功能的描述,更多關於Xposed 框架API的功能可在這里查看)

  一個 Xposed 模塊可以有多個不同的入口,其中實現的方法既可以在 Android 系統啟動時被調用,也可在一個應用程序包被加載時被調用,想要在不同的時期被調用,則該功能類需要實現不同的接口。

    Xposed公共接口
    package:de.robv.android.xposed.IXposedHookZygoteInit       
    interface :IXposedHookZygoteInit   //在Android系統啟動時被調用,作用於初始的zygote進程,可用於實現應用於所有應用的hook

    package:de.robv.android.xposed.IXposedHookLoadPackage
    interface:IXposedHookLoadPackage  //當指定應用被加載時被調用,一般用於hook特定應用的方法

    package:de.robv.android.xposed.IXposedHookInitPackageResources
    interface:IXposedHookInitPackageResources //指定應用的資源進行初始化時被調用,一般用於資源的替換

   

  以實現 IXposedHookLoadPackage 接口的模塊為例,實現一個簡單的hook功能。

  通常使用 findAndHookMethod 方法來定位待 hook 的類中的方法(官方解釋在這里)

    findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback)
     className:被hook的方法所在類的完整名稱,包括 包名 + 類名
     classLoader:可通過 lpparam.classLoader 獲得
      methodName:被hook的方法的名,注意如有混淆,則應該用混淆后的名字
      object... 與為被hook的方法的參數對應
      Callback  :指定該方法被調用時,需要被執行的回調

  例如,想要hook如下代碼,即hook包 com.test.example 中的類 a 下的 b 方法

    package com.test.example
    public class a{
         public int b(String s, int i, MyClass m) 
         {}
    }    

  則對應的 findAndHookMethod方法應該寫作

   findAndHookMethod("com.test.example.a", lpparam.classLoader,"b", String.class, int.class, "com.test.example.MyClass", new XC_MethodHook() {
   @Override
   protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
     do somthing
   }
 });
    這里需要注意:
    (1)對於待hook函數中的java自帶類型的參數,如 int 和 String,則應使用 int.class 和 String.class 作為findAndHookMethod的參數
    (2)若待Hook函數中包含有形如 MyClass 這樣的自定義類型的參數,則直接使用該類型的完整路徑名即可,如 "com.test.example.MyClass" 

  在 findAndHookMethod 方法的參數中,直接定義了一個 XC_MethodHook 回調,其中一般有兩個方法可供使用者重新定義。

   @Override
      protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
          // 該方法在被hook函數之前被調用
      }
     @Override
      protected void afterHookedMethod(MethodHookParam param) throws Throwable {
          // 該方法在被hook函數之后被調用
      }

 

  以下代碼實現了微信的一個日志函數的簡單 hook 。

 1 package xposed.xposed_wechat_log;  //實現的類所在的包
 2 
 3 import android.util.Log;       //系統提供的Log方法的包
 4 import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;  //findAndHookMethod方法所在的包
 5 import de.robv.android.xposed.IXposedHookLoadPackage;           
 6 import de.robv.android.xposed.XC_MethodHook;
 7 import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
 8 
 9 public class test implements IXposedHookLoadPackage {  //test類實現了IXposedHookLoadPackage接口
10     @Override
11     public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {  // handleLoadPackage 在任意包被加載時被調用,參數lpparam,包含有加載的 app 的信息
12         if ( lpparam.packageName.equals("com.tencent.mm")) {             // 可通過 lpparam 中包含的包信息對包進行篩選,如這里表示 com.tencent.mm 包加載時,才執行下一步操作
13     findAndHookMethod("com.tencent.mm.sdk.platformtools.x",lpparam.classLoader, "d" , String.class, String.class, Object[].class,
14             new XC_MethodHook() {
15                 @Override
16                 protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
17                     String str0  = (String) param.args[0];              //可通過 param.args[i]獲得 hook 方法的參數
18                     String str1  = (String) param.args[1];
19                     Object[] obj2= (Object[]) param.args[2];
20                     String  str = obj2 == null ? str1 : String.format(str1, obj2);
21 
22                     if( str==null)
23                         str = "";
24 
25                     Log.e( "Xposed_hook_d"+str0,str);
26                     super.beforeHookedMethod(param);
27                 }
28             });
29      } 
30   } 
31 }
simple_hook

  

  完成上述模塊功能的編寫后,需在 應用名(test) -> app -> src -> main 目錄下新建一個 assets 目錄,並在該目錄下建立一個名為 exposed_init 的文本文件。該文件中記錄模塊中所有實現了Xposed 功能接口的類的完整路徑名,每一行書只寫一個這樣的路徑。

  如在上述實例模塊中,test 類實現了接口 IXposedHookLoadPackage,則 exposed_init 文件中應該有以下內容:

    xposed.test.test  //其中 xposed.test 為包名,test為實現接口的類名

  所有實現了 Xposed 接口的類均要在該文件中記錄,以供 Xposed 框架進行處理。

  之后即可打包完成的項目,將其安裝至設備上進行驗證了。

 

參考資料:

  1. [TUTORIAL]Xposed module development詳細的xposed模塊開發示例

  2. Using the Xposed Framework API關於如何在模塊中使用Xposed框架提供的API

  3.  Development tutorial簡單的xposed模塊示例


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM