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
/
/
运行可执行文件
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
){
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!