近來,ollvm在國內移動安全,尤其是安全加固上的使用越來越廣泛,ollvm的混淆和反混淆也被視為比較高等的知識之一,讓很多人感到無從下手,望塵莫及。如果你在google上搜索ollvm,你會發現第一頁都是中文的搜索結果。其實,llvm和ollvm在國外是比較傳統的東西,說到底也只是C++代碼,難度大概等同於ART系統源碼的程度。
本篇文章目地是為了從另一個從未出現的角度來讓一個完全不懂llvm的新手快速上手ollvm,大神請直接跳過。
注意:本文因角度不同,若引起誤會,純屬個人理解不同,本人不會作出任何解釋,請諒解。
1. 快速理解llvm、clang和ollvm的概念
我們不使用網上那些冗雜的闡述,一句話概括,llvm是一個完整的編譯器架構,作用可以理解為制作一個編譯器,llvm先將源碼生成為與目標機器無關的LLVMIR代碼,然后把LLVMIR代碼先優化,再向目標機器的匯編語言而努力。經典編譯器都可以分為前端、中層優化和后端:
我們從上圖也理解了clang,是前端的一個套件,但在實際使用時,你會感覺到,我們只可以感受到clang,也只是在使用clang,因為編譯的時候,是調用clang或clang++來編譯源碼。Ollvm呢?是基於LLVM的代碼分支的代碼混淆,對誰混淆?什么時候混淆?在中間表示IR層,通過編寫pass(英文翻譯:經過,自己理解,也不需要知道其正規的概念)來混淆IR,這樣目標機器的匯編語言也就被混淆了:
當然,這里需要知道的是,即使不用ollvm,LLVM本身也是有很多pass的,我們這樣簡單的理解,LLVMIR本身是一種與目標機器無關的虛擬化代碼,而在轉化為真實匯編代碼時,肯定要刪除一些虛擬的東西,自然也需要pass。這樣,我們就用不到200字的闡述完成了網上很多長篇大論才能達到的理解。
2、llvm與ollvm在移動加固的發展
在開始制作安卓vmp時,llvm其實是被我們暫時放棄的一個方案。因為從smali上想可以使用llvm,還必須先克服smali到底怎么成為C/C++,當時他被提及,很大原因是安卓系統后端使用了llvm,但其實ART下和llvm關系已經不大了。當時都急於上架vmp,因此大部分都是采用折衷策略,如自定義dex結構、置換指令等。隨着發展,smali2c漸漸成熟,可以smali2c了,自然ollvm是必然的一個選擇。但一個新的問題出現了,就像很多加固網站,需要提供源碼,或編譯過程中的中間文件,說到底還是源碼加固。
所以想做二進制加固的ollvm分支,需要注意一下。想做到二進制加固,沒有一個自己的反匯編解析引擎是不可能的,目前用capstone比較多。但要真正做自己的二進制VMP加固,首先你得有一個反匯編引擎能把指令抽出來,同時轉為了自己的虛擬指令,如果接上了llvm,最完美的方案就是把LLVMIR給虛擬為自己的虛擬指令,這個難度相當於把.netframwork的IL指令給虛擬了。可以先逃避了這兩個問題,ARM指令得到后,利用簡單的函數式指令解析來完成這個虛擬過程。我們認為,如果只有LLVMIR,如ollvm,是沒有虛擬的,只是混淆。
3、利用beyondcompare+sourceinsight4的ollvm快速上手
Ollvm如果你去網上搜索資料學習,大概是這么幾種文章:“LLVM編譯器架構王者-編寫簡單的C虛擬編譯器”、“ollvm+ndk編譯環境的搭建”、“從0開始學習LLVM”、“LLVM PASS的完整編寫”,當然這些文章都很不錯。但對於一個新手來說,看了和沒看其實區別不大。因為我們要學習ollvm,必須先抓住關鍵,到底一個llvm是如何變為ollvm,這是最簡單,最直觀的學習方式。
我們去下載一份llvm4.0源碼(官網http://releases.llvm.org/4.0.1/llvm-4.0.1.src.tar.xz)和ollvm源碼(https://github.com/obfuscator-llvm/obfuscator/tree/llvm-4.0),之所以使用4.01,因為4.0.0是2017.03的,而4.01相對時期接近一點。然后拉入beyondcompare,這里把會話設置取消勾選“比較時間戳”,以及把比較設置為“僅文件”,時間戳因為比較時間不是我們的重點,而之所以不采用原先的文件結構比較,是因為兩者都有一些空文件夾,沒有比較的意義。如圖:
看來主要的差別就在這些文件夾里了。先看最外層的CMakeLists.txt(整個工程使用cmake編譯)等3個文件:基本都是加入了產品的一些信息和協議:
主要是其他四個文件夾:
utils文件夾:主要是Revision的細微差別
tools文件夾:ollvm中有clang文件夾,也可以看出clang是作為工具在llvm中存在,而llvm中並沒有此文件夾,在llvm官網中clang是作為單獨源碼而存在
include文件夾(整個文件夾內都是.h文件夾):
Ollvm多了llvm\Transforms\obfuscation文件夾,可以看出ollvm添加了一些功能頭文件,一共6個文件。其中有5個我們可以理解為具體參數功能,fla 參數表示使用控制流平展(Control Flow Flattening)模式,sub參數表示使用指令替換(Instructions Substitution)模式,bcf參數表示使用控制流偽造(Bogus Control Flow)模式,aesSeed參數表示aes加密隨機種子,split參數表示分離代碼塊,我們常用的是sub\bcf\fla。混淆參數代碼我們在接下來分析,先看Utils.h文件
Utils.h(功能箱):
其實這里也可以看出來,ollvm基於了llvm,使用了llvm的頭文件,這些頭文件也在該include文件夾。
lib文件夾(\lib\Transforms):
這里稍微麻煩點,我們還是從最外層的兩個文件分析,主要添加了obfuscation目錄的編譯和構建:
CMakeLists.txt:
LLVMBuild.txt:
再來看IPO文件夾的兩個文件:
LLVMBuild.txt:
PassManagerBuilder.cpp(從文件名可以看出,pass管理生成,ollvm就是寫pass),我們分三部分解析:
導入ollvm特有的頭文件
添加混淆參數flag:
首先進行全局aes(aesSeed)隨機種子密碼初始化,接着把ollvm混淆功能函數(split\fla\bcf\sub)添加進去,而添加的這些函數,都可以在上面include文件夾的頭文件找到,
BogusControlFlow.h、Flattening.h、Substitution.h、CryptoUtils.h等。
以BogusControlFlow為例來看:
再以CryptoUtils為例來看:
最后以Split為例來看:
Split.h(分離基礎塊,這里可能對於BasicBlock基礎塊不太明白,就簡單理解為代碼塊,也可以推測LLVM基於的是模塊):
如果存在一些疑惑,請仔細對比一看,是不是全部對上了?
最后一個文件夾,obfuscation文件夾,里面就是具體的CPP代碼了,是不是要加點,然后,,,
當然我們現在還是沒有接觸到具體的代碼,但對於以上的邏輯分析清楚特別重要,為以后我們自己的pass添加鋪路。下面以BogusControlFlow.cpp為例,來看看pass的編寫。
********************************************************
我們使用sourceinsight4可以非常清晰的看到該代碼的邏輯組成
先回到BogusControlFlow.h,看到了pass.h
接着來到BogusControlFlow.cpp的llvm pass代碼處,而createBogus函數,我們在前面的PassManagerBuilder.cpp是見到過的。
往下跟,BogusControlFlow是一個結構體,跟到runOnFunction,
runOnFunction調用了bogus函數
bogus調用了addBogusFlow函數
addBogusFlow函數意為添加假流程,看來這里就是比較核心的函數實現了
其實英文寫的都是非常清楚的,現在翻譯挺智能的,意思為:
這里意思大概應該結合着前面學習都理解了,除了phi節點,對數據結構有點了解的朋友都知道節點,節點就有前驅和后續節點,直接看代碼理解,
但在真實代碼中,phi並不存在,因此我們要繼續消除phi,這也是轉為真實指令的重要一步,對於以上代碼,我們可以如下消除:
bb就是我們剛剛見到的BasicBlock,這樣我們就消除了phi節點,同時更進一步理解了addBogusFlow函數的意思,LLVM中使用reg2mem pass來對phi進行消除。至此,一切又回到了我們最初對LLVM的介紹。
七少月 2018.04
圖片處理真麻煩,直接上網盤:鏈接:https://pan.baidu.com/s/1O1y9Kdax9w_5m6CeiX7cZw密碼: kh96