首页
社区
课程
招聘
[原创]使用ollvm自定义简单的字符串加密
发表于: 2020-12-22 17:20 8103

[原创]使用ollvm自定义简单的字符串加密

2020-12-22 17:20
8103

题目出自3W班9月的题目用ollvm9.0实现字符串简单加密
题目主要是为了能熟练ollvm中如何进行一个简单的加密,以及c++部分怎么生成对应的IR指令来达到像c++函数效果。所以主要我们的思路可以切换成。
1、首先确定加密的核心逻辑并用C++实现
2、根据C++的算法。生成一份IR指令来为我们提供参考
3、ollvm里面如何用C++生成相应的IR指令
4、根据参考的IR指令来生成我们需要的加密和解密函数
做完这个题目,基本就可以对ollvm的工作原理有一定的了解,并且改造属于自己的加密或者混淆了。

先贴上测试好的结果: https://github.com/dqzg12300/kOLLVM.git

想要写一个字符串加密的pass,第一步就是先实现一遍c++的算法流程,然后再看一看生成的IR文件,然后再写对应的加密pass,下面看一个自己实现的简单c++字符串加密。

这个简单加密的意思,就是根据复杂度参数。来进行一定次数的迭代,将当前字符每次都异或一下,最后一次是先去反,再异或,解密就是反之。测试结果能正常加密和解密后,我们就先输出一份ir文件。看看在ir中间语言中是如何进行加密和解密的。

clang -emit-llvm -S main.cpp -o main.ll

生成好对应的ir文件后,我们开始写这个加密pass,然后再写的过程中,根据逻辑需要,去ir中找对应的指令处理方式

在ir文件中的层级划分:Module(模块)的下一层是若干Function(函数),然后在Function的下一层是若干BasicBlock(基本快),再BasicBlock的下一层是若干Instruction(指令块)

现在准备就绪,下面开始先准备一个加密的pass,基本代码如下

这里准备好了pass的基本代码后,最后就剩下最重要的核心逻辑,如何把c++的加密方式。在pass中实现,我们的功能是实现字符串加密,那么第一步应该是取得这个函数中的全部字符串,那么我们先看看ir中字符串的特征

可以看到,这个str是一个操作数,想要获取全部字符串,就得先遍历所有指令块中的操作数。然后再根据字符串的特征来进行过滤。下面先看如何遍历所有指令块。

上面遍历了函数中的所有基本快,然后遍历所有指令块,然后遍历所有操作数,然后获取操作数的值,判断该操作数是否是一个字符串,并且打印这个指令块,操作数,以及取到的操作数的值,下面看看打印的结果

那么看到了,我们想获取的字符串是在stripOp中。那么接下来就把所有字符串全部获取出来并转换成string

之前看到的字符串的ir代码看到所有字符串都是全局的,所以要先转换成全局的对象,然后再转换成数值。然后看这里的打印结果

获取到所有的字符串了之后。接下来。我们要先把这个字符串加密,然后再用插入指令块来进行解密。下面继续完善,先把之前搞好的加密算法迁移进来。

这里大致流程和之前一样。只是key我们装起来了。然后每个字节处理都随机一次key。接下来的处理就是插入指令块来对这个加密数据encres进行解密还原处理。

我们想要处理这个加密的数据,首先要先创建一个内存指令,来存放这个加密后的数据。然后再对加密后的数据遍历。进行还原。所以,我们的下一步先创建一个BitCastInst。并且我们需要用一个int8的array来给这个内存指令进行赋值。下面的代码是先创建array指令,然后用array指令创建一个内存指令

上面的就是先创建一个int8的array类型,然后用这个类型创建一个array,然后再用这个array创建内存指令,这些指令都插入在遍历到字符串指令的当前行的前方。这个bitcast将用来存放加密后的字符串数据

接下来就是加密的逻辑处理。和我们之前c++的流程一样,只不过这里需要换成插入指令块的形式来进行加密数据的还原,我直接贴上解密的代码部分,然后里面有详细的注释。

