首页
社区
课程
招聘
[原创]基于llvm的变量轮转混淆pass实现
发表于: 2022-3-14 11:28 16908

[原创]基于llvm的变量轮转混淆pass实现

2022-3-14 11:28
16908

之前有了解过一点点VMP的虚拟机,其中存在一种混淆机制,就是在执行之后将虚拟机的寄存器轮转,这样每个指令的寄存器就不一样了,极大的增加了数据流分析的难度。可能理解有点问题,但是这种思想也可以应用于源码级混淆。所以我就基于LLVM实现了一种变量轮转的混淆方式,发现能够能够阻碍数据流分析,迷惑看代码的人。

在LLVM中,函数内部的变量都是通过allocainst进行分配的,其分配的结果是一个指针类型,在IDA中对应的就是栈空间。

在这里,由于LLVM的分配类型并不相同,不能像虚拟寄存器那样方便轮转。所以先想个办法将这些分配的allocainst统一一下。

具体算法实现很简单,给一个数组指针,数组长度,移位位数,然后移位就可以了。

c代码实现如下

这里并不是重点,熟悉llvm的ir的很快就能写出来。

编译成so,并应用于ll文件。

IDA打开,可以看到f5代码中的变量的意义已经完全不一样了,如果忽略轮转函数,则代码完全没有意义,而想要恢复,则需要脑子一点点去演算变量地址才行。

