deflat 指令碼學習【去除OLLVM混淆】
deflat指令碼連結:GitHub - cq674350529/deflat: use angr to deobfuscation
deflat 指令碼測試
這裡以程式碼混淆與反混淆學習-第一彈中的OLLVM 混淆樣本為例進行去除。【LLVM-4.0】
控制流平坦前 | 控制流平坦後 |
---|---|
python deflat.py --file main-bcf --addr 0x401180
deflat.py 成功去除後效果:
去混淆後,效果還算可以,能分析程式流程了。
deflat 指令碼分析【angr】
利用符號執行去除控制流平坦化 - 部落格 - 騰訊安全應急響應中心 (tencent.com)
利用angr符號執行去除控制流平坦化 - 0x401RevTrain-Tools (bluesadi.github.io)
- 序言:函式的第一個執行的基本塊
- 主(子)分發器:控制程式跳轉到下一個待執行的基本塊
- retn塊:函式出口
- 真實塊:混淆前的基本塊,程式真正執行工作的版塊
- 前處理器:跳轉到主分發器
如第一彈中分析:OLLVM 的控制流平坦化是將程式的一般邏輯劃分為很多個真實執行的塊,然後透過分發器進行連結。其實就是一個Switch結構,每次執行完真實塊後,進行預處理,再跳轉到主分發器,繼續分發,最終達到平坦化的效果。
顯然,去控制流平坦化就是要找到真實塊間的跳轉邏輯,打破Switch結構束縛。
具體來說,有如下步驟:
- 靜態分析CFG得到序言/入口塊、主分發器、子分發器/無用塊、真實塊、預分發器和返回塊。
- 利用符號執行恢復真實塊的前後關係,重建控制流
- 根據第二步重建的控制流Patch程式,輸出恢復後的可執行檔案
靜態分析
首先明確:【以下結論針對OLLVM專案,其他大佬加料的OLLVM混淆還需要單獨分析】
- 函式的開始地址為序言的地址
- 序言的後繼為主分發器
- 後繼為主分發器的塊為前處理器
- 後繼為前處理器的塊為真實塊
- 無後繼的塊為retn塊
- 剩下的為無用塊
angr 獲取類似Ida的 CFG
獲取真實塊、主分發器、前處理器、序言、retn塊和無用塊
獲取真實塊的細節
angr 恢復真實塊執行邏輯,重建控制流
利用angr 強大的符號執行功能,找到各真實塊的連線邏輯。
這裡對於兩個分支的模擬執行,只需關注cmov
指令,就可以分別對應得到eax、ecx,然後獲得後續真實塊。【侷限性很大】
符號執行 symbolic_execution()
函式,返回後繼真實塊。
Patch程式恢復執行邏輯
如此便完成了 deflat指令碼的簡單處理分析。
小結
分析下來,其實就是定位到所有真實塊,然後利用angr符號執行將真實塊間的執行邏輯進行串聯。最後進行patch程式,重建控制流。
但顯然存在一些問題,我們預設了如下規則:
- 函式的開始地址為序言的地址
- 序言的後繼為主分發器
- 後繼為主分發器的塊為前處理器
- 後繼為前處理器的塊為真實塊
- 無後繼的塊為retn塊
- 剩下的為無用塊
但是在實際去除控制流平坦化過程中,上面的預設思路已經被加混淆的開發者做了處理。
例如:
- 後繼為前處理器的塊不一定是真實塊;
- 前處理器不一定存在;
- 存在分支的真實塊跳轉的判斷邏輯,不一定是
cmov
指令; - deflat指令碼預設模擬執行最多兩個分支,但真實情況可能不只兩個分支;
- 可能存在一個向前更新的陣列,依據程式執行進行更新,決定當前真實塊的跳轉【這導致angr對於該塊的模擬執行得不到正確的跳轉】
- 程式在加混淆前,已經被新增了花指令或其他處理,
程式CFG圖
已經被打破; - 某個塊存在死迴圈,會使angr符號執行卡死……
這也導致了,這個deflat指令碼的普適性較低,除了能夠處理OLLVM官方專案做的混淆,對加了其他PASS或者處理的混淆,基本用不了。
所以對於去除不了的OLLVM混淆,我們需要根據程式的實際混淆效果,對deflat指令碼進行修改,再進行去混淆。
【這也要求對deflat 指令碼比較熟悉,可以更快上手】
失敗的花指令控制流平坦化嘗試
使用程式碼混淆與反混淆學習-第一彈中加了花指令的程式,進行OLLVM控制流平坦化混淆,看看效果。
原始碼如下:
# clang 執行內聯彙編加 -fasm-blocks 或者 -fms-extensions 或者 -masm=intel
clang -mllvm -fla -mllvm -split -mllvm -split_num=3 main-call-加花.cpp -lm -fasm-blocks -o main-call-加花
# 需要對原始碼作一些修改
存在較大的問題,我的OLLVM 環境是在Ubuntu上搭建的,對於上述內聯彙編加的花指令無法編譯透過!
【或許可以在Windows 上移植OLLVM,進行編譯(好像挺難的)】
可以看到,花指令用到的標籤、$ 出現報錯。
【最終也沒解決編譯問題,或許本就不可以,ollvm 不具備這樣的處理能力,也可能是我程式碼的問題,如果部落格前的你有任何想法,歡迎與我交流】
TSCTF-J 2022-upx_revenge實戰分析
對upx_revenge
題目進行分析。
首先直接使用deflat 指令碼。
python deflat.py --file upx_revenge_test --addr 0x4016D0
發現沒有找到retn 塊。
處理多個retn塊
回到ida 檢視cfg 圖發現原因:存在其他的退出塊。
這裡需要改進deflat 指令碼,使其存在很多retn塊。
# 其他位置的retn_node,對應改為list處理
if supergraph.out_degree(node) == 0:
retn_node.append(node)
成功執行,但是去除效果不行。
去除後CFG圖 |
---|
多個comv的處理
很明顯看出,程式的真實塊間的邏輯串聯失敗,也就是重建控制流失敗。
產生原因
顯然,這裡存在2個分支,因為有兩個cmov
【相同判斷】,並且call 函式,對分支跳轉是有作用的,這裡var_CC是順序執行,動態更新的。
【deflat 指令碼只處理了執行有一個cmov
指令的情況,且hook了call函式】
【由於var_CC是順序執行,動態更新也可以看出,deflat 指令碼的模擬執行思路已經無法對真實塊的後繼進行確定了】
但這裡做個測試,不hook call 看是什麼效果。
可以知道,取消hook call 對真實塊後繼的查詢毫無影響,這是因為deflat中的模擬執行,只是基於comv
處的模擬。對前文並無任何關聯。
顯然,該deflat指令碼的無法處理了。【】
總結
angr
就upx_revenge 這道題而言,
deflat 指令碼中angr 對區域性的模擬執行顯然無法獲取真實塊間的執行順序,重建控制流顯然也無從談起。當然靜態查詢各個控制流平坦化的功能塊效果還是可以的。
那麼如何透過angr,有序的、聯絡上文地進行模擬執行,獲取真實塊的執行邏輯,顯然是關鍵點!
【??? 後續學習了,有思路再更新】
unicorn
[原創]ARM64 OLLVM反混淆-Android安全-看雪論壇-安全社群|安全招聘|bbs.pediy.com (kanxue.com)
Unicorn反混淆:恢復被OLLVM保護的程式(一) - 簡書 (jianshu.com)
使用unicorn 模擬執行框架獲取真實塊間的執行順序,重建控制流。
【還沒學過 unicorn 使用,,,】
ida
使用IDA microcode去除ollvm混淆(上) - 先知社群 (aliyun.com)
GitHub - PShocker/de-ollvm: IDA Python Script for anti ollvm
利用ida 現成的CFG 圖,以及idc 指令碼,動態執行程式,獲取真實塊的執行順序,從而恢復控制流。