現在混淆的主要目的之一就有讓逆向分析人員不清楚函數的調用流程,給你一堆函數,加了高強度的OLLVM,更不能看了。那麼Trace跟蹤技術就顯得很重要的,如果清楚了函數調用流程,那麼逐個分析,距離成功不就很快了。
萬事開頭難,逆向程序難在不知道從哪開始。
前幾天做了一道AIS3的題目,內含50個加密函數,加密的流程很簡單,關鍵是對這50個加密函數進行了ollvm控制流平坦化魔改(去除也很簡單),主要是想拋磚引玉,鍛鍊和練習trace的技術。這樣在以後遇到高強度的混淆干擾也能有一戰的能力。
題目附件如下:
[stateful]
本文的重點在於總結trace技巧,題目本身不算很難。
分析
打開題目,進入main函數
發現邏輯不是很難,進入state_machine函數
好傢伙,一大坨
嘗試使用OBPO插件去除,發現直接卡死。使用D810也是卡死。
更高級的玩法使用Unicorn進行去除,類似deflat
本文的重點是在不去除平坦化的前提下去trace函數調用流程
發現有50多個state函數,並且每個函數的功能很簡單,我們的目的是:
trace每一個函數,並在梳理調用流程的過程中,輸出關鍵的加密流程,從而寫出解密流程
注意調試的時候,記得傳入參數
Trace
方法一:手動trace
最簡單粗暴的方法,對每一個state函數下斷點,然後運行程序,逐一拿到調用流程。
如果函數過多,這種方法就不太行了
最終筆者運行拿到了調用的流程
a1[14] += a1[35] + a1[8]; a1[9] -= a1[2] + a1[22]; *a1 -= a1[18] + a1[31]; a1[2] += a1[11] + a1[8]; a1[6] += a1[10] + a1[41]; a1[14] -= a1[32] + a1[6]; a1[16] += a1[25] + a1[11]; a1[31] += a1[34] + a1[16]; a1[9] += a1[11] + a1[3]; a1[17] += *a1 + a1[7]; a1[5] += a1[40] + a1[4]; a1[37] -= a1[29] + a1[3]; a1[23] += a1[7] + a1[34]; a1[39] -= a1[25] + a1[38]; a1[27] += a1[18] + a1[20]; a1[20] += a1[19] + a1[24]; a1[15] += a1[22] + a1[10]; a1[30] -= a1[33] + a1[8]; a1[1] -= a1[29] + a1[13]; a1[19] += a1[10] + a1[16]; *a1 += a1[33] + a1[16]; a1[36] += a1[11] + a1[15]; a1[24] += a1[20] + a1[5]; a1[7] += a1[21] + *a1; a1[1] += a1[15] + a1[6]; a1[30] -= a1[13] + a1[2]; a1[1] += a1[16] + a1[40]; a1[31] += a1[1] + a1[16]; a1[32] += a1[5] + a1[25]; a1[13] += a1[25] + a1[28]; a1[7] += a1[10] + *a1; a1[21] += a1[34] + a1[15]; a1[21] -= a1[13] + a1[42]; a1[18] += a1[29] + a1[15]; a1[4] += a1[7] + a1[25]; *a1 += a1[28] + a1[31]; a1[2] += a1[34] + a1[25]; a1[13] += a1[26] + a1[8]; a1[41] -= a1[3] + a1[34]; a1[37] += a1[27] + a1[18]; a1[4] += a1[27] + a1[25]; a1[23] += a1[30] + a1[39]; a1[18] += a1[26] + a1[31]; a1[10] -= a1[12] + a1[22]; a1[4] += a1[6] + a1[22]; a1[37] += a1[12] + a1[16]; a1[15] += a1[40] + a1[8]; a1[17] += a1[38] + a1[24]; a1[8] += a1[14] + a1[16]; a1[5] += a1[37] + a1[20];
其實手都快殘了
【---- 幫助網安學習,以下所有學習資料免費領!領取資料加 we~@x:dctintin,備註 “開源中國” 獲取!】
① 網安學習成長路徑思維導圖
② 60 + 網安經典常用工具包
③ 100+SRC 漏洞分析報告
④ 150 + 網安攻防實戰技術電子書
⑤ 最權威 CISSP 認證考試指南 + 題庫
⑥ 超 1800 頁 CTF 實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
⑧ APP 客戶端安全檢測指南(安卓 + IOS)
方法二:IDA-trace
程序動態調試的時候纔可以使用trace功能
IDA自動進行trace跟蹤,然後稍等片刻
可以發現成功的trace了調用了流程
但是有一點不方便的是,有了調用流程,但是我們還要進入每一個函數,提取加密的流程纔行。
IDA快捷鍵Ctrl+F5可以導出整個程序的僞代碼
然後進一步提取和分析
這裏可以使用IDA-python自動下斷點
Go import idc bpt_addr = 0x5599F331ADA7 bpt_size=1 idaapi.add_bpt(bpt_addr,bpt_size) print("Final")
當然還不夠,我們要達到的效果是,觸發斷點然後輸出相關加密信息到output函數窗口,就是有斷點回調函數
import idaapi # 定義回調函數 def my_bpt_callback(bptno): print("Breakpoint %d hit!" % bptno) # 添加斷點 bpt_addr = 0x5599F331ADA7 bpt_size=1 bpt = idaapi.add_bpt(bpt_addr,bpt_size) # 設置斷點回調 idaapi.add_bpt_chngev_cnd(bpt, idaapi.BPT_EXEC, my_bpt_callback) #設置執行斷點 ----------------------------------------------------------------------- idaapi.BPT_EXEC 表示執行事件
方法三:trace_natives
https://github.com/Pr0214/trace_natives
按照說明,進行輸出,發現是這樣的效果(IDA中,Edit-Plugins-traceNatives)
解密
有了調用流程,剩下的就很簡單了
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <iostream> int main() { unsigned char a1[] = { 0x0F, 0x77, 0xEC, 0x33, 0x44, 0x16, 0x13, 0x59, 0x1D, 0x42, 0x84, 0x75, 0x5F, 0xE4, 0x83, 0xC0, 0x3B, 0xC1, 0x95, 0xCF, 0xDB, 0x33, 0x6C, 0xD2, 0xED, 0x72, 0x5F, 0x0D, 0x74, 0x41, 0x5B, 0x73, 0xA0, 0x33, 0x53, 0x24, 0x02, 0x59, 0x74, 0x60, 0x33, 0xCC, 0x7D }; a1[5] -= a1[37] + a1[20]; a1[8] -= a1[14] + a1[16]; a1[17] -= a1[38] + a1[24]; a1[15] -= a1[40] + a1[8]; a1[37] -= a1[12] + a1[16]; a1[4] -= a1[6] + a1[22]; a1[10] += a1[12] + a1[22]; a1[18] -= a1[26] + a1[31]; a1[23] -= a1[30] + a1[39]; a1[4] -= a1[27] + a1[25]; a1[37] -= a1[27] + a1[18]; a1[41] += a1[3] + a1[34]; a1[13] -= a1[26] + a1[8]; a1[2] -= a1[34] + a1[25]; *a1 -= a1[28] + a1[31]; a1[4] -= a1[7] + a1[25]; a1[18] -= a1[29] + a1[15]; a1[21] += a1[13] + a1[42]; a1[21] -= a1[34] + a1[15]; a1[7] -= a1[10] + *a1; a1[13] -= a1[25] + a1[28]; a1[32] -= a1[5] + a1[25]; a1[31] -= a1[1] + a1[16]; a1[1] -= a1[16] + a1[40]; a1[30] += a1[13] + a1[2]; a1[1] -= a1[15] + a1[6]; a1[7] -= a1[21] + *a1; a1[24] -= a1[20] + a1[5]; a1[36] -= a1[11] + a1[15]; *a1 -= a1[33] + a1[16]; a1[19] -= a1[10] + a1[16]; a1[1] += a1[29] + a1[13]; a1[30] -= a1[33] + a1[8]; a1[15] -= a1[22] + a1[10]; a1[20] -= a1[19] + a1[24]; a1[27] -= a1[18] + a1[20]; a1[39] += a1[25] + a1[38]; a1[23] -= a1[7] + a1[34]; a1[37] += a1[29] + a1[3]; a1[5] -= a1[40] + a1[4]; a1[17] -= *a1 + a1[7]; a1[9] -= a1[11] + a1[3]; a1[31] -= a1[34] + a1[16]; a1[16] -= a1[25] + a1[11]; a1[14] += a1[32] + a1[6]; a1[6] -= a1[10] + a1[41]; a1[2] -= a1[11] + a1[8]; *a1 += a1[18] + a1[31]; a1[9] += a1[2] + a1[22]; a1[14] -= a1[35] + a1[8]; printf("%s", a1); return 0; }
得到flag
AIS3{4re_YOu@sTATEfUl_0r_StA03L3S$_ctF3R}