上面就是把c++的解密流程用插入指令块的方式实现的方式。流程比较繁琐,但是大概意思是差不多的。

最后这里完成后,我们就可以删除指令块中的字符串明文部分。然后只保留密文

到这里整个流程就完成了。这里还有一个点需要注意的是,由于字符串的特性,当使用了多个相同的字符串,实际在汇编层的代码中,会优化为一个字符串,所以在字符串加密的时候,我们要留意解密字符串的作用域。下面举一个例子

这个例子中使用了两个hello。如果我们在使用这个字符串时,调用的解密。那么下面else中的代码则会无法访问到bitcat。因为不在同一个作用域,所以为了防止出现这种情况,我在解密时再做一个特殊的处理,我们先获取第一个指令块的位置,然后所有的字符串解密指令块,都插入在最开始的位置,这样就不会出现作用域的问题了。最后贴上完整代码

 
 
#include <stdio.h>
#include <cstring>
#include <string>
 
int main(int  argc, char** argv) {
    //加密
    std::string str1="hello world!!!";
    //这里是随机的key,先写固定,真实实现的时候再每个字节使用一个随机key
    int randkey=11;
      //加密复杂度
    int kstr_size=10;
    int enclen=randkey+kstr_size;
    char encres[str1.size()];
    int idx=0;
    memset(encres,0,enclen);
    //这里大概就是遍历字符串,每个字符根据加密复杂度进行一定数量迭代异或,最后一次的迭代使用取反再异或
    for(int i=0;i<str1.size();i++){
        printf("cur: %x \r\n",str1[i]);
        for(int y=randkey;y<enclen;y++){
            if(y==randkey){
                encres[i]=str1[i]^y;
            }else if(y==enclen-1){
                encres[i]=encres[i]^(~y);
            }else{
                encres[i]=encres[i]^y;
            }
            printf("%x ",encres[i]);
            idx++;
        }
        printf("\r\n");
    }
    printf("encdata: %s\r\n",encres);
    //下面是解密函数
    char decres[str1.size()];
    for(int i=0;i<str1.size();i++){
        printf("cur enc: %x \r\n",encres[i]);
        for(int y=enclen-1;y>=randkey;y--){
            if(y==enclen-1){
                decres[i]=encres[i]^(~y);
            }else{
                decres[i]=decres[i]^y;
            }
            printf("%x ",decres[i]);
        }
        printf("\r\n");
    }
    printf("res: %s\r\n",decres);
    return 0;
}
#include <stdio.h>
#include <cstring>
#include <string>
 
int main(int  argc, char** argv) {
    //加密
    std::string str1="hello world!!!";
    //这里是随机的key,先写固定,真实实现的时候再每个字节使用一个随机key
    int randkey=11;
      //加密复杂度
    int kstr_size=10;
    int enclen=randkey+kstr_size;
    char encres[str1.size()];
    int idx=0;
    memset(encres,0,enclen);
    //这里大概就是遍历字符串,每个字符根据加密复杂度进行一定数量迭代异或,最后一次的迭代使用取反再异或
    for(int i=0;i<str1.size();i++){
        printf("cur: %x \r\n",str1[i]);
        for(int y=randkey;y<enclen;y++){
            if(y==randkey){
                encres[i]=str1[i]^y;
            }else if(y==enclen-1){
                encres[i]=encres[i]^(~y);
            }else{
                encres[i]=encres[i]^y;
            }
            printf("%x ",encres[i]);
            idx++;
        }
        printf("\r\n");
    }
    printf("encdata: %s\r\n",encres);
    //下面是解密函数
    char decres[str1.size()];
    for(int i=0;i<str1.size();i++){
        printf("cur enc: %x \r\n",encres[i]);
        for(int y=enclen-1;y>=randkey;y--){
            if(y==enclen-1){
                decres[i]=encres[i]^(~y);
            }else{
                decres[i]=decres[i]^y;
            }
            printf("%x ",decres[i]);
        }
        printf("\r\n");
    }
    printf("res: %s\r\n",decres);
    return 0;
}
 
 
 
 
#include <kllvm/Transforms/Obfuscation/Utils.h>
#include "kllvm/Transforms/Obfuscation/KStringEncode.h"
 