Function *VariableRotation::createRotateFunc(Module *m,PointerType *ptrType,Twine &name)
{
    std::vector<Type*> params;
    params.push_back(ptrType);
    params.push_back(Type::getInt32Ty(m->getContext()));
    params.push_back(Type::getInt32Ty(m->getContext()));
    Type *rawType=ptrType->getPointerElementType();
    FunctionType *funcType=FunctionType::get(Type::getVoidTy(m->getContext()),params,false);
    Function *func=Function::Create(funcType,GlobalValue::PrivateLinkage,name,m);
    BasicBlock *entry1=BasicBlock::Create(m->getContext(),"entry1",func);
    BasicBlock *cmp1=BasicBlock::Create(m->getContext(),"cmp1",func);
    BasicBlock *entry2=BasicBlock::Create(m->getContext(),"entry2",func);
    BasicBlock *cmp2=BasicBlock::Create(m->getContext(),"cmp2",func);
    BasicBlock *shift=BasicBlock::Create(m->getContext(),"shift",func);
    BasicBlock *end2=BasicBlock::Create(m->getContext(),"end2",func);
    BasicBlock *end1=BasicBlock::Create(m->getContext(),"end1",func);
    Function::arg_iterator iter=func->arg_begin();
    Value *ptr=iter;
    Value *len=++iter;
    Value *times=++iter;
    IRBuilder<> irb(entry1);
    Value *zero=ConstantInt::get(irb.getInt32Ty(),0);
    Value *one=ConstantInt::get(irb.getInt32Ty(),1);
    AllocaInst *i=irb.CreateAlloca(irb.getInt32Ty());
    AllocaInst *j=irb.CreateAlloca(irb.getInt32Ty());
    AllocaInst *tmp=irb.CreateAlloca(rawType);
    AllocaInst *array=irb.CreateAlloca(ptrType);
    irb.CreateStore(zero,j);
    irb.CreateStore(ptr,array);
    irb.CreateBr(cmp1);
 
    irb.SetInsertPoint(cmp1);
    irb.CreateCondBr(irb.CreateICmpSLT(irb.CreateLoad(j),times),entry2,end1);
 
    irb.SetInsertPoint(entry2);
    irb.CreateStore(zero,i);
    irb.CreateStore(irb.CreateLoad(irb.CreateInBoundsGEP(irb.CreateLoad(array),{zero})),tmp);
    irb.CreateBr(cmp2);
 
    irb.SetInsertPoint(cmp2);
    irb.CreateCondBr(irb.CreateICmpSLT(irb.CreateLoad(i),irb.CreateSub(len,one)),shift,end2);
 
    irb.SetInsertPoint(shift);
    Value *ival=irb.CreateLoad(i);
    Value *arr=irb.CreateLoad(array);
    irb.CreateStore(irb.CreateLoad(irb.CreateInBoundsGEP(arr,{irb.CreateAdd(ival,one)})),irb.CreateInBoundsGEP(rawType,arr,{ival}));
    irb.CreateStore(irb.CreateAdd(ival,one),i);
    irb.CreateBr(cmp2);
 
    irb.SetInsertPoint(end2);
    irb.CreateStore(irb.CreateAdd(irb.CreateLoad(j),one),j);
    irb.CreateStore(irb.CreateLoad(tmp),irb.CreateInBoundsGEP(irb.CreateLoad(array),{irb.CreateSub(len,one)}));
    irb.CreateBr(cmp1);
 
    irb.SetInsertPoint(end1);
    irb.CreateRetVoid();
    return func;
}
Function *VariableRotation::createRotateFunc(Module *m,PointerType *ptrType,Twine &name)
{
    std::vector<Type*> params;
    params.push_back(ptrType);
    params.push_back(Type::getInt32Ty(m->getContext()));
    params.push_back(Type::getInt32Ty(m->getContext()));
    Type *rawType=ptrType->getPointerElementType();
    FunctionType *funcType=FunctionType::get(Type::getVoidTy(m->getContext()),params,false);
    Function *func=Function::Create(funcType,GlobalValue::PrivateLinkage,name,m);
    BasicBlock *entry1=BasicBlock::Create(m->getContext(),"entry1",func);
    BasicBlock *cmp1=BasicBlock::Create(m->getContext(),"cmp1",func);
    BasicBlock *entry2=BasicBlock::Create(m->getContext(),"entry2",func);
    BasicBlock *cmp2=BasicBlock::Create(m->getContext(),"cmp2",func);
    BasicBlock *shift=BasicBlock::Create(m->getContext(),"shift",func);
    BasicBlock *end2=BasicBlock::Create(m->getContext(),"end2",func);
    BasicBlock *end1=BasicBlock::Create(m->getContext(),"end1",func);
    Function::arg_iterator iter=func->arg_begin();
    Value *ptr=iter;
    Value *len=++iter;
    Value *times=++iter;
    IRBuilder<> irb(entry1);
    Value *zero=ConstantInt::get(irb.getInt32Ty(),0);
    Value *one=ConstantInt::get(irb.getInt32Ty(),1);
    AllocaInst *i=irb.CreateAlloca(irb.getInt32Ty());
    AllocaInst *j=irb.CreateAlloca(irb.getInt32Ty());
    AllocaInst *tmp=irb.CreateAlloca(rawType);
    AllocaInst *array=irb.CreateAlloca(ptrType);
    irb.CreateStore(zero,j);
    irb.CreateStore(ptr,array);
    irb.CreateBr(cmp1);
 
    irb.SetInsertPoint(cmp1);
    irb.CreateCondBr(irb.CreateICmpSLT(irb.CreateLoad(j),times),entry2,end1);
 
    irb.SetInsertPoint(entry2);
    irb.CreateStore(zero,i);
    irb.CreateStore(irb.CreateLoad(irb.CreateInBoundsGEP(irb.CreateLoad(array),{zero})),tmp);
    irb.CreateBr(cmp2);
 
    irb.SetInsertPoint(cmp2);
    irb.CreateCondBr(irb.CreateICmpSLT(irb.CreateLoad(i),irb.CreateSub(len,one)),shift,end2);
 
    irb.SetInsertPoint(shift);
    Value *ival=irb.CreateLoad(i);
    Value *arr=irb.CreateLoad(array);
    irb.CreateStore(irb.CreateLoad(irb.CreateInBoundsGEP(arr,{irb.CreateAdd(ival,one)})),irb.CreateInBoundsGEP(rawType,arr,{ival}));
    irb.CreateStore(irb.CreateAdd(ival,one),i);
    irb.CreateBr(cmp2);
 
    irb.SetInsertPoint(end2);
    irb.CreateStore(irb.CreateAdd(irb.CreateLoad(j),one),j);
    irb.CreateStore(irb.CreateLoad(tmp),irb.CreateInBoundsGEP(irb.CreateLoad(array),{irb.CreateSub(len,one)}));
    irb.CreateBr(cmp1);
 
    irb.SetInsertPoint(end1);
    irb.CreateRetVoid();
    return func;
}
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/CFG.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "assert.h"
#include<vector>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<cstdio>
using namespace llvm;
namespace
{
    struct VariableRotation : public ModulePass
    {
        static char ID;
 
