This page looks best with JavaScript enabled

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

 ·  ☕ 4 min read · 👀... views

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

全局变量因为其所在的地址对于全局都是固定的,是否有一种可能,通过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

Share on

Qfrost
WRITTEN BY
Qfrost
CTFer, Anti-Cheater, LLVM Committer