ollvm在VS2017下编译
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!