算是作為一個 Art 學(xué)習(xí)的切入點吧
網(wǎng)上找了很多資料,大多數(shù)基于dvm的資料。
(Dvm可以 參考 鄧凡平先生的 博客 :https://blog.csdn.net/Innost/article/details/50461783)
今天也是自我總結(jié)一下 Art模式下的
在github 搜索 可得如下三個項目,分別介紹一下

XposedInstaller ,這是 Xposed 的插件管理和功能控制 APP,也就是說 Xposed 整體管控功能就是由這個 APP 來完成的,它包括啟用 Xposed 插件功能,下載和啟用指定插件 APP,還可以禁用 Xposed 插件功能等。注意,這個 app 要正常無誤得運行必須能拿到 root 權(quán)限。
Xposed,這個項目屬于 Xposed 框架,其實它就是單獨搞了一套 xposed 版的 zygote。這個 zygote 會替換系統(tǒng)原生的 zygote。所以,它需要由 XposedInstaller 在 root 之后放到 /system/bin 下。
XposedBridge,這個項目也是 Xposed 框架,它屬于 Xposed 框架的 Java 部分,編譯出來是一個 XposedBridge.jar 包。
問題1 :
當(dāng)我們獲取Root以后安裝Xposed會發(fā)生什么?
下載XposedInstaller 導(dǎo)入 AndroidStudio
先看看 安裝之前 :
從頁面初始化開始入手
StatusInstallerFragment-》onCreateView

第一次見到user_de目錄 使用如下

安裝完畢之后 ,當(dāng)我們點擊

獲取手機(jī)的各種信息,拼接成下載的URL

我們以 安卓 7.1 arm64的 為標(biāo)準(zhǔn)
下載完畢的 zip內(nèi)容如下 主要包含 system 和 META-INT兩個文件夾
system->
?? ?? ---lib64->
????? ??------libart.so
????? ??------libart-compiler.so
????? ??------libart-disassembler.so
????? ??------libsigchain.so
????? ??------libxposed_art.so
?? ?? ---lib->
????? ??------libart.so
????? ??------libart-compiler.so
????? ??------libsigchain.so
????? ??------libxposed_art.so
?? ??---framework->
????? ??------XposedBridge.jar(注入到對應(yīng)app進(jìn)程里面的jar包)
?? ?? ---bin->
????? ??------app_process32_xposed
????? ??------app_process64_xposed
????? ??------dex2oat
????? ??------patchoat
?? ?? xposed.prop->
?? ???? ??xposed版本說明文件
META-INT->
?? ?? ---里面有文件配置腳本 flash-script.sh 配置各個文件安裝位置
下載完畢會跳轉(zhuǎn)到 InstallationActivity
onCreate 函數(shù) -》創(chuàng)建 InstallationFragment

-》調(diào)用 startInstallation解壓 zip文件-》執(zhí)行done回調(diào) -》

替換系統(tǒng)system對應(yīng)目錄下的文件-》執(zhí)行刷機(jī)命令-》重啟手機(jī)
問題2:
Xposed 如何注入到zygote 進(jìn)程中的?
首先復(fù)習(xí)一下Art虛擬機(jī)啟動流程:
主要大致流程
①Linux init進(jìn)程解析配置腳本->②app_process(zygote進(jìn)程對應(yīng)的程序)->③ZygoteInit
① 解析配置腳本

service zygote:它告訴init進(jìn)程,現(xiàn)在我們要配置一個名為zygote的服務(wù)。
/system/bin/app_process: 聲明zygote進(jìn)程對應(yīng)的文件路徑。init創(chuàng)建服務(wù)的處理邏輯很簡單,就是啟動(fork)一個子進(jìn)程來運行指定的程序。對zygote服務(wù)而言這個程序就是/system/bin/app_process。
-Xzygote/system/bin--zygote--start-system-server:傳遞給app_process的啟動參數(shù)。
②app_process 創(chuàng)建
frameworks\base\cmds\app_process.cpp-》main函數(shù)


frameworks\base\core\jni\AndroidRuntime.cpp-》start函數(shù)
核心函數(shù)為: init,startVm

三個函數(shù)主要功能:
1. JNI_GetDefaultJavaVMInitArgs -- 獲取虛擬機(jī)的默認(rèn)初始化參數(shù)
2. JNI_CreateJavaVM -- 在進(jìn)程中創(chuàng)建虛擬機(jī)實例
3. JNI_GetCreatedJavaVMs -- 獲取進(jìn)程中創(chuàng)建的虛擬機(jī)實例
ART像Dalvik一樣,都實現(xiàn)Java虛擬機(jī)接口,這三個接口也是ART虛擬機(jī)核心接口。
startVm函數(shù)很復(fù)雜牽扯邏輯也很多,不 逐一描述了。
③ZygoteInit
繼續(xù)查看 frameworks\base\core\jni\AndroidRuntime.cpp-》start函數(shù)

參數(shù)className的值等于“com.android.internal.os.ZygoteInit”,本地變量env是從調(diào)用另外一個成員函數(shù)startVm創(chuàng)建的ART虛擬機(jī)獲得的JNI接口。函數(shù)的目標(biāo)就是要找到一個名稱為com.android.internal.os.ZygoteInit的類,以及它的靜態(tài)成員函數(shù)main,然后就以這個函數(shù)為入口,開始運行ART虛擬機(jī)。為此,函數(shù)執(zhí)行了以下步驟:
① 調(diào)用JNI接口FindClass加載com.android.internal.os.ZygoteInit類。
② 調(diào)用JNI接口GetStaticMethodID找到com.android.internal.os.ZygoteInit類的靜態(tài)成員函數(shù)main。
③ 調(diào)用JNI接口CallStaticVoidMethod開始執(zhí)行com.android.internal.os.ZygoteInit類的靜態(tài)成員函數(shù)main。
下面看看 Xposed是如何做攔截的
開打 Xposed項目

大于21編譯走的是app_main2.cpp 看看 具體改動了哪些
經(jīng)過查閱,被修改的main函數(shù),一共有兩個地方。
其一,紅框的地方 是判斷是否是Xposed版本的虛擬機(jī)

在解析開啟啟動init腳本的時候 添加了--xposedversion 版本號的命令
這塊啟動的已經(jīng)是自定義的虛擬機(jī)了

第二個地方在 start函數(shù)這塊,先看看 原函數(shù)。


也是在這個地方 進(jìn)行的初始化 判斷是否初始化成功 。
initialize 函數(shù)返回的是否加載成功的 一個全局變量 isXposedLoaded


初始化完畢以后開始調(diào)用真正的start函數(shù)
下面看 runtimeStart 函數(shù)
這塊很有趣 在libart.so里面根據(jù)符號表信息嘗試拿到Android::start函數(shù)
上面這些只要有一步失敗了,在刷入的時候就可能變磚。
如果獲取到了,則可以直接通過函數(shù)指針調(diào)用,主要是針對一些特殊的安卓版本號。
如果都沒有找到 可以看到 Log會打印 。
“app_process: could not locate AndroidRuntime::start() method.”

(這個地方有個小技巧,可以對so文件里面的全部函數(shù)名字進(jìn)行逐一字符判斷,
比如可以對這個字符串 判斷 是否含有 R u n t i m e s t a r t這幾個字符,來繞過因為編譯優(yōu)化字符串不同問題)
這樣一來完美替換了原虛擬機(jī)。
在新的虛擬機(jī)里面 會 將 XposedBridge.jar 進(jìn)行注入,這么一來,所有被Xposed fork的進(jìn)程都具備了 XposedBridge.jar 的代碼 。
問題3:
當(dāng)我們findAndHookMethod一個函數(shù)以后Xposed是怎么處理的?
打開XposedBridge項目
找到 findAndHookMethod

跟入XposedBridge.hookMethod
參數(shù)1 是一個 接口 可能傳入的是一個 Constructor (構(gòu)造方法的反射實例)也可能是 Method
Member 類型是Constructor 和Method都已經(jīng)實現(xiàn)的,因為Xposed支持 Hook構(gòu)造和Method。

最終 走到HookMethodNative方法,注冊地方在Xposed里面的libXposed_common.cpp中
slot 是 Method在類中的偏移位置

重點分析一下實現(xiàn)過程
返回到Xposed 項目
libxposed_art.cpp-》XposedBridge_hookMethodNative函數(shù)

ScopedObjectAccess soa(env);
(SOA,就是約定的調(diào)用,包裝env,出了函數(shù)范圍自動釋放)
FromReflectedMethod是ArtMethod里面的方法

也很簡單 就是調(diào)用里面的GetArtMethod,在art虛擬機(jī)中,每一個加載的類方法都有一個對應(yīng)的ArtMethod對象。
返回去 繼續(xù)看 EnableXposedHook 函數(shù)
EnableXposedHook 在art_method.cc里面

(PrettyMethod函數(shù)有個小技巧 當(dāng)我們分析被So中注冊函數(shù)的時候 ,可以直接用ArtMethod的this指針調(diào)用 PrettyMethod 函數(shù)拿到簽名信息)
繼續(xù)查看 backup_method表示其為Hook方法的原方法,然后為備份的ArtMethod創(chuàng)建對應(yīng)的Method對象。

(
fast_jin模式科普:
下文參考資料(《深入理解ART虛擬機(jī)》:
安卓 函數(shù)執(zhí)行 分為兩條線 第一種是 Java層,第二種JNI層 也就是 so層
當(dāng)函數(shù)調(diào)用Java層進(jìn)入到JNI層的是時候,虛擬機(jī)會將執(zhí)行線程的狀態(tài)從Runnable轉(zhuǎn)換為Native。
如果JNI層又調(diào)用Java層相關(guān)函數(shù)的時候,執(zhí)行線程的狀態(tài)又得從Native層轉(zhuǎn)換為Runnable。
線程的切換需要浪費時間,所以,對于某個特別強調(diào)執(zhí)行速度的JNI函數(shù)可以設(shè)置成 fast jni模式
這種模式下執(zhí)行這個native函數(shù) 將不會進(jìn)行 狀態(tài)切換,即執(zhí)行線程的狀態(tài) 始終為Runnable。
當(dāng)然,這種模式的使用對GC有一些影響,所喲最好在那些本身函數(shù)執(zhí)行時間段的,又不會阻塞的情況下使用。
另外,這種模式目前在art虛擬機(jī)內(nèi)部 很多java native都有使用
為了和其他Native函數(shù) 進(jìn)行區(qū)分,當(dāng)使用fast jni模式的函數(shù)的簽名信息 必須以 “!”開頭
)

把Method對象,方法額外信息和原始方法保存至XposedHookInfo結(jié)構(gòu)體中,并調(diào)用SetEntryPointFromJni()把這個結(jié)構(gòu)體變量的內(nèi)存地址保存在ArtMethod對象中。這個方法原本是用來保存native方法的入口地址的,既然使用了這個位置,那么就必須把對應(yīng)的標(biāo)志位清除,代碼實現(xiàn)的最后調(diào)用SetAccessFlags((GetAccessFlags() & ~kAccNative & ~kAccSynchronized) | kAccXposedHookedMethod)來完成標(biāo)志位的清除(設(shè)置Fast_jni模式),此時這個ArtMethod對象對應(yīng)是Hook后的方法,這個方法的實現(xiàn)不是native的。

這么一來完成了整體Hook流程
總結(jié):
執(zhí)行流程:
XposedBridge.hookMethod-》XposedBridge.hookMethodNative-》EnableXposedHook
核心代碼So層里面的 hookMethodNative 和 XposedBridge_hookMethodNative 里面
1,hookMethodNative 先將java層傳入的 被Hook的信息轉(zhuǎn)換成 ArtMethod,方便調(diào)用方法進(jìn)行Hook,調(diào)用EnableXposedHook 方法。
2,在 EnableXposedHook 進(jìn)行簡單的判斷 是否是被Hook的方法,以及是否已經(jīng)被Hook過
準(zhǔn)備一個備份的 ArtMethod 存放 原方法的信息,將備份的ArtMethod 設(shè)置信息,所屬類,告訴虛擬機(jī)這個方法不需要JIT編譯,并將其設(shè)置成Native,準(zhǔn)備一個簡單的結(jié)構(gòu)體XposedHookInfo保存,保存被Hook方法的信息,包括原方法的信息,地址,最后將 入口設(shè)置成 XposedHookInfo,設(shè)置機(jī)械碼執(zhí)行的首地址,將原方法的 CodeItem偏移設(shè)置0。
項目地址:
https://github.com/w296488320/XposedProjectDoc
喜歡文章的話 可以點個關(guān)注,如果對 逆向,脫殼,新技術(shù) 感興趣的 同學(xué) 可以加 我Q群 歡迎各位能人志士 一起討論
歡迎加入故事,群聊號碼:773642813 也可以加入筆者的 小密圈,各種安卓新技術(shù),源碼分享等

參考:
https://blog.csdn.net/Innost/article/details/50461783
https://bbs.meizu.cn/thread-8328245-1-1.html
鄧凡平--- 《深入理解安卓虛擬機(jī)Art》
https://www.kancloud.cn/alex_wsc/androids/473621
https://blog.csdn.net/a314131070/article/details/81092526
https://blog.csdn.net/zjx839524906/article/details/81046844