熱修復(fù)技術(shù)
APP提早發(fā)出去的包队他,如果出現(xiàn)客戶端的問(wèn)題徒役,實(shí)在是干著急,覆水難收于游。因此線上修復(fù)方案迫在眉睫毁葱。
概述
基于Xposed中的思想,通過(guò)修改c層的Method實(shí)例描述贰剥,來(lái)實(shí)現(xiàn)更改與之對(duì)應(yīng)的java方法的行為倾剿,從而達(dá)到修復(fù)的目的。
Xposed
誕生于XDA論壇蚌成,類似一個(gè)應(yīng)用平臺(tái)前痘,不同的是其提供諸多系統(tǒng)級(jí)的應(yīng)用〉S牵可實(shí)現(xiàn)許多神奇的功能芹缔。Xposed需要以越獄為前提,像是iOS中的cydia瓶盛。
Xposed可以修改任何程序的任何java方法(需root)最欠,github上提供了XposedInstaller,是一個(gè)android app惩猫。提供很多framework層芝硬,應(yīng)用層級(jí)的程序。開(kāi)發(fā)者可以為其開(kāi)發(fā)一些系統(tǒng)或應(yīng)用方面的插件轧房,自定義android系統(tǒng)拌阴,它甚至可以做動(dòng)態(tài)權(quán)限管理(XposedMods)。
Android系統(tǒng)啟動(dòng)與應(yīng)用啟動(dòng)
Zygote進(jìn)程是Android手機(jī)系統(tǒng)啟動(dòng)后锯厢,常駐的一個(gè)名為‘受精卵’的進(jìn)程皮官。
- zygote的啟動(dòng)實(shí)現(xiàn)腳本在/init.rc文件中
- 啟動(dòng)過(guò)程中執(zhí)行的二進(jìn)制文件在/system/bin/app_process
任何應(yīng)用程序啟動(dòng)時(shí),會(huì)從zygote進(jìn)程fork出一個(gè)新的進(jìn)程实辑。并裝載一些必要的class捺氢,invoke一些初始化方法。這其中包括像:
- ActivityThread
- ServiceThread
- ApplicationPackageManager
等應(yīng)用啟動(dòng)中必要的類剪撬,觸發(fā)必要的方法摄乒,比如:handleBindApplication,將此進(jìn)程與對(duì)應(yīng)的應(yīng)用綁定的初始化方法残黑;同時(shí)馍佑,會(huì)將zygote進(jìn)程中的dalvik虛擬機(jī)實(shí)例復(fù)制一份,因此每個(gè)應(yīng)用程序進(jìn)程都有自己的dalvik虛擬機(jī)實(shí)例梨水;會(huì)將已有Java運(yùn)行時(shí)加載到進(jìn)程中拭荤;會(huì)注冊(cè)一些android核心類的jni方法到虛擬機(jī)中,支撐從c到j(luò)ava的啟動(dòng)過(guò)程疫诽。
Xposed做了手腳
Xposed在這個(gè)過(guò)程改寫(xiě)了app_process(源碼在Xposed : a modified app_process binary)舅世,替換/system/bin/app_process這個(gè)二進(jìn)制文件旦委。然后做了兩個(gè)事:
- 通過(guò)Xposed的hook技術(shù),在上述過(guò)程中雏亚,對(duì)上面提到的那些加載的類的方法hook缨硝。
- 加載XposedBridge.jar
這時(shí)hook必要的方法是為了方便開(kāi)發(fā)者為它開(kāi)發(fā)插件,加載XposedBridge.jar是為動(dòng)態(tài)hook提供了基礎(chǔ)罢低。在這個(gè)時(shí)候加載它意味著查辩,所有的程序在啟動(dòng)時(shí),都可以加載這個(gè)jar(因?yàn)樯厦嫣岬降膄ork過(guò)程)网持。結(jié)合hook技術(shù)宜岛,從而達(dá)到了控制所有程序的所有方法。
為獲得/system/bin/目錄的讀寫(xiě)權(quán)限翎碑,因而需要以root為前提谬返。
Xposed的hook思想
那么Xposed是怎么hook java方法的呢?要從XposedBridge看起日杈,重點(diǎn)在
XposedBridge.hookmethod(原方法的Member對(duì)象遣铝,含有新方法的XC_MethodHook對(duì)象);莉擒,這里會(huì)調(diào)到
private native synchronized static void hookMethodNative(Member method, Class<?> declaringClass, int slot, Object additionalInfo);
這個(gè)native的方法酿炸,通過(guò)這個(gè)方法,可以讓所hook的方法涨冀,轉(zhuǎn)向native層的一個(gè)c方法填硕。如何做到?
When a transmit from java to native occurs, dvm sets up a native stack.
In dvmCallJNIMethod(), dvmPlatformInvoke is used to call the native method(signature in Method.insns).
在jni這個(gè)中間世界里鹿鳖,類型數(shù)據(jù)由jni表來(lái)溝通java和c的世界扁眯;方法由c++指針結(jié)合DVM*系(如dvmSlotToMethod,dvmDecodeIndirectRef等方法)的api方法,操作虛擬機(jī)翅帜,從而實(shí)現(xiàn)java方法與c方法的世界姻檀。
那么hook的過(guò)程是這樣:首先通過(guò)dexclassload來(lái)load所要hook的方法,分析類后涝滴,進(jìn)c層绣版,見(jiàn)代碼XposedBridge_hookMethodNative方法,拿到要hook的Method類歼疮,然后通過(guò)dvmslotTomethod方法獲取Method*指針杂抽,
Method* method = dvmSlotToMethod(declaredClass, slot);
declaredClass就是所hook方法所在的類,對(duì)應(yīng)的jobject韩脏。slot是Method類中缩麸,描述此java對(duì)象在vm中的索引;那么通過(guò)這個(gè)方法赡矢,我們就獲取了c層的Method指針,通過(guò)
SET_METHOD_FLAG(method, ACC_NATIVE);
將該方法標(biāo)記為一個(gè)native方法匙睹,然后通過(guò)
method->nativeFunc = &hookedMethodCallback;
定向c層方法到hookedMethodCallback愚屁,這樣當(dāng)被hook的java方法執(zhí)行時(shí)济竹,就會(huì)調(diào)到c層的hookedMethodCallback方法痕檬。
通過(guò)meth->nativeFunc重定向MethodCallBridge到hookedMethodCallback這個(gè)方法上,控制這個(gè)c++指針是無(wú)視java的private的送浊。
另外梦谜,在method結(jié)構(gòu)體中有
method->insns = (const u2*) hookInfo;
用insns指向替換成為的方法,以便hookedMethodCallback可以獲取真正期望執(zhí)行的java方法袭景。
現(xiàn)在所有被hook的方法唁桩,都指向了hookedMethodCallbackc方法中,然后在此方法中實(shí)現(xiàn)調(diào)用替換成為的java方法耸棒。
從Xposed提煉精髓
回顧Xposed荒澡,以root為必要條件,在app_process加載XposedBidge.jar与殃,從而實(shí)現(xiàn)有hook所有應(yīng)用的所有方法的能力单山;而后續(xù)動(dòng)態(tài)hook應(yīng)用內(nèi)的方法,其實(shí)只是load了從zypote進(jìn)程復(fù)制出來(lái)的運(yùn)行時(shí)的這個(gè)XposedBidge.jar幅疼,然后hook而已米奸。因此,若在一個(gè)應(yīng)用范圍內(nèi)的hook爽篷,root不是必須的悴晰,只是單純的加載hook的實(shí)現(xiàn)方法,即可修改本應(yīng)用的方法逐工。
業(yè)界內(nèi)也不乏通過(guò)「修改BaseDexClassLoader中的pathList铡溪,來(lái)動(dòng)態(tài)加載dex」方式實(shí)現(xiàn)熱修復(fù)。后者純java實(shí)現(xiàn)泪喊,但需要hack類的優(yōu)化流程棕硫,將打CLASS_ISPREVERIFIED標(biāo)簽的類,去除此標(biāo)簽窘俺,以解決類與類引用不在一個(gè)dex中的異常問(wèn)題饲帅。這會(huì)放棄dex optimize對(duì)啟動(dòng)運(yùn)行速度的優(yōu)化。原則上瘤泪,這對(duì)于方法數(shù)沒(méi)有大到需要multidex的應(yīng)用灶泵,損失更明顯。而前者不觸犯原有的優(yōu)化流程对途,只點(diǎn)殺需要hook的方法赦邻,更為純粹、有效实檀。