OLLVM 之全局变量间接访问源码学习

Posted by Qfrost on 2022-07-07
Estimated Reading Time 6 Minutes
Words 1.4k In Total
Viewed Times

我真的太懒了,半年前写的代码今天才翻出来写博客。

全局变量因为其所在的地址对于全局都是固定的,是否有一种可能,通过Pass将所有全局变量的地址进行加密,在访问时进行解密后访问,静态分析工具就难以对全局变量的访问进行识别,也无法进行直接的交叉引用分析。

尝试自己实现了一下,然后发现第一步就卡住了,因为全局变量、函数等符号的地址,只有在链接后才会确定,我们无法在Pass也就是代码优化阶段得到全局变量的地址。然后我看到了 Goron 它用LLVM的 GEP(Get Element Pointer) 通过将全局变量转化为数组符号然后使用下标来实现间接访问。我对其略微做了一些修改并移植到LLVM NewPass上,不过总体思路和实现方法是一样的,(主要是太菜了想不出其他实现方法了),下面来解析一下实现过程

核心思想是枚举每个函数内使用过的全部全局变量,对每个全局变量生成Enckey(原版是每个函数的所有全局变量公用一个key),生成一组间接全局变量。函数内每个间接全局变量的值=对应全局变量的值+该全局变量对应的EncKey(IndGV = &GV[Enckey])。 再枚举函数内所有指令,若指令是访问全局变量的指令,则替换为 对应间接全局变量-对应EncKey(GV = IndGV[-Enckey]) 的值。

下面看下具体实现。首先有三个全局的成员

1
2
3
std::map<GlobalVariable *, unsigned> GVNumbering;       // 存储全局变量和在GlobalVariables中的位置
std::vector<GlobalVariable *> GlobalVariables; // 存储该函数中用到的所有全局变量
std::vector< ConstantInt * > EncKeys; // 存储该函数中用到的所有全局变量对应的EncKey

遍历每个函数中用到的全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void llvm::IndirectGlobalVariable::EnumUsedGlobalVariable(Function &F) {
for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {
for (User::op_iterator op = (*I).op_begin(); op != (*I).op_end(); ++op) {
Value *val = *op;
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(val)) {
// 该全局变量不存在于map中 则push
if (!GV->isThreadLocal() && GVNumbering.count(GV) == 0) {
GVNumbering[GV] = GlobalVariables.size();
GlobalVariables.push_back((GlobalVariable *) val);
}
}
}
}
}

对函数内用到的所有全局变量生成对应的EncKey

1
2
3
4
5
6
7
8
// 对函数中用到的每个全局变量生成EncKey
for(int i=0;i<GlobalVariables.size();++i) {
uint32_t V = RandomEngine.get_uint32_t() & ~3;
int enc_type = RandomEngine.get_uint8_t() % 2;

ConstantInt *EncKey = ConstantInt::get(Type::getInt32Ty(Ctx), V, false);
EncKeys.push_back(EncKey);
}

对每个全局变量和其对应的EncKey,计算对应的间接全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 通过EncKey对函数创建一个间接全局变量数组  且每个函数唯一
GlobalVariable * llvm::IndirectGlobalVariable::getIndirectGlobalVariables(Function &F) {
std::string GVName(F.getName().str() + "_IndirectGVars");

// 检查模块中是否已有该函数的GV
GlobalVariable *GV = F.getParent()->getNamedGlobal(GVName);
if (GV)
return GV;

// 遍历函数所有全局变量 将所有全局变量地址与EncKey相加 并将结果存在Elements列表中
std::vector<Constant *> Elements;
for (int idx = 0; idx<GlobalVariables.size(); ++idx) {
GlobalVariable* GVar = GlobalVariables[idx];
Constant *CE = nullptr;

// getGetElementPtr 是指针运算指令 即 CE = (BYTE)CE + EncKey
CE = ConstantExpr::getBitCast(GVar, Type::getInt8PtrTy(F.getContext()));
CE = ConstantExpr::getGetElementPtr(Type::getInt8Ty(F.getContext()), CE, EncKeys[idx]);

Elements.push_back(CE);
}

// 创建一个长度为Elements.size的Array类型
ArrayType *ATy = ArrayType::get(Type::getInt8PtrTy(F.getContext()), Elements.size());
// 将Elements转变到ATy类型的ConstantArray
Constant *CA = ConstantArray::get(ATy, ArrayRef<Constant *>(Elements));
// 创建一个名为GVName(funcname_IndirectGVars)的全局变量,类型为ATy 值为CA
GV = new GlobalVariable(*F.getParent(), ATy, false, GlobalValue::LinkageTypes::PrivateLinkage,
CA, GVName);

appendToCompilerUsed(*F.getParent(), {GV}); // 插入全局变量
return GV;
}

