Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 深入理解Android之Xposed詳解

深入理解Android之Xposed詳解

編輯:關於Android編程

一、背景

Xposed,大名鼎鼎得Xposed,是Android平台上最負盛名的一個框架。在這個框架下,我們可以加載很多插件App,這些插件App可以直接或間接操縱系統層面的東西,比如操縱一些本來只對系統廠商才open的功能(實際上是因為Android系統很多API是不公開的,而第三方APP又沒有權限)。有了Xposed後,理論上我們的插件APP可以hook到系統任意一個Java進程(zygote,systemserver,systemui好不啦!)。

功能太強大,自然也有缺點。Xposed不僅僅是一個插件加載功能,而是它從根上Hook了Android Java虛擬機,所以它需要root,所以每次為它啟用新插件APP都需要重新啟動。而如果僅是一個插件加載模塊的話,當前有很多開源的插件加載模塊,就沒這麼復雜了。

Anyway,Xposed強大,我們可以學習其中的精髓,並且可以把它的思想和技術用到自己的插件加載模塊裡。這就是我們要學習Xposed的意義。

Xposed支持32位和64位的dalvik以及ART,同時支持selinux。我仔細看了下,如果拓展把這些東西都講的話,一個是很枯燥,另外一個是背離了我們本章講解插件的主線。所以,本章將圍繞下面幾個點開展介紹:

l 32位dalvik上非selinux模式下的xposed實現原理

64位、selinux模式其實難度不在虛擬機上,而是在selinux上。

二、 初識Xposed

