少量虛假控制流混淆后的算法還原案例
目標apk輸出的結果和第一題很像:

Java層也幾乎一樣:

Sign1函數在Native層中和之前一樣是在JNI_OnLoad方法中通過RegisterNatives方法動態注冊。在sub_11088方法中可以定位到相關代碼。

還是用一樣的腳本,獲取sign1代碼的實際偏移地址:

Sub_10618方法的代碼結構和第一題有相似之處,于是打算由結果開始倒推分析。
根據經驗,變量V20中應該存有結果字節數組。

V20由v22格式化后得到,而v22是通過sub_17030方法生成。

Sub_17030的第一個參數由前面的sub_16248方法生成。

那就直接hook sub_16248和sub_17030兩個方法。當然hook之前還是先固定好傳入的字符串內容。

由結果可以看出,先調用sub_16248方法,將隨機字符串進行處理。得到的結果
‘d5 d6 92 3c a6 bf 4f 04 bc dc 6b 26 50 f8 c7 e9’就是第一題md5哈希后的結果。
而sub_17030是將哈希后的結果再次處理,得到‘ce c6 a6 4a 75 a2 d2 b7 90 6b d8 51 e9 73 ca a7’。

先確認下sub_16248是否和之前的md5一樣。Sub_16248調用了sub_15B68。

其中關鍵代碼為:

Sub_1510C疑似md5_update,hook一下。果然第一部分還是相同的內容,第二部分就是隨機的字符串。

存儲第一部分字節內容的地址為0x420F0。靜態查看時內容與運行時不一致。運行時才是需要的內容,可以判斷是運行時進行了解密操作。


根據交叉引用,定位到運行時進行解密操作的代碼位置:

因為這部分產生的內容不變,暫且就不深入分析了。直接回過頭分析sub_17030方法。
Sub_17030內部實現分為兩部分,分別進行逐字節操作:

這部分靜態分析稍微有點吃力,就嘗試用stalker打印指令流來分析。
在課上的腳本上稍作更改后,對sub_17030進行hook。


同時,固定傳入的字符串。(這里為了與后續的迭代計數器區分,輸入字符串換成了’ fedcba9876543210’)

再次點擊按鈕,就可以得到一長串指令流了。

回到sub_17030的實現,它分為①和②兩部分。

部分①處匯編指令為:

隨便取一個地址”0x171f0”,去前面導出的指令流中搜索,發現沒有執行到。

而部分②的匯編指令是可以搜索到的,說明執行到了。


這么看,估計部分①是虛假控制流。實際執行的只有部分②的指令。那么就只用看部分②的指令了。
部分②反編譯后的第一條語句為:

Index & 0xf對應的匯編指令為:

在指令流日志中搜索所有執行AND操作時index的值,發現從0x0開始遞增到0xf。猜測就是index迭代計數器。

V18指向0x35F50。

其初始化的值為:

整個第一行語句用處就是從0x35F50處逐字節取值。取字節對應的匯編指令為:

可以搜索所有取到的字節值。

發現和初始化的值一致,說明沒有后續的處理。
第二條語句:

后半部分的(index ^ 0xFFFFFFF8) & index 相當于對index作mod 8處理。然后再將結果作為索引從0x42180處逐字節取值。這部分匯編為:

0x42180處的初始化值為:

而日志中打印的所取出的值和初始化的值不同。

應該是在前面的解密函數里做了解密操作,因為結果是固定的(0x65, 0x39, 0x66, 0x30, 0x33, 0x34, 0x32, 0x61),沒有深究。

第三條語句就是一系列的字節操作,后面算法還原時直接照著抄一下就行:

第四條語句是將第三條語句生成的結果與輸入字符串做逐字節操作,也可以照搬一下。

根據剛才的分析寫還原代碼:

用相同的參數進行調用:
可以得到與sign1算法相同的結果:

再hook一組參照組:

運行得到的結果也相同。