首页
社区
课程
招聘
[原创]控制流平坦化一篇就够了(上)
发表于: 2024-11-4 21:12 4576

[原创]控制流平坦化一篇就够了(上)

2024-11-4 21:12
4576

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文件,和汇编语言很很相似
图片描述

顾名思义,就是让流程图平坦、扁平
源码
https://github.com/obfuscator-llvm/obfuscator/blob/llvm-4.0/lib/Transforms/Obfuscation/Flattening.cpp
正常的程序执行流程图
图片描述

控制流平坦化之后的
图片描述

下面开始控制流平坦化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看看效果
图片描述
图片描述

下一篇的内容大概是控制流平坦化的对抗和魔改。

https://github.com/obfuscator-llvm/obfuscator/blob/llvm-4.0/lib/Transforms/Obfuscation/Flattening.cpp
https://bbs.kanxue.com/thread-279624.htm
https://bbs.kanxue.com/thread-282305.htm
https://www.cnblogs.com/BobHuang/p/17640378.html
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                         //运行可执行文件
#include <cstdio>
#include <cstring>
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;
}
#include <cstdio>
#include <cstring>
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所需的头文件
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"   //和输入输出有关
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所需的头文件
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"   //和输入输出有关
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
# 参考官方文档:https://llvm.org/docs/CMake.html#developing-llvm-passes-out-of-source
project(OLLVM++)                              #项目名称 OLLVM++
cmake_minimum_required(VERSION 3.13.4)      #和llvm有关的环境变量
find_package(LLVM REQUIRED CONFIG)
  
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
include(AddLLVM)
include_directories("./include") # 包含 ./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                 #注册LLVMObfuscator模块
  src/HelloWorld.cpp                                    #添加项目的源代码文件
)
# 参考官方文档:https://llvm.org/docs/CMake.html#developing-llvm-passes-out-of-source
project(OLLVM++)                              #项目名称 OLLVM++
cmake_minimum_required(VERSION 3.13.4)      #和llvm有关的环境变量
find_package(LLVM REQUIRED CONFIG)
  
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
include(AddLLVM)
include_directories("./include") # 包含 ./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                 #注册LLVMObfuscator模块
  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){

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 8
支持
分享
最新回复 (8)
雪    币: 5164
活跃值: (6916)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
最近在学习,期待下一篇
2024-11-4 21:36
1
雪    币: 650
活跃值: (3572)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
非常不错
2024-11-4 21:47
1
雪    币: 1050
活跃值: (1278)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
写得好详细,期待下一篇
2024-11-6 07:39
1
雪    币: 4869
活跃值: (3897)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
5
谢谢分享
2024-11-6 09:02
1
雪    币: 1407
活跃值: (3014)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
感谢分享
2024-11-6 10:24
1
雪    币: 1129
活跃值: (2806)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
感谢分享
2024-11-6 10:30
1
雪    币: 619
活跃值: (3094)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
期待下一篇
2024-11-8 10:59
1
雪    币: 172
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
好内容 厉害 期待大佬下一篇文章
2024-11-9 15:05
0
游客
登录 | 注册 方可回帖
返回
//