半年前写的代码今天才翻出来写博客。
全局变量因为其所在的地址对于全局都是固定的,是否有一种可能,通过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
