OLLVM现在是经常遇到了,在学习之前我们先了解一些LLVM的知识。
LLVM是一套编译器基础设施项目,分为前端、中间层标识(IR)、后端。
前端就包括Clang、Rustc等,前端负责将对应语言的源代码转为中间层代码(IR),后端负责将IR转为特定平台的机器码或汇编代码。


Pass,翻译是通过,通过一遍IR也就是遍历IR。在遍历IR的时候进行一些操作,比如优化、插桩、混淆。Pass的通常为.so文件。
分类:
重点介绍一下functionPass,因为控制流平坦化的pass就是基于函数的
参考:https://bbs.kanxue.com/thread-279624.htm#msg_header_h2_1
可以这样理解,LLVM的pass是用来优化分析的,将这些pass的功能改为混淆代码,就是OLLVM项目。
由于后面的pass编写都是针对IR指令的,所以我们有必要对它有更进一步的了解。
IR主要有两种表现形式:
二进制
这两种文件只是表现形式不同,均可以被优化编译成可执行文件。
opt为optimizer的缩写,优化器的意思,使用opt对IR进行优化操作。
流程
test.c --> test.ll --> test_opt.ll (可选)-> test

test.sh
Test/Testprogram.cpp
Transforms/Helloworld.cpp
前面提到的在FunctionPass运行时,会对程序中的每个函数执行runOnFunction函数
CMakeLists.txt

得到的.ll文件,和汇编语言很很相似

顾名思义,就是让流程图平坦、扁平
源码
e0cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6G2j5X3k6#2M7$3y4S2N6r3!0J5i4K6u0V1L8r3I4$3L8g2)9J5c8X3!0T1k6Y4g2K6j5$3q4@1L8%4u0Q4x3V1k6T1L8r3!0T1i4K6u0r3L8r3I4$3L8g2)9J5k6o6c8Q4x3X3f1H3i4K6u0r3L8r3W2T1i4K6u0r3g2s2u0S2L8Y4y4X3L8%4u0E0M7#2)9J5c8V1!0T1k6Y4g2K6j5$3q4@1K9h3!0F1i4K6u0r3c8X3I4S2N6s2c8W2L8X3W2F1k6#2)9J5k6h3y4H3M7l9`.`.
正常的程序执行流程图

控制流平坦化之后的

下面开始控制流平坦化pass的编写
demo还是上面的Testprogram.cpp
编译成IR

对应的流程图

首先将本function中除了第一个BasicBlock的所有块保存到vector容器中,接着对bb的数量进行判断,当bb数量小于等于1时,flatten函数会直接退出并返回false。
接着通过F-begin获取本function的第一个bb,并将其从vector中擦除
获取第一个BB进行特殊处理,首先会判断结尾是不是分支指令(必须是条件分支),如果是则把跳转的两个IR指令(类似汇编语言的cmp和jz jnz)单独分离作为一个基本块
分割第一个基本块之后

对应的流程图


然后再第一个bb的末尾创建switchVar并赋予他一个随机的值,接着创建三个新的basicblock块,分别为loopEntry、loopEnd、swDefault,并设置好跳转关系
对应的流程图

下面开始将vector中的每一个bb都添加到switch-case语句中,每一个bb对应一个case

添加全部的basicblock块之后,还需要修复跳转关系,使得每个bb执行完之后,会重新设置switchVar,从而顺利跳转到下一个case。
分为三类来处理:
然后我们就能得到

对应的流程图就是

太糊了我画了一个>_<

拖入ida看看效果


