diff --git a/include/llvm/Transforms/Obfuscation/StringEncryption.h b/include/llvm/Transforms/Obfuscation/StringEncryption.h new file mode 100644 index 000000000000..52b0a0fa40ad --- /dev/null +++ b/include/llvm/Transforms/Obfuscation/StringEncryption.h @@ -0,0 +1,13 @@ +#ifndef __STRING_ENCRYPTION_H__ +#define __STRING_ENCRYPTION_H__ + +// LLVM include +#include "llvm/Pass.h" + +using namespace llvm; + +namespace llvm { + Pass* createXorStringEncryption(); +} + +#endif diff --git a/lib/Transforms/IPO/PassManagerBuilder.cpp b/lib/Transforms/IPO/PassManagerBuilder.cpp index 217a991c87f2..aa640f59c2da 100644 --- a/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -28,6 +28,7 @@ #include "llvm/Transforms/Obfuscation/Substitution.h" #include "llvm/Transforms/Obfuscation/Flattening.h" #include "llvm/Transforms/Obfuscation/BogusControlFlow.h" +#include "llvm/Transforms/Obfuscation/StringEncryption.h" #include "llvm/PrngAESCtr.h" @@ -73,6 +74,9 @@ BogusControlFlow("bcf",cl::init(false),cl::desc("Enable bogus control flow")); static cl::opt Substitution("sub",cl::init(false),cl::desc("Enable instruction substitutions")); +static cl::opt +StringEncryption("xse",cl::init(false),cl::desc("Enable string encryptions")); + static cl::opt AesSeed("aesSeed",cl::init(""),cl::desc("seed for the AES-CTR PRNG")); @@ -188,9 +192,13 @@ void PassManagerBuilder::populateModulePassManager(PassManagerBase &MPM) { // Substitution if(Substitution) MPM.add(createSubstitution()); + if(StringEncryption) MPM.add(createXorStringEncryption()); + return; } + if(StringEncryption) MPM.add(createXorStringEncryption()); + // Add LibraryInfo if we have some. if (LibraryInfo) MPM.add(new TargetLibraryInfo(*LibraryInfo)); diff --git a/lib/Transforms/Obfuscation/AbstractStringEncryptionPass.cpp b/lib/Transforms/Obfuscation/AbstractStringEncryptionPass.cpp new file mode 100644 index 000000000000..3f43446da3ff --- /dev/null +++ b/lib/Transforms/Obfuscation/AbstractStringEncryptionPass.cpp @@ -0,0 +1,180 @@ +#include +#include +#include +#include "llvm/Pass.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Support/raw_ostream.h" + +#include "AbstractStringEncryptionPass.h" + +using namespace llvm; + +AbstractStringEncryptionPass::AbstractStringEncryptionPass(char ID) : ModulePass(ID) { + +} + +bool AbstractStringEncryptionPass::runOnModule(Module &M) { + bool changed = false; + + uint64_t encryptedStringCounter = 0; + + StringMapGlobalVars.clear(); + std::vector StringGlobalVars; + std::vector StringGlobalVarsToDelete; + + //----------------- + //get strings + for (Module::global_iterator I = M.global_begin(), E = M.global_end(); I != E; ++I) { + GlobalVariable* GV = I; + if(GV->isConstant()){ + Constant* c = GV->getInitializer(); + ConstantDataSequential *cds = dyn_cast(c); + if(cds){ + StringGlobalVars.push_back(I); + } + } + } + + //----------------- + //encrypt strings + for(std::vector::iterator it = StringGlobalVars.begin(); it != StringGlobalVars.end(); ++it){ + GlobalVariable* GV = *it; + ConstantDataSequential *cds = dyn_cast(GV->getInitializer()); + std::string clearstr = ""; + if(cds->isString()){ + clearstr = cds->getAsString(); + }else{ + if(cds->isCString ()){ + clearstr = cds->getAsCString(); + }else{ + errs() << "Can't get string value from " << GV->getName() << " SKIP ENCRYPTION!\n"; + continue; + } + } + + //encrypt current string + std::string encryptedString = stringEncryption(clearstr); + //std::string encryptedString = clearstr; + + //create new global string with the encrypted string + //@todo check if name does not exist in module + std::ostringstream oss; + oss << ".encstr" << encryptedStringCounter; + encryptedStringCounter++; + Constant *cryptedStr = ConstantDataArray::getString(M.getContext(), encryptedString, true); + GlobalVariable* gCryptedStr = new GlobalVariable(M, cryptedStr->getType(), true, GlobalValue::ExternalLinkage, cryptedStr, oss.str()); + StringMapGlobalVars[oss.str()] = gCryptedStr; + + //replace use of clear string with encrypted string + GV->replaceAllUsesWith(gCryptedStr); + //need to remove clear text global + StringGlobalVarsToDelete.push_back(GV); + changed = true; + } + + //---------------------------------------------- + //insert decryption code where string is used + + //iterate every instruction of the module + for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) { + for (Function::iterator bb = I->begin(), e = I->end(); bb != e; ++bb) { + for (BasicBlock::iterator inst = bb->begin(); inst != bb->end(); ++inst) { + //check if instruction is call + CallInst *call = dyn_cast(inst); + if (call != 0) { + handleCall(M, call); + continue; + } + + //check if instruction is load + LoadInst *load = dyn_cast(inst); + if(load != 0){ + handleLoad(M, load); + continue; + } + + } + } + } + + //remove all clear text global variable + for(std::vector::iterator it = StringGlobalVarsToDelete.begin(); it != StringGlobalVarsToDelete.end(); ++it){ + GlobalVariable* GV = *it; + GV->eraseFromParent(); + } + + //M.dump(); + return changed; +} + +void AbstractStringEncryptionPass::handleLoad(Module &M, LoadInst* Load) { + //check if loaded pointer is global + Value* ptrOp = Load->getPointerOperand(); + GlobalVariable *GV = dyn_cast(ptrOp); + if (GV == 0) + return; + + //check if loaded pointer is constant + Constant* c = GV->getInitializer(); + if(c == 0) + return; + ConstantExpr *constExpr = dyn_cast(c); + if(constExpr == 0) + return; + + if (constExpr->getOpcode() == Instruction::GetElementPtr){ + //get GEP instruction + GetElementPtrInst* gepInst = dyn_cast(constExpr->getAsInstruction()); + if(gepInst == 0) + return; + + //check if the string is encrypted + StringRef gepOpName = gepInst->getPointerOperand()->getName(); + std::map::iterator it = StringMapGlobalVars.find(gepOpName.str()); + if(it != StringMapGlobalVars.end()){ + //get size of string + ConstantDataSequential *cds = dyn_cast(it->second->getInitializer()); + uint64_t size = cds->getNumElements(); + //generate IR to decrypt string + LoadInst* newload = new LoadInst(Load->getPointerOperand(), "", false, 8, Load); + Value* decryptedStr = stringDecryption(M, newload, size, Load); + //replace current load with the decryption code + Load->replaceAllUsesWith(decryptedStr); + } + } +} + +void AbstractStringEncryptionPass::handleCall(llvm::Module &M, llvm::CallInst* Call) { + for(unsigned i = 0; i < Call->getNumArgOperands(); i++){ + + llvm::ConstantExpr *constExpr = llvm::dyn_cast(Call->getArgOperand(i)); + //not a constant expr + if (constExpr == 0) + continue; + //not a gep + if (constExpr->getOpcode() != llvm::Instruction::GetElementPtr) + continue; + + llvm::GetElementPtrInst* gepInst = dyn_cast(constExpr->getAsInstruction()); + if(gepInst == 0) + continue; + + //load encrypted string + StringRef gepOpName = gepInst->getPointerOperand()->getName(); + std::map::iterator it = StringMapGlobalVars.find(gepOpName.str()); + if(it != StringMapGlobalVars.end()){ + //get size of string + ConstantDataSequential *cds = dyn_cast(it->second->getInitializer()); + uint64_t size = cds->getNumElements(); + //generate IR to decrypt string + llvm::Value* decryptedStr = stringDecryption(M, it->second, size, Call); + Call->setArgOperand(i, decryptedStr); + } + } +} diff --git a/lib/Transforms/Obfuscation/AbstractStringEncryptionPass.h b/lib/Transforms/Obfuscation/AbstractStringEncryptionPass.h new file mode 100644 index 000000000000..050223723687 --- /dev/null +++ b/lib/Transforms/Obfuscation/AbstractStringEncryptionPass.h @@ -0,0 +1,46 @@ +#ifndef __ABSTRACT_STRING_ENCRYPTION_PASS_H__ +#define __ABSTRACT_STRING_ENCRYPTION_PASS_H__ + +#include +#include +#include + +namespace llvm { + class Value; + class GlobalVariable; + class Module; + class LoadInst; + class CallInst; +} + +namespace llvm { + class AbstractStringEncryptionPass : public llvm::ModulePass { + public: + AbstractStringEncryptionPass(char ID); + + virtual bool runOnModule(llvm::Module &M); + + protected: + /** encryption method + * \param ClearString string to encrypt + * \return encrypted string */ + virtual std::string stringEncryption(const std::string& ClearString) = 0; + /** Decryption method, called every time a encrypted string is used. + * Should generate the llvm IR to decrypt the string + * \param M module + * \param EncryptedString encrypted string value + * \param Size size of encrypted string + * \param Parent parent instruction + * \return value that will replace the load to the encrypted string */ + virtual llvm::Value* stringDecryption(llvm::Module &M, llvm::Value* EncryptedString, const uint64_t Size, llvm::Instruction* Parent) = 0; + + private: + void handleLoad(llvm::Module &M, llvm::LoadInst* Load); + void handleCall(llvm::Module &M, llvm::CallInst* Call); + + private: + std::map StringMapGlobalVars; + }; +} + +#endif diff --git a/lib/Transforms/Obfuscation/CMakeLists.txt b/lib/Transforms/Obfuscation/CMakeLists.txt index 0ca297c1d366..6284be9dbfdd 100644 --- a/lib/Transforms/Obfuscation/CMakeLists.txt +++ b/lib/Transforms/Obfuscation/CMakeLists.txt @@ -5,6 +5,8 @@ add_llvm_library(LLVMObfuscation Substitution.cpp SubstitutionFunction.cpp BogusControlFlow.cpp + AbstractStringEncryptionPass.cpp + XorStringEncryption.cpp ) add_dependencies(LLVMObfuscation intrinsics_gen) diff --git a/lib/Transforms/Obfuscation/XorStringEncryption.cpp b/lib/Transforms/Obfuscation/XorStringEncryption.cpp new file mode 100644 index 000000000000..40a5e30ab959 --- /dev/null +++ b/lib/Transforms/Obfuscation/XorStringEncryption.cpp @@ -0,0 +1,75 @@ +#include +#include "llvm/Pass.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/Support/raw_ostream.h" + +#include "llvm/Transforms/Obfuscation/StringEncryption.h" +#include "XorStringEncryption.h" + +#define DEBUG_TYPE "xorstringencryption" + +using namespace llvm; + +XorStringEncryption::XorStringEncryption(uint32_t KeySize) : AbstractStringEncryptionPass(ID){ + _key = generateRandomKey(KeySize); +} + +XorStringEncryption::XorStringEncryption(const std::string& Key) : AbstractStringEncryptionPass(ID){ + _key = Key; +} + +std::string XorStringEncryption::generateRandomKey(uint32_t Size){ + std::string allowedChar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.-#'?!"; + std::string key; + for(uint32_t i = 0; i < Size; i++){ + int n = rand() % allowedChar.size(); + key += allowedChar[n]; + } + //errs() << key << "\n"; + return key; +} + +std::string XorStringEncryption::stringEncryption(const std::string& str_) { + std::string encstr = str_; + for(uint32_t i = 0; i < encstr.size(); i++){ + encstr[i] ^= _key[i%_key.size()]; + } + return encstr; +} + +llvm::Value* XorStringEncryption::stringDecryption(llvm::Module &M, llvm::Value* encryptedString, const uint64_t Size, llvm::Instruction* parent) { + //allocate a new string + AllocaInst* alloca = new AllocaInst(IntegerType::getInt8Ty(M.getContext()), ConstantInt::get(IntegerType::getInt64Ty(M.getContext()), Size), "", parent); + + for(uint64_t i = 0; i < Size; i++){ + std::vector idxlist; + idxlist.push_back(ConstantInt::get(IntegerType::getInt64Ty(M.getContext()), i)); + GetElementPtrInst* destPtr = GetElementPtrInst::CreateInBounds(alloca, ArrayRef(idxlist), "", parent); + + if(not dyn_cast(encryptedString)){ + idxlist.clear(); + idxlist.push_back(ConstantInt::get(IntegerType::getInt64Ty(M.getContext()), 0)); + idxlist.push_back(ConstantInt::get(IntegerType::getInt64Ty(M.getContext()), i)); + //convert [NB x i8]* to i8 *... + //%cast = getelementptr [NB x i8]* @.str, i64 0, i64 i + } + + GetElementPtrInst* srcPtr = GetElementPtrInst::Create(encryptedString, ArrayRef(idxlist), "", parent); + LoadInst* srcload = new LoadInst(srcPtr, "", false, 8, parent); + + BinaryOperator* clearChar = BinaryOperator::CreateXor(srcload, ConstantInt::get(IntegerType::getInt8Ty(M.getContext()), _key[i%_key.size()]), "", parent); + new StoreInst(clearChar, destPtr, false, 8, parent); + } + return alloca; +} + +char XorStringEncryption::ID = 0; +static RegisterPass X("xorscrypt", "Xor String Encryption Pass"); + +Pass *llvm::createXorStringEncryption() { + return new XorStringEncryption(); +} diff --git a/lib/Transforms/Obfuscation/XorStringEncryption.h b/lib/Transforms/Obfuscation/XorStringEncryption.h new file mode 100644 index 000000000000..0a78c9457937 --- /dev/null +++ b/lib/Transforms/Obfuscation/XorStringEncryption.h @@ -0,0 +1,43 @@ +#ifndef __XOR_STRING_ENCRYPTION_H__ +#define __XOR_STRING_ENCRYPTION_H__ + +#include +#include "AbstractStringEncryptionPass.h" + +namespace llvm { + class Module; + class LoadInst; +} + +namespace llvm { + class XorStringEncryption : public AbstractStringEncryptionPass { + public: + static char ID; + + XorStringEncryption(uint32_t KeySize = 80); + XorStringEncryption(const std::string& Key); + + protected: + /** encryption method + * \param ClearString string to encrypt + * \return encrypted string */ + virtual std::string stringEncryption(const std::string& ClearString); + /** Decryption method, called every time a encrypted string is used. + * Should generate the llvm IR to decrypt the string + * \param M module + * \param EncryptedString encrypted string value + * \param Size size of encrypted string + * \param Parent parent instruction + * \return value that will replace the load to the encrypted string */ + virtual llvm::Value* stringDecryption(llvm::Module &M, llvm::Value* EncryptedString, const uint64_t Size, llvm::Instruction* Parent); + + private: + /** generate random key */ + std::string generateRandomKey(uint32_t Size); + + private: + std::string _key; + }; +} + +#endif