           VariableRotation() : ModulePass(ID) {}
        Function *createRotateFunc(Module *m,PointerType *ptrType,Twine &name);
        void processFunction(Function &f,SetVector<Function*> &shift);
        bool processVars(Function &f,SetVector<AllocaInst*> &vars,Function *rotateFunc);
        bool isUsedByInst(Instruction *inst,Value *var)
        {
            for(Value *ops:inst->operands())
                if(ops==var)
                    return true;
            return false;
        }
 
        bool runOnModule(Module &m) override
        {
            Twine fname1=Twine("shiftFuncitonI8");
            Function *shiftFunc=createRotateFunc(&m,Type::getInt8PtrTy(m.getContext()),fname1);
            SetVector<Function*> shifts;
            shifts.insert(shiftFunc);
            for(Function &f:m)
            {
                if(&f==shiftFunc)
                    continue;
                if(f.hasExactDefinition())
                    processFunction(f,shifts);
            }
            return true;
        }
      };
}
 
char VariableRotation::ID=0;
static RegisterPass<VariableRotation> X("varobfu", "varobfu");
bool VariableRotation::processVars(Function &f,SetVector<AllocaInst*> &vars,Function *rotateFunc)
{
    if(vars.size()<2)
        return false;
    IRBuilder<> irb(&*f.getEntryBlock().getFirstInsertionPt());
    Value *zero=ConstantInt::get(irb.getInt32Ty(),0);
    DataLayout data=f.getParent()->getDataLayout();
    int space=0;
    SetVector<int> value_map;
    printf("function: %s\n",f.getName());
    for(int i=0;i<vars.size();i++)
    {
        AllocaInst *a=vars[i];
        value_map.insert(space);
        printf("address:  %d\n",space);
        space+=data.getTypeAllocSize(a->getAllocatedType());
    }
    ArrayType *arrayType=ArrayType::get(irb.getInt8Ty(),space);
    AllocaInst *array=irb.CreateAlloca(arrayType);
    int prob=30;
    for(BasicBlock &bb:f)
    {
        int offset=0;
        BasicBlock::iterator iter=bb.getFirstInsertionPt();
        if(&bb==&f.getEntryBlock())
            iter++;
        while(iter!=bb.end())
        {
            Instruction *inst=&*iter;
            irb.SetInsertPoint(inst);
            for(int i=0;i<vars.size();i++)
                if(isUsedByInst(inst,vars[i]))
                {
                    if(rand()%100<prob)
                    {
                        int times=rand()%(vars.size()-1)+1;
                        int delta=(space+value_map[(offset+times)%vars.size()]-value_map[offset])%space;
                        irb.CreateCall(FunctionCallee(rotateFunc),{irb.CreateGEP(array,{zero,zero}),ConstantInt::get(irb.getInt32Ty(),space),ConstantInt::get(irb.getInt32Ty(),delta)});
                        offset=(offset+times)%vars.size();
                    }
                    int index=(space+value_map[i]-value_map[offset])%space;
                    Value *gep=irb.CreateGEP(array,{zero,ConstantInt::get(irb.getInt32Ty(),index)});
                    Value *cast=irb.CreateBitOrPointerCast(gep,vars[i]->getType());
                    int c=0;
                    for(Value *ops:inst->operands())
                    {
                        if(ops==vars[i])
                            inst->setOperand(c,cast);
                        c++;
                    }
                    break;
                }
            iter++;
        }
        if(offset!=0)
        {
            irb.SetInsertPoint(bb.getTerminator());
            irb.CreateCall(FunctionCallee(rotateFunc),{irb.CreateGEP(array,{zero,zero}),ConstantInt::get(irb.getInt32Ty(),space),ConstantInt::get(irb.getInt32Ty(),(space-value_map[offset])%space)});
        }
 
    }
    return true;
}
void VariableRotation::processFunction(Function &f,SetVector<Function*> &shift)
{
    SetVector<AllocaInst*> list;
    for(BasicBlock &bb:f)
        for(Instruction &instr:bb)
            if(isa<AllocaInst>(instr))
            {
                AllocaInst *a=(AllocaInst*)&instr;
                list.insert(a);
            }
    if(processVars(f,list,shift[0]))
    {
        for(AllocaInst *a:list)
            a->eraseFromParent();
    }
}
Function *VariableRotation::createRotateFunc(Module *m,PointerType *ptrType,Twine &name)
{
    std::vector<Type*> params;
    params.push_back(ptrType);
    params.push_back(Type::getInt32Ty(m->getContext()));
    params.push_back(Type::getInt32Ty(m->getContext()));
    Type *rawType=ptrType->getPointerElementType();
    FunctionType *funcType=FunctionType::get(Type::getVoidTy(m->getContext()),params,false);
    Function *func=Function::Create(funcType,GlobalValue::PrivateLinkage,name,m);
    BasicBlock *entry1=BasicBlock::Create(m->getContext(),"entry1",func);
    BasicBlock *cmp1=BasicBlock::Create(m->getContext(),"cmp1",func);
    BasicBlock *entry2=BasicBlock::Create(m->getContext(),"entry2",func);
    BasicBlock *cmp2=BasicBlock::Create(m->getContext(),"cmp2",func);
    BasicBlock *shift=BasicBlock::Create(m->getContext(),"shift",func);
    BasicBlock *end2=BasicBlock::Create(m->getContext(),"end2",func);
    BasicBlock *end1=BasicBlock::Create(m->getContext(),"end1",func);
    Function::arg_iterator iter=func->arg_begin();
    Value *ptr=iter;
    Value *len=++iter;
    Value *times=++iter;
    IRBuilder<> irb(entry1);
    Value *zero=ConstantInt::get(irb.getInt32Ty(),0);
    Value *one=ConstantInt::get(irb.getInt32Ty(),1);
    AllocaInst *i=irb.CreateAlloca(irb.getInt32Ty());
    AllocaInst *j=irb.CreateAlloca(irb.getInt32Ty());
    AllocaInst *tmp=irb.CreateAlloca(rawType);
    AllocaInst *array=irb.CreateAlloca(ptrType);
    irb.CreateStore(zero,j);
    irb.CreateStore(ptr,array);
    irb.CreateBr(cmp1);
 
    irb.SetInsertPoint(cmp1);
    irb.CreateCondBr(irb.CreateICmpSLT(irb.CreateLoad(j),times),entry2,end1);
 
    irb.SetInsertPoint(entry2);
    irb.CreateStore(zero,i);
    irb.CreateStore(irb.CreateLoad(irb.CreateInBoundsGEP(irb.CreateLoad(array),{zero})),tmp);
    irb.CreateBr(cmp2);
 
    irb.SetInsertPoint(cmp2);
    irb.CreateCondBr(irb.CreateICmpSLT(irb.CreateLoad(i),irb.CreateSub(len,one)),shift,end2);
 
    irb.SetInsertPoint(shift);
    Value *ival=irb.CreateLoad(i);
    Value *arr=irb.CreateLoad(array);
    irb.CreateStore(irb.CreateLoad(irb.CreateInBoundsGEP(arr,{irb.CreateAdd(ival,one)})),irb.CreateInBoundsGEP(rawType,arr,{ival}));
    irb.CreateStore(irb.CreateAdd(ival,one),i);
    irb.CreateBr(cmp2);
 
    irb.SetInsertPoint(end2);
    irb.CreateStore(irb.CreateAdd(irb.CreateLoad(j),one),j);
    irb.CreateStore(irb.CreateLoad(tmp),irb.CreateInBoundsGEP(irb.CreateLoad(array),{irb.CreateSub(len,one)}));
    irb.CreateBr(cmp1);
 
    irb.SetInsertPoint(end1);
    irb.CreateRetVoid();
    return func;
}
static RegisterStandardPasses Y(PassManagerBuilder::EP_EarlyAsPossible,
  [](const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) {
    PM.add(new VariableRotation());
  });
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/CFG.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "assert.h"
#include<vector>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<cstdio>
using namespace llvm;
namespace
{
    struct VariableRotation : public ModulePass
    {
        static char ID;
 
