热修复技术
熱修復(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)用。可實(shí)現(xiàn)許多神奇的功能。Xposed需要以越獄為前提,像是iOS中的cydia。
Xposed可以修改任何程序的任何java方法(需root),github上提供了XposedInstaller,是一個(gè)android app。提供很多framework層,應(yīng)用層級(jí)的程序。開發(fā)者可以為其開發(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ò)程改寫了app_process(源碼在Xposed : a modified app_process binary),替換/system/bin/app_process這個(gè)二進(jìn)制文件。然后做了兩個(gè)事:
這時(shí)hook必要的方法是為了方便開發(fā)者為它開發(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/目錄的讀寫權(quán)限,因而需要以root為前提。
Xposed的hook思想
那么Xposed是怎么hook java方法的呢?要從XposedBridge看起,重點(diǎn)在
XposedBridge.hookmethod(原方法的Member對(duì)象,含有新方法的XC_MethodHook對(duì)象);,這里會(huì)調(diào)到
這個(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層,見代碼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的方法,更為純粹、有效。
總結(jié)
- 上一篇: linux终端背景图片,ubuntu开关
- 下一篇: 【转】直流无刷电机工作及控制原理