一、知識回顧關于Android中的簽名校驗是一種很普遍的安全防護策略了,很多應用也都做了這部分的工作,在之前我也介紹了幾篇關于如何爆破應用的簽名校驗問題的文章,不了解的同學可以去查看:Android中爆破應用簽名校驗功能,當時介紹完這篇文章之后,其實總結了現在爆破簽名校驗的幾種方式,其中最方便快捷的就是:全局搜索字符串內容:“signature”,因為只要有簽名校驗功能,一定會調用系統的一個方法,而這個方法中就是包含了這個字符串內容。之前的這篇文章中介紹的簽名校驗處理方式也是如此,找到具體簽名校驗功能之后,直接替換正確的簽名信息就可,或者把if判斷手動改成true也可以。但是當時說到一個問題,就是現在應用為了加強防護,幾乎在重要的內容部分中都加了簽名校驗功能,所以如果要手動的改,就需要把每個簽名校驗的地方都得改一下,這樣會顯得很費勁,有的人說了,有一個好的辦法,就是用Xposed來hook這個應用的獲取系統簽名的方法,然后替換方法的返回值即可。這種方式是可以的,但是不是最好的,因為如果你解決了簽名校驗,二次打包給別人用,不可能叫人家還root手機,然后在裝Xposed框架功,是不合理的。所以我們需要從根本上解決這個問題,也是本文介紹的一個重點。后面會介紹一種萬能的高效方法。之前的文章中我們可以看到,大部分簽名校驗都是在Java層,本地做的校驗。所以爆破難度不是很大,而今天我們要介紹的一個應用樣本他的簽名校驗是放在so中,而且結合了服務端進行驗證,難度加大。不過萬變不離其宗,簽名校驗永遠都是需要獲取應用本身的簽名信息,進行比對操作的。
二、爆破簽名校驗下面就開始進入本文的主題,看一下應用樣本的簽名校驗問題,拿到樣本之后,直接反編譯,二次打包,安裝出現如下錯誤信息提示:
這個直接彈出對話框信息,點擊確定就退出程序了,這個比較簡單,反編譯之后,通過提示內容,找到簽名校驗入口,在values/string.xml中找到這個字符串信息即可:
然后用Jadx打開這個應用,全局搜索這個name值:
點擊進入代碼位置即可:
看到i方法中有一個show方法,應該就是對話框展示的邏輯。看看這個i方法調用的地方:
看到了,在之前有一個判斷,如果為false,就是走到i方法,展示對話框,所以這個checkHashKey方法肯定是簽名校驗的方法,進入查看:
是個native方法,全局搜SocketJNI類調用的地方:
在這里看到會加載一個so文件,也就是libmzd.so文件,我們用IDA打開這個文件(文件在反編譯之后的libs目錄下):
找到對應的native函數,查看具體邏輯代碼:
為了更好的閱讀代碼,使用F5,查看對應的C語言代碼,這里的邏輯非常簡單,就是把Java層傳遞的簽名字符串內容,在做一次MD5值,然后和指定的字符串"8f2a24...."作比對即可。那么上面傳入到的checkHashKey方法的值是通過com.xiaoenai.app.utils.aj.m方法:
這里看到就是標準的獲取應用簽名信息的方法。
到這里就看到了一個Java層的簽名校驗方法,這里解決這個問題有很多種方法,可以修改對應的smali方法,把if判斷強制改成true即可。還可以直接修改m方法的返回值為正確的簽名字符串內容。這里選擇第二種,因為我們需要引用的簽名字符串內容,后面會繼續用到。這個獲取方法也很簡單,直接寫一個Demo案例,通過應用包名構建出對應的Context變量,然后就開始獲取簽名信息即可:
運行這個demo程序,前提是你的設備中要安裝一個官方版本的應用,這樣才能獲取到正確的簽名字符串內容:
看到了,其中簽名字符串:"aN+VCd8ns0yqsotX2WuKyScq/ZA=" 就是正確的官方版本的值。下面在順便寫一個直接返回這個字符串的方法,然后變編譯得到對應的smali代碼:
然后把這個方法的的代碼拷貝替換樣本應用的aj.m方法代碼即可:
保存,二次打包即可,記住:這里沒必要手動的編寫smali代碼返回指定的簽名字符串,除非你對smali語法非常熟悉了,不過我是不會這么做的。因為我不熟悉smali,最笨的最有效的方法就是自己手動寫一個Java方法,然后反編譯得到對應的smali代碼即可。二次打包成功之后,安裝應用,運行發現竟然還有錯誤,登錄失敗:
這時候,就想到了,樣本應用可能做了服務端數據驗證,下面就來看看如何解決這個問題,這個問題的突破口比較簡單,不要在用提示字符串信息去找了,因為這錯誤信息可能是服務端返回的,這時候就需要借助抓包了,每次請求一次,就抓一次服務包,這里用了Fiddler工具:
這里看到,請求的參數非常簡單,一個加密之后的數據data和版本號ver,返回的數據提示加密簽名錯誤,所以我們可以在Jadx中全局搜"login2"字符串信息,找到突破口:
找到之后,雙擊進入代碼:
看到最終調用了,a方法,點擊進入a方法:
這里會通過一個過程構造出一個json參數格式,繼續往下看:
攜帶了這些信息參數值,為了更好的看到這個json數據格式,我們可以利用Xposed下一個hook功能:
然后運行這個Xposed模塊,在點擊樣本的登錄功能,查看日志信息:
看到了,result就是最終拼接好的json參數格式內容。其中最后一個字符串sig是將簽名整個參數做一次加密操作,為了在服務端校驗參數的完整性。得到這個json格式之后,會調用com.xiaoenai.app.utils.b.a.a方法: 繼續查看這個方法實現:
繼續跟蹤代碼:
又是到了native方法了,到這里其實上次Java將拼接好的參數信息以json格式傳遞到native層進行加密操作,繼續看native層代碼實現,依然在之前的那個libmzd.so中:
找到對應的核心函數功能,跟蹤查看實現:
繼續跟蹤:
這里看到了,有data字符串內容了,也就是這里開始對上面傳遞的json數據進行加密,然后拼接到data中,在native層在拼接一套json:{"data":"加密之后的值", "ver":"1.1"},而在這里有一個尋找加密key的函數,可惜這里我不在分析了,因為我看的頭疼,他的加密算法還是比較復雜的。所以就放棄分析了。那么到這里,我們有什么方式可以得到加密算法呢?有的同學可能想到了動態調試,這個方法是最好的,但是這個app做了很多反調試策略,動態調試也是不好弄的,而且這個算法有點復雜,及時動態調試,也要詳細閱讀arm指令,才能猜到這個加密算法功能。所以這條路不好走。那么還有其他方法了?當然有,就是文章開頭說到的一句解決簽名校驗的基本法則:全局搜索字符串"signature";在IDA中也是如此,使用Shirt+F12打開so中的字符串窗口,然后搜索signature:
果然找到了,雙擊進入:
看到這里可能是在native層調用了系統獲取簽名的方法,選中紅色框中的內容,然后按X鍵,展示被調用的地方索引:
雙擊進入,這個就是我們之前分析的那個SocketJNI的init方法:
到這里,為了更好的閱讀代碼,需要把這個函數地址改成可讀,也就是JNIEnv*即可,選中紅色框,按下Y鍵即可:
修改成JNIEnv*,確定即可:
這樣就看的比較清楚了,而這部分代碼也非常簡單,就是調用系統獲取簽名的方法,然后賦值到一個變量中,不過這里獲取的不是字符串內容,而是int類型的hashCode值:
在返回到arm代碼處,看到賦值,就是將R0寄存器值搞到R9中去。
到這里,我們一定要思路清楚,就是不管native層怎么加密,最終會利用應用的簽名值來做加密的key信息。所以我們只要解決掉獲取簽名信息值即可,也就是這里的R9寄存器值,我們還需要再一次去獲取正版的簽名信息的hashCode值即可:
然后運行,查看值:-2081383250
然后我們可以修改上面的arm指令:MOV R9,R0;不過這里還不好弄,因為這個hashCode值int類型四個字節,而這里看到一個命令才2個字節,肯定不夠用。需要借助LDR偽指令來進行操作了。但是這里不做這么費勁的修改了。因為假如其他地方也有這樣的功代碼,那么還得去修改,就是回到了我們文章開始說到的那個問題,要解決所有的簽名校驗功能,才是本文的目的。
不過到這里,我們可以先這么嘗試檢驗我們的爆破結果,就是借助Xposed框架,hook這個應用獲取簽名的hashCode方法,將返回值替換成正確的-2081383250值:
修改之后,運行:
果然成功了,而且對于之前的去除對話框那個邏輯也可以不用那么麻煩了,這么一來整個app所有的簽名校驗地方都是爆破了。不過這種方式利用的是Xposed,所以不是最終方案。下面就來介紹一種高新技術來解決這個問題。
三、高效的Hook爆破方式不知道大家是否還記得我之前介紹過一個系統篇系列文章,介紹如何Hook系統的各個服務,比如AMS,PMS等功能,然后在應用內進行攔截activity啟動的功能。不了解的同學可以看這篇文章:Android中Hook系統服務功能;原理非常簡單就是利用反射機制+動態代{過}{濾}理技術,替換系統服務的Binder對象即可。這個技術好處是免root操作,缺點是只能在應用內部進行操作使用。那有的人好奇了,既然只能在應用本身內部有效,那有什么用呀?其實不然,現在有些開發場景就需要這個技術,及時現在沒用到,將來也不一定,這不在這里就用到了。下面我們就用這個技術來Hook系統的PMS服務,攔截應用獲取簽名信息的方法。因為只要在本應用中操作,所以正好符合要求。操作步驟很簡單,我們在樣本應用啟動的入口處,加上我們的攔截代碼即可,那么下面我們先把攔截PMS服務代碼寫好,這個代碼下載地址文章末尾給出。
就是利用反射去hook系統的類,然后用動態代{過}{濾}理構造一個自己的PMS Binder進行替換即可:
在這里我們可以通過方法名來攔截簽名信息,我們用正版的簽名信息構造一個新的Signature對象,然后將其設置已有的對象中即可。因為我們最終還是需要把代碼放到樣本案例的入口處,所以就在這里將這兩個類放到樣本入口類的包下面,具體包名可以手動構造:
然后將這個demo反編譯,得到對應的smali代碼,拷貝到樣本案例對應的包下面,然后在樣本案例的入口處加上方法調用:ServiceManagerWraper.hookPMS(this);對應的smali代碼如下:invoke-static {p0}, Lcom/xiaoenai/app/ServiceManagerWraper;->hookPMS(Landroid/content/Context;)V
而樣本入口可以從AndroidManifest.xml中的application找到:
在Application的onCreate方法入口添加即可,添加完成之后,二次打包,再次使用Jadx打開修改之后的apk包:
入口處代碼已經添加完畢了。然后安裝運行,就可以完成。而這么操作之后應用所有的簽名信息就是正確的。包括native層的簽名校驗邏輯。所以這種方式是最靠譜的。不要改多處簽名校驗的邏輯了。
四、簽名校驗爆破方式總結到這里我們就成功的爆破了一款服務端+Native雙簽名校驗的樣本案例了,而這次操作簽名爆破之后,后面將不會在介紹更多的簽名校驗了,原因很簡單,因為這次操作之后,我們發明了一個通用的爆破方式,可以解決簽名校驗問題了,下面就來總結一下現階段Android中簽名校驗邏輯處理方式:第一、基本法則不能忘:全局搜索字符串"signature",Java層可以用Jadx進行搜索,so中可以用IDA進行搜索。第二、在逆向領域中,字符串信息永遠是第一選擇的爆破的突破口,在之前的文章我已經多次講到了,不管是IDA,還是Jadx工具,只要全局搜索關鍵字符串信息,就可以找到我們想要的入口。第三、本文的重點:發明了一種全新的高效的爆破應用簽名校驗邏輯,就是可以手動Hook樣本應用的PMS功能,然后在應用的入口處加上hook代碼。最終在hook的invoke方法中攔截想要的方法即可。有了本文的思路,后面會開發一個工具,一鍵式解決簽名問題,原理就是利用我之前介紹的 icodetools工具 和本文介紹的hook系統PMS服務,篡改應用簽名信息。關于具體細節和工具開發敬請期待。如果此工具開發完成,那么對于簽名校驗的應用絕對是一個新的挑戰。安全不息,逆向不止!
嚴重聲明:本文的目的只有一個,通過一個案例來分析現在應用逆向分析技巧,如果有人利用本文內容進行任何商業目的和非法牟利,帶來的任何法律責任將由操作者本人承擔,和本文作者沒有任何關系,所以還是由衷的希望大家秉著技術學習的目的閱讀此文,非常感謝!
Hook代碼下載地址:https://github.com/fourbrother/HookPmsSignature
五、總結本文介紹的內容稍微有點多,所以大家看的可能有點累,其實還有一部分內容沒介紹,就是如何訪問已有的so文件中的函數,變量值,這個是我在這個樣本案例中用到的一個方法,限于篇幅原因就不多介紹了。但是一定要記住本文的最后一種爆破簽名校驗方式的方法。此等絕對高級正能量。希望可以言傳。最后看完文章,如果覺得有收獲,就多多點贊擴散分享,如果有打賞那就最好啦啦!!
|