現在混淆的主要目的之一就有讓逆向分析人員不清楚函數的調用流程,給你一堆函數,加了高強度的OLLVM,更不能看了。那么Trace跟蹤技術就顯得很重要的,如果清楚了函數調用流程,那么逐個分析,距離成功不就很快了。
萬事開頭難,逆向程序難在不知道從哪開始。
前幾天做了一道AIS3的題目,內含50個加密函數,加密的流程很簡單,關鍵是對這50個加密函數進行了ollvm控制流平坦化魔改(去除也很簡單),主要是想拋磚引玉,鍛煉和練習trace的技術。這樣在以后遇到高強度的混淆干擾也能有一戰的能力。
題目附件如下:
[stateful]
本文的重點在于總結trace技巧,題目本身不算很難。
分析
打開題目,進入main函數
{width="5.75in" height="3.4270833333333335in"}
發現邏輯不是很難,進入state_machine函數
好家伙,一大坨
{width="5.75in" height="3.4166666666666665in"}
嘗試使用OBPO插件去除,發現直接卡死。使用D810也是卡死。
更高級的玩法使用Unicorn進行去除,類似deflat
本文的重點是在不去除平坦化的前提下去trace函數調用流程
{width="5.75in" height="3.09375in"}
發現有50多個state函數,并且每個函數的功能很簡單,我們的目的是:
trace每一個函數,并在梳理調用流程的過程中,輸出關鍵的加密流程,從而寫出解密流程
注意調試的時候,記得傳入參數
{width="5.75in" height="2.4895833333333335in"}
Trace
方法一:手動trace
最簡單粗暴的方法,對每一個state函數下斷點,然后運行程序,逐一拿到調用流程。
如果函數過多,這種方法就不太行了
{width="5.75in" height="2.1770833333333335in"}
最終筆者運行拿到了調用的流程
?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];
其實手都快殘了
方法二:IDA-trace
程序動態調試的時候才可以使用trace功能
{width="5.75in" height="2.40625in"}
{width="5.75in" height="2.8854166666666665in"}
IDA自動進行trace跟蹤,然后稍等片刻
{width="5.75in" height="2.6041666666666665in"}
可以發現成功的trace了調用了流程
{width="5.75in" height="2.4375in"}
但是有一點不方便的是,有了調用流程,但是我們還要進入每一個函數,提取加密的流程才行。
IDA快捷鍵Ctrl+F5可以導出整個程序的偽代碼
然后進一步提取和分析
{width="5.75in" height="3.6875in"}
這里可以使用IDA-python自動下斷點
??Goimport?idcbpt_addr?=?0x5599F331ADA7bpt_size=1idaapi.add_bpt(bpt_addr,bpt_size)print("Final")
當然還不夠,我們要達到的效果是,觸發斷點然后輸出相關加密信息到output函數窗口,就是有斷點回調函數
??import?idaapi#?定義回調函數def?my_bpt_callback(bptno):print("Breakpoint?%d?hit!"?%?bptno)#?添加斷點bpt_addr?=?0x5599F331ADA7bpt_size=1bpt?=?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)
{width="5.75in" height="1.125in"}
解密
有了調用流程,剩下的就很簡單了
?#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}