之前看chenx6大佬的博客学习了一下编写基础的LLVM Pass,但是那个有很明显的问题是,作者为了处理Function内部重复引用的多次解密的问题,特判了引用次数,如果存在多处对global string的引用是无法进行混淆的。但是实际的编程中很难不会引用多处字符串,所以那个只能混淆简单代码。我后面根据评论区的说法对此优化了一下,改成Function的EntryBasicBlock处解密,但是过不了llvm-dis
,感觉是在alloc栈变量的时候出了问题,暂时不知道怎么解决,后面如果有能力的话再重新写一遍吧。
之后学习了一下pluto-obfuscator项目,里面有一份GlobalEncryption.cpp
,借此机会学习一下,顺便写一份New PassManager版本的。
pluto-obfuscator的全局变量加密对全局整数和数组变量进行了处理,采用的方式是编译期加密,运行期解密,就是在IR阶段对全局变量加密,然后对每个全局变量调用解密函数并添加到.ctors
中,让程序在运行期进行全局构造的时候解密。
看雪有一个帖子总结几种加密方式总结得比较好,搬运一下
首先获取Module的LLVMContext,获取所有的全局变量,添加到GVs
中
然后筛选出需要加密的全局变量,ObfuTimes
是混淆次数,默认为1
然后分别处理数组和整数类型的全局变量,在这里直接进行加密,然后调用insertArrayDecryption
和insertIntDecryption
将解密函数添加到全局构造函数表中
因为在解密的方式上其实很像,这里只记录比较复杂的数组解密,这里用IRBuilder构造了一个for循环来实现解密,整数全局变量只要和密钥异或就行了,原理是一样的
首先构造一个函数,返回值是void
然后构造四个BasicBlock,用于实现for循环的几个阶段
首先获取func
的EntryBasicBlock,然后初始化循环变量
forCond
实现i < length
的部分,如果i < length
成立则跳转到循环体,如果不成立则跳出循环
循环体内就是解密的过程了,其实就是和密钥的每一位进行异或,最后跳转到i++
forInc
实现的就是i++
了,这里比较简单,最后跳转到forCond
进行条件判断,这样就实现了循环
forEnd
就是返回ret
,最后再将函数写入.ctors
中,实现运行期全局构造
以下是我改写成New PassManager的GlobalsEncryption.cpp
,加密部分没做修改,所有代码在libObfuscator/tree/pluto-enc
写个代码测试一下
混淆前后的IR对比
当然这对动调来说是没啥用的
对静态分析来说,这个强度的加密还是有点弱,加密函数很容易就会被看出来,还可以加点另外的平坦化之类的混淆,对分析加密函数也提高难度
解密时机 |
优点 |
局限性 |
.ctors |
可以处理几乎所有的CDS,兼容性好 |
运行期就全是明文了,很容易dump下来,保护性低,只能对抗静态分析 |
Function入口 |
只需要遍历该Function中所有直接或间接引用的CDS,不用考虑Function内部对同一个CDS重复引用顺序等问题 |
进入Function之后即可获得该Function所有使用的CDS明文 |
Instruction前 |
只有实际执行到使用CDS的时候才会加密,性能上最好,解密范围小 |
需要考虑Function中对同一CDS重复引用的解密问题,以及是直接还是间接引用CDS |
INIT_CONTEXT(M);
vector<GlobalVariable
*
> GVs;
for
(GlobalVariable &GV : M.getGlobalList()) {
GVs.push_back(&GV);
}
INIT_CONTEXT(M);
vector<GlobalVariable
*
> GVs;
for
(GlobalVariable &GV : M.getGlobalList()) {
GVs.push_back(&GV);
}
for
(
int
i
=
0
; i < ObfuTimes; i
+
+
) {
for
(GlobalVariable
*
GV : GVs) {
/
/
只对Integer和Array类型进行加密
if
(!GV
-
>getValueType()
-
>isIntegerTy() &&
!GV
-
>getValueType()
-
>isArrayTy()) {
continue
;
}
/
/
筛出
".str"
全局变量,LLVM IR的metadata同样也要保留
if
(GV
-
>hasInitializer() && GV
-
>getInitializer() &&
(GV
-
>getName().contains(
".str"
) || !OnlyStr)
/
/
Do
not
encrypt
globals
having a section named
"llvm.metadata"
&& !GV
-
>getSection().equals(
"llvm.metadata"
)) {
Constant
*
initializer
=
GV
-
>getInitializer();
ConstantInt
*
intData
=
dyn_cast<ConstantInt>(initializer);
ConstantDataArray
*
arrData
=
dyn_cast<ConstantDataArray>(initializer);
for
(
int
i
=
0
; i < ObfuTimes; i
+
+
) {
for
(GlobalVariable
*
GV : GVs) {
/
/
只对Integer和Array类型进行加密
if
(!GV
-
>getValueType()
-
>isIntegerTy() &&
!GV
-
>getValueType()
-
>isArrayTy()) {
continue
;
}
/
/
筛出
".str"
全局变量,LLVM IR的metadata同样也要保留
if
(GV
-
>hasInitializer() && GV
-
>getInitializer() &&
(GV
-
>getName().contains(
".str"
) || !OnlyStr)
/
/
Do
not
encrypt
globals
having a section named
"llvm.metadata"
&& !GV
-
>getSection().equals(
"llvm.metadata"
)) {
Constant
*
initializer
=
GV
-
>getInitializer();
ConstantInt
*
intData
=
dyn_cast<ConstantInt>(initializer);
ConstantDataArray
*
arrData
=
dyn_cast<ConstantDataArray>(initializer);
if
(arrData) {
/
/
获取数组的长度和数组元素的大小
uint32_t eleSize
=
arrData
-
>getElementByteSize();
uint32_t eleNum
=
arrData
-
>getNumElements();
uint32_t arrLen
=
eleNum
*
eleSize;
char
*
data
=
const_cast<char
*
>(arrData
-
>getRawDataValues().data());
char
*
dataCopy
=
new char[arrLen];
memcpy(dataCopy, data, arrLen);
/
/
生成密钥
uint64_t key
=
cryptoutils
-
>get_uint64_t();
/
/
A simple xor encryption
for
(uint32_t i
=
0
; i < arrLen; i
+
+
) {
dataCopy[i] ^
=
((char
*
)&key)[i
%
eleSize];
}
GV
-
>setInitializer(ConstantDataArray::getRaw(
StringRef(dataCopy, arrLen), eleNum, arrData
-
>getElementType()));
GV
-
>setConstant(false);
insertArrayDecryption(M, {GV, key, eleNum});
}
else
if
(intData) {
uint64_t key
=
cryptoutils
-
>get_uint64_t();
ConstantInt
*
enc
=
CONST(intData
-
>getType(), key ^ intData
-
>getZExtValue());
GV
-
>setInitializer(enc);
GV
-
>setConstant(false);
insertIntDecryption(M, {GV, key,
1LL
});
}
}
}
}
if
(arrData) {
/
/
获取数组的长度和数组元素的大小
uint32_t eleSize
=
arrData
-
>getElementByteSize();
uint32_t eleNum
=
arrData
-
>getNumElements();
uint32_t arrLen
=
eleNum
*
eleSize;
char
*
data
=
const_cast<char
*
>(arrData
-
>getRawDataValues().data());
char
*
dataCopy
=
new char[arrLen];
memcpy(dataCopy, data, arrLen);
/
/
生成密钥
uint64_t key
=
cryptoutils
-
>get_uint64_t();
/
/
A simple xor encryption
for
(uint32_t i
=
0
; i < arrLen; i
+
+
) {
dataCopy[i] ^
=
((char
*
)&key)[i
%
eleSize];
}
GV
-
>setInitializer(ConstantDataArray::getRaw(
StringRef(dataCopy, arrLen), eleNum, arrData
-
>getElementType()));
GV
-
>setConstant(false);
insertArrayDecryption(M, {GV, key, eleNum});
}
else
if
(intData) {
uint64_t key
=
cryptoutils
-
>get_uint64_t();
ConstantInt
*
enc
=
CONST(intData
-
>getType(), key ^ intData
-
>getZExtValue());
GV
-
>setInitializer(enc);
GV
-
>setConstant(false);
insertIntDecryption(M, {GV, key,
1LL
});
}
}
}
}
vector<
Type
*
> args;
FunctionType
*
funcType
=
FunctionType::get(
Type
::getVoidTy(M.getContext()), args, false);
string funcName
=
genHashedName(encGV.GV);
FunctionCallee callee
=
M.getOrInsertFunction(funcName, funcType);
Function
*
func
=
cast<Function>(callee.getCallee());
vector<
Type
*
> args;
FunctionType
*
funcType
=
FunctionType::get(
Type
::getVoidTy(M.getContext()), args, false);
string funcName
=
genHashedName(encGV.GV);
FunctionCallee callee
=
M.getOrInsertFunction(funcName, funcType);
Function
*
func
=
cast<Function>(callee.getCallee());
BasicBlock
*
entry
=
BasicBlock::Create(
*
CONTEXT,
"entry"
, func);
/
/
for
(
int
i
=
0
; i < length; i
+
+
)
/
/
条件判断
BasicBlock
*
forCond
=
BasicBlock::Create(
*
CONTEXT,
"for.cond"
, func);
/
/
循环体
BasicBlock
*
forBody
=
BasicBlock::Create(
*
CONTEXT,
"for.body"
, func);
/
/
i
+
+
BasicBlock
*
forInc
=
BasicBlock::Create(
*
CONTEXT,
"for.inc"
, func);
/
/
结束
BasicBlock
*
forEnd
=
BasicBlock::Create(
*
CONTEXT,
"for.inc"
, func);
BasicBlock
*
entry
=
BasicBlock::Create(
*
CONTEXT,
"entry"
, func);
/
/
for
(
int
i
=
0
; i < length; i
+
+
)
/
/
条件判断
BasicBlock
*
forCond
=
BasicBlock::Create(
*
CONTEXT,
"for.cond"
, func);
/
/
循环体
BasicBlock
*
forBody
=
BasicBlock::Create(
*
CONTEXT,
"for.body"
, func);
/
/
i
+
+
BasicBlock
*
forInc
=
BasicBlock::Create(
*
CONTEXT,
"for.inc"
, func);
/
/
结束
BasicBlock
*
forEnd
=
BasicBlock::Create(
*
CONTEXT,
"for.inc"
, func);
IRBuilder<> builder(
*
CONTEXT);
builder.SetInsertPoint(entry);
AllocaInst
*
indexPtr
=
builder.CreateAlloca(TYPE_I32, CONST_I32(
1
),
"i"
);
builder.CreateStore(CONST_I32(
0
), indexPtr);
builder.CreateBr(forCond);
IRBuilder<> builder(
*
CONTEXT);
builder.SetInsertPoint(entry);
AllocaInst
*
indexPtr
=
builder.CreateAlloca(TYPE_I32, CONST_I32(
1
),
"i"
);
builder.CreateStore(CONST_I32(
0
), indexPtr);
builder.CreateBr(forCond);
builder.SetInsertPoint(forCond);
LoadInst
*
index
=
builder.CreateLoad(TYPE_I32, indexPtr);
ICmpInst
*
cond
=
cast<ICmpInst>(builder.CreateICmpSLT(index, CONST_I32(encGV.
len
)));
builder.CreateCondBr(cond, forBody, forEnd);
builder.SetInsertPoint(forCond);
LoadInst
*
index
=
builder.CreateLoad(TYPE_I32, indexPtr);
ICmpInst
*
cond
=
cast<ICmpInst>(builder.CreateICmpSLT(index, CONST_I32(encGV.
len
)));
builder.CreateCondBr(cond, forBody, forEnd);
builder.SetInsertPoint(forBody);
Value
*
indexList[
2
]
=
{CONST_I32(
0
), index};
Value
*
ele
=
builder.CreateGEP(encGV.GV, ArrayRef<Value
*
>(indexList,
2
));
ArrayType
*
arrTy
=
cast<ArrayType>(encGV.GV
-
>getValueType());
Type
*
eleTy
=
arrTy
-
>getElementType();
Value
*
encEle
=
builder.CreateXor(builder.CreateLoad(ele), CONST(eleTy, encGV.key));
builder.CreateStore(encEle, ele);
builder.CreateBr(forInc);
builder.SetInsertPoint(forBody);
Value
*
indexList[
2
]
=
{CONST_I32(
0
), index};
Value
*
ele
=
builder.CreateGEP(encGV.GV, ArrayRef<Value
*
>(indexList,
2
));
ArrayType
*
arrTy
=
cast<ArrayType>(encGV.GV
-
>getValueType());
Type
*
eleTy
=
arrTy
-
>getElementType();
Value
*
encEle
=
builder.CreateXor(builder.CreateLoad(ele), CONST(eleTy, encGV.key));
builder.CreateStore(encEle, ele);
builder.CreateBr(forInc);
builder.SetInsertPoint(forInc);
builder.CreateStore(builder.CreateAdd(index, CONST_I32(
1
)), indexPtr);
builder.CreateBr(forCond);
builder.SetInsertPoint(forInc);
builder.CreateStore(builder.CreateAdd(index, CONST_I32(
1
)), indexPtr);
builder.CreateBr(forCond);
builder.SetInsertPoint(forEnd);
builder.CreateRetVoid();
appendToGlobalCtors(M, func,
0
);
builder.SetInsertPoint(forEnd);
builder.CreateRetVoid();
appendToGlobalCtors(M, func,
0
);
using namespace llvm;
struct EncryptedGV {
GlobalVariable
*
GV;
uint64_t key;
uint32_t
len
;
};
namespace {
static cl::opt<
int
>
ObfuTimes(
"gvobfus-times"
, cl::init(
1
),
cl::desc(
"Run GlobalsEncryption pass <gvobfus-times> time(s)"
));
static cl::opt<
bool
> OnlyStr(
"onlystr"
, cl::init(false),
cl::desc(
"Encrypt string variable only"
));
class
GVObfuscator : public PassInfoMixin<GVObfuscator> {
public:
GVObfuscator() {}
~GVObfuscator() {}
LLVMContext
*
ctx;
virtual void InsertIntDecryption(Module &M, EncryptedGV encGV);
virtual void InsertArrayDecryption(Module &M, EncryptedGV encGV);
virtual PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
};
std::string GenHashedName(GlobalVariable
*
GV) {
Module &M
=
*
GV
-
>getParent();
std::string funcName
=
formatv(
"{0}_{1:x-}"
, M.getName(), M.getMDKindID(GV
-
>getName()));
SHA1 sha1;
sha1.update(funcName);
StringRef digest
=
sha1.final();
std::stringstream ss;
ss << std::
hex
;
for
(size_t i
=
0
; i < digest.size(); i
+
+
) {
ss << std::setw(
2
) << std::setfill(
'0'
) << (unsigned)(digest[i] &
0xFF
);
}
return
ss.
str
();
}
void GVObfuscator::InsertIntDecryption(Module &M, EncryptedGV encGV) {
std::vector<
Type
*
> funcArgs;
FunctionType
*
funcType
=
FunctionType::get(
Type
::getVoidTy(M.getContext()), funcArgs, false);
std::string funcName
=
GenHashedName(encGV.GV);
FunctionCallee callee
=
M.getOrInsertFunction(funcName, funcType);
Function
*
func
=
cast<Function>(callee.getCallee());
BasicBlock
*
entry
=
BasicBlock::Create(
*
ctx,
"entry"
, func);
IRBuilder<> builder(
*
ctx);
builder.SetInsertPoint(entry);
LoadInst
*
val
=
builder.CreateLoad(encGV.GV);
Value
*
xorVal
=
builder.CreateXor(
val, ConstantInt::get(encGV.GV
-
>getValueType(), encGV.key));
builder.CreateStore(xorVal, encGV.GV);
builder.CreateRetVoid();
appendToGlobalCtors(M, func,
0
);
}
void GVObfuscator::InsertArrayDecryption(Module &M, EncryptedGV encGV) {
std::vector<
Type
*
> funcArgs;
FunctionType
*
funcType
=
FunctionType::get(
Type
::getVoidTy(M.getContext()), funcArgs, false);
std::string funcName
=
GenHashedName(encGV.GV);
FunctionCallee callee
=
M.getOrInsertFunction(funcName, funcType);
Function
*
func
=
cast<Function>(callee.getCallee());
BasicBlock
*
entry
=
BasicBlock::Create(
*
ctx,
"entry"
, func);
BasicBlock
*
forCond
=
BasicBlock::Create(
*
ctx,
"for.cond"
, func);
BasicBlock
*
forBody
=
BasicBlock::Create(
*
ctx,
"for.body"
, func);
BasicBlock
*
forInc
=
BasicBlock::Create(
*
ctx,
"for.inc"
, func);
BasicBlock
*
forEnd
=
BasicBlock::Create(
*
ctx,
"for.inc"
, func);
IRBuilder<> builder(
*
ctx);
Type
*
Int32Ty
=
builder.getInt32Ty();
builder.SetInsertPoint(entry);
AllocaInst
*
indexPtr
=
builder.CreateAlloca(Int32Ty, ConstantInt::get(Int32Ty,
1
, false),
"i"
);
builder.CreateStore(ConstantInt::get(Int32Ty,
0
), indexPtr);
builder.CreateBr(forCond);
builder.SetInsertPoint(forCond);
LoadInst
*
index
=
builder.CreateLoad(Int32Ty, indexPtr);
ICmpInst
*
cond
=
cast<ICmpInst>(
builder.CreateICmpSLT(index, ConstantInt::get(Int32Ty, encGV.
len
)));
builder.CreateCondBr(cond, forBody, forEnd);
builder.SetInsertPoint(forBody);
Value
*
indexList[
2
]
=
{ConstantInt::get(Int32Ty,
0
), index};
Value
*
ele
=
builder.CreateGEP(encGV.GV, ArrayRef<Value
*
>(indexList,
2
));
ArrayType
*
arrTy
=
cast<ArrayType>(encGV.GV
-
>getValueType());
Type
*
eleTy
=
arrTy
-
>getElementType();
Value
*
encEle
=
builder.CreateXor(builder.CreateLoad(ele),
ConstantInt::get(eleTy, encGV.key));
builder.CreateStore(encEle, ele);
builder.CreateBr(forInc);
builder.SetInsertPoint(forInc);
builder.CreateStore(builder.CreateAdd(index, ConstantInt::get(Int32Ty,
1
)),
indexPtr);
builder.CreateBr(forCond);
builder.SetInsertPoint(forEnd);
builder.CreateRetVoid();
appendToGlobalCtors(M, func,
0
);
}
PreservedAnalyses GVObfuscator::run(Module &M, ModuleAnalysisManager &MAM) {
outs() <<
"Pass start...\n"
;
ctx
=
&M.getContext();
std::vector<GlobalVariable
*
> GVs;
for
(auto &GV : M.
globals
()) {
GVs.push_back(&GV);
}
for
(
int
i
=
0
; i < ObfuTimes; i
+
+
) {
outs() <<
"Current ObfuTimes: "
<< i <<
"\n"
;
for
(auto
*
GV : GVs) {
/
/
只对Integer和Array类型进行加密
if
(!GV
-
>getValueType()
-
>isIntegerTy() &&
!GV
-
>getValueType()
-
>isArrayTy()) {
continue
;
}
/
/
筛出
".str"
全局变量,LLVM IR的metadata同样也要保留
if
(GV
-
>hasInitializer() && GV
-
>getInitializer() &&
(GV
-
>getName().contains(
".str"
) || !OnlyStr) &&
!GV
-
>getName().contains(
"llvm.metadata"
)) {
Constant
*
initializer
=
GV
-
>getInitializer();
ConstantInt
*
intData
=
dyn_cast<ConstantInt>(initializer);
ConstantDataArray
*
arrayData
=
dyn_cast<ConstantDataArray>(initializer);
/
/
处理数组
if
(arrayData) {
/
/
获取数组的长度和数组元素的大小
outs() <<
"Get global arraydata\n"
;
uint32_t eleSize
=
arrayData
-
>getElementByteSize();
uint32_t eleNum
=
arrayData
-
>getNumElements();
uint32_t arrLen
=
eleNum
*
eleSize;
outs() <<
"Global Variable: "
<<
*
GV <<
"\n"
<<
"Array Length: "
<< eleSize <<
" * "
<< eleNum <<
" = "
<< arrLen <<
"\n"
;
char
*
data
=
const_cast<char
*
>(arrayData
-
>getRawDataValues().data());
char
*
dataCopy
=
new char[arrLen];
memcpy(dataCopy, data, arrLen);
/
/
生成密钥
uint64_t key
=
cryptoutils
-
>get_uint64_t();
for
(uint32_t i
=
0
; i < arrLen; i
+
+
) {
dataCopy[i] ^
=
((char
*
)&key)[i
%
eleSize];
}
GV
-
>setInitializer(
ConstantDataArray::getRaw(StringRef(dataCopy, arrLen), eleNum,
arrayData
-
>getElementType()));
GV
-
>setConstant(false);
InsertArrayDecryption(M, {GV, key, eleNum});
}
/
/
处理整数
else
if
(intData) {
uint64_t key
=
cryptoutils
-
>get_uint64_t();
ConstantInt
*
enc
=
ConstantInt::get(intData
-
>getType(),
key ^ intData
-
>getZExtValue());
GV
-
>setInitializer(enc);
InsertIntDecryption(M, {GV, key,
1LL
});
}
}
}
}
outs() <<
"Pass end...\n"
;
return
PreservedAnalyses::
all
();
}
}
/
/
namespace
/
/
注册Pass
extern
"C"
PassPluginLibraryInfo llvmGetPassPluginInfo() {
return
{.APIVersion
=
LLVM_PLUGIN_API_VERSION,
.PluginName
=
"GVObfuscator"
,
.PluginVersion
=
LLVM_VERSION_STRING,
.RegisterPassBuilderCallbacks
=
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, ModulePassManager &MPM,
ArrayRef<PassBuilder::PipelineElement>)
-
>
bool
{
if
(Name
=
=
"gvobfus"
) {
MPM.addPass(GVObfuscator());
return
true;
}
return
false;
});
}};
}
using namespace llvm;
struct EncryptedGV {
GlobalVariable
*
GV;
uint64_t key;
uint32_t
len
;
};
namespace {
static cl::opt<
int
>
ObfuTimes(
"gvobfus-times"
, cl::init(
1
),
cl::desc(
"Run GlobalsEncryption pass <gvobfus-times> time(s)"
));
static cl::opt<
bool
> OnlyStr(
"onlystr"
, cl::init(false),
cl::desc(
"Encrypt string variable only"
));
class
GVObfuscator : public PassInfoMixin<GVObfuscator> {
public:
GVObfuscator() {}
~GVObfuscator() {}
LLVMContext
*
ctx;
virtual void InsertIntDecryption(Module &M, EncryptedGV encGV);
virtual void InsertArrayDecryption(Module &M, EncryptedGV encGV);
virtual PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
};
std::string GenHashedName(GlobalVariable
*
GV) {
Module &M
=
*
GV
-
>getParent();
std::string funcName
=
formatv(
"{0}_{1:x-}"
, M.getName(), M.getMDKindID(GV
-
>getName()));
SHA1 sha1;
sha1.update(funcName);
StringRef digest
=
sha1.final();
std::stringstream ss;
ss << std::
hex
;
for
(size_t i
=
0
; i < digest.size(); i
+
+
) {
ss << std::setw(
2
) << std::setfill(
'0'
) << (unsigned)(digest[i] &
0xFF
);
}
return
ss.
str
();
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2023-2-8 16:56
被L@in编辑
,原因: