原文鏈接 Android逆向技術(shù)高階大法
安卓應(yīng)用是一個(gè)客戶端勉盅,與傳統(tǒng)軟件類似,需要把軟件打包,然后通過某種渠道(應(yīng)用市場)分發(fā)給用戶,這是常規(guī)的發(fā)布方式,它的更新節(jié)奏很慢,從你在應(yīng)用市場上更新后,到用戶真正的執(zhí)行升級宿礁,這中間很慢的,而且很多用戶根本不會升級新版本蔬芥,這對于互聯(lián)網(wǎng)來說是極不友好的梆靖。傳統(tǒng)的互聯(lián)網(wǎng),用戶刷新一下網(wǎng)頁后笔诵,就能看得到更新了返吻,但對于客戶端,這行不通乎婿,要想實(shí)現(xiàn)小時(shí)級別的發(fā)布和分鐘級別的問題修復(fù)测僵,正規(guī)的發(fā)布渠道是做不到的。于是各路大神和專家開始研究客戶端的前端化谢翎,也就是運(yùn)用各種技術(shù)能讓發(fā)布捍靠,特別是一些問題修復(fù)性的小規(guī)模發(fā)布可以更快的傳遞到用戶手中。
[圖片上傳失敗...(image-e9df75-1695644558585)]
這與正向方法不一樣森逮,谷歌或者水果針對 應(yīng)用市場有明確 的流程的榨婆,這是常規(guī)發(fā)布也即是正向方式。今天我們來聊一聊非正向方法褒侧,非常規(guī)方式良风,來實(shí)現(xiàn)小模塊的發(fā)布和熱修復(fù)。
核心技術(shù)原理
任何一項(xiàng)技術(shù)都離不開編程語言和操作系統(tǒng)上的支持闷供,對于插件化技術(shù)來說烟央,最為核心的原理就是Java支持反射,這是一種運(yùn)行時(shí)修改代碼的技術(shù)歪脏,另外就是動(dòng)態(tài)代理疑俭,這是插件化可行的根本技術(shù)支撐。
說到底婿失,Java仍是一種解釋型語言钞艇,它的核心是JVM鬼贱,即也虛擬機(jī),我們所熟悉的Java編程語言香璃,本質(zhì)上是套在JVM上的一層語法規(guī)則,換了一種語言規(guī)則也是可行的舟误。就好比Kotlin葡秒,Scala和Groovy它們的語法與Java相差很大,但它們編譯過后的字節(jié)碼是完全符合JVM規(guī)范的嵌溢,可以直接運(yùn)行在JVM之上眯牧。
其他的純解釋型語言,如Python和JavaScript赖草,它們在運(yùn)行時(shí)可以動(dòng)態(tài)的加載一段源碼学少,這即是動(dòng)態(tài)化,可以實(shí)現(xiàn)真正的插件化秧骑,運(yùn)行時(shí)直接加載運(yùn)行一段代碼版确。Java略變態(tài)一些,但它本質(zhì)上是JVM乎折,而JVM通過反射和動(dòng)態(tài)代理绒疗,在一定程度上支持了類似的動(dòng)態(tài)化,就是通過ClassLoader來動(dòng)態(tài)加載一些編譯好的Class骂澄。
此為插件化的核心原理吓蘑。
動(dòng)態(tài)代理機(jī)制,可以讀這幾篇文章:
- 動(dòng)態(tài)代理大揭秘坟冲,帶你徹底弄清楚動(dòng)態(tài)代理
- 動(dòng)態(tài)代理
- Java的動(dòng)態(tài)代理(dynamic proxy)
- java動(dòng)態(tài)代理實(shí)現(xiàn)與原理詳細(xì)分析
- 小白也能看懂的插件化DroidPlugin原理(一)-- 動(dòng)態(tài)代理
Hook大法
有了核心原理磨镶,才有可行的方案。Hook主要研究三方面內(nèi)容健提,一是研究ClassLoader琳猫,因?yàn)椴煌膁ex分屬于不同的層級,它們的ClassLoader不一樣矩桂,反射的第一步就是要能加載到想要的Class沸移,這個(gè)要靠找到合適的ClassLoader;二是動(dòng)態(tài)代理機(jī)制侄榴,hook的核心原理就是用動(dòng)態(tài)代理機(jī)制雹锣,創(chuàng)建一個(gè)Mock對象用以替換掉原來的,所以接口Interface是關(guān)鍵癞蚕,原系統(tǒng)設(shè)計(jì)中必須使用大量接口蕊爵,并且是以標(biāo)準(zhǔn)方式使用的(沒有強(qiáng)制向下轉(zhuǎn)型downcast),這樣你創(chuàng)建出來的動(dòng)態(tài)代理去替換才是安全的桦山;三就是學(xué)習(xí)安卓系統(tǒng)核心組件 的流程攒射,以找到最佳的hook地點(diǎn)醋旦。
[圖片上傳失敗...(image-ab881-1695644558585)]
其實(shí),第3條才是對大部分人最為有益的会放。
具體如何做hook饲齐,可以參考以下文章:
- Android 插件化原理解析——Service的插件化
- Android 插件化原理解析——Hook機(jī)制之AMS&PMS
- 探索Android開源框架 - 10. 插件化原理
- 小白也能看懂的插件化DroidPlugin原理(二)-- 反射機(jī)制和Hook入門
由于安卓版本升級的原因,上面這幾個(gè)文章都失效了咧最,例子行不通了捂人。但是這幾遍對于原理解釋的還是相當(dāng)清楚的。
以下文章對于新版本也是適用的矢沿。
- 基于Android9.0的Hook Activity 的啟動(dòng)(插件化)
- Android Hook Activity 的幾種姿勢
- Activity插件化原理第一種方案:Hook Instrumentation
- Activity插件化原理第二種方案:Hook IActivityManager
- 攔截Activity的啟動(dòng)流程繞過AndroidManifest檢測
需要注意的是滥搭,hook這件事情,最基礎(chǔ)的技術(shù)很簡單捣鲸,就通過反射來替換對象瑟匆,把系統(tǒng)中的對象替換為仿造的,仿造有三種方式栽惶,一是直接創(chuàng)建愁溜,這需要類是比較簡單的情況,并不需要開放出來媒役,通過反射一切皆可創(chuàng)建祝谚;二是繼續(xù),這個(gè)對于復(fù)雜對象也能仿造酣衷,如Instrumentation交惯,但是需要類是開放出來的;三是接口穿仪,通過動(dòng)態(tài)代理 創(chuàng)建仿造對象(也即代理 )席爽。核心技術(shù)就這些。其他的啊片,全是對于系統(tǒng)代碼的理解只锻,找到可行的關(guān)鍵點(diǎn)來進(jìn)行hook。
另外就是紫谷,谷歌對逆向方法限制越來越嚴(yán)了齐饮,反射系統(tǒng)的東西,會有限制笤昨,有時(shí)僅是打印日志祖驱,但指不定哪天就不給反射了。
Accessing hidden field Landroid/app/ActivityManager;->IActivityManagerSingleton:Landroid/util/Singleton; (light greylist, reflection)
插件化原理
學(xué)習(xí)一門技術(shù)最好的方式就是去研讀優(yōu)秀的開源庫的源碼瞒窒,對于插件化捺僻,現(xiàn)在有很多比較成熟 的開源框架存在了,可以挑幾個(gè)比較有代表性的來研究 一下。
DroidPlugin
這個(gè)基于動(dòng)態(tài)代理創(chuàng)建的插件方法匕坯,較為流行束昵,里面有大量的hook技術(shù),網(wǎng)絡(luò)上也有很多解析此框架的文章葛峻,可以幫助理解锹雏。
它用了大量的hook,優(yōu)點(diǎn)就是插件本身可以是正常的apk术奖,無太多的限制逼侦,就用常規(guī)的app開發(fā)方式開發(fā)就好,這是它的最大優(yōu)勢腰耙,因?yàn)閷Σ寮o限制,所以框架本身就需要做大量的hook铲球,是學(xué)習(xí)hook技法的良好例子挺庞。
DL : Apk動(dòng)態(tài)加載框架
這個(gè)是以靜態(tài)代理為基礎(chǔ)創(chuàng)建的插件框架,并沒有大量的hook稼病,可以參看它的解析文章选侨。
任大神的框架適配性較好,基本上是純軟件層的技術(shù)(靜態(tài)代理)然走,沒怎么hook援制。當(dāng)然缺點(diǎn)也相當(dāng)明顯,就是對于插件的開發(fā)要求很苛刻芍瑞,必須實(shí)現(xiàn)框架本身自定義的一坨東西晨仑,與安卓標(biāo)準(zhǔn)的app開發(fā)差異較大,且越來越大拆檬,并且對于打包和開發(fā)過程并無工具支持洪己,在實(shí)際應(yīng)用過程中較為麻煩。退一步講竟贯,并未有真正達(dá)到插化的目的答捕,它對插件的限制較大。
現(xiàn)在已經(jīng)基本沒人用了屑那,不過這屬于開山之作拱镐。
Qigsaw
這個(gè)與其他插件框架的最大差別在于,它最接近于官方的東西(App bundle)持际,它的重點(diǎn)在于項(xiàng)目模塊化和打包上面沃琅,對于常規(guī)理解上的『插件』所做的事情特別少,hook特別少选酗,安裝和加載插件的過程比較很簡單阵难,接近原生,核心在于它的打包過程芒填。這里有詳細(xì)的介紹呜叫。
另外空繁,包建強(qiáng)的書《Android插件化開發(fā)指南》也可以讀一讀的,書的好處在于朱庆,它畢竟是一個(gè)整體盛泡,從基礎(chǔ)的技術(shù)原理到hook原理都有講,還是相當(dāng)不錯(cuò)的娱颊。不過書比較舊了 傲诵,要結(jié)合作者的勘誤,以及網(wǎng)上的文章一起來消化理解箱硕。
熱修復(fù)原理
除了插件化拴竹,另外一個(gè)大廠熱衷的技術(shù)便是熱修復(fù)号醉,這也是大廠頭部應(yīng)用的標(biāo)配技術(shù)惨篱。其實(shí)插件化,也能實(shí)現(xiàn)熱修復(fù)呀邢,比如某個(gè)插件惠昔,一般是廠里的一個(gè)業(yè)務(wù)幕与,出問題了,緊急打包發(fā)布一個(gè)修復(fù)的版本镇防,然后更新插件啦鸣。不過,這略顯笨重来氧,相當(dāng)于用牛刀去殺雞了诫给,總之就是效率不高。
[圖片上傳失敗...(image-1b28bb-1695644558585)]
真正的熱修復(fù)技術(shù)講究效率啦扬,且要小巧蝙搔,針對 點(diǎn)對點(diǎn)式的修復(fù)。它的核心原理就是替換考传,用反射去替換類(修改dex classloader中的dex順序)吃型,以及對方法的替換(侵入虛擬機(jī)中的method表,進(jìn)行替換)僚楞,還分冷生效(類替換一般是冷生效勤晚,也即下次啟動(dòng)時(shí)生效)和熱生效(方法替換一般是熱的,下次調(diào)用此方法時(shí)就生效了泉褐,因?yàn)樗⒉簧婕癱lassloader赐写,無需要重新加載類),還有插樁式的膜赃,在代碼中直接插樁挺邀,先檢查有沒有patch,有patch就先運(yùn)行patch(這個(gè)思路最簡單,適配性也好端铛,但實(shí)行難度大泣矛,需要對現(xiàn)有代碼進(jìn)行插樁)。
這幾篇文章有比較詳細(xì)的討論禾蚕。
具體的熱修復(fù)工具
xposed派系
也即原生的Xposed和Xposed framework
以及大阿里的衍生版本dexposed换淆。
針對 方法可以熱生效的hook哗总,當(dāng)年Dalvik時(shí)代,這個(gè)東西還是相當(dāng)牛逼的倍试,時(shí)過境遷雖然Art上無法用了讯屈,但不妨用來學(xué)習(xí)。
Andfix
原產(chǎn)自支付寶的與Xposed類似的方法級的hook工具县习,支持Dalvik與Art耻煤,值得使用和學(xué)習(xí)。
AndroidMethodHook
可以用來學(xué)習(xí)sophix准颓,sophix是大阿里的東西,把a(bǔ)ndfix以及dexposed商業(yè)化了棺妓,不再開源免費(fèi)用了攘已。這個(gè)項(xiàng)目比較接近它們,可以用來學(xué)習(xí)怜跑。
Tinker
微信出品的Tinker样勃,核心技術(shù)還是用dex替換實(shí)現(xiàn)的class替換,冷生效性芬。
它的重點(diǎn)在于補(bǔ)丁dex的差量生成峡眶,以及發(fā)布平臺,還做成了收費(fèi)平臺植锉,變成一種服務(wù)辫樱。所以,你看核心技術(shù)是由目標(biāo)平臺(安卓)決定的俊庇,原理大家也都懂狮暑,各家也都大差不差的,也都有開源現(xiàn)成的方案可以用辉饱,但這遠(yuǎn)遠(yuǎn)不夠搬男,整個(gè)鏈路是值得深挖的,這也是能產(chǎn)生商業(yè)價(jià)值的地方彭沼。
HotFix
安卓App熱補(bǔ)丁動(dòng)態(tài)修復(fù)技術(shù)介紹
Nuwa
Robust
Android熱更新方案Robust開源缔逛,新增自動(dòng)化補(bǔ)丁工具
這個(gè)與Nuwa一樣,都用了代碼插樁,當(dāng)然插樁過程褐奴,是用了字節(jié)碼工具(如ASM)按脚,進(jìn)行編譯時(shí)自動(dòng)化處理,最終字節(jié)碼(APK)是受影響的歉糜,但源碼層面是無感知的乘寒。
瓶頸在哪里
插件化這項(xiàng)技術(shù),它的成本特別高匪补,但收益有限伞辛,需要龐大的研發(fā)體系來支持,并且只有長期投入夯缺,才能產(chǎn)出一些價(jià)值蚤氏。因此,現(xiàn)在來說只有頭部大廠才真正玩得轉(zhuǎn)踊兜。
技術(shù)本身并不是瓶頸
這項(xiàng)技術(shù)的可行性是由Java決定的竿滨,因此一直是可行的。但每年的Android版本捏境,都會對核心組件進(jìn)行不同程度的強(qiáng)化和升級于游,這會導(dǎo)致之前的一些方案可能一下子就失效了。另外垫言,手機(jī)廠商可能也會做一些修改贰剥,不過一般都比較小。
安卓 版本升級筷频,會對插件化有影響蚌成,甚至?xí)尙F(xiàn)有方案全部失效,但這個(gè)還真不是這項(xiàng)技術(shù)的瓶頸凛捏。因?yàn)榘沧?升級較慢担忧,正常一年一個(gè)版本,但是對核心組件大變化坯癣,通常幾年才有一次瓶盛,這個(gè)速度對比三方技術(shù)的演進(jìn)還是相當(dāng)慢的。前面說了這項(xiàng)技術(shù)頭部大廠最為受益示罗,因此他們會有專門的專家級別的人物在研究蓬网,谷歌出了政策,很快就會對策出來鹉勒,一般用不了多久帆锋,插件化技術(shù)大拿們就能給出針對 新版本的解決方案。
由于開源和技術(shù)分享禽额,很快便會在業(yè)界普及锯厢。因此皮官,單就技術(shù)本身,絕不是瓶頸实辑,并且由于開源的發(fā)展捺氢,核心業(yè)務(wù)本身都是開源的,大家都能很快使用最先進(jìn)的技術(shù)剪撬。
[圖片上傳失敗...(image-2ec266-1695644558585)]
網(wǎng)絡(luò)和平臺能力才是瓶頸
插件化這個(gè)事情摄乒,想要真正的用好,光有核心業(yè)務(wù)還是不夠的残黑。核心業(yè)務(wù)現(xiàn)在都有現(xiàn)成的開源庫馍佑,拿過來就可以用,但這遠(yuǎn)遠(yuǎn)不夠梨水。
就從一個(gè)插件從開發(fā)人員手中到用戶手中拭荤,并成功安裝生效,這一過程拆來看需要多少東西吧:
- 插件的開發(fā)疫诽,需要一些輔助工具舅世。理想的情況下,一個(gè)插件模塊的開發(fā)奇徒,應(yīng)該與常規(guī)應(yīng)用開發(fā)是一樣的雏亚,但畢竟它的構(gòu)建目標(biāo)是一個(gè)插件,而非標(biāo)準(zhǔn)的app摩钙,所以你需要針對核心業(yè)務(wù)插件框架適用的一些開發(fā)工具罢低。這個(gè)一般開源框架中都有提供,但不見得有那么好腺律。
- 構(gòu)建和打包。如果是一個(gè)合格的插件化框架宜肉,一定會有怎么構(gòu)建 打包的配套設(shè)施匀钧。
- 測試和調(diào)試。這里面的難點(diǎn)在于之斯,如何能盡可能的模擬真實(shí)的流程,并且能方便的來實(shí)施測試和驗(yàn)證結(jié)果
- 發(fā)布上線控制佑刷。一些細(xì)節(jié)就是如何精準(zhǔn)推送酿炸,如何做灰度發(fā)布瘫絮,以及發(fā)現(xiàn)問題后如何快速回滾(你看,這哪一項(xiàng)涉及插件化技術(shù))
- 下載填硕÷贡睿客戶端的一個(gè)最大的問題就是壮莹,客戶端在客戶那里翅帜,我們發(fā)布的東西都在服務(wù)器上,如何能讓插件順利的送達(dá)到用戶手中命满。別小看這個(gè),網(wǎng)絡(luò)問題永遠(yuǎn)是出錯(cuò)誤原因里面最多的一個(gè)胶台,而且容易被測試忽略,因?yàn)檠邪l(fā)人員自己的網(wǎng)絡(luò)環(huán)境一般簡單且穩(wěn)定腋妙。(一個(gè)最簡單的測試就是讯榕,當(dāng)你在電梯里,地鐵里愚屁,高鐵時(shí),廁所里送浊,山上丘跌,河里,村里耸棒,手機(jī)里面的應(yīng)用還有幾個(gè)能正常聯(lián)網(wǎng)的报辱?)
- 安裝和生效。這個(gè)也是插件化的核心業(yè)務(wù)碍现,框架都會支持的。難點(diǎn)在于校驗(yàn)爽篷,就是客戶端拿到的插件是不是符合預(yù)期的慢睡,文件有沒有損壞膨疏,有沒被篡改钻弄。
- 降級窘俺。這個(gè)通常插件化框架不會提供。降級的意思就是如果插件安裝更新失敗了瘤泪,你怎么辦灶泵?能否回滾对途,如果這個(gè)插件徹底廢了,有沒有H5頁面可以用惶洲?
我們粗略來看膳犹,就能分出上面7個(gè)步驟,其實(shí)還有更細(xì)的铐料。上面這些里豺旬,插件化開源框架一定能解決的是2和6,1和3會在一定程度上支持篓跛。而其他的只有靠自己了耘分,當(dāng)然 也可能會有一些開源軟件可以用绑警,但它們并不純是為了插件化而做的。這些東西都屬于研發(fā)效率平臺渴频,甚至是涉及軟件流程北启,基本上都屬于商業(yè)公司的核心業(yè)務(wù)機(jī)密了拔第,基本上是不可能開源的场钉,而且不同的公司文化制度流程都不一樣,即使開源給你了泳猬,也不一定用得上宇植。但這恰恰又是最能體現(xiàn)一個(gè)公司結(jié)合技術(shù)實(shí)力的地方指郁,小公司或者綜合能力差的公司,即使有現(xiàn)成的插件化框架方案給你闲坎,你也用不好,因?yàn)榕涮自O(shè)施不行手形。再次佐證悯恍,插件化這東西只有頭部大廠才能玩得轉(zhuǎn),并產(chǎn)生正收益瞬欧。
這些才是真正的瓶頸罢防。
這是逆向工程技術(shù)
插件化需要用到大量的反射和動(dòng)態(tài)代理技術(shù)去hook安卓系統(tǒng),從而實(shí)現(xiàn)官方并不直接支持的特性野建,這屬于逆向工程恬叹,與官方倡導(dǎo)的方向并不一致。
而且唯鸭,只有在國內(nèi)圈子里面才比較流行硅确,國外的一些大廠和專家似乎并不愿意花時(shí)間和精力搞這些事情。很難簡單的用好與壞來評價(jià)缭付,只能說文化不同。
逆向工程技術(shù)局限性較大官份,很難長久發(fā)展烙丛, 一旦官方把某個(gè)關(guān)鍵地方堵住(不能說是漏洞钠右,而一些關(guān)鍵的對象和接口)忘蟹,很多插件框架可能就廢掉了,當(dāng)然了道高一尺狠毯,魔高一丈褥芒,總還是能找到可以hook的地方,仍總感覺怪怪的献酗。
常規(guī)的技術(shù)坷牛,如編程范式(函數(shù)式編程,Reactive颜及,RxJava)蹂楣,編程語言,平臺框架和輪子(如Picasso乾翔,如OkHttp)施戴,這些是純正的技術(shù)赞哗,不受制于任何平臺,不但能長久發(fā)展肪笋,更能反過來推動(dòng)官方進(jìn)步(如OkHttp已被谷歌內(nèi)置為安卓內(nèi)部作為HTTP協(xié)議的實(shí)現(xiàn))。
綜合來說猜揪,除非你需要專門研究插件化坛梁,并能得到收益之外(對業(yè)務(wù)划咐,對公司,對個(gè)人)褐缠,對于插件化技術(shù)队魏,了解一下就夠了,而且這東西并不能真正的提升軟件質(zhì)量(它帶來的問題比它解決的要多很多)胡桨。不如把時(shí)間花在業(yè)務(wù)上面登失,花在編程范式,花在編程語言状婶,花在流行的框架和輪子上面馅巷,這更能提升軟件質(zhì)量,且是終生受益的稍刀。畢竟,假如代碼質(zhì)量夠好综膀,發(fā)出去的版本都可控局齿,都能達(dá)到預(yù)期,也就沒必要折騰插件化了(即使是對大廠頭部應(yīng)用來說讥此,版本的發(fā)布仍主要是靠正常的apk發(fā)布谣妻,插件迭代一般用在正常版本來不及時(shí)使用比如電商的雙11期間)。
研發(fā)工具(如Instant Run)取胎,調(diào)試工具(如獲取 一些運(yùn)行時(shí)的信息湃窍,在線調(diào)試),測試工具(如Mock)觉痛,不侵入源碼式編程(動(dòng)態(tài)插樁茵休,AOP和依賴注入)才是反射和動(dòng)態(tài)代理以及Hook的最終歸宿榕莺,是值得我們深入研究和學(xué)習(xí)的方向。
參考資料
- 動(dòng)態(tài)注入技術(shù)(hook技術(shù))
- Android插件化原理解析——Hook機(jī)制之動(dòng)態(tài)代理
- 插件化知識詳細(xì)分解及原理 之代理吧史,hook唠雕,反射
- 盤點(diǎn)Android常用Hook技術(shù)
- 理解 Android Hook 技術(shù)以及簡單實(shí)戰(zhàn)
- Android Hook技術(shù)防范漫談
- Android插件化——高手必備的Hook技術(shù)
- Android Hook 機(jī)制之簡單實(shí)戰(zhàn)
- 字節(jié)跳動(dòng)開源 Android PLT hook 方案 bhook