遍历函数内所有指令,对是全局变量访问的指令,替换为一条GEP指令,访问 IndGV[0-EncKey]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {
Instruction *Inst = &*I;
if (PHINode *PHI = dyn_cast<PHINode>(Inst)) {
for (unsigned int i = 0; i < PHI->getNumIncomingValues(); ++i) {
Value *val = PHI->getIncomingValue(i);
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(val)) {
if (GVNumbering.count(GV) == 0) {
continue;
}

Instruction *IP = PHI->getIncomingBlock(i)->getTerminator();
IRBuilder<> IRB(IP);

Value *Idx = ConstantInt::get(Type::getInt32Ty(Ctx), GVNumbering[GV]);
Value *GEP = IRB.CreateGEP(cast<PointerType>(GVars->getType()->getScalarType())->getElementType(), GVars, {Zero, Idx});
LoadInst *EncGVAddr = IRB.CreateLoad(Type::getInt8PtrTy(Ctx), GEP, GV->getName());
Constant *X;

X = ConstantExpr::getSub(Zero, EncKeys[GVNumbering[GV]]);
Value *GVAddr = IRB.CreateGEP(cast<PointerType>(EncGVAddr->getType()->getScalarType())->getElementType(), EncGVAddr, X);
GVAddr = IRB.CreateBitCast(GVAddr, GV->getType());
GVAddr->setName("IndGV");
Inst->replaceUsesOfWith(GV, GVAddr);
}
}
}
else {
for (User::op_iterator op = Inst->op_begin(); op != Inst->op_end(); ++op) {
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(*op)) {

// GlobalVariable不存在于map中 continue
if (GVNumbering.count(GV) == 0) {
continue;
}

IRBuilder<> IRB(Inst); // 使用IRBuilder在Inst前连续插入语句
Value *Idx = ConstantInt::get(Type::getInt32Ty(Ctx), GVNumbering[GV]); // 获取这个全局变量在Array中的Index

// GEP(Get Element Pointer) 第一个参数接受Array 第二个参数接收一个Vector 第一个元素恒为Zero 第二个元素接受要取Array的下标
Value *GEP = IRB.CreateGEP(cast<PointerType>(GVars->getType()->getScalarType())->getElementType(), GVars, {Zero, Idx});
LoadInst *EncGVAddr = IRB.CreateLoad(Type::getInt8PtrTy(Ctx), GEP, GV->getName()); // CreateLoad获取加密地址

Constant *X;
Value *GVAddr;
X = ConstantExpr::getSub(Zero, EncKeys[GVNumbering[GV]]); // 0-EncKey
GVAddr = IRB.CreateGEP(cast<PointerType>(EncGVAddr->getType()->getScalarType())->getElementType(), EncGVAddr, X); // EncGVAddr+(0-EncKey) == GVAddr

GVAddr = IRB.CreateBitCast(GVAddr, GV->getType());
GVAddr->setName("IndGV");
Inst->replaceUsesOfWith(GV, GVAddr);
}
}
}
}

最后,return PreservedAnalyses::all(); 以保存IR

IndirectGlobalVariable




浙ICP备19044916号-1