关于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
源文件
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
第二步:
将头文件放在如下位置: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字符串加密那些事的主要内容,如果未能解决你的问题,请参考以下文章