ollvm在VS2017下編譯

huhuf6發表於2020-12-07

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進行註冊。

ollvm全稱obfuscator-llvm,即帶有函式混淆功能的llvm。
對於LLVM來說,其前端是clang,在編譯原始碼檔案的時候使用的編譯工具也是clang。而生成中間IR程式碼後需要對IR程式碼進行一些操作,例如新增一些程式碼混淆功能。LLVM的做法是通過編寫Pass(其實就是對應的一個個類,每個類實現不同的功能)來實現混淆的功能。所以實現混淆,其實就是編寫功能性的Pass。

開啟include資料夾

 
ollvm在VS2017下編譯
include資料夾

 

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

 
ollvm在VS2017下編譯
Obfuscation標頭檔案

 

此處是存放OLLVM專案中自己寫的pass的標頭檔案的地方,由此可知,如果我們需要些自己的pass的話,那麼對應的pass類的標頭檔案也需要在include\llvm\Transforms新建一個資料夾專門用來存放標頭檔案。標頭檔案的具體內容暫且不管,接下來再去看看實現檔案在哪裡。

開啟與include資料夾平行的lib資料夾並進入lib\Transforms\Obfuscation目錄:

 
ollvm在VS2017下編譯
Obfuscation所在目錄

開啟Obfuscation目錄,可以看到與之前的標頭檔案一一對應的實現檔案:
 
ollvm在VS2017下編譯
實現檔案

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

下面介紹編譯過程
1、到https://github.com/heroims/obfuscator/tree/llvm-9.0下載大佬移植好的ollvm,原版的ollvm只支援到4.0
2、用cmake生成ollvm的VS2017專案檔案,命令如下
cmake -G "Visual Studio 15 2017" -A x64 -Thost=x64 -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF ../ollvm資料夾目錄
3、用vs2017開啟llvm.sln,對clang和clang-format進行編譯,用release編譯,加快速度
4、編譯完成後,將relase下的bin和lib複製到ndk目錄中的\android-ndk-r21b\toolchains\llvm\prebuilt\windows-x86_64,對lib和bin進行替換,替換前將原lib和bin檔案進行備份
5、上述步驟完成後,用ue4對專案進行打包確認無誤即可
檢測clang的版本

 

 OK!



相關文章