关于ollvm字符串加密那些事

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于ollvm字符串加密那些事相关的知识,希望对你有一定的参考价值。

参考技术A 方法:一个字节一个字节的与一个随机字节异或
特征:导出函数中.datadiv_decode000000000000000
这里分为三种情况来介绍:

我们可以使用010editer把值异或回去

以上方法用作静态分析也可以看到,但是效率太低了
这里我们考虑使用frida去找到已经解密在内存中的数据查看

简单改改类型得到以下信息:

IDA静态直接进去到37050地址,发现是一个data段的加密数据,这个数据在运行时是已经解谜的,所以直接去hook查看这个地址的值即可

实际上是在init_array中的函数解密字符串,完成后在内存中就是明文状态了

但是解密函数依旧会出现在init_array节区

可以看到函数名已经变化了,不同于标准的函数名
但是这里用类似的方式进行处理

我们在回到JNI_Onload中找到解密出来字符串指针

知道它是五个长度直接转为六个长度的字节数组

这里在ida中看到的也是加密后的,可以看到右边有这个字符串解密的函数引用,这就是对应我们刚开始在init_array中的字符串解密函数
由于我们刚才将这里的长度定义为了一个六个字节长度的数组,所以这里F5一下可以看到如下:

综上可得在这个解密函数中,每一次换了随机的异或常量,这里就代表有一个新的字符串

这个玩意儿只是异或的不同写法,和上面的异或一个道理,且仅存在与arm64中

解密函数就出现在使用字符串的前面,在哪里用那里解密
这里也是很明显的特点

这都是处于.bss段,意味着一开始没有初始化,只是一个引用放在这里
面对这种东西我们自然也是可以用frida去hook查看这个(0x3E195)位置的值
或者用hook libart.so去hook住RegisterNative
函数,再或者偷个懒用jnitrace即可hook关键函数,拿到解密后的字符串
这种操作他的解密函数可以很复杂,也不要求解密后的大小等于加密后的大小

回看前两种处理方式,都是简单异或操作完成的,数据都是处于.data段,异或完成后依旧是放在原位置的,程序真正运行前已经解密完成,第三种方式是独立的.bss段存放解密后的字符串,即运行到指定位置后才开始解密

最后补充一句:
frida的inlinehook对于thumb指令不太稳定(主要就是这种两字节的指令不一定成功)
arm 和 arm64 都不错(四字节都没问题)

ollvm 新增字符串加密功能

好久没弄ollvm了,可以继续了,今天给ollvm新增了一个pass,用来加密字符串,这个pass是从别的库里面扒出来的。

本文是基于在Windows 上使用VS2017编译出来的ollvm,在这个基础上来添加。

 

第一步:

寻找两个pass的代码

头文件

技术图片
 1 #ifndef _STRING_OBFUSCATION_H_
 2 #define _STRING_OBFUSCATION_H_
 3 
 4 
 5 // LLVM include
 6 #include "llvm/Pass.h"
 7 #include "llvm/IR/Function.h"
 8 #include "llvm/IR/Instructions.h"
 9 #include "llvm/ADT/Statistic.h"
10 #include "llvm/Transforms/IPO.h"
11 #include "llvm/IR/Module.h"
12 #include "llvm/Support/CommandLine.h"
13 #include "llvm/CryptoUtils.h"
14 
15 // Namespace
16 using namespace llvm;
17 using namespace std;
18 
19 namespace llvm 
20     Pass *createStringObfuscation(bool flag);
21 
22 
23 #endif
View Code

源文件