#include <string>
using namespace llvm;
 
namespace {
      //加密复杂度
    const int defaultKStringSize = 0x10;
    static cl::opt<int>
            KStringSize("kstr_size", cl::desc("Choose the probability [%] each basic blocks will be obfuscated by the -kstr pass"), cl::value_desc("king string encode Encryption length"), cl::init(defaultKStringSize), cl::Optional);
 
    struct KStringEncode: public FunctionPass{
        static char ID; // Pass identification
        bool flag;
        KStringEncode() : FunctionPass(ID) {}
        KStringEncode(bool flag) : FunctionPass(ID) {this->flag = flag; KStringEncode();}
        virtual bool runOnFunction(Function &F){
              //先检查加密复杂度是否在合法范围
            if ( !((KStringSize > 0) && (KStringSize <= 100)) ) {
                errs()<<"KStringEncode application basic blocks percentage -kstr_size=x must be 0 < x <= 100";
                return false;
            }
            if(toObfuscate(flag,&F,"kstr")) {
                kstr(F);
            }
            return false;
        }
        void kstr(Function& func){
            //todo 这里再写具体的pass逻辑
        }
    };
 
}
char KStringEncode::ID = 0;
static RegisterPass<KStringEncode> X("kstr", "inserting bogus control flow");
 
Pass *llvm::createKStringEncode() {
    return new KStringEncode();
}
 
Pass *llvm::createKStringEncode(bool flag) {
    return new KStringEncode(flag);
}
#include <kllvm/Transforms/Obfuscation/Utils.h>
#include "kllvm/Transforms/Obfuscation/KStringEncode.h"
 
#include <string>
using namespace llvm;
 
namespace {
      //加密复杂度
    const int defaultKStringSize = 0x10;
    static cl::opt<int>
            KStringSize("kstr_size", cl::desc("Choose the probability [%] each basic blocks will be obfuscated by the -kstr pass"), cl::value_desc("king string encode Encryption length"), cl::init(defaultKStringSize), cl::Optional);
 
    struct KStringEncode: public FunctionPass{
        static char ID; // Pass identification
        bool flag;
        KStringEncode() : FunctionPass(ID) {}
        KStringEncode(bool flag) : FunctionPass(ID) {this->flag = flag; KStringEncode();}
        virtual bool runOnFunction(Function &F){
              //先检查加密复杂度是否在合法范围
            if ( !((KStringSize > 0) && (KStringSize <= 100)) ) {
                errs()<<"KStringEncode application basic blocks percentage -kstr_size=x must be 0 < x <= 100";
                return false;
            }
            if(toObfuscate(flag,&F,"kstr")) {
                kstr(F);
            }
            return false;
        }
        void kstr(Function& func){
            //todo 这里再写具体的pass逻辑
        }
    };
 
}
char KStringEncode::ID = 0;
static RegisterPass<KStringEncode> X("kstr", "inserting bogus control flow");
 
Pass *llvm::createKStringEncode() {
    return new KStringEncode();
}
 
Pass *llvm::createKStringEncode(bool flag) {
    return new KStringEncode(flag);
}
@.str = private unnamed_addr constant [15 x i8] c"hello world!!!\00", align 1
@.str = private unnamed_addr constant [15 x i8] c"hello world!!!\00", align 1
void kstr(Function& func){
            for(BasicBlock& bb:func){
                for(Instruction& ins :bb){
                    for(Value* val:ins.operands()){
                        Value* stripOp=val->stripPointerCasts();
                        if(stripOp->getName().contains(".str")){
                            errs()<<ins<<"\n";
                            errs()<<*val<<"\n";
                            errs()<<*stripOp<<"\n";
                        }
                    }
                }
            }
        }
