一舌狗、知識回顧
關(guān)于Android中的簽名校驗是一種很普遍的安全防護(hù)策略了诊杆,很多應(yīng)用也都做了這部分的工作,在之前我也介紹了幾篇關(guān)于如何爆破應(yīng)用的簽名校驗問題的文章欠气,不了解的同學(xué)可以去查看:Android中爆破應(yīng)用簽名校驗功能职辨,當(dāng)時介紹完這篇文章之后盗蟆,其實總結(jié)了現(xiàn)在爆破簽名校驗的幾種方式,其中最方便快捷的就是:全局搜索字符串內(nèi)容:“signature”,因為只要有簽名校驗功能舒裤,一定會調(diào)用系統(tǒng)的一個方法喳资,而這個方法中就是包含了這個字符串內(nèi)容。
之前的這篇文章中介紹的簽名校驗處理方式也是如此腾供,找到具體簽名校驗功能之后仆邓,直接替換正確的簽名信息就可鲜滩,或者把if判斷手動改成true也可以。但是當(dāng)時說到一個問題节值,就是現(xiàn)在應(yīng)用為了加強(qiáng)防護(hù)徙硅,幾乎在重要的內(nèi)容部分中都加了簽名校驗功能,所以如果要手動的改察署,就需要把每個簽名校驗的地方都得改一下闷游,這樣會顯得很費(fèi)勁,有的人說了贴汪,有一個好的辦法,就是用Xposed來hook這個應(yīng)用的獲取系統(tǒng)簽名的方法休吠,然后替換方法的返回值即可扳埂。這種方式是可以的,但是不是最好的瘤礁,因為如果你解決了簽名校驗阳懂,二次打包給別人用,不可能叫人家還root手機(jī)柜思,然后在裝Xposed框架功岩调,是不合理的。所以我們需要從根本上解決這個問題赡盘,也是本文介紹的一個重點(diǎn)号枕。后面會介紹一種萬能的高效方法。
之前的文章中我們可以看到陨享,大部分簽名校驗都是在Java層葱淳,本地做的校驗。所以爆破難度不是很大抛姑,而今天我們要介紹的一個應(yīng)用樣本他的簽名校驗是放在so中赞厕,而且結(jié)合了服務(wù)端進(jìn)行驗證,難度加大定硝。不過萬變不離其宗皿桑,簽名校驗永遠(yuǎn)都是需要獲取應(yīng)用本身的簽名信息,進(jìn)行比對操作的蔬啡。
二诲侮、爆破簽名校驗
下面就開始進(jìn)入本文的主題,看一下應(yīng)用樣本的簽名校驗問題星爪,拿到樣本之后浆西,直接反編譯,二次打包顽腾,安裝出現(xiàn)如下錯誤信息提示:
這個直接彈出對話框信息近零,點(diǎn)擊確定就退出程序了诺核,這個比較簡單,反編譯之后久信,通過提示內(nèi)容窖杀,找到簽名校驗入口,在values/string.xml中找到這個字符串信息即可:
然后用Jadx打開這個應(yīng)用裙士,全局搜索這個name值:
點(diǎn)擊進(jìn)入代碼位置即可:
看到i方法中有一個show方法入客,應(yīng)該就是對話框展示的邏輯⊥茸担看看這個i方法調(diào)用的地方:
看到了桌硫,在之前有一個判斷,如果為false啃炸,就是走到i方法铆隘,展示對話框,所以這個checkHashKey方法肯定是簽名校驗的方法南用,進(jìn)入查看:
是個native方法膀钠,全局搜SocketJNI類調(diào)用的地方:
在這里看到會加載一個so文件,也就是libmzd.so文件裹虫,我們用IDA打開這個文件(文件在反編譯之后的libs目錄下):
找到對應(yīng)的native函數(shù)肿嘲,查看具體邏輯代碼:
為了更好的閱讀代碼,使用F5筑公,查看對應(yīng)的C語言代碼雳窟,這里的邏輯非常簡單,就是把Java層傳遞的簽名字符串內(nèi)容十酣,在做一次MD5值涩拙,然后和指定的字符串"8f2a24...."作比對即可。那么上面?zhèn)魅氲降腸heckHashKey方法的值是通過com.xiaoenai.app.utils.aj.m方法:
這里看到就是標(biāo)準(zhǔn)的獲取應(yīng)用簽名信息的方法耸采。
到這里就看到了一個Java層的簽名校驗方法兴泥,這里解決這個問題有很多種方法,可以修改對應(yīng)的smali方法虾宇,把if判斷強(qiáng)制改成true即可搓彻。還可以直接修改m方法的返回值為正確的簽名字符串內(nèi)容。這里選擇第二種嘱朽,因為我們需要引用的簽名字符串內(nèi)容旭贬,后面會繼續(xù)用到。這個獲取方法也很簡單搪泳,直接寫一個Demo案例稀轨,通過應(yīng)用包名構(gòu)建出對應(yīng)的Context變量,然后就開始獲取簽名信息即可:
運(yùn)行這個demo程序岸军,前提是你的設(shè)備中要安裝一個官方版本的應(yīng)用奋刽,這樣才能獲取到正確的簽名字符串內(nèi)容:
看到了瓦侮,其中簽名字符串:"aN+VCd8ns0yqsotX2WuKyScq/ZA=" 就是正確的官方版本的值。下面在順便寫一個直接返回這個字符串的方法佣谐,然后變編譯得到對應(yīng)的smali代碼:
然后把這個方法的的代碼拷貝替換樣本應(yīng)用的aj.m方法代碼即可:
保存肚吏,二次打包即可,記紫粱辍:這里沒必要手動的編寫smali代碼返回指定的簽名字符串罚攀,除非你對smali語法非常熟悉了,不過我是不會這么做的雌澄。因為我不熟悉smali哀军,最笨的最有效的方法就是自己手動寫一個Java方法澳腹,然后反編譯得到對應(yīng)的smali代碼即可烤宙。
二次打包成功之后漫试,安裝應(yīng)用品擎,運(yùn)行發(fā)現(xiàn)竟然還有錯誤竖配,登錄失斄律恪:
這時候吨灭,就想到了沛厨,樣本應(yīng)用可能做了服務(wù)端數(shù)據(jù)驗證宙地,下面就來看看如何解決這個問題,這個問題的突破口比較簡單逆皮,不要在用提示字符串信息去找了宅粥,因為這錯誤信息可能是服務(wù)端返回的,這時候就需要借助抓包了电谣,每次請求一次秽梅,就抓一次服務(wù)包,這里用了Fiddler工具:
這里看到剿牺,請求的參數(shù)非常簡單企垦,一個加密之后的數(shù)據(jù)data和版本號ver,返回的數(shù)據(jù)提示加密簽名錯誤晒来,所以我們可以在Jadx中全局搜"login2"字符串信息钞诡,找到突破口:
找到之后,雙擊進(jìn)入代碼:
看到最終調(diào)用了湃崩,a方法荧降,點(diǎn)擊進(jìn)入a方法:
這里會通過一個過程構(gòu)造出一個json參數(shù)格式,繼續(xù)往下看:
攜帶了這些信息參數(shù)值攒读,為了更好的看到這個json數(shù)據(jù)格式朵诫,我們可以利用Xposed下一個hook功能:
然后運(yùn)行這個Xposed模塊,在點(diǎn)擊樣本的登錄功能薄扁,查看日志信息:
看到了剪返,result就是最終拼接好的json參數(shù)格式內(nèi)容废累。其中最后一個字符串sig是將簽名整個參數(shù)做一次加密操作,為了在服務(wù)端校驗參數(shù)的完整性随夸。得到這個json格式之后九默,會調(diào)用com.xiaoenai.app.utils.b.a.a方法:
繼續(xù)查看這個方法實現(xiàn):
繼續(xù)跟蹤代碼:
又是到了native方法了,到這里其實上次Java將拼接好的參數(shù)信息以json格式傳遞到native層進(jìn)行加密操作宾毒,繼續(xù)看native層代碼實現(xiàn)驼修,依然在之前的那個libmzd.so中:
找到對應(yīng)的核心函數(shù)功能,跟蹤查看實現(xiàn):
繼續(xù)跟蹤:
這里看到了诈铛,有data字符串內(nèi)容了乙各,也就是這里開始對上面?zhèn)鬟f的json數(shù)據(jù)進(jìn)行加密,然后拼接到data中幢竹,在native層在拼接一套json:{"data":"加密之后的值", "ver":"1.1"}耳峦,而在這里有一個尋找加密key的函數(shù),可惜這里我不在分析了焕毫,因為我看的頭疼蹲坷,他的加密算法還是比較復(fù)雜的。所以就放棄分析了邑飒。
那么到這里循签,我們有什么方式可以得到加密算法呢?有的同學(xué)可能想到了動態(tài)調(diào)試疙咸,這個方法是最好的县匠,但是這個app做了很多反調(diào)試策略,動態(tài)調(diào)試也是不好弄的撒轮,而且這個算法有點(diǎn)復(fù)雜乞旦,及時動態(tài)調(diào)試,也要詳細(xì)閱讀arm指令题山,才能猜到這個加密算法功能兰粉。所以這條路不好走。
那么還有其他方法了臀蛛?當(dāng)然有亲桦,就是文章開頭說到的一句解決簽名校驗的基本法則:全局搜索字符串"signature";在IDA中也是如此,使用Shirt+F12打開so中的字符串窗口浊仆,然后搜索signature:
果然找到了客峭,雙擊進(jìn)入:
Android逆向之旅---爆破應(yīng)用簽名的一種全新高效方式(Native+服務(wù)器驗證)
看到這里可能是在native層調(diào)用了系統(tǒng)獲取簽名的方法,選中紅色框中的內(nèi)容抡柿,然后按X鍵舔琅,展示被調(diào)用的地方索引:
雙擊進(jìn)入,這個就是我們之前分析的那個SocketJNI的init方法:
到這里洲劣,為了更好的閱讀代碼备蚓,需要把這個函數(shù)地址改成可讀课蔬,也就是JNIEnv即可,選中紅色框郊尝,按下Y鍵即可:
修改成JNIEnv二跋,確定即可:
這樣就看的比較清楚了,而這部分代碼也非常簡單流昏,就是調(diào)用系統(tǒng)獲取簽名的方法扎即,然后賦值到一個變量中,不過這里獲取的不是字符串內(nèi)容况凉,而是int類型的hashCode值:
在返回到arm代碼處谚鄙,看到賦值,就是將R0寄存器值搞到R9中去刁绒。
到這里闷营,我們一定要思路清楚,就是不管native層怎么加密知市,最終會利用應(yīng)用的簽名值來做加密的key信息傻盟。所以我們只要解決掉獲取簽名信息值即可,也就是這里的R9寄存器值嫂丙,我們還需要再一次去獲取正版的簽名信息的hashCode值即可:
然后運(yùn)行莫杈,查看值:-2081383250
然后我們可以修改上面的arm指令:MOV R9,R0奢入;不過這里還不好弄,因為這個hashCode值int類型四個字節(jié)媳叨,而這里看到一個命令才2個字節(jié)腥光,肯定不夠用。需要借助LDR偽指令來進(jìn)行操作了糊秆。但是這里不做這么費(fèi)勁的修改了武福。因為假如其他地方也有這樣的功代碼,那么還得去修改痘番,就是回到了我們文章開始說到的那個問題捉片,要解決所有的簽名校驗功能,才是本文的目的汞舱。
不過到這里伍纫,我們可以先這么嘗試檢驗我們的爆破結(jié)果,就是借助Xposed框架昂芜,hook這個應(yīng)用獲取簽名的hashCode方法莹规,將返回值替換成正確的-2081383250值:
修改之后,運(yùn)行:
果然成功了泌神,而且對于之前的去除對話框那個邏輯也可以不用那么麻煩了良漱,這么一來整個app所有的簽名校驗地方都是爆破了舞虱。不過這種方式利用的是Xposed,所以不是最終方案母市。下面就來介紹一種高新技術(shù)來解決這個問題矾兜。
三、高效的Hook爆破方式
不知道大家是否還記得我之前介紹過一個系統(tǒng)篇系列文章患久,介紹如何Hook系統(tǒng)的各個服務(wù)椅寺,比如AMS,PMS等功能,然后在應(yīng)用內(nèi)進(jìn)行攔截activity啟動的功能墙杯。不了解的同學(xué)可以看這篇文章:Android中Hook系統(tǒng)服務(wù)功能配并;原理非常簡單就是利用反射機(jī)制+動態(tài)代理技術(shù),替換系統(tǒng)服務(wù)的Binder對象即可高镐。這個技術(shù)好處是免root操作溉旋,缺點(diǎn)是只能在應(yīng)用內(nèi)部進(jìn)行操作使用。那有的人好奇了嫉髓,既然只能在應(yīng)用本身內(nèi)部有效观腊,那有什么用呀?其實不然算行,現(xiàn)在有些開發(fā)場景就需要這個技術(shù)梧油,及時現(xiàn)在沒用到,將來也不一定州邢,這不在這里就用到了儡陨。下面我們就用這個技術(shù)來Hook系統(tǒng)的PMS服務(wù),攔截應(yīng)用獲取簽名信息的方法量淌。因為只要在本應(yīng)用中操作骗村,所以正好符合要求。
操作步驟很簡單呀枢,我們在樣本應(yīng)用啟動的入口處胚股,加上我們的攔截代碼即可,那么下面我們先把攔截PMS服務(wù)代碼寫好裙秋,這個代碼下載地址文章末尾給出琅拌。
就是利用反射去hook系統(tǒng)的類,然后用動態(tài)代理構(gòu)造一個自己的PMS Binder進(jìn)行替換即可:
在這里我們可以通過方法名來攔截簽名信息摘刑,我們用正版的簽名信息構(gòu)造一個新的Signature對象进宝,然后將其設(shè)置已有的對象中即可。因為我們最終還是需要把代碼放到樣本案例的入口處枷恕,所以就在這里將這兩個類放到樣本入口類的包下面即彪,具體包名可以手動構(gòu)造:
然后將這個demo反編譯,得到對應(yīng)的smali代碼,拷貝到樣本案例對應(yīng)的包下面隶校,然后在樣本案例的入口處加上方法調(diào)用:ServiceManagerWraper.hookPMS(this); 對應(yīng)的smali代碼如下:
invoke-static {p0}, Lcom/xiaoenai/app/ServiceManagerWraper;->hookPMS(Landroid/content/Context;)V
而樣本入口可以從AndroidManifest.xml中的application找到:
在Application的onCreate方法入口添加即可漏益,添加完成之后,二次打包深胳,再次使用Jadx打開修改之后的apk包:
入口處代碼已經(jīng)添加完畢了绰疤。然后安裝運(yùn)行,就可以完成舞终。而這么操作之后應(yīng)用所有的簽名信息就是正確的轻庆。包括native層的簽名校驗邏輯。所以這種方式是最靠譜的敛劝。不要改多處簽名校驗的邏輯了余爆。
四、簽名校驗爆破方式總結(jié)
到這里我們就成功的爆破了一款服務(wù)端+Native雙簽名校驗的樣本案例了夸盟,而這次操作簽名爆破之后蛾方,后面將不會在介紹更多的簽名校驗了,原因很簡單上陕,因為這次操作之后桩砰,我們發(fā)明了一個通用的爆破方式,可以解決簽名校驗問題了释簿,下面就來總結(jié)一下現(xiàn)階段Android中簽名校驗邏輯處理方式:
第一亚隅、基本法則不能忘:全局搜索字符串"signature",Java層可以用Jadx進(jìn)行搜索庶溶,so中可以用IDA進(jìn)行搜索煮纵。
第二、在逆向領(lǐng)域中偏螺,字符串信息永遠(yuǎn)是第一選擇的爆破的突破口醉途,在之前的文章我已經(jīng)多次講到了,不管是IDA,還是Jadx工具砖茸,只要全局搜索關(guān)鍵字符串信息,就可以找到我們想要的入口殴穴。
第三凉夯、本文的重點(diǎn):發(fā)明了一種全新的高效的爆破應(yīng)用簽名校驗邏輯,就是可以手動Hook樣本應(yīng)用的PMS功能采幌,然后在應(yīng)用的入口處加上hook代碼劲够。最終在hook的invoke方法中攔截想要的方法即可。
有了本文的思路休傍,后面會開發(fā)一個工具征绎,一鍵式解決簽名問題,原理就是利用我之前介紹的 icodetools工具 和本文介紹的hook系統(tǒng)PMS服務(wù),篡改應(yīng)用簽名信息人柿。關(guān)于具體細(xì)節(jié)和工具開發(fā)敬請期待柴墩。如果此工具開發(fā)完成,那么對于簽名校驗的應(yīng)用絕對是一個新的挑戰(zhàn)凫岖。安全不息江咳,逆向不止!
嚴(yán)重聲明:****本文的目的只有一個哥放,通過一個案例來分析現(xiàn)在應(yīng)用逆向分析技巧歼指,如果有人利用本文內(nèi)容進(jìn)行任何商業(yè)目的和非法牟利,帶來的任何法律責(zé)任將由操作者本人承擔(dān)甥雕,和本文作者沒有任何關(guān)系踩身,所以還是由衷的希望大家秉著技術(shù)學(xué)習(xí)的目的閱讀此文,非常感謝社露!
Hook代碼下載地址:https://github.com/fourbrother/HookPmsSignature
五挟阻、總結(jié)
本文介紹的內(nèi)容稍微有點(diǎn)多,所以大家看的可能有點(diǎn)累呵哨,其實還有一部分內(nèi)容沒介紹赁濒,就是如何訪問已有的so文件中的函數(shù),變量值孟害,這個是我在這個樣本案例中用到的一個方法拒炎,限于篇幅原因就不多介紹了。但是一定要記住本文的最后一種爆破簽名校驗方式的方法挨务。此等絕對高級正能量击你。希望可以言傳。最后看完文章谎柄,如果覺得有收獲丁侄,就多多點(diǎn)贊擴(kuò)散分享,如果有打賞那就最好啦啦3住鸿摇!
更多內(nèi)容:點(diǎn)擊這里
關(guān)注微信公眾號,最新技術(shù)干貨實時推送
編碼美麗技術(shù)圈
微信掃一掃進(jìn)入我的"技術(shù)圈"世界
掃一掃加小編微信添加時請注明:“編碼美麗”非常感謝劈猿!