0x1,首先介紹一下編譯環境配置
1、UE4.25
2.vs2017(15.9),注:2019編譯總是出現錯誤
3、cmake3.18.5,cmake的作用是為ollvm源碼編譯成適合於在vs2017上能夠進行編譯的項目解決文件。
4、andriod studio,最新版就行,為apk打包提供環境。
5、IDA,檢查函數混淆的結果。
6、NDK r21b,andriod沒有集成ndk,需要手動安裝並且在ue4中指定路徑。
7、需要注意的是llvmbuild需要Python 2.x,如果你只有或CMake優先選擇了Python 3.x,則會導致以下錯誤:
ModuleNotFoundError: No module named 'llvmbuild'
0x2,ollvm簡介
在介紹ollvm之前,需要先了解一下llvm
LLVM(low level virtual machine)從本質上來說,是一個開源編譯器框架,能夠提供程序語言的編譯期優化、鏈接優化、在線編譯優化、代碼生成。LLVM有兩個特點:
(1)LLVM有一個特定指令格式的IR語言,我們可以通過書寫Pass來對其IR進行優化。
(2)可以作為多種語言的后端,提供與編程語言無關的優化和針對多種CPU的代碼生成功能。
LLVM主要由Clang前端、IR優化器(Pass)和LLVM后端構成。其功能分別是:
clang前端:將平台相關的源碼生成與平台無關的IR(llvm Bitcode)。
IR優化器:主要對IR進行優化。
llvm后端:將優化后的IR轉換為與平台相關的匯編代碼或者機器碼。
Clang前端以.c文件為輸入,經語法詞法分析后解析為抽象語法數,最后通過LLVM內聯API變為LLVM IR。其功能為:詞法分析器:把輸入的程序代碼切成token;語法分析器:接收token流解析為AST。
gcc和clang的區別
GCC特性:除支持C/C++/ Objective-C/Objective-C++語言外,還是支持Java/Ada/Fortran/Go等;當前的Clang的C++支持落后於GCC;支持更多平台;更流行,廣泛使用,支持完備。
Clang特性:是一個C、C++、Objective-C和Objective-C++編程語言的編譯器前端。它采用了底層虛擬機(LLVM)作為其后端。它的目標是提供一個GNU編譯器套裝(GCC)的替代品。編譯速度快;內存占用小;兼容GCC;設計清晰簡單、容易理解,易於擴展增強;基於庫的模塊化設計,易於IDE集成;出錯提示更友好。
IR優化器:
LLVM IR包含三種格式:一種是在內存中的編譯中間語言;一種是硬盤上存儲的二進制中間語言(以.bc結尾),最后一種是可讀的中間格式(以.ll結尾)。這三種中間格式是完全相等的。LLVM IR是LLVM優化和進行代碼生成的關鍵。根據可讀的IR,我們可以知道再最終生成目標代碼之前,我們已經生成了什么樣的代碼。我們通過Pass來對IR進行相應的優化。
文本格式如下:
define i32 @add1(i32 %a, i32 %b) { entry: %tmp1 = add i32 %a, %b ret i32 %tmp1 } define i32 @add2(i32 %a, i32 %b) { entry: %tmp1 = icmp eq i32 %a, 0 br i1 %tmp1, label %done, label %recurse recurse: %tmp2 = sub i32 %a, 1 %tmp3 = add i32 %b, 1 %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3) ret i32 %tmp4 done: ret i32 %b
llvm后端
Llvm clang編譯器主要是將各平台源代碼編譯成與平台無關的IR指令集,這將支撐對IR的優化及轉換操作,而llvm后端的主要工作是優化IR指令,並將這些與平台無關的IR指令轉換成目標設備相關的指令。
由上圖所示,LLVM IR進入后端要經過pass優化,指令選擇,指令調度,寄存器分配,代碼布局優化以及匯編發行等過程。上述各過程都是pass優化的過程,普通(白色)pass可由用戶自定義,內置(灰色)pass由一系列小的pass構成,換句話說我們可以對每一個階段都可以進行不同程度的優化。同時無須為每個目標平台編寫重復的代碼。
LLVM的pass均用C ++類編寫,用戶編寫的Pass都繼承於內置的父Pass
類,然后重新父類的某個方法(即虛函數)。大多數pass都寫在一個 .cpp
文件中,並且它們的類的子Pass
類是在匿名名稱空間中定義的(這使其對定義文件完全私有)。並且在外部定義pass ID(用於識別pass),以及對Pass進行注冊。
打開include文件夾

其實從文件夾名稱就能判斷include文件夾是頭文件所在的地方,include文件夾之下包含兩個文件夾:llvm和llvm-c。
llvm文件夾下有如下目錄:llvm\Transforms\Obfuscation
,可以看到此文件夾下有一些頭文件:

此處是存放OLLVM項目中自己寫的pass的頭文件的地方,由此可知,如果我們需要些自己的pass的話,那么對應的pass類的頭文件也需要在include\llvm\Transforms
新建一個文件夾專門用來存放頭文件。頭文件的具體內容暫且不管,接下來再去看看實現文件在哪里。
打開與include
文件夾平行的lib
文件夾並進入lib\Transforms\Obfuscation
目錄:

打開
Obfuscation
目錄,可以看到與之前的頭文件一一對應的實現文件:

至此,與我們編寫自己的pass一樣,在
include\llvm\Transforms\Obfuscation
定義頭文件,在
lib\Transforms\Obfuscation
寫實現文件。這樣,我們就明白了該如何開始寫自己的項目。不過要注意的是,不管是LLVM還是OLLVM,它們都是通過編寫makefile來實現項目的運行的,所以我們得熟練掌握makefile的編寫與依賴,才能玩轉自己的項目。
下面介紹編譯過程

NDK整合:
將編譯后的relase下lib和bin目錄中的clang、clang++、clang-format替換掉ndk llvm中bin的同名文件
刪除llvm中的lib64,將lib文件夾中的文件復制到llvm的lib中即可(可能是因為我們自己編譯的llvm鏈接的庫的目錄在相對路徑的lib下)
OK!