void kstr(Function& func){
            for(BasicBlock& bb:func){
                for(Instruction& ins :bb){
                    for(Value* val:ins.operands()){
                        Value* stripOp=val->stripPointerCasts();
                        if(stripOp->getName().contains(".str")){
                            errs()<<ins<<"\n";
                            errs()<<*val<<"\n";
                            errs()<<*stripOp<<"\n";
                        }
                    }
                }
            }
        }
  store i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i64 0, i64 0), i8** %str, align 8
i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i64 0, i64 0)
@.str = private unnamed_addr constant [7 x i8] c"kanxue\00", align 1
  store i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i64 0, i64 0), i8** %str, align 8
i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i64 0, i64 0)
@.str = private unnamed_addr constant [7 x i8] c"kanxue\00", align 1
//封装一个转换操作数值为字符串的函数
std::string ConvertOpToString(Value* op){
            GlobalVariable* globalVar= dyn_cast<GlobalVariable>(op);
            if(!globalVar){
                errs()<<"dyn cast gloabl err";
                return "";
            }
            ConstantDataSequential* cds=dyn_cast<ConstantDataSequential>(globalVar->getInitializer());
            if(!cds){
                errs()<<"dyn cast constant data err";
                return "";
            }
            return cds->getRawDataValues();;
        }
 
        void kstr(Function& func){
            for(BasicBlock& bb:func){
                for(Instruction& ins :bb){
                    for(Value* val:ins.operands()){
                        Value* stripOp=val->stripPointerCasts();
                        if(stripOp->getName().contains(".str")){
                            std::string strdata=ConvertOpToString(stripOp);
                            errs()<<strdata<<"\n";
                        }
                    }
                }
            }
        }
//封装一个转换操作数值为字符串的函数
std::string ConvertOpToString(Value* op){
            GlobalVariable* globalVar= dyn_cast<GlobalVariable>(op);
            if(!globalVar){
                errs()<<"dyn cast gloabl err";
                return "";
            }
            ConstantDataSequential* cds=dyn_cast<ConstantDataSequential>(globalVar->getInitializer());
            if(!cds){
                errs()<<"dyn cast constant data err";
                return "";
            }
            return cds->getRawDataValues();;
        }
 
        void kstr(Function& func){
            for(BasicBlock& bb:func){
                for(Instruction& ins :bb){
                    for(Value* val:ins.operands()){
                        Value* stripOp=val->stripPointerCasts();
                        if(stripOp->getName().contains(".str")){
                            std::string strdata=ConvertOpToString(stripOp);
                            errs()<<strdata<<"\n";
                        }
                    }
                }
            }
        }
kanxue
hello ollvm:%d
kanxue
hello ollvm:%d
//转换字符串
        std::string ConvertOpToString(Value* op){
            GlobalVariable* globalVar= dyn_cast<GlobalVariable>(op);
            if(!globalVar){
                errs()<<"dyn cast gloabl err";
                return "";
            }
            ConstantDataSequential* cds=dyn_cast<ConstantDataSequential>(globalVar->getInitializer());
            if(!cds){
                errs()<<"dyn cast constant data err";
                return "";
            }
            return cds->getRawDataValues();;
        }
 
        void kstr(Function& func){
            for(BasicBlock& bb:func){
                for(Instruction& ins :bb){
                    for(Value* val:ins.operands()){
                        Value* stripOp=val->stripPointerCasts();
                        if(stripOp->getName().contains(".str")){
                            std::string strdata=ConvertOpToString(stripOp);
                            errs()<<strdata<<"\n";
 
                            //加密流程
                            uint8_t keys[strdata.size()];
                            char encres[strdata.size()];
                            int idx=0;
                            memset(encres,0,strdata.size());
                            for(int i=0;i<strdata.size();i++){
                                uint8_t randkey=llvm::cryptoutils->get_uint8_t();
                                keys[i]=randkey;
                                int enclen=randkey+defaultKStringSize;
                                for(int y=randkey;y<enclen;y++){
                                    if(y==randkey){
                                        encres[i]=strdata[i]^y;
                                    }else if(y==enclen-1){
                                        encres[i]=encres[i]^(~y);
                                    }else{
                                        encres[i]=encres[i]^y;
                                    }
                                    idx++;
                                }
                            }
                        }
                    }
                }
            }
        }