           VariableRotation() : ModulePass(ID) {}
        Function *createRotateFunc(Module *m,PointerType *ptrType,Twine &name);
        void processFunction(Function &f,SetVector<Function*> &shift);
        bool processVars(Function &f,SetVector<AllocaInst*> &vars,Function *rotateFunc);
        bool isUsedByInst(Instruction *inst,Value *var)
        {
            for(Value *ops:inst->operands())
                if(ops==var)
                    return true;
            return false;
        }
 
        bool runOnModule(Module &m) override
        {
            Twine fname1=Twine("shiftFuncitonI8");
            Function *shiftFunc=createRotateFunc(&m,Type::getInt8PtrTy(m.getContext()),fname1);
            SetVector<Function*> shifts;
            shifts.insert(shiftFunc);
            for(Function &f:m)
            {
                if(&f==shiftFunc)
                    continue;
                if(f.hasExactDefinition())
                    processFunction(f,shifts);
            }
            return true;
        }
      };
}
 
char VariableRotation::ID=0;
static RegisterPass<VariableRotation> X("varobfu", "varobfu");
bool VariableRotation::processVars(Function &f,SetVector<AllocaInst*> &vars,Function *rotateFunc)
{
    if(vars.size()<2)
        return false;
    IRBuilder<> irb(&*f.getEntryBlock().getFirstInsertionPt());
    Value *zero=ConstantInt::get(irb.getInt32Ty(),0);
    DataLayout data=f.getParent()->getDataLayout();
    int space=0;
    SetVector<int> value_map;
    printf("function: %s\n",f.getName());
    for(int i=0;i<vars.size();i++)
    {
        AllocaInst *a=vars[i];
        value_map.insert(space);
        printf("address:  %d\n",space);
        space+=data.getTypeAllocSize(a->getAllocatedType());
    }
    ArrayType *arrayType=ArrayType::get(irb.getInt8Ty(),space);
    AllocaInst *array=irb.CreateAlloca(arrayType);
    int prob=30;
    for(BasicBlock &bb:f)

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2022-3-14 11:30 被R1mao编辑 ,原因:
收藏
免费 9
支持
分享
最新回复 (11)
雪    币: 14303
活跃值: (10786)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
2
666
2022-3-14 11:39
0
雪    币: 24823
活跃值: (5304)
能力值: ( LV12,RANK:529 )
在线值:
发帖
回帖
粉丝
3
编译器优化: 复制传播+死代码删除
2022-3-14 12:03
0
雪    币: 3115
活跃值: (3303)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
4
会飞的鱼油 编译器优化: 复制传播+死代码删除
怎么编译优化呢
2022-3-14 12:14
0
雪    币: 3115
活跃值: (3303)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
5
会飞的鱼油 编译器优化: 复制传播+死代码删除
是用o2重新编译一遍吗
2022-3-14 12:23
0
雪    币: 24823
活跃值: (5304)
能力值: ( LV12,RANK:529 )
在线值:
发帖
回帖
粉丝
6
R1mao 是用o2重新编译一遍吗
你用retdec反编译测试下,设置好优化选项,你这个混淆应该是可以被它优化掉的
2022-3-14 12:41
0
雪    币: 3115
活跃值: (3303)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
7
大佬有什么参考文献吗,我想看看具体的优化选项。之前试了把ida的f5拿出来用o2编译好像是去不掉的。
2022-3-14 12:46
0
雪    币: 3115
活跃值: (3303)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
8
python retdec-decompiler.py ---backend-aggressive-opts test 大佬我这样写可以吗
2022-3-14 12:57
0
雪    币: 24823
活跃值: (5304)
能力值: ( LV12,RANK:529 )
在线值:
发帖
回帖
粉丝
9
R1mao 大佬有什么参考文献吗,我想看看具体的优化选项。之前试了把ida的f5拿出来用o2编译好像是去不掉的。
优化算法原理可以看看编译原理相关书籍的优化算法部分,retdec我很久没用了,你看看官方文档吧
2022-3-14 13:11
0
雪    币: 201
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
大佬,您的这篇文章还有一些疑问的地方希望向您请教一下,QQ3144880835
2022-8-4 15:14
0
雪    币: 136
活跃值: (549)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11

挖坟找到这贴, 理论上抛开安全性不谈这个算法没考虑到AA和Escape?

最后于 2023-10-28 13:58 被naville编辑 ,原因:
2023-10-28 13:57
0
雪    币: 242
活跃值: (199)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
大佬,我想请教一下,如果要想像ollvm一样使用clang命令行调用,是只需要在passmanagerbuilder里面加个选项就好了吗
2024-5-7 11:11
0
游客
登录 | 注册 方可回帖
返回
//