技术图片
  1 #define DEBUG_TYPE "objdiv"
  2 #include <string>
  3 #include <sstream>
  4 
  5 #include "llvm/ADT/Statistic.h"
  6 #include "llvm/IR/Function.h"
  7 #include "llvm/IR/Constants.h"
  8 #include "llvm/IR/Module.h"
  9 #include "llvm/IR/Value.h"
 10 #include "llvm/Pass.h"
 11 #include "llvm/Support/raw_ostream.h"
 12 #include "llvm/CryptoUtils.h"
 13 #include "llvm/Transforms/Obfuscation/StringObfuscation.h"
 14 #include "llvm/IR/IRBuilder.h"
 15 #include "llvm/Transforms/Utils/ModuleUtils.h"
 16 
 17 using namespace llvm;
 18 
 19 STATISTIC(GlobalsEncoded, "Counts number of global variables encoded");
 20 
 21 #define ZooPrint(_F, ...) fprintf(stdout, "File : [%s](%d) " _F, __FILE__, __LINE__, __VA_ARGS__)
 22 
 23 namespace llvm 
 24 
 25     struct encVar 
 26     public:
 27         GlobalVariable *var;
 28         uint8_t key;
 29     ;
 30 
 31     class StringObfuscationPass : public llvm::ModulePass 
 32     public:
 33         static char ID; // pass identification
 34         bool is_flag = false;
 35         StringObfuscationPass() : ModulePass(ID) 
 36         StringObfuscationPass(bool flag) : ModulePass(ID)
 37         
 38             is_flag = flag;
 39         
 40 
 41         virtual bool runOnModule(Module &M) 
 42             ZooPrint(" Run On Module : %d \\n", is_flag);
 43             if (!is_flag)
 44                 return false;
 45             std::vector<GlobalVariable*> toDelConstGlob;
 46             //std::vector<GlobalVariable*> encGlob;
 47             std::vector<encVar*> encGlob;
 48             ZooPrint(" M.Size : %d \\n", M.size());
 49             int i = 0;
 50             for (Module::global_iterator gi = M.global_begin(), ge = M.global_end(); gi != ge; ++gi)
 51             
 52 
 53 #if 0
 54                 //    老式代码,原来的样子
 55                 @.str = private unnamed_addr constant[13 x i8] c"\\E4\\BD\\A0\\E5\\A5\\BD\\E4\\B8\\96\\E7\\95\\8C\\00", align 1
 56                 @__CFConstantStringClassReference = external global[0 x i32]
 57                 @.str.1 = private unnamed_addr constant[3 x i16][i16 20320, i16 22909, i16 0], section "__TEXT,__ustring", align 2
 58                 //    新式字符串的样子
 59                 @"\\01??_C@_07CHPFNFHA@123456?6?$AA@" = linkonce_odr unnamed_addr constant [8 x i8] c"123456\\0A\\00", comdat, align 1
 60                 @"\\01??_C@_03PMGGPEJJ@?$CFd?6?$AA@" = linkonce_odr unnamed_addr constant [4 x i8] c"%d\\0A\\00", comdat, align 1
 61                 @__local_stdio_printf_options._OptionsStorage = internal global i64 0, align 8
 62 #endif
 63                 // Loop over all global variables
 64                 GlobalVariable* gv = &(*gi);
 65                 //errs() << "Global var " << gv->getName();
 66                 //std::string::size_type str_idx = gv->getName().str().find(".str.");
 67                 std::string section(gv->getSection());
 68 
 69                 ZooPrint(" %d : String : \\"%s\\" , section : \\"%s\\" , isConstant : %d , hasInitializer : %d , isa : %d , r : %d \\n", i++, gv->getName().str().c_str(), section.c_str(), gv->isConstant(), gv->hasInitializer(), isa<ConstantDataSequential>(gv->getInitializer()), gv->getName().str().substr(0, 8) == "\\"\\x01??_C@_");
 70                 //    ZooPrint("      0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X \\n", gv->getName()[0] & 0xFF, gv->getName()[1] & 0xFF, gv->getName()[2] & 0xFF, gv->getName()[3] & 0xFF, gv->getName()[4] & 0xFF, gv->getName()[5] & 0xFF, gv->getName()[6] & 0xFF, gv->getName()[7] & 0xFF);
 71 
 72                 // Let‘s encode the static ones
 73                 //if (gv->getName().str().substr(0, 4) == ".str"&&
 74                 if (gv->getName().str().substr(0, 7) == "\\x01??_C@_" &&
 75                     gv->isConstant() &&
 76                     gv->hasInitializer() &&
 77                     isa<ConstantDataSequential>(gv->getInitializer()) &&
 78                     section != "llvm.metadata" &&
 79                     section.find("__objc_methname") == std::string::npos
 80                     /*&&gv->getType()->getArrayElementType()->getArrayElementType()->isIntegerTy()*/)
 81                 
 82                     ZooPrint(" In Global Encode \\n");
 83                     ++GlobalsEncoded;
 84                     //errs() << " is constant";
 85 
 86                     // Duplicate global variable
 87                     GlobalVariable *dynGV = new GlobalVariable(M,
 88                         gv->getType()->getElementType(),
 89                         !(gv->isConstant()), gv->getLinkage(),
 90                         (Constant*)0, gv->getName(),
 91                         (GlobalVariable*)0,
 92                         gv->getThreadLocalMode(),
 93                         gv->getType()->getAddressSpace());
 94                     // dynGV->copyAttributesFrom(gv);
 95                     dynGV->setInitializer(gv->getInitializer());
 96 
 97                     std::string tmp = gv->getName().str();
 98                     //  errs()<<"GV: "<<*gv<<"\\n";
 99 
100                     Constant *initializer = gv->getInitializer();
101                     ConstantDataSequential *cdata = dyn_cast<ConstantDataSequential>(initializer);
102                     if (cdata) 
103                         const char *orig = cdata->getRawDataValues().data();
104                         unsigned len = cdata->getNumElements()*cdata->getElementByteSize();
105 
106                         encVar *cur = new encVar();
107                         cur->var = dynGV;
108                         cur->key = llvm::cryptoutils->get_uint8_t();
109                         // casting away const is undef. behavior in C++
110                         // TODO a clean implementation would retrieve the data, generate a new constant
111                         // set the correct type, and copy the data over.
112                         //char *encr = new char[len];
113                         //Constant *initnew = ConstantDataArray::getString(M.getContext(), encr, true);
114                         char *encr = const_cast<char *>(orig);
115                         // Simple xor encoding
116                         for (unsigned i = 0; i != len; ++i) 
117                             encr[i] = orig[i] ^ cur->key;
118                         
119 
120                         // FIXME Second part of the unclean hack.
121                         dynGV->setInitializer(initializer);
122 
123                         // Prepare to add decode function for this variable
124                         encGlob.push_back(cur);
125                     
126                     else 
127                         // just copying default initializer for now
128                         dynGV->setInitializer(initializer);
129                     
130 
131                     // redirect references to new GV and remove old one
132                     gv->replaceAllUsesWith(dynGV);
133                     toDelConstGlob.push_back(gv);
134 
135                 
136             
137 
138             // actuallte delete marked globals
139             for (unsigned i = 0, e = toDelConstGlob.size(); i != e; ++i)
140                 toDelConstGlob[i]->eraseFromParent();
141 
142             addDecodeFunction(&M, &encGlob);
143 
144 
145             return true;
146         
147 
148     private:
149         void addDecodeFunction(Module *mod, std::vector<encVar*> *gvars) 
150             ZooPrint(" Add Decode Function \\n");
151             // Declare and add the function definition
152             //errs()<<"Successful enter decode function"<<"\\n";
153             std::vector<Type*>FuncTy_args;
154             FunctionType* FuncTy = FunctionType::get(
155                 /*Result=*/Type::getVoidTy(mod->getContext()),  // returning void
156                 /*Params=*/FuncTy_args,  // taking no args
157                 /*isVarArg=*/false);
158             uint64_t StringObfDecodeRandomName = cryptoutils->get_uint64_t();
159             std::string  random_str;
160             std::stringstream random_stream;
161             random_stream << StringObfDecodeRandomName;
162             random_stream >> random_str;
163             StringObfDecodeRandomName++;
164             Constant* c = mod->getOrInsertFunction(".datadiv_decode" + random_str, FuncTy);
165             Function* fdecode = cast<Function>(c);
166             fdecode->setCallingConv(CallingConv::C);
167 
168 
169             BasicBlock* entry = BasicBlock::Create(mod->getContext(), "entry", fdecode);
170 
171             IRBuilder<> builder(mod->getContext());
172             builder.SetInsertPoint(entry);
173 
174 
175             for (unsigned i = 0, e = gvars->size(); i != e; ++i) 
176                 GlobalVariable *gvar = (*gvars)[i]->var;
177                 uint8_t key = (*gvars)[i]->key;
178 
179                 Constant *init = gvar->getInitializer();
180                 ConstantDataSequential *cdata = dyn_cast<ConstantDataSequential>(init);
181 
182                 unsigned len = cdata->getNumElements()*cdata->getElementByteSize();
183                 --len;
184 
185                 BasicBlock *preHeaderBB = builder.GetInsertBlock();
186                 BasicBlock* for_body = BasicBlock::Create(mod->getContext(), "for-body", fdecode);
187                 BasicBlock* for_end = BasicBlock::Create(mod->getContext(), "for-end", fdecode);
188                 builder.CreateBr(for_body);
189                 builder.SetInsertPoint(for_body);
190                 PHINode *variable = builder.CreatePHI(Type::getInt32Ty(mod->getContext()), 2, "i");
191                 Value *startValue = builder.getInt32(0);
192                 Value *endValue = builder.getInt32(len);
193                 variable->addIncoming(startValue, preHeaderBB);
194                 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
195 
196                 //LoadInst *Load=builder.CreateLoad(gvar);
197                 //errs()<<"Load: "<<*(Load->getPointerOperand())<<"\\n";
198                 Value* indexList[2] =  ConstantInt::get(variable->getType(), 0), variable ;
199                 Value *const_key = builder.getInt8(key);
200                 Value *GEP = builder.CreateGEP(gvar, ArrayRef<Value*>(indexList, 2), "arrayIdx");
201                 LoadInst *loadElement = builder.CreateLoad(GEP, false);
202                 loadElement->setAlignment(1);
203                 //errs()<<"Type: "<<*loadElement<<"\\n";
204                 //CastInst* extended = new ZExtInst(const_key, loadElement->getType(), "extended", for_body);
205                 //Value* extended = builder.CreateZExtOrBitCast(const_key, loadElement->getType(),"extended");
206                 Value *Xor = builder.CreateXor(loadElement, const_key, "xor");
207                 StoreInst *Store = builder.CreateStore(Xor, GEP, false);
208                 Store->setAlignment(1);
209 
210                 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
211                 Value *stepValue = builder.getInt32(1);
212                 Value *nextValue = builder.CreateAdd(variable, stepValue, "next-value");
213                 Value *endCondition = builder.CreateICmpULT(variable, endValue, "end-condition");
214                 endCondition = builder.CreateICmpNE(endCondition, builder.getInt1(0), "loop-condition");
215                 BasicBlock *loopEndBB = builder.GetInsertBlock();
216                 builder.CreateCondBr(endCondition, loopEndBB, for_end);
217                 builder.SetInsertPoint(for_end);
218                 variable->addIncoming(nextValue, loopEndBB);
219 
220             
221             builder.CreateRetVoid();
222             appendToGlobalCtors(*mod, fdecode, 0);
223 
224 
225         
226 
227     ;
228 
229     Pass *createStringObfuscation(bool flag);
230 
231 
232 #if 0
233 RegisterPass(const char *PassArg, const char *Name, bool CFGOnly = false, bool is_analysis = false)
234 
235 上面这个是RegisterPass的构造函数。
236 参数说明:
237 
238 template<typename passName>        :YourPassName;
239                     PassArg        :opt调用时所用的命行参数;
240                     Name        :此pass的简要说明;
241                     CFGOnly        :如果一个遍历CFG而不修改它,那么这个参数被设置为true;
242                     is_analysis    :如果一个Pass是一个分析Pass,例如dominator tree pass,那么这个参数被设置为true。
243 例子:
244 
245 static RegisterPass<Hello> X("hello", "Hello World Pass", false, false);
246 #endif
247 
248 char StringObfuscationPass::ID = 0;
249 static RegisterPass<StringObfuscationPass> X("GVDiv", "Global variable (i.e., const char*) diversification pass", false, true);
250 
251 Pass * llvm::createStringObfuscation(bool flag) 
252     ZooPrint("new my pass \\n");
253     return new StringObfuscationPass(flag);
254 
View Code

 

第二步:

将头文件放在如下位置:ollvm\\obfuscator-llvm-4.0\\include\\llvm\\Transforms\\Obfuscation\\StringObfuscation.h

将源文件放在如下位置:ollvm\\obfuscator-llvm-4.0\\lib\\Transforms\\Obfuscation\\StringEncode.cpp

 

第三步:

将源文件放到如下工程中

技术图片

 

第四步:

在此文件中新增代码:ollvm\\obfuscator-llvm-4.0\\lib\\Transforms\\IPO\\PassManagerBuilder.cpp

新增导入头文件

 1 #include "llvm/Transforms/Obfuscation/StringObfuscation.h" 

新增全局变量代码如下

1 static cl::opt<std::string> Seed("seed", cl::init(""),
2                             cl::desc("seed for the random"));
3 
4 //    全局开关,根据参数判断是否设置
5 static cl::opt<bool> StringObf("sobf", cl::init(false),
6                             cl::desc("Enable the string obfuscation"));

在:PassManagerBuilder::populateModulePassManager 函数中,新增挂载新的pass代码,如下

 1 MPM.add(createStringObfuscation(StringObf)); 

意义为根据全局开关来判断是否启用当前pass

 

 

经过以上四步,问题全部解决了,直接重新编译ollvm即可。

后续可以修改pass代码,可以修改解密函数。

新增其他pass新增步骤也如上。

 

 

 

使用方式如下

 1 G:\\ollvm\\Test>G:\\ollvm\\build\\RelWithDebInfo\\bin\\clang.exe -mllvm -sobf -mllvm -fla main.c 

含义是,开启字符串加密,并且启动代码扁平化

效果:

源代码如下

技术图片

编译后如下

技术图片

已经开启了代码扁平化,原始字符串也已经不一样了,具体情况,

看data段就好了:

技术图片

已经完全没个人样了

 

 

重点在最后,忘了,补充一句,由于字符串在ollvm里面是以UTF8的格式保存的,所以中文字符串天然就是乱码,

有时间想办法来解决一下中文字符串的乱码问题。

这个pass挺简单的,我也没怎么改就拿来用了,后续有时间再改一下吧。

以上是关于关于ollvm字符串加密那些事的主要内容,如果未能解决你的问题,请参考以下文章

关于js正则表达式的那些事

SQL开发实战技巧系列:关于SQL不得不说的那些事

关于DevOps 的那些事

关于元素水平垂直居中的那些事?

关于ArrayList的那些事

[DB那些事]数据库加密