下一篇的内容大概是控制流平坦化的对抗和魔改。
228K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6G2j5X3k6#2M7$3y4S2N6r3!0J5i4K6u0V1L8r3I4$3L8g2)9J5c8X3!0T1k6Y4g2K6j5$3q4@1L8%4u0Q4x3V1k6T1L8r3!0T1i4K6u0r3L8r3I4$3L8g2)9J5k6o6c8Q4x3X3f1H3i4K6u0r3L8r3W2T1i4K6u0r3g2s2u0S2L8Y4y4X3L8%4u0E0M7#2)9J5c8V1!0T1k6Y4g2K6j5$3q4@1K9h3!0F1i4K6u0r3c8X3I4S2N6s2c8W2L8X3W2F1k6#2)9J5k6h3y4H3M7l9`.`.
https://bbs.kanxue.com/thread-279624.htm
https://bbs.kanxue.com/thread-282305.htm
bd4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2U0L8X3u0D9L8$3N6K6i4K6u0W2j5$3!0E0i4K6u0r3b7X3!0T1d9s2g2S2L8X3N6Q4x3V1k6H3i4K6u0r3x3e0M7$3y4o6l9K6y4K6S2Q4x3X3g2Z5N6r3#2D9
https://bbs.kanxue.com/thread-255130.htm
https://bbs.kanxue.com/thread-266082.htm
clang -S -emit-llvm fileName.c -o fileName.ll
clang -S -emit-llvm fileName.c -o fileName.ll
clang -c -emit-llvm fileName.c -o fileName.bc
clang -c -emit-llvm fileName.c -o fileName.bc
opt -load LLVMObfuscator.so -hlw -S fileName.ll -o fileName_opt.ll
opt -load LLVMObfuscator.so -hlw -S fileName.ll -o fileName_opt.ll
clang fileName_opt.ll -o fileName
clang fileName_opt.ll -o fileName
cd ./Build
cmake ../Transforms //对transforms的项目进行编译,得到编译后的`.so`文件
make //得到pass.so
cd ../Test
clang -S -emit-llvm TestProgram.cpp -o TestProgram.ll //clang将源代码转换为中间代码
opt -load ../Build/LLVMObfuscator.so -hlw -S TestProgram.ll -o TestProgram_hlw.ll ////opt加载so文件,用hlw pass进行优化
clang TestProgram_hlw.ll -o TestProgram_hlw //将优化后的中间代码编译为可执行文件
./TestProgram_hlw //运行可执行文件
cd ./Build
cmake ../Transforms //对transforms的项目进行编译,得到编译后的`.so`文件
make //得到pass.so
cd ../Test
clang -S -emit-llvm TestProgram.cpp -o TestProgram.ll //clang将源代码转换为中间代码
opt -load ../Build/LLVMObfuscator.so -hlw -S TestProgram.ll -o TestProgram_hlw.ll ////opt加载so文件,用hlw pass进行优化
clang TestProgram_hlw.ll -o TestProgram_hlw //将优化后的中间代码编译为可执行文件
./TestProgram_hlw //运行可执行文件
int func1(int a,int b);
int main()
{
printf("%d\n",func1(1,2));
return 0;
}
int func1(int a,int b)
{
int result;
if(a>0){
result=a+b;
}
else{
result=a-b;
}
return result;
}
int func1(int a,int b);
int main()
{
printf("%d\n",func1(1,2));
return 0;
}
int func1(int a,int b)
{
int result;
if(a>0){
result=a+b;
}
else{
result=a-b;
}
return result;
}
//在此编写LLVM Pass的代码
//导入llvm所需的头文件
using namespace llvm;
//定义我们自己的命名空间
namespace{
//首先需要继承FunctionPass
class HelloWorld : public FunctionPass{ //自定义的HelloWorld类继承FunctionPass
public:
static char ID;
HelloWorld() : FunctionPass(ID) {} //HelloWorld的构造函数
bool runOnFunction(Function &F);
};
}
bool HelloWorld::runOnFunction(Function &F){
//todo 对函数的分析或修改代码
outs() << "Hello," << F.getName() << "\n"; //获取llvm的输出流
}
char HelloWorld::ID = 0;
//注册
static RegisterPass<HelloWorld> X("hlw","对Pass的描述"); //注册该Pass
//在此编写LLVM Pass的代码
//导入llvm所需的头文件
using namespace llvm;
//定义我们自己的命名空间
namespace{
//首先需要继承FunctionPass
class HelloWorld : public FunctionPass{ //自定义的HelloWorld类继承FunctionPass
public:
static char ID;
HelloWorld() : FunctionPass(ID) {} //HelloWorld的构造函数
bool runOnFunction(Function &F);
};
}
bool HelloWorld::runOnFunction(Function &F){
//todo 对函数的分析或修改代码
outs() << "Hello," << F.getName() << "\n"; //获取llvm的输出流
}
char HelloWorld::ID = 0;
//注册
static RegisterPass<HelloWorld> X("hlw","对Pass的描述"); //注册该Pass
project(OLLVM++)
cmake_minimum_required(VERSION 3.13.4)
find_package(LLVM REQUIRED CONFIG)
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
include(AddLLVM)
include_directories("./include")
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
add_llvm_library( LLVMObfuscator MODULE
src/HelloWorld.cpp
)
project(OLLVM++)
cmake_minimum_required(VERSION 3.13.4)
find_package(LLVM REQUIRED CONFIG)
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
include(AddLLVM)
include_directories("./include")
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
add_llvm_library( LLVMObfuscator MODULE
src/HelloWorld.cpp
)
基本块1
基本块2
if(condition){
基本块3
}else{
基本块4
}
基本块5
基本块6
基本块1
基本块2
if(condition){
基本块3
}else{
基本块4
}
基本块5
基本块6
基本块1
switchVar = 2;
while(true){
switch(switchVar){
case 2:
基本块2
switchVar = condition ? 3 : 4;
case 3:
基本块3
switchVar = 5
case 4:
基本块4
switchVar = 5
case 5:
基本块5
switchVar = 6
case 6:
基本块6
goto end;
}
}
end:
基本块1
switchVar = 2;
while(true){
switch(switchVar){
case 2:
基本块2
switchVar = condition ? 3 : 4;
case 3:
基本块3
switchVar = 5
case 4:
基本块4
switchVar = 5
case 5:
基本块5
switchVar = 6
case 6:
基本块6
goto end;
}
}
end:
clang -S -emit-llvm TestProgram.cpp -o TestProgram.ll
clang -S -emit-llvm TestProgram.cpp -o TestProgram.ll
vector<BasicBlock*> origBB;
//save all
for(Function::iterator i=f->begin();i!=f->end();++i){
//address of bb
BasicBlock *tmp=&*i;
origBB.push_back(tmp);
BasicBlock *bb=&*i;
//if have invoke eg:call function
if(isa<InvokeInst>(bb->getTerminator())){
return false;
}
}
//outs() << "Hello," << origBB.size() << "\n";
//printf("\nsizeof origbb\n");
if(origBB.size()<=1){
return false;
}
vector<BasicBlock*> origBB;
//save all
for(Function::iterator i=f->begin();i!=f->end();++i){
//address of bb
BasicBlock *tmp=&*i;
origBB.push_back(tmp);
BasicBlock *bb=&*i;
//if have invoke eg:call function
if(isa<InvokeInst>(bb->getTerminator())){
return false;
}
}
//outs() << "Hello," << origBB.size() << "\n";
//printf("\nsizeof origbb\n");
if(origBB.size()<=1){
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!