//转换字符串
        std::string ConvertOpToString(Value* op){
            GlobalVariable* globalVar= dyn_cast<GlobalVariable>(op);
            if(!globalVar){
                errs()<<"dyn cast gloabl err";
                return "";
            }
            ConstantDataSequential* cds=dyn_cast<ConstantDataSequential>(globalVar->getInitializer());
            if(!cds){
                errs()<<"dyn cast constant data err";
                return "";
            }
            return cds->getRawDataValues();;
        }
 
        void kstr(Function& func){
            for(BasicBlock& bb:func){
                for(Instruction& ins :bb){
                    for(Value* val:ins.operands()){
                        Value* stripOp=val->stripPointerCasts();
                        if(stripOp->getName().contains(".str")){
                            std::string strdata=ConvertOpToString(stripOp);
                            errs()<<strdata<<"\n";
 
                            //加密流程
                            uint8_t keys[strdata.size()];
                            char encres[strdata.size()];
                            int idx=0;
                            memset(encres,0,strdata.size());
                            for(int i=0;i<strdata.size();i++){
                                uint8_t randkey=llvm::cryptoutils->get_uint8_t();
                                keys[i]=randkey;
                                int enclen=randkey+defaultKStringSize;
                                for(int y=randkey;y<enclen;y++){
                                    if(y==randkey){
                                        encres[i]=strdata[i]^y;
                                    }else if(y==enclen-1){
                                        encres[i]=encres[i]^(~y);
                                    }else{
                                        encres[i]=encres[i]^y;
                                    }
                                    idx++;
                                }
                            }
                        }
                    }
                }
            }
        }
 
ArrayType* arrType=ArrayType::get(Type::getInt8Ty(func.getContext()),strdata.size());
                            AllocaInst* arrayInst=new AllocaInst(arrType,0,nullptr,1,Twine(stripOp->getName()+".arr"),&ins);
                            BitCastInst* bitInst=new BitCastInst(arrayInst,Type::getInt8PtrTy(func.getParent()->getContext()),Twine(stripOp->getName()+"bitcast"),&ins);
ArrayType* arrType=ArrayType::get(Type::getInt8Ty(func.getContext()),strdata.size());
                            AllocaInst* arrayInst=new AllocaInst(arrType,0,nullptr,1,Twine(stripOp->getName()+".arr"),&ins);
                            BitCastInst* bitInst=new BitCastInst(arrayInst,Type::getInt8PtrTy(func.getParent()->getContext()),Twine(stripOp->getName()+"bitcast"),&ins);
 