本章先來介紹下Xposed,這是一個大工程,包含多個項目,我也是花了不少時間才把它整個玩轉起來的。Xposed包含如下幾個工程:

  • XposedInstaller,這是Xposed的插件管理和功能控制APP,也就是說Xposed整體管控功能就是由這個APP來完成的,它包括啟用Xposed插件功能,下載和啟用指定插件APP,還可以禁用Xposed插件功能等。注意,這個app要正常無誤得運行必須能拿到root權限。
  • Xposed,這個項目屬於Xposed框架,其實它就是單獨搞了一套xposed版的zygote。這個zygote會替換系統原生的zygote。所以,它需要由XposedInstaller在root之後放到/system/bin下。
  • XposedBridge。這個項目也是Xposed框架,它屬於Xposed框架的Java部分,編譯出來是一個XposedBridge.jar包。
  • XposedTools。Xposed和XposedBridge編譯依賴於Android源碼,而且還有一些定制化的東西。所以XposedTools就是用來幫助我們編譯Xposed和XposedBridge的。

    提示,提示,提示:

    我把所有相關代碼都下載並放到下面的地址了:

    https://code.csdn.net/Innost/xposed-learning 裡邊包含:

    1 xposed所有代碼庫的內容

    2 XposedDemo:Xposed插件APP Demo,非常簡單

    3 XposedDemoTarget:XposedDemo將hook上的目標App

    2.1 編譯Xposed

    下面介紹下如何編譯Xposed,這裡以Android 4.4.4為例。做Android開發最好配一個Nexus手機或Pad。我用得是Nexus 7 2013Wi-Fi版。Anyway,Xposed的編譯和具體機器無關,不過下面的前提條件需要滿足:

    • 准備Android 4.4.4源碼。我分享了很多AOSP源碼,注意:我提供的源碼沒有.repo和.git,雖然省了很多空間,不過在編譯Xposed的時候,還是需要有.repo。
    • 下載我提供的Xposed源碼和測試Demo。

      好,馬上開始我們的步驟:

      2.1.1 下載支持庫

      根據XposedTools的說明,我們先要修改下AOSP源碼裡的.repo,具體步驟如下:

      • 進入AOSP/.repo目錄。
      • 在.repo目錄下新建local_manifests目錄。
      • 把XposedTools/local_manifests/下的目標文件拷貝過去。local_manifests/目錄下是各種API版本(即SDK=19,21之類)對應的xml文件。由於本例對應的SDK版本是19,所以需要把該目錄下xposed_sdk19.xml文件拷貝到.repo/local_manifests/目錄下。

        這個文件是什麼內容呢?來看圖1:

        \

        這個文件內容是啥意思呢?

        • :新添遠程倉庫地址為github。
        • 第一個:為frameworks/base/cmds/下新增加xposed工程。當然,這個工程我們剛才下載過了。你可以直接copy到指定目錄下。
        • :移除AOSP/build目錄。xposed有自己的處理。
        • 第二個:下載xposed自己的build。path="build",表示下載路徑就是AOSP/build。

          配置好後,請在AOSP目錄下執行repo sync。這樣它會根據manifests更新AOSP源碼。當然,也可以只是下載frameworks/base/cmds/xposed工程和更新build工程。

          注意,repo sync是一個重型操作,會導致所有工程都進行一次同步。我建議的方法是直接下載和更新對應的工程

          比如,下載xposed工程,用repo sync frameworks/base/cmds/xposed即可。對於build目錄,先把原來的build挪到其他地方。然後repo sync build即可。

          好了,到此,所有源碼都已經ready了。

          2.1.2 修改build.conf

          下面我們進入XposedTools目錄,然後修改其中的build.conf文件。該文件用於指示AOSP源碼等參數。如圖2所示:

          \

          XposdTools提供了一個build.conf.sample模板,圖2中的build.conf文件是在這個模板基礎上修改而來。紅框中是我修改的結果。其他選項沒有變化。

          2.1.3 執行build.pl

          到XposedTools目錄下,執行:./build.pl -t arm:19命令,這表明我要編譯arm平台上SDK=19版本的xposed框架。注意,./build.pl --help會打印出使用方法。build.pl是一個perl腳本。圖3是編譯過程截圖:

          \

          圖3中,你可以發現build.pl跑到AOSP源碼目錄下,執行了:

          • . build/envsetup.sh:初始化AOSP編譯環境
          • lunch aosp_flo-userdebug:選擇交叉編譯平台。注意,這一塊我是修改了XposedTools/xposed.pom,使它單獨為我的nexus 7編譯,而不是針對ARM平台做generic的編譯。
          • make -j4 xposed libxposed_dalvik:編譯xposed和libxposed_dalvik這兩個目標文件。

            在使用build.pl時,它還依賴一些Perl的類庫,請童鞋們按照下面步驟下載這些依賴庫:

            sudo apt-get install libconfig-inifiles-perl

            sudo apt-get install libio-all-perl

            sudo apt-get install libfile-readbackwards-perl

            sudo apt-get install libfile-tail-perl

            sudo apt-get install libtie-ixhash-perl

            build.pl執行過程中,如果報還有其他依賴庫未找到,請通過下面命令

            apt-cache search perl XXX 來查找需要apt-get install哪個目標庫。XXX是build.pl執行過程中報錯時提供的庫信息

            2.1.4 編譯結果

            編譯完成後,將產生一個zip包到AOSP/out/sdk19/arm下。AOSP/out是我在build.conf中指定的目錄。如圖4所示:

            \

            編譯結果是一個xposed-v65-arm-custom-build-xyz-20151030.zip包,這個包可以通過recovery刷到手機上。包的內容就是files文件夾下的內容,包含:

            • system/bin/app_process_xposed:xposed版zygote。
            • system/bin/libxposed_dalvik.so:xposed框架的native層。
            • system/xposed.prop:xposed框架信息,包含版本號等。內容如右邊圖所示。

              三、 XposedInstaller

              XposedInstaller是Xposed的App,用於管理Xposed框架和插件App。本節我們主要討論它是如何安裝Xposed框架和插件App的。

              XposedInstaller啟動界面如圖5所示:

              \

              圖5中可知XposedInstaller提供好幾項子頁面。第一個“框架”用來安裝或卸載Xposed框架的。我們來看它。

              3.1 安裝Xposed框架

              如圖6所示:

              \

              注意,圖6右上角的“程序自帶”兩個版本號,分別是app_process版本號為58,XposedBridge.jar版本號是54.

              而“激活”這兩項為空,因為我們的系統還沒有安裝Xposed框架。

              “程序自帶”是什麼意思?原來,XposedInstaller在自己的assets目錄下攜帶了所需要的xposed框架程序和模塊,如圖7所示:

              \

              從圖7可知,XposdInstaller自帶了xposed版zyote(比如app_process_xposed_sdk16),為了更好得支持不同版本的Android,它還區分了SDK版本。另外,XposedInstaller也支持刷機包把xposed框架模塊刷入系統,比如Xposed-Installer-Recovery.zip,裡邊包含的主要內容就是一個腳本,在recovery模式下運行,其內部也是把assets裡的文件拷貝到/system相關目錄中。這一塊我們後續看代碼就知道怎麼玩兒了。

              安裝Xposed框架的主要功能由InstallerFragment.java提供,我們看看相關代碼。

              3.1.1 onCreateView

              onCreateView函數是Fragment裡初始化UI的核心回調,其代碼如圖8所示:

              \

              圖8代碼中最後的refreshVersions用於獲取版本號,也就是圖6中右上角“程序自帶”要顯示的信息,它包括兩個東西:

              • xposed版app_process的版本,包括程序自帶(也就是XposedInstaller放在assets目錄下的)和系統安裝的。當然,只有該系統安裝過Xposed版app_process才有必要檢查它的版本。圖6中由於沒有裝過,所以“激活”那一欄顯示為“-”。
              • XposedBridge.jar:也包括程序自帶和系統已安裝兩個jar包的版本。

                檢查版本主要是為了兼容性考慮。代碼中的refreshVersions用於獲取他們的版本號,代碼如圖9所示:

                \

                圖9中:

                • getInstalledAppProcessVersion:讀取/system/bin/app_process的版本號。
                • getLatestAppProcessVersion:讀取自帶app_process_sdkXX的版本號。
                • getJarInstalledVersion,讀取/data/data/de.robv.android.xposed.installer/bin/ XposedBridge.jar版本號。對,你沒看錯,XposedBridge.jar是放在XposedInstaller自己的目錄下的。
                • getJarLatestVersion:讀取自帶XposedBridge.jar的版本號。

                  想知道怎麼獲取版本系想你嗎?來看圖10:

                  \

                  • 對於app_process,直接讀取文件內容,然後找到紅框中的那一行,後面的58就是版本號。
                  • 對於XposedBrdige.jar,打開這個jar包,讀取assets/VERSION的內容,裡邊就是存儲了一個版本號信息。

                    太簡單了,不惜得說。

                    注意XposedBridge.jar包安裝版的位置,它在/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar裡

                    3.1.2 安裝xposed框架

                    InstallerFragment的install函數用於安裝xposed框架相關的模塊。這個函數一點也不復雜,不過還是給大家看看。如圖11所示:

                    \

                    install函數沒什麼難度,但是我們要總結下xposed框架安裝到底做了什麼手腳:

                    • 將xposed版的app_process拷貝到/system/bin/,系統原來的app_process改名為app_process.orig
                    • 將XposedBridge.jar放到/data/data/de.robv.android.xposed.installer下
                    • 刪除/data/data/de.robv.android.xposed.installer/conf/disabled文件。如果有disabled文件則表明整個Xposed功能被禁止使用。

                      嗯嗯,也沒什麼太多可說的。

                      3.1.3 安裝xposed插件

                      xposed插件,在xposed世界裡我們說它是插件,但是放到Android世界裡它就是一種特殊的APP。這種類型的APP由xposed框架識別並加載,然後hook到其他的App進程。

                      當然,這裡既然提到進程,那麼這些APP就必須要被先啟動起來才是。

                      XposedInstaller的插件管控由ModulesFragment界面來處理。本節主要想介紹下XposedInstaller是怎麼對待Xposed插件APP的。

                      來看代碼,如圖12所示:

                      \

                      我們重點來看看Xposed插件APP是怎麼被load的,代碼其實在ModuleUtil的reloadInstalledModules函數中。代碼很簡單如圖13所示:

                      \

                      圖13左下角已經告訴各位Xposed插件APP該是什麼樣的了,右下角是XposedDemo示例。

                      在AndroidManifest.xml裡定義這些東西只是告訴Xposed自己是一個插件APP。但是作為一個掛鉤插件,Xposed還需要知道這個APP裡哪個類是用來掛鉤的。這句話的意思是:

                      1 這個APP是一個插件APP。該APP包含很多功能。

                      2 這個APP包含的眾多功能中,有一個功能是給目標進程掛鉤。掛鉤操作是Xposed框架來做的,所以它需要知道該APP中哪個類是繼承了Xposed鉤子接口。這樣,Xposed框架找到插件APP之後會觸發這個app的鉤子接口進行掛鉤。

                      要做到第二步就得在assets/下放一個名叫xposed_init文件,裡邊指明插件APP的掛鉤類名。XposedDemo的

                      assets/xposed_init的內容就是:com.xposed.demo.MyXposedModule。當然,這個插件類必須實現Xposed的IXposedHookLoadPackage接口類。這個我們以後再討論。

                      四、 Xposed框架介紹

                      Xposed框架分為xposed版app_process和XposedBridge.jar兩部分。app_process就是zygote,我們先看看xposed版的zygote干了些什麼。

                      4.1 Xposed版zygote

                      注意,本章只分析32位,dalvik版的xposed app_process,其入口main函數位於app_main.cpp裡。

                      圖14所示的代碼展示了Xposed版zygote與眾不同之處。

                      \

                      圖14中,左上角的框是app_main的main函數,裡邊有兩處不同之處:

                      • AppRuntime:此處的AppRuntime類是xposed版的AppRuntime。它的與眾不同之處從左下圖的onVmCreated開始。當檢查到isXposedLoaded為真是時,將調用xposed::onVmCreated函數。
                      • xposed::onVmCreated函數位於右上框,它先判斷當前虛擬機是ART還是dalvik,然後加載xposed一個特殊so,此處是/system/bin/libxposed_dalvik.so。注意,這裡的xposed框架和XposedInstaller略有區別。因為XposedInstaller比較舊了(也就支持sdk 16的樣子),還沒有涉及到ART和dalvik的區別。在onVmCreate中,它將調用libxposed_dalvik.so中的xposedInitLib函數,然後再調用so中設置的onVmCreated函數。這個onVmCreated函數由xposedInitLib設置。
                      • 左上框中還有一個xposed:initialized函數。這個函數初始化了XposedShared類型的全局變量xposed,同時還把一個重要的jar包加到了CLASSPATH裡。來看代碼。如圖15所示:

                        \

                        圖9展示了initialize函數的內容,主要是最後一個把/system/bin/XposedBridge.jar(這個jar包的位置和我們在XposedInstaller那裡看到得不同,原因前面解釋過了)加到CLASSPATH比較重要。

                        注意,我們這裡雖然對initialize介紹的內容很少,但實際上這個函數要真正看明白還是很需要技術實力的:

                        1 logcat:start:裡邊fork了logcat進程用來存儲xposed自己的log

                        2 service:startAll:為了完美支持selinux,這裡的處理更是很有技巧。selinux是一個完整的知識體系,想徹底掌握它的童鞋請參考我的三部曲文章《深入理解SELinux SEAndroid》

                        1&2其實很真實得反映出xposed的作者在Android、Linux上水平很高,經驗很豐富。

                        最後,如果xposed框架啟用成功,那麼zygote的入口類將由以前的com.android.internal.os.ZygoteInit變成de.robv.android.xposed.XposedBridge。

                        下面我們按照執行流程,把相關函數分析一遍:

                        • AppRuntime的onVmCreated,它最終會導致libxposed_dalvik.so中的onVmCreated被調用。我們直接分析最後這個onVmCreated。
                        • 然後AppRuntime裡會調用de.robv.android.xposed.XposedBridge.main函數。

                          4.2 onVmCreated

                          圖16為代碼:

                          \

                          onVmCreated比較簡單了:

                          • 針對android/content/res/XResource&XTypedArray進行了一些處理,這是為將來資源替換時候做准備。以後我們碰到再說。
                          • 為XposedBridge注冊JNI函數

                            xposed官方說MIUI大量用了xposed的東西,並且不共享,以後碰到MIUI的問題他們不再支持。Don't know what to say....

                            4.3 XposdBridge.main函數

                            main函數代碼如圖17所示:

                            \

                            main函數裡有三個重要函數:

                            • initNative:好好學習下
                            • initForZygote:好好學習下
                            • loadModules:加載App插件

                              4.3.1 initNative

                              initNative很重要,來看代碼,如圖18所示:

                              \

                              圖18中,我們重點看一下callback_XposedBridge_initNative和register_natives_XResources這兩個函數。這兩個函數比較簡單,我們統一放到圖19中:

                              \

                              不多說了,沒什麼難度。

                              4.3.2 initForZygote

                              從這個函數開始,xposed就開始給系統一些關鍵函數掛鉤子了。我們看看它怎麼玩兒的。代碼如圖20所示:

                              \

                              圖20中我在eclipse裡用了代碼縮略顯示的方法,可知一共有五個框,分別hook了一些關鍵內容。我們從上到下一次分析。先來分析下Xposed框架提供的掛鉤函數findAndHookMethod。

                              (1) findAndHookMethod介紹

                              findAndHookMethod用來對指定類的指定函數進行掛鉤。這個函數很重要,開發插件APP時用得最多。來看它的代碼,如圖21所示:

                              \

                              findAndHookMethod代碼Java層面的邏輯還是比較好理解的:

                              • 首先是通過class相關的函數找到指定的函數對象。這裡的查找需要指定函數所在的類,函數名,函數參數信息等,屬於精確(exact)查找。注意,findAndHookMethod函數的最後一個參數必須是XC_MethodHook類型,即鉤子對象的數據類型。
                              • 然後就是XposedBridge.hookMethod。

                                來看hookMethod,如圖22所示:

                                \

                                由hookMethod可知,一個目標函數可以掛多個鉤子,這些鉤子由一個集合來存儲。然後我們將轉到JNI層去看看hookMethodNative干了什麼事情。這才是hook的核心。代碼如圖23所示:

                                \

                                hookMethodNative完成了真正的掛鉤處理,其思想很簡單:

                                • 先找到目標函數在native層對應的Method對象。
                                • 修改這個Method為native方法,並設置nativeFunc為hookedMethodCallback函數。
                                • 然後還要保存原函數的一些信息到insns域。

                                  我們在《深入理解Android之dalvik》一文中介紹過,JVM調用java函數時候,發現這個函數為native的話,就調用它的nativeFunc。下面我們看看鉤子函數的調用。

                                  (2) 調用鉤子函數

                                  hook鉤子函數後我們就要調用它。上一節我們發現xposed在掛鉤子的時候會把原函數改造成native屬性(即Dalvik會按native函數的方式調用它),對應的nativeFunc是hookedMethodCallback,其代碼如圖24所示:

                                  \

                                  注意喔,我們現在已經在處理被掛鉤函數的調用了喔....從JNI會進入到java層的鉤子函數dispatch總入口,及handleHookedMethod,這個函數比較復雜,我們一段一段來看它。

                                  \

                                  很簡單。接著看第二段,如圖26所示:

                                  \

                                  貌似也很簡單喔...

                                  現在來看原目標函數的調用,即invokeOriginalMethodNative。代碼如圖27所示:

                                  \

                                  同樣很容易,不多說了。到此,我們已經看到了Xposed掛鉤的所有過程。好像也沒什麼復雜的,只要對dalvik稍微屬性點,應該是比較容易做的。

                                  當然,本文只是給大家show了主要流程,真要自己動手做,我覺得難點在於參數傳遞等方面。anyway,了解了大體流程,後面的事情也好辦,多嘗試幾次就好。

                                  下面我們回過頭來看initForZygote裡加的幾個鉤子都干了什麼。

                                  (3) initForZygote鉤子設置

                                  圖20的initForZygote代碼示意中可知xposed對下面幾個函數進行hook(此處先不討論鉤子函數干了什麼)

                                  • ActivityThread.handleBindApplication:這個函數是ActivityManagerService內部調用ActivityThread.bindApplication間接觸發的。該函數的詳情可參考《深入理解Android卷2》第六章深入理解ActivityManagerService的“ApplicationThread的bindApplication分析”一節。handleBindApplication主要工作是初始化APP(APP由zygote進程fork而來,在hanldeBindApplication之前,這個APP進程和zygote沒什麼區別。只有調用完handleBindApplication之後,這個APP進程才是APP,比如該進程有了對應的名字,Aplication對象被創建等)。
                                  • com.android.server.ServerThread. initAndLoop函數:這個函數是給system_server用的,用於啟動系統各種重要服務。
                                  • LoadedApk構造函數:通過hookAllConstructors來對LoadedApk各種構造函數進行掛鉤。LoadedApk是APK文件在APP裡的代表對象,裡邊有很多重要的信息。
                                  • android.app.ApplicationPackageManager. getResourcesForApplication:掛鉤PackageManager的資源獲取函數。
                                  • hookResources:對資源進行hook。這一塊由於涉及到android APP對資源的管控,以後有機會我們再詳細介紹。

                                    4.3.3 loadModules

                                    main函數在initForZygote之後的下一個動作就是loadModules,這就是加載所有的插件APP。我們來看看這個函數,代碼如圖28所示:

                                    \

                                    我們來看下loadModules的處理:

                                    • 先從XposedInstaller那獲取當前已經安裝(並啟用)的插件APP列表,然後調用loadModule來加載這個APP。
                                    • loadModule從APP的assets/xposed_init文件裡看看這個apk裡聲明了哪些鉤子類。
                                    • loadModule校驗這些鉤子類是不是符合Xposed定義的鉤子類類型,然後作對應處理。圖29列出了Xposed支持的鉤子類類型。

                                      \

                                      圖30展示了hookLoadPackage和hookInitPackageResource兩個函數的內容,特別簡單。

                                      \

                                      嗯嗯,就是把對應的鉤子函數保存起來先。

                                      好了,下面我們就來開始分析initForZygote裡掛上的幾個鉤子分別有什麼用。

                                      4.3.4 handleBindApplication鉤子

                                      initForZygote為hanldeBindApplication設置了前處理鉤子,代碼如圖31所示:

                                      \

                                      前面說過,在APP生命周期內,handleBindApplication是APP剛准備好相關信息的一個重要點,在這個點去進行掛鉤處理簡直是最好不過了。當然,此處的掛鉤處理也就是准備好這個APP的相關信息然後調用所有的IXposedHookLoadPackage類型的鉤子。

                                      注意,除了handleBindApplication之外,由於一個APP進程事實上可以加載多個APK(比如那些申明同樣的uid和運行在同一進程的APP),在LoadedApk的構造函數中也做了類似的處理

                                      IXposedHookLoadPackage鉤子一般會干些什麼呢?圖31對XposedInstaller的處理就很明顯了。一般而言,這種鉤子會對目標APP中感興趣的函數進行掛鉤(調用findAndHookMethod),比如XposedInstaller對getActiveXposedVersion進行了掛鉤,用於返回系統裡正在使用的Xposed框架版本。

                                      我們應該在鉤子函數裡干些什麼?這是一個重要問題。我也不廢話了,直接上XposedDemo的源碼,如圖32所示:

                                      \

                                      圖32是我在xposed-learning項目中提供的XposedDemo示例,可知:

                                      • Xposed框架對handleBindApplication進行hook後,當有APP啟動的時候,該框架就會調用所有IXposedHookLoadPackage類型的鉤子。
                                      • 這種鉤子一定要區分當前被hook的APP是不是自己想要Hook的APP。如果不是,請直接return。如果是自己的目標APP,則進一步通過findAndHookMethod進行掛鉤。

                                        簡單點說,我們在IXposedHookLoadPackage的handleLoadPackage中把該掛的鉤子都掛上就好。

                                        4.3.5 initAndLoop鉤子

                                        initAndLoop是system_server進程的關鍵函數,在這個函數裡Android Framework的絕大部分Service都將被創建。真是藝高人膽大,這個進程居然都提供了掛鉤處理。其代碼如圖33所示:

                                        \

                                        代碼倒是很簡單,無非是針對system_server進行hook。插件函數如果想區分被hook的進程是否為system_server的話,只需要判斷packageName是否為"android"即可。

                                        再次強調,system_server是Android Java層Framework的核心,要hook它需要萬分小心,否則或導致手機系統出現會各種不穩定,崩潰,重啟等情況。

                                        下面我們介紹下Xposed框架對資源是怎麼Hook的。

                                        4.4 對資源的Hook

                                        前面章節介紹了Xposed框架如何對代碼調用邏輯進行hook。在Android APP中,除了代碼邏輯外,Xposed還支持對資源進行Hook。對資源Hook的原理其實和對代碼調用進行Hook的原理類似。這裡我們簡單介紹下Xposed框架如何對資源進行Hook。

                                        在Android APP中,資源有三個重要類:

                                        • ResourcesManager:一個APP進程有一個ResourcesManager。一個ResourcesManager可以管理多個Resouces。
                                        • Resources:Resources是一個APK中資源的代表,注意,它不是指單獨的一種類型的資源(比如字符串,圖片等),它是一個APK文件中資源文件的代表。Resources對象有一個AssetManager,AssetManager能操作APK文件(其實就是一個壓縮包)assets以及res目錄裡的內容。
                                        • Resources通過ResourcesKey將自己保存到ResourceManager中的一個ArrayMap裡。
                                        • 對於資源掛鉤來說,Xposed框架及插件APP做如下幾件重要的事情:
                                        • 和handleBindApplication類似,在APP進程某個時間點的時候(猜測:初始化資源相關模塊)進行Hook,然後調用IXposedHookInitPackageResources類型的鉤子。
                                        • IXposedHookInitPackageResources類型的鉤子通過Xposed提供的XResources類的setReplacement對原有的資源進行替換。在Xposed框架中,XResources是用於替代Resources的。
                                        • App運行時候,Xposed框架截獲相關的資源獲取函數(比如Context的getString),先看看是否被replace了,如果是,則返回被replace的結果。

                                          就這麼簡單。我們一步一步來看。

                                          注意,XposedDemo並沒有hook資源,請感興趣的童鞋們自行加上該功能進行測試。

                                          4.4.1 hookResource

                                          hookResource對ResourceManager等進行了掛鉤處理。來看代碼,如圖34所示:

                                          \

                                          hookResources主要是對ResourcesManager的getTopLevelResources進行了Hook。APP中原來使用的是Resources代表資源,Hook之後,Xposed用XResources代替了Resources。圖35展示了XResources的派生關系。

                                          \

                                          接著看hookResources第二段代碼,如圖36所示:

                                          \

                                          第二段代碼中,Xposed資源Hook框架調用了資源hook類型的鉤子。同樣,我們需要關注在這種類型的鉤子裡插件APP要干得事情,那就是:

                                          • 通過XResources的setReplacement函數將目標資源的id和內容進行替換。

                                            上面替換的還是APP自己的資源,除此之外,Xposed還能替換系統資源(即framework-res.apk裡聲明的資源),這部分代碼也在hookResources裡,來看圖37:

                                            \

                                            圖37中,Xposed將Resources裡代表系統資源的mSystem對象也進行了替換。不過這裡沒有獨立調用回調。沒關系,因為在第二部分的回調中,插件APP通過XResources的setSystemWideReplacement可以對系統資源進行替換。

                                            注意,hookResources之後,APP裡調用的Resources相關的函數就全部轉到XResources來處理了。比如獲取字符串的getString函數,其最終會調用到XResources的getText函數,代碼如圖38所示:

                                            \

                                            到此,我們對Xposed框架如何hook資源進行了介紹。不過,layout資源的hook我這裡並沒有介紹,請童鞋們閱讀XResources的getLayout函數和init函數。

                                            五、 總結

                                            到此Xposed 32位dalvik版框架基本介紹完了。這一趟絕對不是本篇這20多頁文章這麼輕松的事情。正如我在《深入理解Android之Dalvik》一文寫得那樣,我是在研究xposed的時候,發現必須要搞清楚dalvik,所以才先寫了dalvik的文章,然後才能走到今天這一步。

                                            Xposed是一個成熟框架,高度體現了開發者在Java虛擬機這塊有著非常深厚的知識積累。同時,如果加上selinux的話,那開發者對linux系統也是相當相當熟悉。另外,貌似開發者是用業余時間搞出來的,這在當下上班時間強制為996的碼農而言幾乎是不可能的事情。

                                            再次向開發者致敬,也同時呼吁基於xposed框架的派生框架開發者遵守相關開源協議。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved