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);