Ubuntu 20.04
CLion2022.02
llvm 9.0.1
ubuntu的镜像我这里就不提供了,直接去官网下载就好啦
clion我这里有一篇pojie文章分享给大家:CLion_pojie
然后llvm大家直接去官网下载就好啦 llvm官网
编译llvm
(这里我插一句,大家一定不要相信llvm官网的鬼话,说的用windows上的visual studio也可以编译,用他的那个powershell,呵呵,之后你就会遇到各种各样奇奇怪怪的错误,还是用linux系统吧)
第一步用cmake生成相关的构建文件
其中-G Ninja参数表示生成Ninja系统的构建文件,采用Ninja系统会有比较快的编译速度
-DCMAKE_BUILD_TYPE=RELEASE表示生成Release版本的LLVM,这种构建方式会进行优化,并且生成的目标文件体积会更小
-DLLVM_TARGETS_TO_BUILD="X86"表示编译的目标平台是X86平台
-DLLVM_ENABLE_PROJECTS="clang"表示我们除了编译LLVM以外,还要编译clang
然后运行编译命令 ninja
这一步会等待很长时间,cpu基本处于满载状态:
按照这样的顺序编译完成的环境中,clang应该在这个目录下:
这样clang的环境就弄好了
按照我之前发的pojie文章安装好clion之后直接打开这个CMakelists文件,按照project的方式打开:
然后导入文件就好啦,然后再cmake的设置中加入debug和release:
输入 -G Ninja -DLLVM_ENABLE_PROJECTS="clang"
就会生成两个文件:
之后要从这两个文件的目录中编译出so文件,然后用opt命令操作
然后按照llvm官网的write pass的文章中,在这个目录下编写cpp和txt文件:
再添加一下:
最后用ninja编译一下这个工程,建议编译release版本,速度快:
如果能生成这个so文件,恭喜您,环境搭建完成
(windows的铁粉在win上搭环境弄了三天最后还是g了!)
我这里贴一下CLion中的cpp的代码是怎么实现函数加密的吧:
还有对应的CMakeList文件:
然后使用ninja命令,就能生成so文件(作为函数加密的pass):
在这个路径下会找到生成的so文件
然后新建一个文件夹作为测试空间
将clang的路径添加到文件夹的环境变量中:
首先新建一个文件夹,编写c文件(作为测试文件),然后生成ll文件,
这里我总结了几个命令:
这个是hello.c文件:
然后利用上面的命令生成ll文件
到这里说明我们的文件夹的环境配置好了
然后接下来该使用opt了,(opt就是把刚刚编写的cpp文件生成的so文件加载编译的一个llvm工具)(所以刚刚的cpp文件中就应该实现对函数的加密),我这里遇到了一个问题,虽然设置了环境变量,但是opt不能用,所以直接从变量路径中拖出来opt使用也可以的:
干脆直接拖吧,这样还省事
这里用哪个编译出来的clang和opt可以
location1
我又在release里面编译了一遍,所以之后的路径是
location2
这样就能生成bc文件
然后我们就可以发现对应的函数名称发生了变化
然后使用clang编译一下:
程序正常执行
然后用ida看看函数加密情况
发现函数加密成功!
贴一下cpp代码:
然后ninja编译一下,生成so文件
还是用刚刚的opt-load命令:
发现main函数的名字已经被md5加密了
这里说一句,刚刚的cpp文件把main函数的名字给md5加密了,这样是不可以的:
所以刚刚的cpp文件应该修改这一处的文件
修改完之后就没有问题了,将除了main函数以外的所有的函数的名字都进行了md5加密
然后尝试在源码之外开发一个pass,因为这样一直使用源码开发很麻烦:developing-llvm-passes-out-of-source
按照官方文档给的结构创建一个工程
创建完工程后将工程导入clion中,然后根据报错信息一个个的改就好啦
这里学习了几个linux的命令,纪录一下:
在某个目录下找文件
找到该文件的绝对路径:
我这里贴一下clion中的代码和工程目录吧:
CMakeLists.txt
EncodeFunctionName.cpp和原来一样
CmakeLists.txt
这两个cmakelists要根据自己的错误来配置,路径和版本是不一样的
然后build一下,生成so文件
然后继续之前的操作:
这里不知道出了个什么问题,非要让我用encode2执行命令
这样每次输入命令很麻烦所以接下来尝试把pass注册到clang里面去
首先创建一个.h的头文件:
然后在cpp文件中实现这个函数,并且导入相对应的头文件:
.a是静态库文件
.so是一个共享的库
接下来我们要把pass编译成一个静态库也就是一个.a文件
修改EncodeFucntionName文件夹中的两个文件
然后修改ipo文件夹中的文件
在passermanagerbuilder.cpp里面加入头文件然后文件中加入参数
在llvmbuild.txt文件中加入EncodeFunctionName
最后我贴上Encode_Function_name.cpp的代码:
这样所有的修改就ok了
然后用ninja编译一下release版本的就可以了
成功编译出.a文件
然后重新编译一下clang
然后用clang编译一下
(就能达到和之前一样的效果)
这里总结一下思路:
就是把之前的opt -load xxxx.pass -encode命令替换成了clang,也就是我们之前说的将llvmpass集成到clang里面,也就是说只用clang就能实现函数加密,所以我们要先用ninja编译一下clang
首先从git上下载第四次分支的代码
然后复制代码obfuscation到llvm/lib/Tranforms和llvm/include/lib/Tranforms
然后对着ollvm一个个的改,这里不是重点,就不一一介绍了
然后再修改代码的过程中,有一个AESsead需要注意一下:
这个是随机生成的种子,如果种子一样的画,就会生成一样的方案
如果是随机的种子,那么编译出来的方案也是随机的
然后修改完源码会由于环境版本不匹配问题产生很多错误,一个个的改就好啦google上github上面都有,放个链接吧
这样就是编译成功了
然后再编译一下clang:
这样所有的环境搭建好之后,看看官网上面的例子
前面三种都是混淆用的
最后一个选项是自己创建函数选择要不要混淆函数
看第一个选项再官网中的描述,也就是其他文章中的指令替换
前两种混淆再ida中都可以自动的帮我们优化掉,然后加了随机数之后ida就不行了
然后编写c文件作为测试用例:
然后用刚刚生成的clang(里面已经集成及ollvm的pass)编译一下:
从编译的结果并看不出来什么不同
然后拖入ida7.5看看
指令只有这一处发生了变化:
add eax, 8
变成了:
sub eax, 8BAA3A92h
add eax, 8
add eax, 8BAA3A92h
如果看伪c代码两个的结果是一样的
然后增加混淆力度看看:
拖入ida中看看:
贴一下c源码:
执行一下混淆命令:
然后拖入ida看看
先看看bcf_loop=1的时候:
他就比源程序混淆出来一个while语句(然后我刚刚动态调试的时候发现x,y的值都是0,所以这个while分支是不成立的):
这个时候还能看看,然后加到3:
发现他的混淆力度还是可以的一条if语句出来这么多分支
然后要是动态跟着一路走过去发现,所有的while语句都只判断了前半部分,剩下的都没执行
走到了最后函数的返回值是10,然后返回linux虚拟机看看返回值,确实是10
这一大堆不成立的while语句就是其他文章中的虚假控制流吧
我给大家贴一下程序的执行流程吧:
然后一直到这里才是我们程序的原始的分支
然后又是虚假控制流:
一直到这里程序才执行完:
混淆到9的时候直接出不来了xswl:
然后改成999:
然后如果想看伪c代码的话也会耗费很长时间:
bcf:
太6了
然后我们会发现一个规律,就是无论你bcf_loop到多少,总会有一段或者两端的while1的内容来恶心你:
如果要是混淆用到这个手法的话,可以用动态调试的方法来跟着程序执行,不过这样比较慢,应为许多不执行的while语句(但是ida不这么认为啊)也会跟到程序的执行过程中,所以我想出来了一种跟踪变量的方法,可以正着跟,也可以从函数的返回值出发:
这里我简单的说一下我的想法,当然网上也有很多别的更好的方法,一会会说一下unicorn模拟执行,我这个跟踪变量的方法只能用于比较简单的程序:
先从v19-》v22
然后往上跟,发现了两个值,正好是我们刚刚源程序中写的值1,10:
然后这样一直网上跟也能发现程序的逻辑,不过这种方法比较恶心,还是学习一下其他大佬的unicorn方法吧,还有符号执行方法
然后这个虚假控制流还可以指定混淆的百分比:
发现他的混淆力度也是很强的:
看官方文档介绍就是将if-else分支的基本块,转化成switch的基本快
我们这里还是用官网中给的c代码:
执行一下混淆代码:
然后拖入ida看看:
混淆的也比较明显了
ida中识别的switch-case语句不是很准确
然后生成一下.ll文件看看程序流程就比较明显了:
代码如下:
然后加一些参数看看:
然后把混淆力度加到三看看:
cfg:
其实也没有很复杂这样看来
然后还可以指定函数来进行混淆:
然后输入命令:
看看cfg:
我们会发现,main函数没有被混淆
然后这个foo函数被混淆了
然后融合所有科技来看看:
可见混淆的力度是很大的,编译过程中都花费了很多时间
还有clang的调试啥的,字数太多了,打字都卡了
最后感谢一下imyang大佬的讲解
还有就是,一定不要删快照,mdzz,删了一个快照都不能用了呜呜呜
cmake
-
G Ninja
-
DCMAKE_BUILD_TYPE
=
RELEASE
-
DLLVM_TARGETS_TO_BUILD
=
"X86"
-
DLLVM_ENABLE_PROJECTS
=
"clang"
-
DLLVM_OPTIMIZED_TABLEGEN
=
ON ..
/
llvm
cmake
-
G Ninja
-
DCMAKE_BUILD_TYPE
=
RELEASE
-
DLLVM_TARGETS_TO_BUILD
=
"X86"
-
DLLVM_ENABLE_PROJECTS
=
"clang"
-
DLLVM_OPTIMIZED_TABLEGEN
=
ON ..
/
llvm
using namespace llvm;
namespace {
struct EncodeFunctionName : public FunctionPass {
static char
ID
;
EncodeFunctionName() : FunctionPass(
ID
) {}
bool
runOnFunction(Function &F) override {
errs() <<
"Encode Function Name: "
;
if
(F.getName().compare(
"test_hello1"
)
=
=
0
){
F.setName(
"kanxue function"
);
}
errs()<<F.getName()<<
'\n'
;
return
false;
}
};
}
char EncodeFunctionName::
ID
=
0
;
static RegisterPass<EncodeFunctionName> X(
"Encode"
,
"Encode Function Name Pass"
,
false
/
*
Only looks at CFG
*
/
,
false
/
*
Analysis Pass
*
/
);
static llvm::RegisterStandardPasses Y(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
llvm::legacy::PassManagerBase &PM) { PM.add(new EncodeFunctionName()); });
using namespace llvm;
namespace {
struct EncodeFunctionName : public FunctionPass {
static char
ID
;
EncodeFunctionName() : FunctionPass(
ID
) {}
bool
runOnFunction(Function &F) override {
errs() <<
"Encode Function Name: "
;
if
(F.getName().compare(
"test_hello1"
)
=
=
0
){
F.setName(
"kanxue function"
);
}
errs()<<F.getName()<<
'\n'
;
return
false;
}
};
}
char EncodeFunctionName::
ID
=
0
;
static RegisterPass<EncodeFunctionName> X(
"Encode"
,
"Encode Function Name Pass"
,
false
/
*
Only looks at CFG
*
/
,
false
/
*
Analysis Pass
*
/
);
static llvm::RegisterStandardPasses Y(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
llvm::legacy::PassManagerBase &PM) { PM.add(new EncodeFunctionName()); });
ninja LLVMEncodeFunctionName
ninja LLVMEncodeFunctionName
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/lib/LLVMEncodeFunctionName.so'
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/lib/LLVMEncodeFunctionName.so'
export PATH
=
/
home
/
linuxer
/
llvm
/
llvm_source_code
/
llvm
/
cmake
-
build
-
release
/
bin
:$PATH
export PATH
=
/
home
/
linuxer
/
llvm
/
llvm_source_code
/
llvm
/
cmake
-
build
-
release
/
bin
:$PATH
clang
-
emit
-
llvm
-
S hello_clang.c
-
o hello_clang.ll
lli hello_clang.ll
llvm
-
as hello_clang.ll
-
o hello_clang.bc
llc hello_clang.bc
-
o hello_clang.s
clang hello_clang.s
-
o hello_clang_s
llvm
-
dis hello_clang.bc
-
o hello_clang_re.ll
clang
-
emit
-
llvm
-
S hello_clang.c
-
o hello_clang.ll
lli hello_clang.ll
llvm
-
as hello_clang.ll
-
o hello_clang.bc
llc hello_clang.bc
-
o hello_clang.s
clang hello_clang.s
-
o hello_clang_s
llvm
-
dis hello_clang.bc
-
o hello_clang_re.ll
void test_hello2();
void test_hello1() {
printf(
"test_hello1\r\n"
);
}
int
main(
int
argc, char const
*
argv[])
{
if
(argc >
2
) {
printf(
"hello kanxue clang!\r\n"
);
}
else
{
printf(
"hello pediy clang!\r\n"
);
}
test_hello1();
test_hello2();
return
0
;
}
void test_hello2() {
printf(
"test_hello2\r\n"
);
}
void test_hello2();
void test_hello1() {
printf(
"test_hello1\r\n"
);
}
int
main(
int
argc, char const
*
argv[])
{
if
(argc >
2
) {
printf(
"hello kanxue clang!\r\n"
);
}
else
{
printf(
"hello pediy clang!\r\n"
);
}
test_hello1();
test_hello2();
return
0
;
}
void test_hello2() {
printf(
"test_hello2\r\n"
);
}
'/home/linuxer/llvm/build/bin/'
'/home/linuxer/llvm/build/bin/'
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/bin/'
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/bin/'
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/bin/opt'
-
load
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/lib/LLVMEncodeFunctionName.so'
-
Encode hello_clang.ll
-
o hello_clang_encode.bc
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/bin/opt'
-
load
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/lib/LLVMEncodeFunctionName.so'
-
Encode hello_clang.ll
-
o hello_clang_encode.bc
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/bin/clang'
hello_clang_encode.bc
-
o hello_clang_encode
然后执行一下:
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/bin/clang'
hello_clang_encode.bc
-
o hello_clang_encode
然后执行一下:
using namespace llvm;
namespace {
struct EncodeFunctionName : public FunctionPass {
static char
ID
;
EncodeFunctionName() : FunctionPass(
ID
) {}
bool
runOnFunction(Function &F) override {
errs() <<
"Encode Function Name: "
<<F.getName()<<
" -> "
;
if
(F.getName().compare(
"main"
)
=
=
0
){
llvm::MD5 Hasher;
llvm::MD5::MD5Result
Hash
;
Hasher.update(
"kanxue_"
);
Hasher.update(F.getName());
Hasher.final(
Hash
);
SmallString<
32
> HexString;
llvm::MD5::stringifyResult(
Hash
, HexString);
F.setName(HexString);
}
errs()<<F.getName()<<
'\n'
;
return
false;
}
};
}
char EncodeFunctionName::
ID
=
0
;
static RegisterPass<EncodeFunctionName> X(
"Encode"
,
"Encode Function Name Pass"
,
false
/
*
Only looks at CFG
*
/
,
false
/
*
Analysis Pass
*
/
);
static llvm::RegisterStandardPasses Y(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
llvm::legacy::PassManagerBase &PM) { PM.add(new EncodeFunctionName()); });
using namespace llvm;
namespace {
struct EncodeFunctionName : public FunctionPass {
static char
ID
;
EncodeFunctionName() : FunctionPass(
ID
) {}
bool
runOnFunction(Function &F) override {
errs() <<
"Encode Function Name: "
<<F.getName()<<
" -> "
;
if
(F.getName().compare(
"main"
)
=
=
0
){
llvm::MD5 Hasher;
llvm::MD5::MD5Result
Hash
;
Hasher.update(
"kanxue_"
);
Hasher.update(F.getName());
Hasher.final(
Hash
);
SmallString<
32
> HexString;
llvm::MD5::stringifyResult(
Hash
, HexString);
F.setName(HexString);
}
errs()<<F.getName()<<
'\n'
;
return
false;
}
};
}
char EncodeFunctionName::
ID
=
0
;
static RegisterPass<EncodeFunctionName> X(
"Encode"
,
"Encode Function Name Pass"
,
false
/
*
Only looks at CFG
*
/
,
false
/
*
Analysis Pass
*
/
);
static llvm::RegisterStandardPasses Y(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
llvm::legacy::PassManagerBase &PM) { PM.add(new EncodeFunctionName()); });
find .
-
name
"LLVMConfig.cmake"
find .
-
name
"LLVMConfig.cmake"
readlink
-
f .
/
lib
/
cmake
/
llvm
/
LLVMConfig.cmake
readlink
-
f .
/
lib
/
cmake
/
llvm
/
LLVMConfig.cmake
add_library( LLVMEncodeFunctionName2 MODULE
EncodeFunctionName.cpp
)
add_library( LLVMEncodeFunctionName2 MODULE
EncodeFunctionName.cpp
)
cmake_minimum_required(VERSION
3.23
)
project(OUTPASS)
set
(LLVM_DIR
/
home
/
linuxer
/
llvm
/
llvm_source_code
/
llvm
/
cmake
-
build
-
release
/
lib
/
cmake
/
llvm
/
)
find_package(LLVM REQUIRED CONFIG)
list
(APPEND CMAKE_MODULE_PATH
"${LLVM_CMAKE_DIR}"
)
include(AddLLVM)
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
add_subdirectory(EncodeFunctionName2)
cmake_minimum_required(VERSION
3.23
)
project(OUTPASS)
set
(LLVM_DIR
/
home
/
linuxer
/
llvm
/
llvm_source_code
/
llvm
/
cmake
-
build
-
release
/
lib
/
cmake
/
llvm
/
)
find_package(LLVM REQUIRED CONFIG)
list
(APPEND CMAKE_MODULE_PATH
"${LLVM_CMAKE_DIR}"
)
include(AddLLVM)
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
add_subdirectory(EncodeFunctionName2)
linuxer@ubuntu:~
/
llvm
/
study_llvm
/
01
$
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/bin/opt'
-
load
'/home/linuxer/llvm/study_llvm/02/outPass/cmake-build-release/EncodeFunctionName2/LLVMEncodeFunctionName2.so'
-
encode2
hello_clang.ll
-
o hello_clang_encode.bc
EncodeFunctionName22: test_hello1
-
>
9c119247aefaa12cdd417eb3d57d5b2a
EncodeFunctionName22: main
-
> main
EncodeFunctionName22: test_hello2
-
> af0aaac8b98b759ace7b9eacbd2238a6
linuxer@ubuntu:~
/
llvm
/
study_llvm
/
01
$
'/home/linuxer/llvm/llvm_source_code/llvm/cmake-build-release/bin/opt'
-
load
'/home/linuxer/llvm/study_llvm/02/outPass/cmake-build-release/EncodeFunctionName2/LLVMEncodeFunctionName2.so'
-
encode2
hello_clang.ll
-
o hello_clang_encode.bc
EncodeFunctionName22: test_hello1
-
>
9c119247aefaa12cdd417eb3d57d5b2a
EncodeFunctionName22: main
-
> main
EncodeFunctionName22: test_hello2
-
> af0aaac8b98b759ace7b9eacbd2238a6
using namespace llvm;
namespace {
struct EncodeFunctionName : public FunctionPass {
static char
ID
;
bool
enable_flag;
EncodeFunctionName() : FunctionPass(
ID
) {}
EncodeFunctionName(
bool
flag) : FunctionPass(
ID
) {enable_flag
=
flag;}
bool
runOnFunction(Function &F) override {
errs() <<
"Encode Function Name: "
<<F.getName()<<
" -> "
;
if
(enable_flag){
if
(F.getName().compare(
"main"
) !
=
0
){
llvm::MD5 Hasher;
llvm::MD5::MD5Result
Hash
;
Hasher.update(
"kanxue_"
);
Hasher.update(F.getName());
Hasher.final(
Hash
);
SmallString<
32
> HexString;
llvm::MD5::stringifyResult(
Hash
, HexString);
F.setName(HexString);
}
}
errs()<<F.getName()<<
'\n'
;
return
false;
}
};
}
char EncodeFunctionName::
ID
=
0
;
static RegisterPass<EncodeFunctionName> X(
"Encode"
,
"Encode Function Name Pass"
,
false
/
*
Only looks at CFG
*
/
,
false
/
*
Analysis Pass
*
/
);
static llvm::RegisterStandardPasses Y(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
llvm::legacy::PassManagerBase &PM) { PM.add(new EncodeFunctionName()); });
Pass
*
llvm::createEncodeFunctionName(
bool
flag){
return
new EncodeFunctionName(flag);}
using namespace llvm;
namespace {
struct EncodeFunctionName : public FunctionPass {
static char
ID
;
bool
enable_flag;
EncodeFunctionName() : FunctionPass(
ID
) {}
EncodeFunctionName(
bool
flag) : FunctionPass(
ID
) {enable_flag
=
flag;}
bool
runOnFunction(Function &F) override {
errs() <<
"Encode Function Name: "
<<F.getName()<<
" -> "
;
if
(enable_flag){
if
(F.getName().compare(
"main"
) !
=
0
){
llvm::MD5 Hasher;
llvm::MD5::MD5Result
Hash
;
Hasher.update(
"kanxue_"
);
Hasher.update(F.getName());
Hasher.final(
Hash
);
SmallString<
32
> HexString;
llvm::MD5::stringifyResult(
Hash
, HexString);
F.setName(HexString);
}
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2022-11-27 19:41
被以和爲貴编辑
,原因: