Frida動靜態結合分析Base64簽名校驗的四個變種
目標app為algorithmbase_10.apk。
點擊CHECK會有加密后的字符串,看樣子是Base64編碼:

用jadx查看源碼中關鍵代碼:

輸出的值由MainActivity類的靜態方法encodeFromJni_10生成,該方法需要傳入一個16個字符的隨機字符串。

encodeFromJni_10是一個native方法:

其實現應該在libnative-lib.so中:

在ida中打開so文件,查看encodeFromJni_10方法的具體實現。
用findcrypt腳本定位到base64編碼表:
既然能被識別,那應該是個標準的編碼表,先從logcat中得到一批參數密文對:

分別放入CyberChef中直接嘗試下:


看起來就是單純的Base64編碼。
為了方便對更多輸入值進行確認,寫了個frida腳本,將encodeFromJni_10執行后的值和調用Base64.encodeToString后的值進行比較,查看是否相等。

多次測試后發現確實都相等,確定就是標準的Base64編碼。
目標app為algorithmbase_11.apk.
打開測試后,和之前的algorithmbase_10.apk結果十分相似,也像是用Base64編碼:

直接定位其so中的encodeFromJni_111方法實現.
用findcrypt沒有找到可用信息:

但離返回值最近的方法(后面做內存回收的不算)調用中可以發現類似base64編碼表的內容:


雙擊查看完整內容:
猜測應該是非標準的base64編碼,使用了自定義的編碼表,將編碼表復制到CyberChef中(還要加上等號):

從logcat中取兩個測試案例,驗證沒有問題。

目標app為algorithmbase_12.apk。
點擊測試后發現比之前兩個app結果更加雜亂:

還是直接查看so文件中對應native方法encodeFromJni_112的實現。發現與之前的111極為相似。而加密的關鍵函數內部的實現,除了使用的編碼表外一模一樣。

按理說就是使用了另一種自定義編碼表的base64編碼才對。但是提取出其編碼表并放入CyberChef中測試,發現密文結果并不一致。

CyberChef的結果為:

Logcat獲取到的實際結果為:
?????????????????????????????????????????????
?????????????????????????????????????????????
會不會是base64編碼完后還有什么步驟?還是說傳入的參數在base64編碼之前有被修改呢?為了確定這點,用frida hook了做base64操作的函數,輸出其執行前后參數的內容。

發現傳入的第二個參數就是原始生成的隨機字符串,而函數執行完后第一個參數中存放的內容就是最后打印出來的密文。

看來沒有什么額外加密步驟。那唯一的變數就是編碼表的內容了。會不會是運行的時候編碼表被改了呢。直接用一段腳本打印運行時內存中的編碼表內容。

發現果不其然和靜態看的不一樣。
將這段編碼表內容放入CyberChef中,就可以得到正確的結果了:


那么編碼表是何時被修改的呢?在ida中查找下編碼表的引用:

發現有一處指向了另外一個之前沒關注到的函數內部。很明顯編碼表就是在這里做了修改。

這個方法也是在encodeFromJni_112方法比較前期的地方就被調用。

其他應該就沒什么問題了。
目標app為algorithmbase_13.apk.
看結果又是變種base64編碼的樣子:

直接去觀察so中native方法encodeFromJni_113用到的編碼表:

和12用到的一樣,不過測試發現用這個表得到的結果和打印出來的值不一致。猜測又是運行時被修改了。
先用之前的腳本確定沒有其他加密過程:


再查看下運行時內存中的編碼表內容:

將這部分內容作為編碼表,到CyberChef上測試,發現提示編碼表長度不對。

檢查后發現CyberChef中的base64編碼表對‘-’的位置有要求,如果單純的放在兩個字母或者數字中間會被判定為表示區間的字符。可以用’\’進行轉義:

但是轉完后生成的結果和目標結果不一致:

之前已經用frida hook腳本確定了所有加密只可能在函數sub_8b04中完成,那問題肯定還在這個函數中。
在ida中對照12和13兩個apk的sub_8b04函數,發現13的sub_8b04函數中有兩行代碼與12不一致,多了一步異或操作。這里異或的變量v4值等于傳入的第三個參數的值,也就是待編碼內容的長度。


看來是base64編碼的另一種魔改方式,這在CyberChef上應該是模擬不了了,需要自己實現一段代碼。

只需要在標準的base64編碼實現上,修改對應的地方即可。
再次運行就可以得到正確的內容了:
