From b07a7434e0cc06887981a4923187be7e44694f42 Mon Sep 17 00:00:00 2001 From: Zhang <403799106@qq.com> Date: Sat, 26 May 2018 10:35:58 +0100 Subject: [PATCH] Ooops --- .travis.yml | 10 - .../Obfuscation/FunctionCallObfuscate.cpp | 357 +++++++++++++----- .../Obfuscation/FunctionWrapper.cpp | 9 +- 3 files changed, 270 insertions(+), 106 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 81aebb72c..000000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: c++ -branches: - except: - - master -os: - - osx - - linux -compiler: clang -script: -- mkdir Build && cd Build && cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=MinSizeRel -DLLVM_INCLUDE_EXAMPLES=off -DLLVM_TARGETS_TO_BUILD="ARM;X86;AArch64" -DLLVM_APPEND_VC_REV=on ../ && make -j8 diff --git a/lib/Transforms/Obfuscation/FunctionCallObfuscate.cpp b/lib/Transforms/Obfuscation/FunctionCallObfuscate.cpp index 15dbc8051..aad5f465a 100644 --- a/lib/Transforms/Obfuscation/FunctionCallObfuscate.cpp +++ b/lib/Transforms/Obfuscation/FunctionCallObfuscate.cpp @@ -1,5 +1,7 @@ /* - * LLVM FunctionWrapper Pass + * LLVM CallSite Obfuscation Pass + * It works by scanning all CallSites that refers to a function outside of + * current translation unit then replace then with dlopen/dlsym calls Copyright (C) 2017 Zhang(https://github.com/Naville/) This program is free software: you can redistribute it and/or modify @@ -11,129 +13,300 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ + +#include "json.hpp" +#include "llvm/ADT/Triple.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/Constants.h" #include "llvm/IR/IRBuilder.h" -#include "llvm/IR/InstIterator.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/Value.h" #include "llvm/Pass.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Obfuscation/Obfuscation.h" +#include #include +#include #include #include +#include #include using namespace llvm; using namespace std; -static cl::opt - ProbRate("fw_prob", - cl::desc("Choose the probability [%] For Each CallSite To Be " - "Obfuscated By FunctionWrapper"), - cl::value_desc("Probability Rate"), cl::init(30), cl::Optional); -static cl::opt ObfTimes( - "fw_times", - cl::desc( - "Choose how many time the FunctionWrapper pass loop on a CallSite"), - cl::value_desc("Number of Times"), cl::init(2), cl::Optional); +using json = nlohmann::json; +static cl::opt + SymbolConfigPath("fcoconfig", + cl::desc("FunctionCallObfuscate Configuration Path"), + cl::value_desc("filename"), cl::init("+-x/")); namespace llvm { -struct FunctionWrapper : public ModulePass { +struct FunctionCallObfuscate : public FunctionPass { static char ID; + json Configuration; bool flag; - FunctionWrapper() : ModulePass(ID) { this->flag = true; } - FunctionWrapper(bool flag) : ModulePass(ID) { this->flag = flag; } + FunctionCallObfuscate() : FunctionPass(ID) { this->flag = true; } + FunctionCallObfuscate(bool flag) : FunctionPass(ID) { this->flag = flag; } StringRef getPassName() const override { - return StringRef("FunctionWrapper"); + return StringRef("FunctionCallObfuscate"); } - bool runOnModule(Module &M) override { - vector callsites; - for (Module::iterator iter = M.begin(); iter != M.end(); iter++) { - Function &F = *iter; - if (toObfuscate(flag, &F, "fw")) { - errs() << "Running FunctionWrapper On " << F.getName() << "\n"; - for (inst_iterator fi = inst_begin(&F); fi != inst_end(&F); fi++) { - Instruction *Inst = &*fi; - if (isa(Inst) || isa(Inst)) { - if ((int)llvm::cryptoutils->get_range(100) <= ProbRate) { - callsites.push_back(new CallSite(Inst)); - } - } - } + virtual bool doInitialization(Module &M) override { + // Basic Defs + if (SymbolConfigPath == "+-x/") { + SmallString<32> Path; + if (sys::path::home_directory(Path)) { // Stolen from LineEditor.cpp + sys::path::append(Path, "Hikari", "SymbolConfig.json"); + SymbolConfigPath = Path.str(); } } - for (CallSite *CS : callsites) { - for (int i = 0; i < ObfTimes && CS != nullptr; i++) { - CS = HandleCallSite(CS); - } + ifstream infile(SymbolConfigPath); + if (infile.good()) { + errs() << "Loading Symbol Configuration From:" << SymbolConfigPath + << "\n"; + infile >> this->Configuration; + } else { + errs() << "Failed To Loading Symbol Configuration From:" + << SymbolConfigPath << "\n"; } - return true; - } // End of runOnModule - CallSite *HandleCallSite(CallSite *CS) { - Value *calledFunction = CS->getCalledFunction(); - if (calledFunction == nullptr) { - calledFunction = CS->getCalledValue()->stripPointerCasts(); + Triple tri(M.getTargetTriple()); + if (tri.getVendor() != Triple::VendorType::Apple) { + return false; } - // Filter out IndirectCalls that depends on the context - // Otherwise It'll be blantantly troublesome since you can't reference an - // Instruction outside its BB Too much trouble for a hobby project - // To be precise, we only keep CS that refers to a non-intrinsic function - // either directly or through casting - if (calledFunction == nullptr || - (!isa(calledFunction) && - !isa(calledFunction)) || - CS->getIntrinsicID() != Intrinsic::ID::not_intrinsic) { - return nullptr; + Type *Int64Ty = Type::getInt64Ty(M.getContext()); + Type *Int32Ty = Type::getInt32Ty(M.getContext()); + Type *Int8PtrTy = Type::getInt8PtrTy(M.getContext()); + Type *Int8Ty = Type::getInt8Ty(M.getContext()); + // Generic ObjC Runtime Declarations + FunctionType *IMPType = + FunctionType::get(Int8PtrTy, {Int8PtrTy, Int8PtrTy}, true); + PointerType *IMPPointerType = PointerType::get(IMPType, 0); + vector classReplaceMethodTypeArgs; + classReplaceMethodTypeArgs.push_back(Int8PtrTy); + classReplaceMethodTypeArgs.push_back(Int8PtrTy); + classReplaceMethodTypeArgs.push_back(IMPPointerType); + classReplaceMethodTypeArgs.push_back(Int8PtrTy); + FunctionType *class_replaceMethod_type = + FunctionType::get(IMPPointerType, classReplaceMethodTypeArgs, false); + M.getOrInsertFunction("class_replaceMethod", class_replaceMethod_type); + FunctionType *sel_registerName_type = + FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); + M.getOrInsertFunction("sel_registerName", sel_registerName_type); + FunctionType *objc_getClass_type = + FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); + M.getOrInsertFunction("objc_getClass", objc_getClass_type); + M.getOrInsertFunction("objc_getMetaClass", objc_getClass_type); + StructType *objc_property_attribute_t_type = reinterpret_cast( + M.getTypeByName("struct.objc_property_attribute_t")); + if (objc_property_attribute_t_type == NULL) { + vector types; + types.push_back(Int8PtrTy); + types.push_back(Int8PtrTy); + objc_property_attribute_t_type = StructType::create( + ArrayRef(types), "struct.objc_property_attribute_t"); + M.getOrInsertGlobal("struct.objc_property_attribute_t", + objc_property_attribute_t_type); } - if (Function *tmp = dyn_cast(calledFunction)) { - if (tmp->getName().startswith("clang.")) { - // Clang Intrinsic - return nullptr; - } + vector allocaClsTypeVector; + vector addIvarTypeVector; + vector addPropTypeVector; + allocaClsTypeVector.push_back(Int8PtrTy); + allocaClsTypeVector.push_back(Int8PtrTy); + addIvarTypeVector.push_back(Int8PtrTy); + addIvarTypeVector.push_back(Int8PtrTy); + addPropTypeVector.push_back(Int8PtrTy); + addPropTypeVector.push_back(Int8PtrTy); + addPropTypeVector.push_back(objc_property_attribute_t_type->getPointerTo()); + if (tri.isArch64Bit()) { + // We are 64Bit Device + allocaClsTypeVector.push_back(Int64Ty); + addIvarTypeVector.push_back(Int64Ty); + addPropTypeVector.push_back(Int64Ty); + } else { + // Not 64Bit.However we are still on apple platform.So We are + // ARMV7/ARMV7S/i386 + // PowerPC is ignored, feel free to open a PR if you want to + allocaClsTypeVector.push_back(Int32Ty); + addIvarTypeVector.push_back(Int32Ty); + addPropTypeVector.push_back(Int32Ty); } - // Create a new function which in turn calls the actual function - vector types; - for (unsigned i = 0; i < CS->getNumArgOperands(); i++) { - types.push_back(CS->getArgOperand(i)->getType()); + addIvarTypeVector.push_back(Int8Ty); + addIvarTypeVector.push_back(Int8PtrTy); + // Types Collected. Now Inject Functions + FunctionType *addIvarType = + FunctionType::get(Int8Ty, ArrayRef(addIvarTypeVector), false); + M.getOrInsertFunction("class_addIvar", addIvarType); + FunctionType *addPropType = + FunctionType::get(Int8Ty, ArrayRef(addPropTypeVector), false); + M.getOrInsertFunction("class_addProperty", addPropType); + FunctionType *class_getName_Type = + FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); + M.getOrInsertFunction("class_getName", class_getName_Type); + FunctionType *objc_getMetaClass_Type = + FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); + M.getOrInsertFunction("objc_getMetaClass", objc_getMetaClass_Type); + return true; + } + void HandleObjC(Module &M) { + // Iterate all CLASSREF uses and replace with objc_getClass() call + // Strings are encrypted in other passes + for (auto G = M.global_begin(); G != M.global_end(); G++) { + GlobalVariable &GV = *G; + if (GV.getName().str().find("OBJC_CLASSLIST_REFERENCES") == 0) { + if (GV.hasInitializer()) { + string className = GV.getInitializer()->getName(); + className.replace(className.find("OBJC_CLASS_$_"), + strlen("OBJC_CLASS_$_"), ""); + for (auto U = GV.user_begin(); U != GV.user_end(); U++) { + if (Instruction *I = dyn_cast(*U)) { + IRBuilder<> builder(I); + Function *objc_getClass_Func = + cast(M.getFunction("objc_getClass")); + Value *newClassName = + builder.CreateGlobalStringPtr(StringRef(className)); + CallInst *CI = + builder.CreateCall(objc_getClass_Func, {newClassName}); + // We need to bitcast it back to avoid IRVerifier + Value *BCI = builder.CreateBitCast(CI, I->getType()); + I->replaceAllUsesWith(BCI); + I->eraseFromParent(); + } + } + GV.removeDeadConstantUsers(); + if (GV.getNumUses() == 0) { + GV.dropAllReferences(); + GV.eraseFromParent(); + } + } + } + // Selector Convert + else if (GV.getName().str().find("OBJC_SELECTOR_REFERENCES") == 0) { + if (GV.hasInitializer()) { + ConstantExpr *CE = dyn_cast(GV.getInitializer()); + Constant *C = CE->getOperand(0); + GlobalVariable *SELNameGV = dyn_cast(C); + ConstantDataArray *CDA = + dyn_cast(SELNameGV->getInitializer()); + StringRef SELName = CDA->getAsString(); // This is REAL Selector Name + for (auto U = GV.user_begin(); U != GV.user_end(); U++) { + if (Instruction *I = dyn_cast(*U)) { + IRBuilder<> builder(I); + Function *sel_registerName_Func = + cast(M.getFunction("sel_registerName")); + Value *newGlobalSELName = builder.CreateGlobalStringPtr(SELName); + CallInst *CI = + builder.CreateCall(sel_registerName_Func, {newGlobalSELName}); + // We need to bitcast it back to avoid IRVerifier + Value *BCI = builder.CreateBitCast(CI, I->getType()); + I->replaceAllUsesWith(BCI); + I->eraseFromParent(); + } + } + GV.removeDeadConstantUsers(); + if (GV.getNumUses() == 0) { + GV.dropAllReferences(); + GV.eraseFromParent(); + } + } + } } - FunctionType *ft = - FunctionType::get(CS->getType(), ArrayRef(types), false); - Function *func = - Function::Create(ft, GlobalValue::LinkageTypes::InternalLinkage, - "HikariFunctionWrapper", CS->getParent()->getModule()); - appendToCompilerUsed(*func->getParent(), {func}); - // FIXME: Correctly Steal Function Attributes - //func->addFnAttr(Attribute::AttrKind::OptimizeNone); - //func->addFnAttr(Attribute::AttrKind::NoInline); - func->copyAttributesFrom(cast(calledFunction)); - BasicBlock *BB = BasicBlock::Create(func->getContext(), "", func); - IRBuilder<> IRB(BB); - vector params; - for (auto arg = func->arg_begin(); arg != func->arg_end(); arg++) { - params.push_back(arg); + } + virtual bool runOnFunction(Function &F) override { + // Construct Function Prototypes + if (toObfuscate(flag, &F, "fco") == false) { + return false; } - Value *retval = IRB.CreateCall(ConstantExpr::getBitCast(cast(calledFunction),CS->getCalledValue()->getType()), ArrayRef(params)); - if (ft->getReturnType()->isVoidTy()) { - IRB.CreateRetVoid(); - } else { - IRB.CreateRet(retval); + errs() << "Running FunctionCallObfuscate On " << F.getName() << "\n"; + Module *M = F.getParent(); + FixFunctionConstantExpr(&F); + HandleObjC(*M); + Type *Int32Ty = Type::getInt32Ty(M->getContext()); + Type *Int8PtrTy = Type::getInt8PtrTy(M->getContext()); + // ObjC Runtime Declarations + FunctionType *dlopen_type = FunctionType::get( + Int8PtrTy, {Int8PtrTy, Int32Ty}, + false); // int has a length of 32 on both 32/64bit platform + FunctionType *dlsym_type = + FunctionType::get(Int8PtrTy, {Int8PtrTy, Int8PtrTy}, false); + Function *dlopen_decl = + cast(M->getOrInsertFunction("dlopen", dlopen_type)); + Function *dlsym_decl = + cast(M->getOrInsertFunction("dlsym", dlsym_type)); + // Begin Iteration + for (BasicBlock &BB : F) { + for (auto I = BB.getFirstInsertionPt(), end = BB.end(); I != end; ++I) { + Instruction &Inst = *I; + if (isa(&Inst) || isa(&Inst)) { + CallSite CS(&Inst); + Function *calledFunction = CS.getCalledFunction(); + if (calledFunction == NULL) { + /* + Note: + For Indirect Calls: + CalledFunction is NULL and calledValue is usually a bitcasted + function pointer. We'll need to strip out the hiccups and obtain + the called Function* from there + */ + calledFunction = + dyn_cast(CS.getCalledValue()->stripPointerCasts()); + } + // Simple Extracting Failed + // Use our own implementation + if (calledFunction == NULL) { + DEBUG_WITH_TYPE( + "opt", errs() + << "Failed To Extract Function From Indirect Call: " + << *CS.getCalledValue() << "\n"); + continue; + } + // It's only safe to restrict our modification to external symbols + // Otherwise stripped binary will crash + if (!calledFunction->empty() || + calledFunction->getName().equals("dlsym") || + calledFunction->getName().equals("dlopen") || + calledFunction->isIntrinsic()) { + continue; + } + // errs()<<"Searching For:"<getName()<<" In + // Configuration\n"; + if (this->Configuration.find(calledFunction->getName().str()) != + this->Configuration.end()) { + string sname = this->Configuration[calledFunction->getName().str()] + .get(); + StringRef calledFunctionName = StringRef(sname); + BasicBlock *EntryBlock = CS->getParent(); + IRBuilder<> IRB(EntryBlock, EntryBlock->getFirstInsertionPt()); + vector dlopenargs; + dlopenargs.push_back(Constant::getNullValue(Int8PtrTy)); + dlopenargs.push_back( + ConstantInt::get(Int32Ty, RTLD_NOW | RTLD_GLOBAL)); + Value *Handle = + IRB.CreateCall(dlopen_decl, ArrayRef(dlopenargs)); + // Create dlsym call + vector args; + args.push_back(Handle); + args.push_back(IRB.CreateGlobalStringPtr(calledFunctionName)); + Value *fp = IRB.CreateCall(dlsym_decl, ArrayRef(args)); + Value *bitCastedFunction = + IRB.CreateBitCast(fp, CS.getCalledValue()->getType()); + CS.setCalledFunction(bitCastedFunction); + } + } + } } - CS->setCalledFunction(func); - CS->mutateFunctionType(ft); - Instruction *Inst = CS->getInstruction(); - delete CS; - return new CallSite(Inst); + return true; } }; -ModulePass *createFunctionWrapperPass() { return new FunctionWrapper(); } -ModulePass *createFunctionWrapperPass(bool flag) { - return new FunctionWrapper(flag); +FunctionPass *createFunctionCallObfuscatePass() { + return new FunctionCallObfuscate(); +} +FunctionPass *createFunctionCallObfuscatePass(bool flag) { + return new FunctionCallObfuscate(flag); } } // namespace llvm - -char FunctionWrapper::ID = 0; -INITIALIZE_PASS(FunctionWrapper, "funcwra", "Enable FunctionWrapper.", true, - true) +char FunctionCallObfuscate::ID = 0; +INITIALIZE_PASS(FunctionCallObfuscate, "fcoobf", + "Enable Function CallSite Obfuscation.", true, true) diff --git a/lib/Transforms/Obfuscation/FunctionWrapper.cpp b/lib/Transforms/Obfuscation/FunctionWrapper.cpp index 3c50ffc59..15dbc8051 100644 --- a/lib/Transforms/Obfuscation/FunctionWrapper.cpp +++ b/lib/Transforms/Obfuscation/FunctionWrapper.cpp @@ -106,22 +106,23 @@ struct FunctionWrapper : public ModulePass { "HikariFunctionWrapper", CS->getParent()->getModule()); appendToCompilerUsed(*func->getParent(), {func}); // FIXME: Correctly Steal Function Attributes - func->addFnAttr(Attribute::AttrKind::OptimizeNone); - func->addFnAttr(Attribute::AttrKind::NoInline); + //func->addFnAttr(Attribute::AttrKind::OptimizeNone); + //func->addFnAttr(Attribute::AttrKind::NoInline); + func->copyAttributesFrom(cast(calledFunction)); BasicBlock *BB = BasicBlock::Create(func->getContext(), "", func); IRBuilder<> IRB(BB); vector params; for (auto arg = func->arg_begin(); arg != func->arg_end(); arg++) { params.push_back(arg); } - Value *retval = IRB.CreateCall(calledFunction, ArrayRef(params)); + Value *retval = IRB.CreateCall(ConstantExpr::getBitCast(cast(calledFunction),CS->getCalledValue()->getType()), ArrayRef(params)); if (ft->getReturnType()->isVoidTy()) { IRB.CreateRetVoid(); } else { IRB.CreateRet(retval); } CS->setCalledFunction(func); - CS->mutateFunctionType(func->getFunctionType()); + CS->mutateFunctionType(ft); Instruction *Inst = CS->getInstruction(); delete CS; return new CallSite(Inst);