title
5-3 OLLVM简介与移植
LLVM源码外开发Pass
在LLVM源码内开发Pass,需要在LLVM源码项目中创建文件目录,才能开发Pass。
源码外开发Pass,参考:Building LLVM with CMake — LLVM 15.0.0 documentation
源码外开发时,项目的目录格式
<**project dir**>/
** **|
** CMakeLists**.txt
** <**pass name**>/**
** **|
** CMakeLists**.txt
** Pass**.cpp
** ..**.
project dir/CMakeLists.txt内容如下:
find_package(LLVM REQUIRED CONFIG)
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
add_subdirectory(
project dir/pass name/CMakeLists.txt内容如下:
add_library(LLVMPassname MODULE Pass.cpp)
编译项目
build cmake ..
build make
编译完成后,在项目build目录中,产生so文件
使用opt加载Pass到IR文件
opt –load /home/kanxue/ollvm2/outpass/build/FunctionName2/libLLVMFunctionName2.so -function helloworld.ll -o helloworld-opt.bc
Clang集成Pass
在VScode命令行中,要llvm编译一个可执行文件,首先需要加载opt产生bc文件,在通过clang编译bc文件得到可执行文件。
如何使用Clang直接编译可执行文件?
00:08:30
OLLVM介绍
参考:github.com/obfuscator-llvm/obfuscator/wiki
基于旧版本llvm源码来开发大量混淆功能的Pass,主要支持三种混淆方案
指令替换,Instructions Substitution -mllvm -sub
原理:等价替换,a=b+c** 替换为 a=b-(-c) **
%0 = load i32*** %a, align 4 **//c取出来
%1 = load i32*** %b, align 4 **//b取出来
%2 = sub i32 0, **%1 **//0-c得负数
**%3 = sub nsw i32 %0, **%2 //b-c得到a
二次变换,a=b+c** 替换为 **a=-(-b+(-c))
%0 = load i32* %a, align 4
%1 = load i32* %b, align 4
%2 = sub i32 0, %0
%3 = sub i32 0, %1
%4 = add i32 %2, %3
%5 = sub nsw i32 0, %4
随机数,r = rand (); a = b + r; a = a + c; a = a - r 对同一个随机数进行+和-操作,最终达到平衡
%0 = load i32*** %a, align **4
%1 = load i32*** %b, align **4
**%2 = add i32 %0, **1107414009
**%3 = add i32 %2, **%1
**%4 = sub nsw i32 %3, **1107414009
虚假控制流,Bogus Control Flow -mllvm -bcf
原始代码流程:
**#include **<stdlib.h>
int main(int argc**,** char** argv**)** {
** int a = atoi(argv[1]);**
** if(**a == 0)
** return 1;**
** **else
** return 10;**
** return 0;**
}
经过虚假控制流混淆后:
控制流程平坦化,Control Flow Flattening -mllvm -fla
原理:将基本块(if.else)转为switch, 通过对一个变量反复的修改,赋值。来控制进入case的顺序,从而确定执行基本块的顺序。
原始代码流程:
**#include **<stdlib.h>
int main(int argc**,** char** argv**)** {
** int a = atoi(argv[1]);**
** if(**a == 0)
** return 1;**
** **else
** return 10;**
** return 0;**
}
经过控制流平坦化处理:
OLLVM函数注解
通过函数注解可以指定任意函数执行何种混淆特性(因此三种混淆方式可以同时出现在一个程序中)
int foo() __attribute((__annotate__((“fla”))));
int foo() {
** return 2;**
}
移植OLLVM项目
为了解决ollvm项目只更新到llvm4.0版本的问题,需要把ollvm Obfuscation移植到llvm最新版本中(这里是9.0示例)
克隆ollvm项目git clone https://github.com/obfuscator-llvm/obfuscator.git** (可能需要切换分支到llvm-4.0)**
将 Obfuscation 代码目录从Obfuscation/lib/Transforms/Obfuscation** 完整的拷贝到llvm/lib/Transforms/ 目录下**
将 Obfuscation 头文件目录从Obfuscation/include/llvm/Transforms/Obfuscation** 完整的拷贝到**llvm/include/llvm/Transforms/
查看其他文件的提交信息,把有更改的根据更改内容,对当前llvm进行补充。
obfuscator/lib/Tansforms/CMakeLists.txt 中加入了新的子目录add_subdirectory(Obfuscation)
obfuscator/lib/Tansforms/LLVMBuild.txt 中的subdirectories中增加Obfuscation
obfuscator/lib/Tansforms/IPO/LLVMBuild.txt 中的required_libraries中增加Obfuscation
obfuscator/lib/Tansforms/IPO/PassManagerBuilder.cpp 查看提交记录,从最初一次提交开始,把新增/修改代码复制到llvm的PassManagerBuilder.cpp 中
Obfuscation/include/llvm/CryptoUtils.h 文件拷贝到llvm/include/llvm/Tansforms/Obfuscation/ 目录下
修改CryptoUtils.cpp中的头文件包含#include ‘llvm/CryptoUtils.h’** 为**#include ‘llvm/Transforms/Obfuscation/CryptoUtils.h’
编译时其他报错的头文件引用代码,也需要如上方式修改。
有些llvm中的API在新版本修改了函数的返回值类型,遇到编译错误时,也需要进行调整Instruction* tbb=fi->getTerminator();
有些llvm中的API在新版本中删除了,需要将未定义的函数注释掉,FunctionPass *lower = createLowerSwitchPass();lower->runOnFunction(*f);** (这将导致删除了控制流程平坦化中创建switch的作用)**
在fork的用户中,找到heroims,参考其他人修复的错误。
//PassManagerBuilder.cpp
MPM.add(createBogus(BogusControlFlow));
**#if **LLVM_VERSION_MAJOR >= 9
** MPM**.add(createLowerSwitchPass());
**#**endif
MPM.add(createFlattening(Flattening));
编译项目:ninja LLVMObfuscation
项目编译后产生的只是.a的静态库lib/libLLVMObfuscation.a
还需要编译Clang:ninja clang
生成可执行文件:clang -mllvm -sub helloworld.c -o helloworld_sub
**生成IR文件: **clang -emit-llvm -S -mllvm -sub helloworld.c -o helloworld.ll
**生成原始IR文件(未混淆): **clang -emit-llvm -S -sub helloworld.c -o helloworld.ll
混淆IR文件和原始IR文件对比,以便于认识其中的差异
obfuscator 还需要根据Pull requests修复BogusControlFlow.cpp中的一个bug#76防止编译卡死