ArrayType* arrType=ArrayType::get(Type::getInt8Ty(func.getContext()),strdata.size());
AllocaInst* arrayInst=new AllocaInst(arrType,0,nullptr,1,Twine(stripOp->getName()+".arr"),&ins);
BitCastInst* bitInst=new BitCastInst(arrayInst,Type::getInt8PtrTy(func.getParent()->getContext()),Twine(stripOp->getName()+".bitcast"),&ins);
//创建一个对象用来存放当前加密字节解密时每次异或的结果
AllocaInst* eor_res=new AllocaInst(Type::getInt8Ty(func.getContext()),0,nullptr,1,Twine(stripOp->getName()+".alloc.res"),&ins);
for(int i=0;i<strdata.size();i++){
    uint8_t randkey=keys[i];
    int enclen=randkey+defaultKStringSize;
    ConstantInt* enc_const=ConstantInt::get(Type::getInt8Ty(func.getContext()),encres[i]);
    //用来存放解密结果的bitcat
    ConstantInt* i_const=ConstantInt::get(Type::getInt8Ty(func.getContext()),i);
    GetElementPtrInst* element=GetElementPtrInst::CreateInBounds(bitInst,i_const);
    element->insertBefore(&ins);
    StoreInst* last_store=nullptr;
    for(int y=enclen-1;y>=randkey;y--){
        /*下面是获取y的指令块*/
        //先是创建一个数值y
        ConstantInt *eor_data = ConstantInt::get(Type::getInt8Ty(func.getContext()),y);
        //申请一个int8的内存来存放数值y
        AllocaInst* eor_alloc=new AllocaInst(Type::getInt8Ty(func.getContext()),0,nullptr,1,Twine(stripOp->getName()+".alloc.y"),&ins);
        //将数值y赋值给申请的内存空间
        StoreInst* store_eor=new StoreInst(eor_data,eor_alloc);
        store_eor->insertAfter(eor_alloc);
        //从内存空间中加载里面的数值y
        LoadInst* eor_load=new LoadInst(eor_alloc,"");
        eor_load->insertAfter(store_eor);
        //如果是第一次异或
        if(y==enclen-1){
            //然后进行取反计算
            BinaryOperator* binNotOp=BinaryOperator::CreateNot(eor_load);
            binNotOp->insertAfter(eor_load);
              //然后异或
            BinaryOperator* binXorOp=BinaryOperator::CreateXor(enc_const,binNotOp);
            binXorOp->insertAfter(binNotOp);
            //将加密字节设置为上次异或的结果
            StoreInst* store_eor_res=new StoreInst(binXorOp,eor_res);
            store_eor_res->insertAfter(store_data);
        }else{
            //加载获取上次异或的结果
            LoadInst* eor_load_res=new LoadInst(eor_res,stripOp->getName()+".load");
            eor_load_res->insertAfter(store_eor);
            //然后再进行异或计算
            BinaryOperator* binXorOp=BinaryOperator::CreateXor(eor_load_res,eor_load);
            binXorOp->insertAfter(eor_load);
            //将计算后的结果存放回数组中
            StoreInst* store_data=new StoreInst(binXorOp,eor_res);
            store_data->insertAfter(binXorOp);
            //当循环到最后一次时,获取一下最后一次赋值的指令块地址。方便后面接着往后插指令块
            if(y==randkey){
                last_store=store_data;
            }
        }
    }
      //读取这个字节经过多次异或后的最终结果
    LoadInst* dec_res=new LoadInst(eor_res,stripOp->getName()+".dec.res");
    dec_res->insertAfter(last_store);
    //将这个结果写入到前面用来存放的解密结果bitcat处
    StoreInst* store_data=new StoreInst(dec_res,element);
    store_data->insertAfter(dec_res);
}
ArrayType* arrType=ArrayType::get(Type::getInt8Ty(func.getContext()),strdata.size());
AllocaInst* arrayInst=new AllocaInst(arrType,0,nullptr,1,Twine(stripOp->getName()+".arr"),&ins);
BitCastInst* bitInst=new BitCastInst(arrayInst,Type::getInt8PtrTy(func.getParent()->getContext()),Twine(stripOp->getName()+".bitcast"),&ins);
//创建一个对象用来存放当前加密字节解密时每次异或的结果
AllocaInst* eor_res=new AllocaInst(Type::getInt8Ty(func.getContext()),0,nullptr,1,Twine(stripOp->getName()+".alloc.res"),&ins);
for(int i=0;i<strdata.size();i++){
    uint8_t randkey=keys[i];
    int enclen=randkey+defaultKStringSize;
    ConstantInt* enc_const=ConstantInt::get(Type::getInt8Ty(func.getContext()),encres[i]);
    //用来存放解密结果的bitcat
    ConstantInt* i_const=ConstantInt::get(Type::getInt8Ty(func.getContext()),i);
    GetElementPtrInst* element=GetElementPtrInst::CreateInBounds(bitInst,i_const);

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2020-12-22 17:43 被misskings编辑 ,原因: 修改标题
收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 116
活跃值: (1012)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
虽然没怎么看懂 但原理好像和dex的字符串加密差不多
2022-2-8 09:31
0
游客
登录 | 注册 方可回帖
返回
//