ollvm在VS2017下編譯

  • 2020 年 12 月 7 日
  • 筆記

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文件夾

 
include文件夾

 

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

 
Obfuscation頭文件

 

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

打開與include文件夾平行的lib文件夾並進入lib\Transforms\Obfuscation目錄:

 
Obfuscation所在目錄

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

 
實現文件

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

下面介紹編譯過程

1、到//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!