├── .DS_Store ├── CMakeLists.txt ├── LICENSE ├── Readme.md ├── demo.cpp └── hikari ├── AntiClassDump.cpp ├── AntiClassDump.h ├── AntiDebugging.cpp ├── AntiDebugging.h ├── AntiHook.h ├── AntiHooking.cpp ├── BogusControlFlow.cpp ├── BogusControlFlow.h ├── ConstantEncryption.cpp ├── ConstantEncryption.h ├── CryptoUtils.cpp ├── CryptoUtils.h ├── Flattening.cpp ├── Flattening.h ├── FunctionCallObfuscate.cpp ├── FunctionCallObfuscate.h ├── FunctionWrapper.cpp ├── FunctionWrapper.h ├── IndirectBranch.cpp ├── IndirectBranch.h ├── Makefile ├── Obfuscation.cpp ├── Obfuscation.h ├── Split.h ├── SplitBasicBlocks.cpp ├── StringEncryption.cpp ├── StringEncryption.h ├── SubstituteImpl.cpp ├── SubstituteImpl.h ├── Substitution.cpp ├── Substitution.h ├── Utils.cpp ├── Utils.h ├── compat └── CallSite.h ├── diff.md └── json.hpp /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lich4/llvm-pass-hikari/ed9e401980afa1b63e019d35847055053e448983/.DS_Store -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | project(MyPassDemo) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_BUILD_TYPE "Debug") 6 | 7 | find_package(LLVM REQUIRED CONFIG) # LLVMConfig.cmake初始化环境 8 | 9 | list(APPEND CMAKE_MODULE_PATH "${LLVM_DIR}") # 兼容LLVM<=13 10 | 11 | include(AddLLVM) # 导入add_llvm_pass_plugin函数 12 | include(HandleLLVMOptions) 13 | 14 | add_definitions(${LLVM_DEFINITIONS}) 15 | include_directories(${LLVM_INCLUDE_DIRS}) 16 | link_directories(${LLVM_LIBRARY_DIRS}) 17 | 18 | if(NOT COMMAND add_llvm_pass_plugin) # 兼容LLVM<=9 19 | message(WARNING "add_llvm_pass_plugin not exist") 20 | function(add_llvm_pass_plugin name) 21 | cmake_parse_arguments(ARG "NO_MODULE" "SUBPROJECT" "" ${ARGN}) 22 | set(link_into_tools_default OFF) 23 | add_llvm_library(${name} MODULE ${ARG_UNPARSED_ARGUMENTS}) 24 | message(STATUS "Registering ${name} as a pass plugin (static build: ${LLVM_${name_upper}_LINK_INTO_TOOLS})") 25 | endfunction(add_llvm_pass_plugin) 26 | endif() 27 | 28 | add_llvm_pass_plugin(MyPassDemo${LLVM_VERSION_MAJOR} 29 | demo.cpp 30 | ) 31 | 32 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | Example LLVM passes - based on LLVM 8-18, LLVM7- is not tested. 3 | 4 | ## build 5 | 6 | Build LLVM 7 | ```bash 8 | cd /path/to/llvm 9 | cmake -S llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS="clang;lld" -DCMAKE_BUILD_TYPE=Debug 10 | cmake --build build 11 | ``` 12 | 13 | Build Demo 14 | ```bash 15 | cd /path/to/llvm-pass-hikari 16 | export LLVM_DIR=/path/to/llvm/build/lib/cmake/llvm 17 | cmake -B build --fresh 18 | cmake --build build 19 | ``` 20 | 21 | ## Test 22 | 23 | Test environment: MacOS 13.x 24 | 25 | ### Test with clang 26 | 27 | ```bash 28 | # for LLVM<=12 Legacy Pass 29 | llvm12/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -Xclang -load -Xclang build/MyPassDemo12.dylib /tmp/1.cpp 30 | # for LLVM<=12 Legacy Pass (Equal to above) 31 | llvm12/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fplugin=build/MyPassDemo12.dylib /tmp/1.cpp 32 | # for LLVM=9/10/11 New Pass (O3 required) 33 | llvm11/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fexperimental-new-pass-manager -fpass-plugin=build/MyPassDemo11.dylib /tmp/1.cpp -O3 34 | # for LLVM=12 New Pass 35 | llvm12/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fexperimental-new-pass-manager -fpass-plugin=build/MyPassDemo12.dylib /tmp/1.cpp 36 | # for LLVM=13/14 Legacy Pass 37 | llvm13/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -flegacy-pass-manager -fplugin=build/MyPassDemo13.dylib /tmp/1.cpp 38 | # for LLVM=13/14 New Pass 39 | llvm13/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo13.dylib /tmp/1.cpp 40 | # for LLVM>=15 New Pass 41 | llvm15/build/bin/clang -isysroot `xcrun --sdk macosx --show-sdk-path` -fpass-plugin=build/MyPassDemo15.dylib /tmp/1.cpp 42 | ``` 43 | 44 | ### Test with opt 45 | 46 | ```bash 47 | # for LLVM all versions 48 | llvm8/build/bin/clang -emit-llvm -c -isysroot `xcrun --sdk macosx --show-sdk-path` -o /tmp/test.bc /tmp/test.cpp 49 | llvm8/build/bin/opt -load-pass-plugin build/MyPassDemo8.dylib -passes all -S -o /tmp/test_new.bc /tmp/test.bc 50 | ``` 51 | 52 | NOTICE: opt support ll as input from LLVM15 53 | 54 | ```bash 55 | llvm15/build/bin/clang -emit-llvm -S -isysroot `xcrun --sdk macosx --show-sdk-path` -o /tmp/test.ll /tmp/test.cpp 56 | llvm15/build/bin/opt -load-pass-plugin build/MyPassDemo15.dylib -passes all -S -o /tmp/test_new.ll /tmp/test.ll 57 | ``` 58 | 59 | ### compile 60 | 61 | * c/cpp -> bc `clang -emit-llvm -c` 62 | * c/cpp -> ll `clang -emit-llvm -S` 63 | * c/cpp -> obj `clang -c` 64 | * ll/bc -> obj/asm `llc` 65 | * bc/obj -> bin `lld` 66 | * ll/bc -> ll `opt -S` (ll -> ll in LLVM>=15) 67 | * ll/bc -> bc `opt` (ll -> bc in LLVM>=15) 68 | * ll -> bc `llvm-as` 69 | * bc -> ll `llvm-dis` 70 | * c/cpp/obj/asm -> bin `clang` 71 | 72 | -------------------------------------------------------------------------------- /demo.cpp: -------------------------------------------------------------------------------- 1 | #include "llvm/IR/LegacyPassManager.h" 2 | #include "llvm/Pass.h" 3 | #include "llvm/Passes/PassBuilder.h" 4 | #include "llvm/Passes/PassPlugin.h" 5 | #include "llvm/Support/raw_ostream.h" 6 | #if LLVM_VERSION_MAJOR <= 15 7 | #include "llvm/Transforms/IPO/PassManagerBuilder.h" 8 | #endif 9 | using namespace llvm; 10 | 11 | #define PASSNAME "MyPassDemo" 12 | 13 | // ---------------- Legacy Pass ---------------- // 14 | #if LLVM_VERSION_MAJOR <= 14 15 | class MyPassDemoLegacy : public ModulePass { 16 | public: 17 | static char ID; 18 | MyPassDemoLegacy() : ModulePass(ID) {} 19 | virtual bool runOnModule(Module& M) override { 20 | errs() << "MyPassDemoLegacy\n"; 21 | return false; 22 | } 23 | }; 24 | char MyPassDemoLegacy::ID = 0; 25 | static RegisterStandardPasses RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, 26 | [](const PassManagerBuilder &, legacy::PassManagerBase &PM) { 27 | PM.add(new MyPassDemoLegacy()); 28 | } 29 | ); 30 | #endif // LLVM_VERSION_MAJOR <= 14 31 | // ---------------- Legacy Pass ---------------- // 32 | 33 | // ---------------- New Pass ---------------- // 34 | #if LLVM_VERSION_MAJOR <= 13 35 | #define OptimizationLevel PassBuilder::OptimizationLevel 36 | #endif 37 | 38 | class MyPassDemo : public PassInfoMixin { 39 | public: 40 | PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM) { 41 | errs() << "MyPassDemo\n"; 42 | return PreservedAnalyses::all(); 43 | }; 44 | static bool isRequired() { return true; } 45 | }; 46 | 47 | extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() { 48 | return { 49 | .APIVersion = LLVM_PLUGIN_API_VERSION, 50 | .PluginName = PASSNAME, 51 | .PluginVersion = "1.0", 52 | .RegisterPassBuilderCallbacks = [](PassBuilder &PB) { 53 | PB.registerPipelineStartEPCallback( 54 | [](ModulePassManager &MPM 55 | #if LLVM_VERSION_MAJOR >= 12 56 | , OptimizationLevel Level 57 | #endif 58 | ) { 59 | MPM.addPass(MyPassDemo()); 60 | }); 61 | PB.registerPipelineParsingCallback( 62 | [](StringRef Name, ModulePassManager& MPM, ArrayRef) { 63 | MPM.addPass(MyPassDemo()); 64 | return true; 65 | }); 66 | } 67 | }; 68 | } 69 | // ---------------- New Pass ---------------- // 70 | 71 | __attribute__((constructor)) void onInit() { 72 | } 73 | 74 | -------------------------------------------------------------------------------- /hikari/AntiClassDump.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | /* 5 | For maximum usability. We provide two modes for this pass, as defined in 6 | AntiClassDump.h THIN mode is used on per-module 7 | basis without LTO overhead and structs are left in the module where possible. 8 | This is particularly useful for cases where LTO is not possible. For example 9 | static library. Full mode is used at LTO stage, this mode constructs 10 | dependency graph and perform full wipe-out as well as llvm.global_ctors 11 | injection. 12 | This pass only provides thin mode 13 | */ 14 | 15 | #include "AntiClassDump.h" 16 | #if LLVM_VERSION_MAJOR >= 17 17 | #include "llvm/TargetParser/Triple.h" 18 | #else 19 | #include "llvm/ADT/Triple.h" 20 | #endif 21 | #include "llvm/IR/Constants.h" 22 | #include "llvm/IR/IRBuilder.h" 23 | #include "llvm/IR/InlineAsm.h" 24 | #include "llvm/IR/Instructions.h" 25 | #include "llvm/IR/Module.h" 26 | #include "llvm/IR/Value.h" 27 | #include "llvm/Support/CommandLine.h" 28 | #include "llvm/Support/raw_ostream.h" 29 | #include "Utils.h" 30 | #include "llvm/Transforms/Utils/ModuleUtils.h" 31 | #include 32 | #include 33 | #include 34 | 35 | using namespace llvm; 36 | 37 | static cl::opt 38 | UseInitialize("acd-use-initialize", cl::init(true), cl::NotHidden, 39 | cl::desc("[AntiClassDump]Inject codes to +initialize")); 40 | static cl::opt 41 | RenameMethodIMP("acd-rename-methodimp", cl::init(false), cl::NotHidden, 42 | cl::desc("[AntiClassDump]Rename methods imp")); 43 | namespace llvm { 44 | struct AntiClassDump : public ModulePass { 45 | static char ID; 46 | bool appleptrauth; 47 | bool opaquepointers; 48 | Triple triple; 49 | AntiClassDump() : ModulePass(ID) {} 50 | StringRef getPassName() const override { return "AntiClassDump"; } 51 | bool doInitialization(Module &M) override { 52 | // Basic Defs 53 | triple = Triple(M.getTargetTriple()); 54 | if (triple.getVendor() != Triple::VendorType::Apple) { 55 | // We only support AAPL's ObjC Implementation ATM 56 | errs() 57 | << M.getTargetTriple() 58 | << " is Not Supported For LLVM AntiClassDump\nProbably GNU Step?\n"; 59 | return false; 60 | } 61 | Type *Int8PtrTy = Type::getInt8Ty(M.getContext())->getPointerTo(); 62 | // Generic ObjC Runtime Declarations 63 | FunctionType *IMPType = 64 | FunctionType::get(Int8PtrTy, {Int8PtrTy, Int8PtrTy}, true); 65 | PointerType *IMPPointerType = PointerType::getUnqual(IMPType); 66 | FunctionType *class_replaceMethod_type = FunctionType::get( 67 | IMPPointerType, {Int8PtrTy, Int8PtrTy, IMPPointerType, Int8PtrTy}, 68 | false); 69 | M.getOrInsertFunction("class_replaceMethod", class_replaceMethod_type); 70 | FunctionType *sel_registerName_type = 71 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); 72 | M.getOrInsertFunction("sel_registerName", sel_registerName_type); 73 | FunctionType *objc_getClass_type = 74 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); 75 | M.getOrInsertFunction("objc_getClass", objc_getClass_type); 76 | M.getOrInsertFunction("objc_getMetaClass", objc_getClass_type); 77 | // Types Collected. Now Inject Functions 78 | FunctionType *class_getName_Type = 79 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); 80 | M.getOrInsertFunction("class_getName", class_getName_Type); 81 | FunctionType *objc_getMetaClass_Type = 82 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); 83 | M.getOrInsertFunction("objc_getMetaClass", objc_getMetaClass_Type); 84 | appleptrauth = hasApplePtrauth(&M); 85 | #if LLVM_VERSION_MAJOR >= 17 86 | opaquepointers = true; 87 | #else 88 | opaquepointers = !M.getContext().supportsTypedPointers(); 89 | #endif 90 | return true; 91 | } 92 | bool runOnModule(Module &M) override { 93 | errs() << "Running AntiClassDump On " << M.getSourceFileName() << "\n"; 94 | SmallVector OLCGVs; 95 | for (GlobalVariable &GV : M.globals()) { 96 | #if LLVM_VERSION_MAJOR >= 18 97 | if (GV.getName().starts_with("OBJC_LABEL_CLASS_$")) { 98 | #else 99 | if (GV.getName().startswith("OBJC_LABEL_CLASS_$")) { 100 | #endif 101 | OLCGVs.emplace_back(&GV); 102 | } 103 | } 104 | if (!OLCGVs.size()) { 105 | errs() << "No ObjC Class Found in :" << M.getSourceFileName() << "\n"; 106 | return false; 107 | } 108 | for (GlobalVariable *OLCGV : OLCGVs) { 109 | ConstantArray *OBJC_LABEL_CLASS_CDS = 110 | dyn_cast(OLCGV->getInitializer()); 111 | assert(OBJC_LABEL_CLASS_CDS && 112 | "OBJC_LABEL_CLASS_$ Not ConstantArray.Is the target using " 113 | "unsupported legacy runtime?"); 114 | SmallVector 115 | readyclses; // This is for storing classes that 116 | // can be used in handleClass() 117 | std::deque tmpclses; // This is temporary storage for classes 118 | std::unordered_map 119 | dependency; 120 | std::unordered_map 121 | GVMapping; // Map ClassName to corresponding GV 122 | for (unsigned int i = 0; i < OBJC_LABEL_CLASS_CDS->getNumOperands(); 123 | i++) { 124 | ConstantExpr *clsEXPR = 125 | opaquepointers 126 | ? nullptr 127 | : dyn_cast(OBJC_LABEL_CLASS_CDS->getOperand(i)); 128 | GlobalVariable *CEGV = dyn_cast( 129 | opaquepointers ? OBJC_LABEL_CLASS_CDS->getOperand(i) 130 | : clsEXPR->getOperand(0)); 131 | ConstantStruct *clsCS = 132 | dyn_cast(CEGV->getInitializer()); 133 | /* 134 | First Operand MetaClass. 135 | Second Operand SuperClass 136 | Fifth Operand ClassRO 137 | */ 138 | GlobalVariable *SuperClassGV = 139 | dyn_cast_or_null(clsCS->getOperand(1)); 140 | SuperClassGV = readPtrauth(SuperClassGV); 141 | std::string supclsName = ""; 142 | std::string clsName = CEGV->getName().str(); 143 | clsName.replace(clsName.find("OBJC_CLASS_$_"), strlen("OBJC_CLASS_$_"), 144 | ""); 145 | 146 | if (SuperClassGV) { // We need to handle Classed that doesn't have a 147 | // base 148 | supclsName = SuperClassGV->getName().str(); 149 | supclsName.replace(supclsName.find("OBJC_CLASS_$_"), 150 | strlen("OBJC_CLASS_$_"), ""); 151 | } 152 | dependency[clsName] = supclsName; 153 | GVMapping[clsName] = CEGV; 154 | if (supclsName == "" /*NULL Super Class*/ || 155 | (SuperClassGV && 156 | !SuperClassGV->hasInitializer() /*External Super Class*/)) { 157 | readyclses.emplace_back(clsName); 158 | } else { 159 | tmpclses.emplace_back(clsName); 160 | } 161 | // Sort Initialize Sequence Based On Dependency 162 | while (tmpclses.size()) { 163 | std::string clstmp = tmpclses.front(); 164 | tmpclses.pop_front(); 165 | std::string SuperClassName = dependency[clstmp]; 166 | if (SuperClassName != "" && 167 | std::find(readyclses.begin(), readyclses.end(), SuperClassName) == 168 | readyclses.end()) { 169 | // SuperClass is unintialized non-null class.Push back and waiting 170 | // until baseclass is allocated 171 | tmpclses.emplace_back(clstmp); 172 | } else { 173 | // BaseClass Ready. Push into ReadyClasses 174 | readyclses.emplace_back(clstmp); 175 | } 176 | } 177 | 178 | // Now run handleClass for each class 179 | for (std::string className : readyclses) { 180 | handleClass(GVMapping[className], &M); 181 | } 182 | } 183 | } 184 | return true; 185 | } // runOnModule 186 | std::unordered_map 187 | splitclass_ro_t(ConstantStruct *class_ro, 188 | Module *M) { // Split a class_ro_t structure 189 | std::unordered_map info; 190 | StructType *objc_method_list_t_type = 191 | StructType::getTypeByName(M->getContext(), "struct.__method_list_t"); 192 | for (unsigned i = 0; i < class_ro->getType()->getNumElements(); i++) { 193 | Constant *tmp = dyn_cast(class_ro->getAggregateElement(i)); 194 | if (tmp->isNullValue()) { 195 | continue; 196 | } 197 | Type *type = tmp->getType(); 198 | if ((!opaquepointers && 199 | type == PointerType::getUnqual(objc_method_list_t_type)) || 200 | (opaquepointers && 201 | #if LLVM_VERSION_MAJOR >= 18 202 | (tmp->getName().starts_with("_OBJC_$_INSTANCE_METHODS") || 203 | tmp->getName().starts_with("_OBJC_$_CLASS_METHODS")))) { 204 | #else 205 | (tmp->getName().startswith("_OBJC_$_INSTANCE_METHODS") || 206 | tmp->getName().startswith("_OBJC_$_CLASS_METHODS")))) { 207 | #endif 208 | // Insert Methods 209 | GlobalVariable *methodListGV = 210 | readPtrauth(cast(tmp->stripPointerCasts())); 211 | assert(methodListGV->hasInitializer() && 212 | "MethodListGV doesn't have initializer"); 213 | ConstantStruct *methodListStruct = 214 | cast(methodListGV->getInitializer()); 215 | // Extracting %struct._objc_method array from %struct.__method_list_t = 216 | // type { i32, i32, [0 x %struct._objc_method] } 217 | info["METHODLIST"] = 218 | cast(methodListStruct->getOperand(2)); 219 | } 220 | } 221 | return info; 222 | } // splitclass_ro_t 223 | void handleClass(GlobalVariable *GV, Module *M) { 224 | assert(GV->hasInitializer() && 225 | "ObjC Class Structure's Initializer Missing"); 226 | ConstantStruct *CS = dyn_cast(GV->getInitializer()); 227 | StringRef ClassName = GV->getName(); 228 | ClassName = ClassName.substr(strlen("OBJC_CLASS_$_")); 229 | StringRef SuperClassName = 230 | readPtrauth( 231 | cast(CS->getOperand(1)->stripPointerCasts())) 232 | ->getName(); 233 | SuperClassName = SuperClassName.substr(strlen("OBJC_CLASS_$_")); 234 | errs() << "Handling Class:" << ClassName 235 | << " With SuperClass:" << SuperClassName << "\n"; 236 | 237 | // Let's extract stuffs 238 | // struct _class_t { 239 | // struct _class_t *isa; 240 | // struct _class_t * const superclass; 241 | // void *cache; 242 | // IMP *vtable; 243 | // struct class_ro_t *ro; 244 | // } 245 | GlobalVariable *metaclassGV = readPtrauth( 246 | cast(CS->getOperand(0)->stripPointerCasts())); 247 | GlobalVariable *class_ro = readPtrauth( 248 | cast(CS->getOperand(4)->stripPointerCasts())); 249 | assert(metaclassGV->hasInitializer() && "MetaClass GV Initializer Missing"); 250 | GlobalVariable *metaclass_ro = readPtrauth(cast( 251 | metaclassGV->getInitializer() 252 | ->getOperand(metaclassGV->getInitializer()->getNumOperands() - 1) 253 | ->stripPointerCasts())); 254 | // Begin IRBuilder Initializing 255 | std::unordered_map Info = splitclass_ro_t( 256 | cast(metaclass_ro->getInitializer()), M); 257 | BasicBlock *EntryBB = nullptr; 258 | if (Info.find("METHODLIST") != Info.end()) { 259 | ConstantArray *method_list = cast(Info["METHODLIST"]); 260 | for (unsigned i = 0; i < method_list->getNumOperands(); i++) { 261 | ConstantStruct *methodStruct = 262 | cast(method_list->getOperand(i)); 263 | // methodStruct has type %struct._objc_method = type { i8*, i8*, i8* } 264 | // which contains {GEP(NAME),GEP(TYPE),BitCast(IMP)} 265 | // Let's extract these info now 266 | // methodStruct->getOperand(0)->getOperand(0) is SELName 267 | GlobalVariable *SELNameGV = cast( 268 | opaquepointers ? methodStruct->getOperand(0) 269 | : methodStruct->getOperand(0)->getOperand(0)); 270 | ConstantDataSequential *SELNameCDS = 271 | cast(SELNameGV->getInitializer()); 272 | StringRef selname = SELNameCDS->getAsCString(); 273 | if ((selname == "initialize" && UseInitialize) || 274 | (selname == "load" && !UseInitialize)) { 275 | Function *IMPFunc = cast(readPtrauth(cast( 276 | methodStruct->getOperand(2)->stripPointerCasts()))); 277 | errs() << "Found Existing initializer\n"; 278 | EntryBB = &(IMPFunc->getEntryBlock()); 279 | } 280 | } 281 | } else { 282 | errs() << "Didn't Find ClassMethod List\n"; 283 | } 284 | if (!EntryBB) { 285 | // We failed to find existing +initializer,create new one 286 | errs() << "Creating initializer\n"; 287 | FunctionType *InitializerType = FunctionType::get( 288 | Type::getVoidTy(M->getContext()), ArrayRef(), false); 289 | Function *Initializer = Function::Create( 290 | InitializerType, GlobalValue::LinkageTypes::PrivateLinkage, 291 | "AntiClassDumpInitializer", M); 292 | EntryBB = BasicBlock::Create(M->getContext(), "", Initializer); 293 | ReturnInst::Create(EntryBB->getContext(), EntryBB); 294 | } 295 | IRBuilder<> *IRB = new IRBuilder<>(EntryBB, EntryBB->getFirstInsertionPt()); 296 | // We now prepare ObjC API Definitions 297 | Function *objc_getClass = M->getFunction("objc_getClass"); 298 | // End of ObjC API Definitions 299 | Value *ClassNameGV = IRB->CreateGlobalStringPtr(ClassName); 300 | // Now Scan For Props and Ivars in OBJC_CLASS_RO AND OBJC_METACLASS_RO 301 | // Note that class_ro_t's structure is different for 32 and 64bit runtime 302 | CallInst *Class = IRB->CreateCall(objc_getClass, {ClassNameGV}); 303 | // Add Methods 304 | ConstantStruct *metaclassCS = 305 | cast(class_ro->getInitializer()); 306 | ConstantStruct *classCS = 307 | cast(metaclass_ro->getInitializer()); 308 | if (!metaclassCS->getAggregateElement(5)->isNullValue()) { 309 | errs() << "Handling Instance Methods For Class:" << ClassName << "\n"; 310 | HandleMethods(metaclassCS, IRB, M, Class, false); 311 | 312 | errs() << "Updating Instance Method Map For Class:" << ClassName << "\n"; 313 | Type *objc_method_type = 314 | StructType::getTypeByName(M->getContext(), "struct._objc_method"); 315 | ArrayType *AT = ArrayType::get(objc_method_type, 0); 316 | Constant *newMethodList = ConstantArray::get(AT, ArrayRef()); 317 | GlobalVariable *methodListGV = readPtrauth(cast( 318 | metaclassCS->getAggregateElement(5)->stripPointerCasts())); 319 | StructType *oldGVType = 320 | cast(methodListGV->getInitializer()->getType()); 321 | SmallVector newStructType; 322 | SmallVector newStructValue; 323 | // I'm fully aware that it's consistent Int32 on all platforms 324 | // This is future-proof 325 | newStructType.emplace_back(oldGVType->getElementType(0)); 326 | newStructValue.emplace_back( 327 | methodListGV->getInitializer()->getAggregateElement(0u)); 328 | newStructType.emplace_back(oldGVType->getElementType(1)); 329 | newStructValue.emplace_back( 330 | ConstantInt::get(oldGVType->getElementType(1), 0)); 331 | newStructType.emplace_back(AT); 332 | newStructValue.emplace_back(newMethodList); 333 | StructType *newType = 334 | StructType::get(M->getContext(), ArrayRef(newStructType)); 335 | Constant *newMethodStruct = ConstantStruct::get( 336 | newType, 337 | ArrayRef(newStructValue)); // l_OBJC_$_CLASS_METHODS_ 338 | GlobalVariable *newMethodStructGV = new GlobalVariable( 339 | *M, newType, true, GlobalValue::LinkageTypes::PrivateLinkage, 340 | newMethodStruct, "ACDNewInstanceMethodMap"); 341 | appendToCompilerUsed(*M, {newMethodStructGV}); 342 | newMethodStructGV->copyAttributesFrom(methodListGV); 343 | Constant *bitcastExpr = ConstantExpr::getBitCast( 344 | newMethodStructGV, 345 | opaquepointers ? newType->getPointerTo() 346 | : PointerType::getUnqual(StructType::getTypeByName( 347 | M->getContext(), "struct.__method_list_t"))); 348 | metaclassCS->handleOperandChange(metaclassCS->getAggregateElement(5), 349 | opaquepointers ? newMethodStructGV 350 | : bitcastExpr); 351 | methodListGV->replaceAllUsesWith( 352 | opaquepointers 353 | ? newMethodStructGV 354 | : ConstantExpr::getBitCast( 355 | newMethodStructGV, 356 | methodListGV->getType())); // llvm.compiler.used doesn't 357 | // allow Null/Undef Value 358 | methodListGV->dropAllReferences(); 359 | methodListGV->eraseFromParent(); 360 | errs() << "Updated Instance Method Map of:" << class_ro->getName() 361 | << "\n"; 362 | } 363 | // MethodList has index of 5 364 | // We need to create a new type first then bitcast to required type later 365 | // Since the original type's contained arraytype has count of 0 366 | GlobalVariable *methodListGV = nullptr; // is striped MethodListGV 367 | if (!classCS->getAggregateElement(5)->isNullValue()) { 368 | errs() << "Handling Class Methods For Class:" << ClassName << "\n"; 369 | HandleMethods(classCS, IRB, M, Class, true); 370 | methodListGV = readPtrauth(cast( 371 | classCS->getAggregateElement(5)->stripPointerCasts())); 372 | } 373 | errs() << "Updating Class Method Map For Class:" << ClassName << "\n"; 374 | Type *objc_method_type = 375 | StructType::getTypeByName(M->getContext(), "struct._objc_method"); 376 | ArrayType *AT = ArrayType::get(objc_method_type, 1); 377 | Constant *MethName = nullptr; 378 | if (UseInitialize) { 379 | MethName = cast(IRB->CreateGlobalStringPtr("initialize")); 380 | } else { 381 | MethName = cast(IRB->CreateGlobalStringPtr("load")); 382 | } 383 | // This method signature is generated by clang 384 | // See 385 | // http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?revision=320954&view=markup 386 | // ASTContext::getObjCEncodingForMethodDecl 387 | // The one hard-coded here is generated for macOS 64Bit 388 | Constant *MethType = nullptr; 389 | if (triple.isOSDarwin() && triple.isArch64Bit()) { 390 | MethType = IRB->CreateGlobalStringPtr("v16@0:8"); 391 | } else if (triple.isOSDarwin() && triple.isArch32Bit()) { 392 | MethType = IRB->CreateGlobalStringPtr("v8@0:4"); 393 | } else { 394 | errs() << "Unknown Platform.Blindly applying method signature for " 395 | "macOS 64Bit\n"; 396 | MethType = IRB->CreateGlobalStringPtr("v16@0:8"); 397 | } 398 | Constant *BitCastedIMP = cast( 399 | IRB->CreateBitCast(IRB->GetInsertBlock()->getParent(), 400 | objc_getClass->getFunctionType()->getParamType(0))); 401 | std::vector methodStructContents; //{GEP(NAME),GEP(TYPE),IMP} 402 | methodStructContents.emplace_back(MethName); 403 | methodStructContents.emplace_back(MethType); 404 | methodStructContents.emplace_back(BitCastedIMP); 405 | Constant *newMethod = ConstantStruct::get( 406 | cast(objc_method_type), 407 | ArrayRef(methodStructContents)); // objc_method_t 408 | Constant *newMethodList = ConstantArray::get( 409 | AT, ArrayRef(newMethod)); // Container of objc_method_t 410 | std::vector newStructType; 411 | std::vector newStructValue; 412 | // I'm fully aware that it's consistent Int32 on all platforms 413 | // This is future-proof 414 | newStructType.emplace_back(Type::getInt32Ty(M->getContext())); 415 | newStructValue.emplace_back( 416 | ConstantInt::get(Type::getInt32Ty(M->getContext()), 417 | 0x18)); // 0x18 is extracted from 418 | // built-code on macOS.No 419 | // idea what does it mean 420 | newStructType.emplace_back(Type::getInt32Ty(M->getContext())); 421 | newStructValue.emplace_back( 422 | ConstantInt::get(Type::getInt32Ty(M->getContext()), 423 | 1)); // this is class count 424 | newStructType.emplace_back(AT); 425 | newStructValue.emplace_back(newMethodList); 426 | StructType *newType = 427 | StructType::get(M->getContext(), ArrayRef(newStructType)); 428 | Constant *newMethodStruct = ConstantStruct::get( 429 | newType, 430 | ArrayRef(newStructValue)); // l_OBJC_$_CLASS_METHODS_ 431 | GlobalVariable *newMethodStructGV = new GlobalVariable( 432 | *M, newType, true, GlobalValue::LinkageTypes::PrivateLinkage, 433 | newMethodStruct, "ACDNewClassMethodMap"); 434 | appendToCompilerUsed(*M, {newMethodStructGV}); 435 | if (methodListGV) { 436 | newMethodStructGV->copyAttributesFrom(methodListGV); 437 | } 438 | Constant *bitcastExpr = ConstantExpr::getBitCast( 439 | newMethodStructGV, 440 | opaquepointers ? newType->getPointerTo() 441 | : PointerType::getUnqual(StructType::getTypeByName( 442 | M->getContext(), "struct.__method_list_t"))); 443 | opaquepointers ? classCS->setOperand(5, bitcastExpr) 444 | : classCS->handleOperandChange( 445 | classCS->getAggregateElement(5), bitcastExpr); 446 | if (methodListGV) { 447 | methodListGV->replaceAllUsesWith(ConstantExpr::getBitCast( 448 | newMethodStructGV, 449 | methodListGV->getType())); // llvm.compiler.used doesn't allow 450 | // Null/Undef Value 451 | methodListGV->dropAllReferences(); 452 | methodListGV->eraseFromParent(); 453 | } 454 | errs() << "Updated Class Method Map of:" << class_ro->getName() << "\n"; 455 | // End ClassCS Handling 456 | } // handleClass 457 | void HandleMethods(ConstantStruct *class_ro, IRBuilder<> *IRB, Module *M, 458 | Value *Class, bool isMetaClass) { 459 | Function *sel_registerName = M->getFunction("sel_registerName"); 460 | Function *class_replaceMethod = M->getFunction("class_replaceMethod"); 461 | Function *class_getName = M->getFunction("class_getName"); 462 | Function *objc_getMetaClass = M->getFunction("objc_getMetaClass"); 463 | StructType *objc_method_list_t_type = 464 | StructType::getTypeByName(M->getContext(), "struct.__method_list_t"); 465 | for (unsigned int i = 0; i < class_ro->getType()->getNumElements(); i++) { 466 | Constant *tmp = dyn_cast(class_ro->getAggregateElement(i)); 467 | if (tmp->isNullValue()) { 468 | continue; 469 | } 470 | Type *type = tmp->getType(); 471 | if ((!opaquepointers && 472 | type == PointerType::getUnqual(objc_method_list_t_type)) || 473 | (opaquepointers && 474 | #if LLVM_VERSION_MAJOR >= 18 475 | (tmp->getName().starts_with("_OBJC_$_INSTANCE_METHODS") || 476 | tmp->getName().starts_with("_OBJC_$_CLASS_METHODS")))) { 477 | #else 478 | (tmp->getName().startswith("_OBJC_$_INSTANCE_METHODS") || 479 | tmp->getName().startswith("_OBJC_$_CLASS_METHODS")))) { 480 | #endif 481 | // Insert Methods 482 | GlobalVariable *methodListGV = 483 | readPtrauth(cast(tmp->stripPointerCasts())); 484 | assert(methodListGV->hasInitializer() && 485 | "MethodListGV doesn't have initializer"); 486 | ConstantStruct *methodListStruct = 487 | cast(methodListGV->getInitializer()); 488 | // Extracting %struct._objc_method array from %struct.__method_list_t = 489 | // type { i32, i32, [0 x %struct._objc_method] } 490 | if (methodListStruct->getOperand(2)->isZeroValue()) { 491 | return; 492 | } 493 | ConstantArray *methodList = 494 | cast(methodListStruct->getOperand(2)); 495 | for (unsigned int i = 0; i < methodList->getNumOperands(); i++) { 496 | ConstantStruct *methodStruct = 497 | cast(methodList->getOperand(i)); 498 | // methodStruct has type %struct._objc_method = type { i8*, i8*, i8* } 499 | // which contains {GEP(NAME),GEP(TYPE),IMP} 500 | // Let's extract these info now 501 | // We should first register the selector 502 | Constant *SELName = IRB->CreateGlobalStringPtr( 503 | cast( 504 | cast( 505 | opaquepointers 506 | ? methodStruct->getOperand(0) 507 | : cast(methodStruct->getOperand(0)) 508 | ->getOperand(0)) 509 | ->getInitializer()) 510 | ->getAsCString()); 511 | CallInst *SEL = IRB->CreateCall(sel_registerName, {SELName}); 512 | Type *IMPType = 513 | class_replaceMethod->getFunctionType()->getParamType(2); 514 | Value *BitCastedIMP = IRB->CreateBitCast( 515 | appleptrauth 516 | ? opaquepointers 517 | ? cast(methodStruct->getOperand(2)) 518 | ->getInitializer() 519 | ->getOperand(0) 520 | : cast( 521 | cast(methodStruct->getOperand(2)) 522 | ->getInitializer() 523 | ->getOperand(0)) 524 | ->getOperand(0) 525 | : methodStruct->getOperand(2), 526 | IMPType); 527 | std::vector replaceMethodArgs; 528 | if (isMetaClass) { 529 | CallInst *className = IRB->CreateCall(class_getName, {Class}); 530 | CallInst *MetaClass = 531 | IRB->CreateCall(objc_getMetaClass, {className}); 532 | replaceMethodArgs.emplace_back(MetaClass); // Class 533 | } else { 534 | replaceMethodArgs.emplace_back(Class); // Class 535 | } 536 | replaceMethodArgs.emplace_back(SEL); // SEL 537 | replaceMethodArgs.emplace_back(BitCastedIMP); // imp 538 | replaceMethodArgs.emplace_back(IRB->CreateGlobalStringPtr( 539 | cast( 540 | cast( 541 | opaquepointers 542 | ? methodStruct->getOperand(1) 543 | : cast(methodStruct->getOperand(1)) 544 | ->getOperand(0)) 545 | ->getInitializer()) 546 | ->getAsCString())); // type 547 | IRB->CreateCall(class_replaceMethod, 548 | ArrayRef(replaceMethodArgs)); 549 | if (RenameMethodIMP) { 550 | Function *MethodIMP = cast( 551 | appleptrauth 552 | ? opaquepointers 553 | ? cast(methodStruct->getOperand(2)) 554 | ->getInitializer() 555 | ->getOperand(0) 556 | : cast( 557 | cast( 558 | methodStruct->getOperand(2)->getOperand(0)) 559 | ->getInitializer() 560 | ->getOperand(0)) 561 | ->getOperand(0) 562 | : opaquepointers ? methodStruct->getOperand(2) 563 | : methodStruct->getOperand(2)->getOperand(0)); 564 | MethodIMP->setName("ACDMethodIMP"); 565 | } 566 | } 567 | } 568 | } 569 | } 570 | GlobalVariable *readPtrauth(GlobalVariable *GV) { 571 | if (GV->getSection() == "llvm.ptrauth") { 572 | Value *V = GV->getInitializer()->getOperand(0); 573 | return cast( 574 | opaquepointers ? V : cast(V)->getOperand(0)); 575 | } 576 | return GV; 577 | } 578 | }; 579 | } // namespace llvm 580 | ModulePass *llvm::createAntiClassDumpPass() { return new AntiClassDump(); } 581 | char AntiClassDump::ID = 0; 582 | INITIALIZE_PASS(AntiClassDump, "acd", "Enable Anti-ClassDump.", false, false) 583 | -------------------------------------------------------------------------------- /hikari/AntiClassDump.h: -------------------------------------------------------------------------------- 1 | #ifndef _ANTI_CLASSDUMP_H_ 2 | #define _ANTI_CLASSDUMP_H_ 3 | 4 | #include "llvm/IR/PassManager.h" 5 | #include "llvm/Pass.h" 6 | 7 | namespace llvm { 8 | 9 | ModulePass *createAntiClassDumpPass(); 10 | void initializeAntiClassDumpPass(PassRegistry &Registry); 11 | 12 | } // namespace llvm 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /hikari/AntiDebugging.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LLVM AntiDebugging Pass 3 | Copyright (C) 2017 Zhang(https://github.com/Naville/) 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | */ 18 | #include "AntiDebugging.h" 19 | #if LLVM_VERSION_MAJOR >= 17 20 | #include "llvm/ADT/SmallString.h" 21 | #include "llvm/TargetParser/Triple.h" 22 | #else 23 | #include "llvm/ADT/Triple.h" 24 | #endif 25 | #include "llvm/IR/Constants.h" 26 | #include "llvm/IR/IRBuilder.h" 27 | #include "llvm/IR/InlineAsm.h" 28 | #include "llvm/IR/Instructions.h" 29 | #include "llvm/IRReader/IRReader.h" 30 | #include "llvm/Linker/Linker.h" 31 | #include "llvm/Support/CommandLine.h" 32 | #include "llvm/Support/Path.h" 33 | #include "llvm/Support/SourceMgr.h" 34 | #include "llvm/Support/raw_ostream.h" 35 | #include "CryptoUtils.h" 36 | #include "Utils.h" 37 | #include 38 | 39 | using namespace llvm; 40 | 41 | static cl::opt PreCompiledIRPath( 42 | "adbextirpath", 43 | cl::desc("External Path Pointing To Pre-compiled AntiDebugging IR"), 44 | cl::value_desc("filename"), cl::init("")); 45 | static cl::opt 46 | ProbRate("adb_prob", 47 | cl::desc("Choose the probability [%] For Each Function To Be " 48 | "Obfuscated By AntiDebugging"), 49 | cl::value_desc("Probability Rate"), cl::init(40), cl::Optional); 50 | 51 | namespace llvm { 52 | struct AntiDebugging : public ModulePass { 53 | static char ID; 54 | bool flag; 55 | bool initialized; 56 | Triple triple; 57 | AntiDebugging() : ModulePass(ID) { 58 | this->flag = true; 59 | this->initialized = false; 60 | } 61 | AntiDebugging(bool flag) : ModulePass(ID) { 62 | this->flag = flag; 63 | this->initialized = false; 64 | } 65 | StringRef getPassName() const override { return "AntiDebugging"; } 66 | bool initialize(Module &M) { 67 | if (PreCompiledIRPath == "") { 68 | SmallString<32> Path; 69 | if (sys::path::home_directory(Path)) { // Stolen from LineEditor.cpp 70 | sys::path::append(Path, "Hikari"); 71 | Triple tri(M.getTargetTriple()); 72 | sys::path::append(Path, "PrecompiledAntiDebugging-" + 73 | Triple::getArchTypeName(tri.getArch()) + 74 | "-" + Triple::getOSTypeName(tri.getOS()) + 75 | ".bc"); 76 | PreCompiledIRPath = Path.c_str(); 77 | } 78 | } 79 | std::ifstream f(PreCompiledIRPath); 80 | if (f.good()) { 81 | errs() << "Linking PreCompiled AntiDebugging IR From:" 82 | << PreCompiledIRPath << "\n"; 83 | SMDiagnostic SMD; 84 | std::unique_ptr ADBM( 85 | parseIRFile(StringRef(PreCompiledIRPath), SMD, M.getContext())); 86 | Linker::linkModules(M, std::move(ADBM), Linker::Flags::LinkOnlyNeeded); 87 | // FIXME: Mess with GV in ADBCallBack 88 | Function *ADBCallBack = M.getFunction("ADBCallBack"); 89 | if (ADBCallBack) { 90 | assert(!ADBCallBack->isDeclaration() && 91 | "AntiDebuggingCallback is not concrete!"); 92 | ADBCallBack->setVisibility( 93 | GlobalValue::VisibilityTypes::HiddenVisibility); 94 | ADBCallBack->setLinkage(GlobalValue::LinkageTypes::PrivateLinkage); 95 | ADBCallBack->removeFnAttr(Attribute::AttrKind::NoInline); 96 | ADBCallBack->removeFnAttr(Attribute::AttrKind::OptimizeNone); 97 | ADBCallBack->addFnAttr(Attribute::AttrKind::AlwaysInline); 98 | } 99 | Function *ADBInit = M.getFunction("InitADB"); 100 | if (ADBInit) { 101 | assert(!ADBInit->isDeclaration() && 102 | "AntiDebuggingInitializer is not concrete!"); 103 | ADBInit->setVisibility(GlobalValue::VisibilityTypes::HiddenVisibility); 104 | ADBInit->setLinkage(GlobalValue::LinkageTypes::PrivateLinkage); 105 | ADBInit->removeFnAttr(Attribute::AttrKind::NoInline); 106 | ADBInit->removeFnAttr(Attribute::AttrKind::OptimizeNone); 107 | ADBInit->addFnAttr(Attribute::AttrKind::AlwaysInline); 108 | } 109 | } else { 110 | errs() << "Failed To Link PreCompiled AntiDebugging IR From:" 111 | << PreCompiledIRPath << "\n"; 112 | } 113 | this->initialized = true; 114 | this->triple = Triple(M.getTargetTriple()); 115 | return true; 116 | } 117 | bool runOnModule(Module &M) override { 118 | if (ProbRate > 100) { 119 | errs() << "AntiDebugging application function percentage " 120 | "-adb_prob=x must be 0 < x <= 100"; 121 | return false; 122 | } 123 | for (Function &F : M) { 124 | if (toObfuscate(flag, &F, "adb") && F.getName() != "ADBCallBack" && 125 | F.getName() != "InitADB") { 126 | errs() << "Running AntiDebugging On " << F.getName() << "\n"; 127 | if (!this->initialized) 128 | initialize(M); 129 | if (cryptoutils->get_range(100) <= ProbRate) 130 | runOnFunction(F); 131 | } 132 | } 133 | return true; 134 | } 135 | bool runOnFunction(Function &F) { 136 | BasicBlock *EntryBlock = &(F.getEntryBlock()); 137 | // Now operate on Linked AntiDBGCallbacks 138 | Function *ADBCallBack = F.getParent()->getFunction("ADBCallBack"); 139 | Function *ADBInit = F.getParent()->getFunction("InitADB"); 140 | if (ADBCallBack && ADBInit) { 141 | CallInst::Create(ADBInit, "", 142 | cast(EntryBlock->getFirstInsertionPt())); 143 | } else { 144 | errs() << "The ADBCallBack and ADBInit functions were not found\n"; 145 | if (!F.getReturnType() 146 | ->isVoidTy()) // We insert InlineAsm in the Terminator, which 147 | // causes register contamination if the return type 148 | // is not Void. 149 | return false; 150 | if (triple.isOSDarwin() && triple.isAArch64()) { 151 | errs() << "Injecting Inline Assembly AntiDebugging For:" 152 | << F.getParent()->getTargetTriple() << "\n"; 153 | std::string antidebugasm = ""; 154 | switch (cryptoutils->get_range(2)) { 155 | case 0: { 156 | std::string s[] = {"mov x0, #31\n", "mov w0, #31\n", "mov x1, #0\n", 157 | "mov w1, #0\n", "mov x2, #0\n", "mov w2, #0\n", 158 | "mov x3, #0\n", "mov w3, #0\n", "mov x16, #26\n", 159 | "mov w16, #26\n"}; // svc ptrace 160 | bool c[5] = {false, false, false, false, false}; 161 | while (c[0] != true || c[1] != true || c[2] != true || c[3] != true || 162 | c[4] != true) { 163 | switch (cryptoutils->get_range(5)) { 164 | case 0: 165 | if (!c[0]) { 166 | antidebugasm += s[cryptoutils->get_range(2)]; 167 | c[0] = true; 168 | } 169 | break; 170 | case 1: 171 | if (!c[1]) { 172 | antidebugasm += s[2 + cryptoutils->get_range(2)]; 173 | c[1] = true; 174 | } 175 | break; 176 | case 2: 177 | if (!c[2]) { 178 | antidebugasm += s[4 + cryptoutils->get_range(2)]; 179 | c[2] = true; 180 | } 181 | break; 182 | case 3: 183 | if (!c[3]) { 184 | antidebugasm += s[6 + cryptoutils->get_range(2)]; 185 | c[3] = true; 186 | } 187 | break; 188 | case 4: 189 | if (!c[4]) { 190 | antidebugasm += s[8 + cryptoutils->get_range(2)]; 191 | c[4] = true; 192 | } 193 | break; 194 | default: 195 | break; 196 | } 197 | } 198 | break; 199 | } 200 | case 1: { 201 | std::string s[] = {"mov x0, #26\n", "mov w0, #26\n", "mov x1, #31\n", 202 | "mov w1, #31\n", "mov x2, #0\n", "mov w2, #0\n", 203 | "mov x3, #0\n", "mov w3, #0\n", "mov x16, #0\n", 204 | "mov w16, #0\n"}; // svc syscall ptrace 205 | bool c[5] = {false, false, false, false, false}; 206 | while (c[0] != true || c[1] != true || c[2] != true || c[3] != true || 207 | c[4] != true) { 208 | switch (cryptoutils->get_range(5)) { 209 | case 0: 210 | if (!c[0]) { 211 | antidebugasm += s[cryptoutils->get_range(2)]; 212 | c[0] = true; 213 | } 214 | break; 215 | case 1: 216 | if (!c[1]) { 217 | antidebugasm += s[2 + cryptoutils->get_range(2)]; 218 | c[1] = true; 219 | } 220 | break; 221 | case 2: 222 | if (!c[2]) { 223 | antidebugasm += s[4 + cryptoutils->get_range(2)]; 224 | c[2] = true; 225 | } 226 | break; 227 | case 3: 228 | if (!c[3]) { 229 | antidebugasm += s[6 + cryptoutils->get_range(2)]; 230 | c[3] = true; 231 | } 232 | break; 233 | case 4: 234 | if (!c[4]) { 235 | antidebugasm += s[8 + cryptoutils->get_range(2)]; 236 | c[4] = true; 237 | } 238 | break; 239 | default: 240 | break; 241 | } 242 | } 243 | break; 244 | } 245 | } 246 | antidebugasm += 247 | "svc #" + std::to_string(cryptoutils->get_range(65536)) + "\n"; 248 | InlineAsm *IA = InlineAsm::get( 249 | FunctionType::get(Type::getVoidTy(EntryBlock->getContext()), false), 250 | antidebugasm, "", true, false); 251 | Instruction *I = nullptr; 252 | for (BasicBlock &BB : F) 253 | I = BB.getTerminator(); 254 | #if LLVM_VERSION_MAJOR >= 16 255 | CallInst::Create(IA, std::nullopt, "", I); 256 | #else 257 | CallInst::Create(IA, None, "", I); 258 | #endif 259 | } else { 260 | errs() << "Unsupported Inline Assembly AntiDebugging Target: " 261 | << F.getParent()->getTargetTriple() << "\n"; 262 | } 263 | } 264 | return true; 265 | } 266 | }; 267 | 268 | ModulePass *createAntiDebuggingPass(bool flag) { 269 | return new AntiDebugging(flag); 270 | } 271 | } // namespace llvm 272 | 273 | char AntiDebugging::ID = 0; 274 | INITIALIZE_PASS(AntiDebugging, "adb", "Enable AntiDebugging.", false, false) 275 | -------------------------------------------------------------------------------- /hikari/AntiDebugging.h: -------------------------------------------------------------------------------- 1 | #ifndef _ANTI_DEBUGGING_H_ 2 | #define _ANTI_DEBUGGING_H_ 3 | 4 | #include "llvm/IR/PassManager.h" 5 | #include "llvm/Pass.h" 6 | 7 | namespace llvm { 8 | 9 | ModulePass *createAntiDebuggingPass(bool flag); 10 | void initializeAntiDebuggingPass(PassRegistry &Registry); 11 | 12 | } // namespace llvm 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /hikari/AntiHook.h: -------------------------------------------------------------------------------- 1 | #ifndef _ANTI_HOOK_H_ 2 | #define _ANTI_HOOK_H_ 3 | 4 | #include "llvm/IR/PassManager.h" 5 | #include "llvm/Pass.h" 6 | 7 | namespace llvm { 8 | 9 | ModulePass *createAntiHookPass(bool flag); 10 | void initializeAntiHookPass(PassRegistry &Registry); 11 | 12 | } // namespace llvm 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /hikari/AntiHooking.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | LLVM Anti Hooking Pass 3 | Copyright (C) 2017 Zhang(https://github.com/Naville/) 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | */ 18 | #include "llvm/IR/Constants.h" 19 | #include "llvm/IR/IRBuilder.h" 20 | #include "llvm/IR/InlineAsm.h" 21 | #include "llvm/IR/InstIterator.h" 22 | #include "llvm/IR/Instructions.h" 23 | #include "llvm/IR/Module.h" 24 | #include "llvm/IRReader/IRReader.h" 25 | #include "llvm/Linker/Linker.h" 26 | #include "llvm/Support/CommandLine.h" 27 | #include "llvm/Support/Path.h" 28 | #include "llvm/Support/SourceMgr.h" 29 | #include "llvm/Support/raw_ostream.h" 30 | #if LLVM_VERSION_MAJOR >= 17 31 | #include "llvm/ADT/SmallString.h" 32 | #include "llvm/TargetParser/Triple.h" 33 | #else 34 | #include "llvm/ADT/Triple.h" 35 | #endif 36 | #include "AntiHook.h" 37 | #include "CryptoUtils.h" 38 | #include "Utils.h" 39 | #include "compat/CallSite.h" 40 | #include "llvm/Transforms/Utils/ModuleUtils.h" 41 | #include 42 | 43 | // Arm A64 Instruction Set for A-profile architecture 2022-12, Page 56 44 | #define AARCH64_SIGNATURE_B 0b000101 45 | // Arm A64 Instruction Set for A-profile architecture 2022-12, Page 75 46 | #define AARCH64_SIGNATURE_BR 0b1101011000011111000000 47 | // Arm A64 Instruction Set for A-profile architecture 2022-12, Page 79 48 | #define AARCH64_SIGNATURE_BRK 0b11010100001 49 | 50 | using namespace llvm; 51 | 52 | static cl::opt 53 | PreCompiledIRPath("adhexrirpath", 54 | cl::desc("External Path Pointing To Pre-compiled Anti " 55 | "Hooking Handler IR"), 56 | cl::value_desc("filename"), cl::init("")); 57 | 58 | static cl::opt CheckInlineHook("ah_inline", cl::init(true), cl::NotHidden, 59 | cl::desc("Check Inline Hook for AArch64")); 60 | static bool CheckInlineHookTemp = true; 61 | 62 | static cl::opt 63 | CheckObjectiveCRuntimeHook("ah_objcruntime", cl::init(true), cl::NotHidden, 64 | cl::desc("Check Objective-C Runtime Hook")); 65 | static bool CheckObjectiveCRuntimeHookTemp = true; 66 | 67 | static cl::opt AntiRebindSymbol("ah_antirebind", cl::init(false), 68 | cl::NotHidden, 69 | cl::desc("Make fishhook unavailable")); 70 | static bool AntiRebindSymbolTemp = false; 71 | 72 | namespace llvm { 73 | struct AntiHook : public ModulePass { 74 | static char ID; 75 | bool flag; 76 | bool initialized; 77 | bool opaquepointers; 78 | Triple triple; 79 | AntiHook() : ModulePass(ID) { 80 | this->flag = true; 81 | this->initialized = false; 82 | } 83 | AntiHook(bool flag) : ModulePass(ID) { 84 | this->flag = flag; 85 | this->initialized = false; 86 | } 87 | StringRef getPassName() const override { return "AntiHook"; } 88 | bool initialize(Module &M) { 89 | this->triple = Triple(M.getTargetTriple()); 90 | if (PreCompiledIRPath == "") { 91 | SmallString<32> Path; 92 | if (sys::path::home_directory(Path)) { // Stolen from LineEditor.cpp 93 | sys::path::append(Path, "Hikari"); 94 | sys::path::append(Path, 95 | "PrecompiledAntiHooking-" + 96 | Triple::getArchTypeName(triple.getArch()) + "-" + 97 | Triple::getOSTypeName(triple.getOS()) + ".bc"); 98 | PreCompiledIRPath = Path.c_str(); 99 | } 100 | } 101 | std::ifstream f(PreCompiledIRPath); 102 | if (f.good()) { 103 | errs() << "Linking PreCompiled AntiHooking IR From:" << PreCompiledIRPath 104 | << "\n"; 105 | SMDiagnostic SMD; 106 | std::unique_ptr ADBM( 107 | parseIRFile(StringRef(PreCompiledIRPath), SMD, M.getContext())); 108 | Linker::linkModules(M, std::move(ADBM), Linker::Flags::OverrideFromSrc); 109 | } else { 110 | errs() << "Failed To Link PreCompiled AntiHooking IR From:" 111 | << PreCompiledIRPath << "\n"; 112 | } 113 | #if LLVM_VERSION_MAJOR >= 17 114 | opaquepointers = true; 115 | #else 116 | opaquepointers = !M.getContext().supportsTypedPointers(); 117 | #endif 118 | 119 | if (triple.getVendor() == Triple::VendorType::Apple && 120 | StructType::getTypeByName(M.getContext(), "struct._objc_method")) { 121 | Type *Int8PtrTy = Type::getInt8Ty(M.getContext())->getPointerTo(); 122 | M.getOrInsertFunction("objc_getClass", 123 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false)); 124 | M.getOrInsertFunction("sel_registerName", 125 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false)); 126 | FunctionType *IMPType = 127 | FunctionType::get(Int8PtrTy, {Int8PtrTy, Int8PtrTy}, true); 128 | PointerType *IMPPointerType = PointerType::getUnqual(IMPType); 129 | M.getOrInsertFunction( 130 | "method_getImplementation", 131 | FunctionType::get(IMPPointerType, 132 | {PointerType::getUnqual(StructType::getTypeByName( 133 | M.getContext(), "struct._objc_method"))}, 134 | false)); 135 | M.getOrInsertFunction( 136 | "class_getInstanceMethod", 137 | FunctionType::get(PointerType::getUnqual(StructType::getTypeByName( 138 | M.getContext(), "struct._objc_method")), 139 | {Int8PtrTy, Int8PtrTy}, false)); 140 | M.getOrInsertFunction( 141 | "class_getClassMethod", 142 | FunctionType::get(PointerType::getUnqual(StructType::getTypeByName( 143 | M.getContext(), "struct._objc_method")), 144 | {Int8PtrTy, Int8PtrTy}, false)); 145 | } 146 | return true; 147 | } 148 | 149 | bool runOnModule(Module &M) override { 150 | for (Function &F : M) { 151 | if (toObfuscate(flag, &F, "antihook")) { 152 | errs() << "Running AntiHooking On " << F.getName() << "\n"; 153 | if (!this->initialized) 154 | initialize(M); 155 | if (!toObfuscateBoolOption(&F, "ah_inline", &CheckInlineHookTemp)) 156 | CheckInlineHookTemp = CheckInlineHook; 157 | if (triple.isAArch64() && CheckInlineHookTemp) { 158 | HandleInlineHookAArch64(&F); 159 | } 160 | if (!toObfuscateBoolOption(&F, "ah_antirebind", &AntiRebindSymbolTemp)) 161 | AntiRebindSymbolTemp = AntiRebindSymbol; 162 | if (AntiRebindSymbolTemp) 163 | for (Instruction &I : instructions(F)) 164 | if (isa(&I) || isa(&I)) { 165 | CallSite CS(&I); 166 | Function *Called = CS.getCalledFunction(); 167 | if (!Called) 168 | Called = dyn_cast( 169 | CS.getCalledValue()->stripPointerCasts()); 170 | if (Called && Called->isDeclaration() && 171 | Called->isExternalLinkage(Called->getLinkage()) && 172 | !Called->isIntrinsic() && 173 | #if LLVM_VERSION_MAJOR >= 18 174 | !Called->getName().starts_with("clang.")) { 175 | #else 176 | !Called->getName().startswith("clang.")) { 177 | #endif 178 | GlobalVariable *GV = cast(M.getOrInsertGlobal( 179 | ("AntiRebindSymbol_" + Called->getName()).str(), 180 | Called->getType())); 181 | if (!GV->hasInitializer()) { 182 | GV->setConstant(true); // make the gv not writable 183 | GV->setInitializer(Called); 184 | GV->setLinkage(GlobalValue::LinkageTypes::PrivateLinkage); 185 | } 186 | appendToCompilerUsed(M, {GV}); 187 | Value *Load = 188 | new LoadInst(GV->getValueType(), GV, Called->getName(), &I); 189 | Value *BitCasted = BitCastInst::CreateBitOrPointerCast( 190 | Load, CS.getCalledValue()->getType(), "", &I); 191 | CS.setCalledFunction(BitCasted); 192 | } 193 | } 194 | if (!toObfuscateBoolOption(&F, "ah_objcruntime", 195 | &CheckObjectiveCRuntimeHookTemp)) 196 | CheckObjectiveCRuntimeHookTemp = CheckObjectiveCRuntimeHook; 197 | if (!CheckObjectiveCRuntimeHookTemp) 198 | continue; 199 | GlobalVariable *methodListGV = nullptr; 200 | ConstantStruct *methodStruct = nullptr; 201 | for (User *U : F.users()) { 202 | if (opaquepointers) 203 | if (ConstantStruct *CS = dyn_cast(U)) 204 | if (CS->getType()->getName() == "struct._objc_method") 205 | methodStruct = CS; 206 | for (User *U2 : U->users()) { 207 | if (!opaquepointers) 208 | if (ConstantStruct *CS = dyn_cast(U2)) 209 | if (CS->getType()->getName() == "struct._objc_method") 210 | methodStruct = CS; 211 | for (User *U3 : U2->users()) 212 | for (User *U4 : U3->users()) { 213 | if (opaquepointers) { 214 | #if LLVM_VERSION_MAJOR >= 18 215 | if (U4->getName().starts_with("_OBJC_$_INSTANCE_METHODS") || 216 | U4->getName().starts_with("_OBJC_$_CLASS_METHODS")) 217 | methodListGV = dyn_cast(U4); 218 | } else 219 | for (User *U5 : U4->users()) { 220 | if (U5->getName().starts_with("_OBJC_$_INSTANCE_METHODS") || 221 | U5->getName().starts_with("_OBJC_$_CLASS_METHODS")) 222 | #else 223 | if (U4->getName().startswith("_OBJC_$_INSTANCE_METHODS") || 224 | U4->getName().startswith("_OBJC_$_CLASS_METHODS")) 225 | methodListGV = dyn_cast(U4); 226 | } else 227 | for (User *U5 : U4->users()) { 228 | if (U5->getName().startswith("_OBJC_$_INSTANCE_METHODS") || 229 | U5->getName().startswith("_OBJC_$_CLASS_METHODS")) 230 | #endif 231 | methodListGV = dyn_cast(U5); 232 | } 233 | } 234 | } 235 | } 236 | if (methodListGV && methodStruct) { 237 | GlobalVariable *SELNameGV = cast( 238 | methodStruct->getOperand(0)->stripPointerCasts()); 239 | ConstantDataSequential *SELNameCDS = 240 | cast(SELNameGV->getInitializer()); 241 | bool classmethod = 242 | #if LLVM_VERSION_MAJOR >= 18 243 | methodListGV->getName().starts_with("_OBJC_$_CLASS_METHODS"); 244 | #else 245 | methodListGV->getName().startswith("_OBJC_$_CLASS_METHODS"); 246 | #endif 247 | std::string classname = 248 | methodListGV->getName() 249 | .substr(strlen(classmethod ? "_OBJC_$_CLASS_METHODS_" 250 | : "_OBJC_$_INSTANCE_METHODS_")) 251 | .str(); 252 | std::string selname = SELNameCDS->getAsCString().str(); 253 | HandleObjcRuntimeHook(&F, classname, selname, classmethod); 254 | } 255 | } 256 | } 257 | return true; 258 | } // End runOnFunction 259 | 260 | void HandleInlineHookAArch64(Function *F) { 261 | BasicBlock *A = &(F->getEntryBlock()); 262 | BasicBlock *C = A->splitBasicBlock(A->getFirstNonPHIOrDbgOrLifetime()); 263 | BasicBlock *B = 264 | BasicBlock::Create(F->getContext(), "HookDetectedHandler", F); 265 | BasicBlock *Detect = BasicBlock::Create(F->getContext(), "", F); 266 | BasicBlock *Detect2 = BasicBlock::Create(F->getContext(), "", F); 267 | // Change A's terminator to jump to B 268 | // We'll add new terminator in B to jump C later 269 | A->getTerminator()->eraseFromParent(); 270 | BranchInst::Create(Detect, A); 271 | 272 | IRBuilder<> IRBDetect(Detect); 273 | IRBuilder<> IRBDetect2(Detect2); 274 | IRBuilder<> IRBB(B); 275 | 276 | Type *Int64Ty = Type::getInt64Ty(F->getContext()); 277 | Type *Int32Ty = Type::getInt32Ty(F->getContext()); 278 | Type *Int32PtrTy = Type::getInt32Ty(F->getContext())->getPointerTo(); 279 | 280 | Value *Load = 281 | IRBDetect.CreateLoad(Int32Ty, IRBDetect.CreateBitCast(F, Int32PtrTy)); 282 | Value *LS2 = IRBDetect.CreateLShr(Load, ConstantInt::get(Int32Ty, 26)); 283 | Value *ICmpEQ2 = IRBDetect.CreateICmpEQ( 284 | LS2, ConstantInt::get(Int32Ty, AARCH64_SIGNATURE_B)); 285 | Value *LS3 = IRBDetect.CreateLShr(Load, ConstantInt::get(Int32Ty, 21)); 286 | Value *ICmpEQ3 = IRBDetect.CreateICmpEQ( 287 | LS3, ConstantInt::get(Int32Ty, AARCH64_SIGNATURE_BRK)); 288 | Value *Or = IRBDetect.CreateOr(ICmpEQ2, ICmpEQ3); 289 | IRBDetect.CreateCondBr(Or, B, Detect2); 290 | 291 | Value *PTI = IRBDetect2.CreatePtrToInt(F, Int64Ty); 292 | Value *AddFour = IRBDetect2.CreateAdd(PTI, ConstantInt::get(Int64Ty, 4)); 293 | Value *ITP = IRBDetect2.CreateIntToPtr(AddFour, Int32PtrTy); 294 | Value *Load2 = IRBDetect2.CreateLoad(Int32Ty, ITP); 295 | Value *LS4 = IRBDetect2.CreateLShr(Load2, ConstantInt::get(Int32Ty, 10)); 296 | Value *ICmpEQ4 = IRBDetect2.CreateICmpEQ( 297 | LS4, ConstantInt::get(Int32Ty, AARCH64_SIGNATURE_BR)); 298 | Value *AddEight = IRBDetect2.CreateAdd(PTI, ConstantInt::get(Int64Ty, 8)); 299 | Value *ITP2 = IRBDetect2.CreateIntToPtr(AddEight, Int32PtrTy); 300 | Value *Load3 = IRBDetect2.CreateLoad(Int32Ty, ITP2); 301 | Value *LS5 = IRBDetect2.CreateLShr(Load3, ConstantInt::get(Int32Ty, 10)); 302 | Value *ICmpEQ5 = IRBDetect2.CreateICmpEQ( 303 | LS5, ConstantInt::get(Int32Ty, AARCH64_SIGNATURE_BR)); 304 | Value *Or2 = IRBDetect2.CreateOr(ICmpEQ4, ICmpEQ5); 305 | IRBDetect2.CreateCondBr(Or2, B, C); 306 | CreateCallbackAndJumpBack(&IRBB, C); 307 | } 308 | 309 | void HandleObjcRuntimeHook(Function *ObjcMethodImp, std::string classname, 310 | std::string selname, bool classmethod) { 311 | /* 312 | We split the originalBB A into: 313 | A < - RuntimeHook Detection 314 | | \ 315 | | B for handler() 316 | | / 317 | C < - Original Following BB 318 | */ 319 | Module *M = ObjcMethodImp->getParent(); 320 | 321 | BasicBlock *A = &(ObjcMethodImp->getEntryBlock()); 322 | BasicBlock *C = A->splitBasicBlock(A->getFirstNonPHIOrDbgOrLifetime()); 323 | BasicBlock *B = BasicBlock::Create(A->getContext(), "HookDetectedHandler", 324 | ObjcMethodImp, C); 325 | // Delete A's terminator 326 | A->getTerminator()->eraseFromParent(); 327 | 328 | IRBuilder<> IRBA(A); 329 | IRBuilder<> IRBB(B); 330 | 331 | Type *Int8PtrTy = Type::getInt8Ty(M->getContext())->getPointerTo(); 332 | 333 | Value *GetClass = IRBA.CreateCall(M->getFunction("objc_getClass"), 334 | {IRBA.CreateGlobalStringPtr(classname)}); 335 | Value *GetSelector = IRBA.CreateCall(M->getFunction("sel_registerName"), 336 | {IRBA.CreateGlobalStringPtr(selname)}); 337 | Value *GetMethod = 338 | IRBA.CreateCall(M->getFunction(classmethod ? "class_getClassMethod" 339 | : "class_getInstanceMethod"), 340 | {GetClass, GetSelector}); 341 | Value *GetMethodImp = IRBA.CreateCall( 342 | M->getFunction("method_getImplementation"), {GetMethod}); 343 | Value *IcmpEq = 344 | IRBA.CreateICmpEQ(IRBA.CreateBitCast(GetMethodImp, Int8PtrTy), 345 | ConstantExpr::getBitCast(ObjcMethodImp, Int8PtrTy)); 346 | IRBA.CreateCondBr(IcmpEq, C, B); 347 | CreateCallbackAndJumpBack(&IRBB, C); 348 | } 349 | void CreateCallbackAndJumpBack(IRBuilder<> *IRBB, BasicBlock *C) { 350 | Module *M = C->getModule(); 351 | Function *AHCallBack = M->getFunction("AHCallBack"); 352 | if (AHCallBack) { 353 | IRBB->CreateCall(AHCallBack); 354 | } else { 355 | if (triple.isOSDarwin() && triple.isAArch64()) { 356 | std::string exitsvcasm = "mov w16, #1\n"; 357 | exitsvcasm += 358 | "svc #" + std::to_string(cryptoutils->get_range(65536)) + "\n"; 359 | InlineAsm *IA = 360 | InlineAsm::get(FunctionType::get(IRBB->getVoidTy(), false), 361 | exitsvcasm, "", true, false); 362 | IRBB->CreateCall(IA); 363 | } else { 364 | FunctionType *ABFT = 365 | FunctionType::get(Type::getVoidTy(M->getContext()), false); 366 | Function *abort_declare = 367 | cast(M->getOrInsertFunction("abort", ABFT).getCallee()); 368 | abort_declare->addFnAttr(Attribute::AttrKind::NoReturn); 369 | IRBB->CreateCall(abort_declare); 370 | } 371 | } 372 | IRBB->CreateBr(C); 373 | } 374 | }; 375 | } // namespace llvm 376 | 377 | ModulePass *llvm::createAntiHookPass(bool flag) { return new AntiHook(flag); } 378 | char AntiHook::ID = 0; 379 | INITIALIZE_PASS(AntiHook, "antihook", "AntiHook", false, false) 380 | -------------------------------------------------------------------------------- /hikari/BogusControlFlow.h: -------------------------------------------------------------------------------- 1 | //===- BogusControlFlow.h - BogusControlFlow Obfuscation 2 | // pass-------------------------===// 3 | // 4 | // The LLVM Compiler Infrastructure 5 | // 6 | // This file is distributed under the University of Illinois Open Source 7 | // License. See LICENSE.TXT for details. 8 | // 9 | //===--------------------------------------------------------------------------------===// 10 | // 11 | // This file contains includes and defines for the bogusControlFlow pass 12 | // 13 | //===--------------------------------------------------------------------------------===// 14 | 15 | #ifndef _BOGUSCONTROLFLOW_H_ 16 | #define _BOGUSCONTROLFLOW_H_ 17 | 18 | #include "llvm/IR/PassManager.h" 19 | #include "llvm/Pass.h" 20 | 21 | namespace llvm { 22 | 23 | FunctionPass *createBogusControlFlowPass(bool flag); 24 | void initializeBogusControlFlowPass(PassRegistry &Registry); 25 | 26 | } // namespace llvm 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /hikari/ConstantEncryption.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | LLVM ConstantEncryption Pass 3 | Copyright (C) 2017 Zhang(http://mayuyu.io) 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | */ 18 | #include "ConstantEncryption.h" 19 | #include "llvm/IR/Constants.h" 20 | #include "llvm/IR/IRBuilder.h" 21 | #include "llvm/IR/InstIterator.h" 22 | #include "llvm/IR/Instructions.h" 23 | #include "llvm/IR/IntrinsicInst.h" 24 | #include "llvm/IR/Module.h" 25 | #include "llvm/IR/NoFolder.h" 26 | #include "CryptoUtils.h" 27 | #include "SubstituteImpl.h" 28 | #include "Utils.h" 29 | #include "compat/CallSite.h" 30 | #include "llvm/Transforms/Utils/ModuleUtils.h" 31 | #include 32 | 33 | using namespace llvm; 34 | 35 | static cl::opt 36 | SubstituteXor("constenc_subxor", 37 | cl::desc("Substitute xor operator of ConstantEncryption"), 38 | cl::value_desc("Substitute xor operator"), cl::init(false), 39 | cl::Optional); 40 | static bool SubstituteXorTemp = false; 41 | 42 | static cl::opt SubstituteXorProb( 43 | "constenc_subxor_prob", 44 | cl::desc( 45 | "Choose the probability [%] each xor operator will be Substituted"), 46 | cl::value_desc("probability rate"), cl::init(40), cl::Optional); 47 | static uint32_t SubstituteXorProbTemp = 40; 48 | 49 | static cl::opt 50 | ConstToGV("constenc_togv", 51 | cl::desc("Replace ConstantInt with GlobalVariable"), 52 | cl::value_desc("ConstantInt to GlobalVariable"), cl::init(false), 53 | cl::Optional); 54 | static bool ConstToGVTemp = false; 55 | 56 | static cl::opt 57 | ConstToGVProb("constenc_togv_prob", 58 | cl::desc("Choose the probability [%] each ConstantInt will " 59 | "replaced with GlobalVariable"), 60 | cl::value_desc("probability rate"), cl::init(50), 61 | cl::Optional); 62 | static uint32_t ConstToGVProbTemp = 50; 63 | 64 | static cl::opt ObfTimes( 65 | "constenc_times", 66 | cl::desc( 67 | "Choose how many time the ConstantEncryption pass loop on a function"), 68 | cl::value_desc("Number of Times"), cl::init(1), cl::Optional); 69 | static uint32_t ObfTimesTemp = 1; 70 | 71 | namespace llvm { 72 | struct ConstantEncryption : public ModulePass { 73 | static char ID; 74 | bool flag; 75 | bool dispatchonce; 76 | std::set handled_gvs; 77 | ConstantEncryption(bool flag) : ModulePass(ID) { this->flag = flag; } 78 | ConstantEncryption() : ModulePass(ID) { this->flag = true; } 79 | bool shouldEncryptConstant(Instruction *I) { 80 | if (isa(I) || isa(I) || 81 | isa(I) || isa(I) || I->isAtomic()) 82 | return false; 83 | if (AllocaInst *AI = dyn_cast(I)) 84 | if (AI->isSwiftError()) 85 | return false; 86 | if (isa(I) || isa(I)) { 87 | CallSite CS(I); 88 | if (CS.getCalledFunction() && 89 | #if LLVM_VERSION_MAJOR >= 18 90 | CS.getCalledFunction()->getName().starts_with("hikari_")) { 91 | #else 92 | CS.getCalledFunction()->getName().startswith("hikari_")) { 93 | #endif 94 | return false; 95 | } 96 | } 97 | if (dispatchonce) 98 | if (AllocaInst *AI = dyn_cast(I)) { 99 | if (AI->getAllocatedType()->isIntegerTy()) 100 | for (User *U : AI->users()) 101 | if (LoadInst *LI = dyn_cast(U)) 102 | for (User *LU : LI->users()) 103 | if (isa(LU) || isa(LU)) { 104 | CallSite CS(LU); 105 | Value *calledFunction = CS.getCalledFunction(); 106 | if (!calledFunction) 107 | calledFunction = CS.getCalledValue()->stripPointerCasts(); 108 | if (!calledFunction || 109 | (!isa(calledFunction) && 110 | !isa(calledFunction)) || 111 | CS.getIntrinsicID() != Intrinsic::not_intrinsic) 112 | continue; 113 | if (calledFunction->getName() == "_dispatch_once" || 114 | calledFunction->getName() == "dispatch_once") 115 | return false; 116 | } 117 | } 118 | return true; 119 | } 120 | bool runOnModule(Module &M) override { 121 | dispatchonce = M.getFunction("dispatch_once"); 122 | for (Function &F : M) 123 | if (toObfuscate(flag, &F, "constenc") && !F.isPresplitCoroutine()) { 124 | errs() << "Running ConstantEncryption On " << F.getName() << "\n"; 125 | FixFunctionConstantExpr(&F); 126 | if (!toObfuscateUint32Option(&F, "constenc_times", &ObfTimesTemp)) 127 | ObfTimesTemp = ObfTimes; 128 | if (!toObfuscateBoolOption(&F, "constenc_togv", &ConstToGVTemp)) 129 | ConstToGVTemp = ConstToGV; 130 | if (!toObfuscateBoolOption(&F, "constenc_subxor", &SubstituteXorTemp)) 131 | SubstituteXorTemp = SubstituteXor; 132 | if (!toObfuscateUint32Option(&F, "constenc_subxor_prob", 133 | &SubstituteXorProbTemp)) 134 | SubstituteXorProbTemp = SubstituteXorProb; 135 | if (SubstituteXorProbTemp > 100) { 136 | errs() << "-constenc_subxor_prob=x must be 0 < x <= 100"; 137 | return false; 138 | } 139 | if (!toObfuscateUint32Option(&F, "constenc_togv_prob", 140 | &ConstToGVProbTemp)) 141 | ConstToGVProbTemp = ConstToGVProb; 142 | if (ConstToGVProbTemp > 100) { 143 | errs() << "-constenc_togv_prob=x must be 0 < x <= 100"; 144 | return false; 145 | } 146 | uint32_t times = ObfTimesTemp; 147 | while (times) { 148 | EncryptConstants(F); 149 | if (ConstToGVTemp) { 150 | Constant2GlobalVariable(F); 151 | } 152 | times--; 153 | } 154 | } 155 | return true; 156 | } 157 | 158 | bool isDispatchOnceToken(GlobalVariable *GV) { 159 | if (!dispatchonce) 160 | return false; 161 | for (User *U : GV->users()) { 162 | if (isa(U) || isa(U)) { 163 | CallSite CS(U); 164 | Value *calledFunction = CS.getCalledFunction(); 165 | if (!calledFunction) 166 | calledFunction = CS.getCalledValue()->stripPointerCasts(); 167 | if (!calledFunction || 168 | (!isa(calledFunction) && 169 | !isa(calledFunction)) || 170 | CS.getIntrinsicID() != Intrinsic::not_intrinsic) 171 | continue; 172 | if (calledFunction->getName() == "_dispatch_once" || 173 | calledFunction->getName() == "dispatch_once") { 174 | Value *onceToken = U->getOperand(0); 175 | if (dyn_cast_or_null( 176 | onceToken->stripPointerCasts()) == GV) 177 | return true; 178 | } 179 | } 180 | if (StoreInst *SI = dyn_cast(U)) 181 | for (User *SU : SI->getPointerOperand()->users()) 182 | if (LoadInst *LI = dyn_cast(SU)) 183 | for (User *LU : LI->users()) 184 | if (isa(LU) || isa(LU)) { 185 | CallSite CS(LU); 186 | Value *calledFunction = CS.getCalledFunction(); 187 | if (!calledFunction) 188 | calledFunction = CS.getCalledValue()->stripPointerCasts(); 189 | if (!calledFunction || 190 | (!isa(calledFunction) && 191 | !isa(calledFunction)) || 192 | CS.getIntrinsicID() != Intrinsic::not_intrinsic) 193 | continue; 194 | if (calledFunction->getName() == "_dispatch_once" || 195 | calledFunction->getName() == "dispatch_once") 196 | return true; 197 | } 198 | } 199 | return false; 200 | } 201 | 202 | bool isAtomicLoaded(GlobalVariable *GV) { 203 | for (User *U : GV->users()) { 204 | if (LoadInst *LI = dyn_cast(U)) { 205 | if (LI->isAtomic()) 206 | return true; 207 | } 208 | } 209 | return false; 210 | } 211 | 212 | void EncryptConstants(Function &F) { 213 | for (Instruction &I : instructions(F)) { 214 | if (!shouldEncryptConstant(&I)) 215 | continue; 216 | CallInst *CI = dyn_cast(&I); 217 | for (unsigned i = 0; i < I.getNumOperands(); i++) { 218 | if (CI && CI->isBundleOperand(i)) 219 | continue; 220 | Value *Op = I.getOperand(i); 221 | if (isa(Op)) 222 | HandleConstantIntOperand(&I, i); 223 | if (GlobalVariable *G = dyn_cast(Op)) 224 | if (G->hasInitializer() && 225 | (G->hasPrivateLinkage() || G->hasInternalLinkage()) && 226 | isa(G->getInitializer())) 227 | HandleConstantIntInitializerGV(G); 228 | } 229 | } 230 | } 231 | 232 | void Constant2GlobalVariable(Function &F) { 233 | Module &M = *F.getParent(); 234 | const DataLayout &DL = M.getDataLayout(); 235 | for (Instruction &I : instructions(F)) { 236 | if (!shouldEncryptConstant(&I)) 237 | continue; 238 | CallInst *CI = dyn_cast(&I); 239 | InvokeInst *II = dyn_cast(&I); 240 | for (unsigned int i = 0; i < I.getNumOperands(); i++) { 241 | if (CI && CI->isBundleOperand(i)) 242 | continue; 243 | if (II && II->isBundleOperand(i)) 244 | continue; 245 | if (ConstantInt *CI = dyn_cast(I.getOperand(i))) { 246 | if (!(cryptoutils->get_range(100) <= ConstToGVProbTemp)) 247 | continue; 248 | GlobalVariable *GV = new GlobalVariable( 249 | *F.getParent(), CI->getType(), false, 250 | GlobalValue::LinkageTypes::PrivateLinkage, 251 | ConstantInt::get(CI->getType(), CI->getValue()), "CToGV"); 252 | appendToCompilerUsed(*F.getParent(), GV); 253 | I.setOperand(i, new LoadInst(GV->getValueType(), GV, "", &I)); 254 | } 255 | } 256 | } 257 | for (Instruction &I : instructions(F)) { 258 | if (!shouldEncryptConstant(&I)) 259 | continue; 260 | if (BinaryOperator *BO = dyn_cast(&I)) { 261 | if (!BO->getType()->isIntegerTy()) 262 | continue; 263 | if (!(cryptoutils->get_range(100) <= ConstToGVProbTemp)) 264 | continue; 265 | IntegerType *IT = cast(BO->getType()); 266 | uint64_t dummy; 267 | if (IT == Type::getInt8Ty(IT->getContext())) 268 | dummy = cryptoutils->get_uint8_t(); 269 | else if (IT == Type::getInt16Ty(IT->getContext())) 270 | dummy = cryptoutils->get_uint16_t(); 271 | else if (IT == Type::getInt32Ty(IT->getContext())) 272 | dummy = cryptoutils->get_uint32_t(); 273 | else if (IT == Type::getInt64Ty(IT->getContext())) 274 | dummy = cryptoutils->get_uint64_t(); 275 | else 276 | continue; 277 | GlobalVariable *GV = new GlobalVariable( 278 | M, BO->getType(), false, GlobalValue::LinkageTypes::PrivateLinkage, 279 | ConstantInt::get(BO->getType(), dummy), "CToGV"); 280 | StoreInst *SI = 281 | new StoreInst(BO, GV, false, DL.getABITypeAlign(BO->getType())); 282 | SI->insertAfter(BO); 283 | LoadInst *LI = new LoadInst(GV->getValueType(), GV, "", false, 284 | DL.getABITypeAlign(BO->getType())); 285 | LI->insertAfter(SI); 286 | BO->replaceUsesWithIf(LI, [SI](Use &U) { return U.getUser() != SI; }); 287 | } 288 | } 289 | } 290 | 291 | void HandleConstantIntInitializerGV(GlobalVariable *GVPtr) { 292 | if (!(flag || AreUsersInOneFunction(GVPtr)) || isDispatchOnceToken(GVPtr) || 293 | isAtomicLoaded(GVPtr)) 294 | return; 295 | // Prepare Types and Keys 296 | std::pair keyandnew; 297 | ConstantInt *Old = dyn_cast(GVPtr->getInitializer()); 298 | bool hasHandled = true; 299 | if (handled_gvs.find(GVPtr) == handled_gvs.end()) { 300 | hasHandled = false; 301 | keyandnew = PairConstantInt(Old); 302 | handled_gvs.insert(GVPtr); 303 | } 304 | ConstantInt *XORKey = keyandnew.first; 305 | ConstantInt *newGVInit = keyandnew.second; 306 | if (hasHandled || !XORKey || !newGVInit) 307 | return; 308 | GVPtr->setInitializer(newGVInit); 309 | bool isSigned = XORKey->getValue().isSignBitSet() || 310 | newGVInit->getValue().isSignBitSet() || 311 | Old->getValue().isSignBitSet(); 312 | for (User *U : GVPtr->users()) { 313 | BinaryOperator *XORInst = nullptr; 314 | if (LoadInst *LI = dyn_cast(U)) { 315 | if (LI->getType() != XORKey->getType()) { 316 | Instruction *IntegerCast = 317 | BitCastInst::CreateIntegerCast(LI, XORKey->getType(), isSigned); 318 | IntegerCast->insertAfter(LI); 319 | XORInst = 320 | BinaryOperator::Create(Instruction::Xor, IntegerCast, XORKey); 321 | XORInst->insertAfter(IntegerCast); 322 | Instruction *IntegerCast2 = 323 | BitCastInst::CreateIntegerCast(XORInst, LI->getType(), isSigned); 324 | IntegerCast2->insertAfter(XORInst); 325 | LI->replaceUsesWithIf(IntegerCast2, [IntegerCast](Use &U) { 326 | return U.getUser() != IntegerCast; 327 | }); 328 | } else { 329 | XORInst = BinaryOperator::Create(Instruction::Xor, LI, XORKey); 330 | XORInst->insertAfter(LI); 331 | LI->replaceUsesWithIf( 332 | XORInst, [XORInst](Use &U) { return U.getUser() != XORInst; }); 333 | } 334 | } else if (StoreInst *SI = dyn_cast(U)) { 335 | XORInst = BinaryOperator::Create(Instruction::Xor, SI->getOperand(0), 336 | XORKey, "", SI); 337 | SI->replaceUsesOfWith(SI->getValueOperand(), XORInst); 338 | } 339 | if (XORInst && SubstituteXorTemp && 340 | cryptoutils->get_range(100) <= SubstituteXorProbTemp) 341 | SubstituteImpl::substituteXor(XORInst); 342 | } 343 | } 344 | 345 | void HandleConstantIntOperand(Instruction *I, unsigned opindex) { 346 | std::pair keyandnew = 347 | PairConstantInt(cast(I->getOperand(opindex))); 348 | ConstantInt *Key = keyandnew.first; 349 | ConstantInt *New = keyandnew.second; 350 | if (!Key || !New) 351 | return; 352 | BinaryOperator *NewOperand = 353 | BinaryOperator::Create(Instruction::Xor, New, Key, "", I); 354 | 355 | I->setOperand(opindex, NewOperand); 356 | if (SubstituteXorTemp && 357 | cryptoutils->get_range(100) <= SubstituteXorProbTemp) 358 | SubstituteImpl::substituteXor(NewOperand); 359 | } 360 | 361 | std::pair 362 | PairConstantInt(ConstantInt *C) { 363 | if (!C) 364 | return std::make_pair(nullptr, nullptr); 365 | IntegerType *IT = cast(C->getType()); 366 | uint64_t K; 367 | if (IT == Type::getInt1Ty(IT->getContext()) || 368 | IT == Type::getInt8Ty(IT->getContext())) 369 | K = cryptoutils->get_uint8_t(); 370 | else if (IT == Type::getInt16Ty(IT->getContext())) 371 | K = cryptoutils->get_uint16_t(); 372 | else if (IT == Type::getInt32Ty(IT->getContext())) 373 | K = cryptoutils->get_uint32_t(); 374 | else if (IT == Type::getInt64Ty(IT->getContext())) 375 | K = cryptoutils->get_uint64_t(); 376 | else 377 | return std::make_pair(nullptr, nullptr); 378 | ConstantInt *CI = 379 | cast(ConstantInt::get(IT, K ^ C->getValue())); 380 | return std::make_pair(ConstantInt::get(IT, K), CI); 381 | } 382 | }; 383 | 384 | ModulePass *createConstantEncryptionPass(bool flag) { 385 | return new ConstantEncryption(flag); 386 | } 387 | } // namespace llvm 388 | char ConstantEncryption::ID = 0; 389 | INITIALIZE_PASS(ConstantEncryption, "constenc", 390 | "Enable ConstantInt GV Encryption.", false, false) 391 | -------------------------------------------------------------------------------- /hikari/ConstantEncryption.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONSTANT_ENCRYPTION_H_ 2 | #define _CONSTANT_ENCRYPTION_H_ 3 | 4 | #include "llvm/IR/PassManager.h" 5 | #include "llvm/Pass.h" 6 | 7 | namespace llvm { 8 | 9 | ModulePass *createConstantEncryptionPass(bool flag); 10 | void initializeConstantEncryptionPass(PassRegistry &Registry); 11 | 12 | } // namespace llvm 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /hikari/CryptoUtils.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | #include "CryptoUtils.h" 5 | #include "llvm/Support/Format.h" 6 | #include "llvm/Support/raw_ostream.h" 7 | #include 8 | 9 | using namespace llvm; 10 | namespace llvm { 11 | ManagedStatic cryptoutils; 12 | } 13 | CryptoUtils::CryptoUtils() {} 14 | 15 | uint32_t CryptoUtils::scramble32( 16 | uint32_t in, std::unordered_map &VMap) { 17 | if (VMap.find(in) == VMap.end()) { 18 | uint32_t V = get_uint32_t(); 19 | VMap[in] = V; 20 | return V; 21 | } else { 22 | return VMap[in]; 23 | } 24 | } 25 | CryptoUtils::~CryptoUtils() { 26 | if (eng != nullptr) 27 | delete eng; 28 | } 29 | void CryptoUtils::prng_seed() { 30 | using namespace std::chrono; 31 | std::uint_fast64_t ms = 32 | duration_cast(system_clock::now().time_since_epoch()) 33 | .count(); 34 | errs() << format("std::mt19937_64 seeded with current timestamp: %" PRIu64 "", 35 | ms) 36 | << "\n"; 37 | eng = new std::mt19937_64(ms); 38 | } 39 | void CryptoUtils::prng_seed(std::uint_fast64_t seed) { 40 | errs() << format("std::mt19937_64 seeded with: %" PRIu64 "", seed) << "\n"; 41 | eng = new std::mt19937_64(seed); 42 | } 43 | std::uint_fast64_t CryptoUtils::get_raw() { 44 | if (eng == nullptr) 45 | prng_seed(); 46 | return (*eng)(); 47 | } 48 | uint32_t CryptoUtils::get_range(uint32_t min, uint32_t max) { 49 | if (max == 0) 50 | return 0; 51 | std::uniform_int_distribution dis(min, max - 1); 52 | return dis(*eng); 53 | } 54 | -------------------------------------------------------------------------------- /hikari/CryptoUtils.h: -------------------------------------------------------------------------------- 1 | //===- CryptoUtils.h - Cryptographically Secure Pseudo-Random 2 | // Generator------------------===// 3 | 4 | /* 5 | Copyright (C) 2017 Zhang(https://github.com/Naville/) 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | You should have received a copy of the GNU Affero General Public License 15 | along with this program. If not, see . 16 | */ 17 | #ifndef _CRYPTO_UTILS_H_ 18 | #define _CRYPTO_UTILS_H_ 19 | 20 | #include "llvm/Support/ManagedStatic.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace llvm { 29 | 30 | class CryptoUtils { 31 | public: 32 | CryptoUtils(); 33 | ~CryptoUtils(); 34 | void prng_seed(std::uint_fast64_t seed); 35 | void prng_seed(); 36 | template T get() { 37 | std::uint_fast64_t num = get_raw(); 38 | return static_cast(num); 39 | }; 40 | // Return a value in [0,max) 41 | uint32_t get_range(uint32_t max) { return get_range(0, max); } 42 | uint32_t get_range(uint32_t min, uint32_t max); 43 | uint32_t get_uint32_t() { return get(); }; 44 | uint64_t get_uint64_t() { return get(); }; 45 | uint32_t get_uint8_t() { return get(); }; 46 | uint32_t get_uint16_t() { return get(); }; 47 | 48 | // Scramble32 originally uses AES to generates the mapping relationship 49 | // between a BB and its switchvar Hikari updates this by doing this using 50 | // mt19937_64 in C++ STLs which is a faster but less cryprographically secured 51 | // This method try to find the corresponding value from the VMap first, if not 52 | // then use RNG to generate,fill and return the value 53 | uint32_t 54 | scramble32(uint32_t in, 55 | std::unordered_map &VMap); 56 | 57 | private: 58 | std::mt19937_64 *eng = nullptr; 59 | std::uint_fast64_t get_raw(); 60 | }; 61 | extern ManagedStatic cryptoutils; 62 | 63 | } // namespace llvm 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /hikari/Flattening.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | #include "Flattening.h" 5 | #include "llvm/IR/Constants.h" 6 | #include "llvm/IR/Instructions.h" 7 | #include "llvm/Passes/PassBuilder.h" 8 | #include "CryptoUtils.h" 9 | #include "Utils.h" 10 | #include "llvm/Transforms/Utils/LowerSwitch.h" 11 | 12 | using namespace llvm; 13 | 14 | namespace { 15 | struct Flattening : public FunctionPass { 16 | static char ID; // Pass identification, replacement for typeid 17 | bool flag; 18 | Flattening() : FunctionPass(ID) { this->flag = true; } 19 | Flattening(bool flag) : FunctionPass(ID) { this->flag = flag; } 20 | bool runOnFunction(Function &F) override; 21 | void flatten(Function *f); 22 | }; 23 | } // namespace 24 | 25 | char Flattening::ID = 0; 26 | FunctionPass *llvm::createFlatteningPass(bool flag) { 27 | return new Flattening(flag); 28 | } 29 | INITIALIZE_PASS(Flattening, "cffobf", "Enable Control Flow Flattening.", false, 30 | false) 31 | bool Flattening::runOnFunction(Function &F) { 32 | Function *tmp = &F; 33 | // Do we obfuscate 34 | if (toObfuscate(flag, tmp, "fla") && !F.isPresplitCoroutine()) { 35 | errs() << "Running ControlFlowFlattening On " << F.getName() << "\n"; 36 | flatten(tmp); 37 | } 38 | 39 | return true; 40 | } 41 | 42 | void Flattening::flatten(Function *f) { 43 | SmallVector origBB; 44 | BasicBlock *loopEntry, *loopEnd; 45 | LoadInst *load; 46 | SwitchInst *switchI; 47 | AllocaInst *switchVar, *switchVarAddr; 48 | const DataLayout &DL = f->getParent()->getDataLayout(); 49 | 50 | // SCRAMBLER 51 | std::unordered_map scrambling_key; 52 | // END OF SCRAMBLER 53 | 54 | PassBuilder PB; 55 | FunctionAnalysisManager FAM; 56 | FunctionPassManager FPM; 57 | PB.registerFunctionAnalyses(FAM); 58 | FPM.addPass(LowerSwitchPass()); 59 | FPM.run(*f, FAM); 60 | 61 | for (BasicBlock &BB : *f) { 62 | if (BB.isEHPad() || BB.isLandingPad()) { 63 | errs() << f->getName() 64 | << " Contains Exception Handing Instructions and is unsupported " 65 | "for flattening in the open-source version of Hikari.\n"; 66 | return; 67 | } 68 | if (!isa(BB.getTerminator()) && 69 | !isa(BB.getTerminator())) 70 | return; 71 | origBB.emplace_back(&BB); 72 | } 73 | 74 | // Nothing to flatten 75 | if (origBB.size() <= 1) 76 | return; 77 | 78 | // Remove first BB 79 | origBB.erase(origBB.begin()); 80 | 81 | // Get a pointer on the first BB 82 | Function::iterator tmp = f->begin(); 83 | BasicBlock *insert = &*tmp; 84 | 85 | // If main begin with an if 86 | BranchInst *br = nullptr; 87 | if (isa(insert->getTerminator())) 88 | br = cast(insert->getTerminator()); 89 | 90 | if ((br && br->isConditional()) || 91 | insert->getTerminator()->getNumSuccessors() > 1) { 92 | BasicBlock::iterator i = insert->end(); 93 | --i; 94 | 95 | if (insert->size() > 1) { 96 | --i; 97 | } 98 | 99 | BasicBlock *tmpBB = insert->splitBasicBlock(i, "first"); 100 | origBB.insert(origBB.begin(), tmpBB); 101 | } 102 | 103 | // Remove jump 104 | Instruction *oldTerm = insert->getTerminator(); 105 | 106 | // Create switch variable and set as it 107 | switchVar = new AllocaInst(Type::getInt32Ty(f->getContext()), 108 | DL.getAllocaAddrSpace(), "switchVar", oldTerm); 109 | switchVarAddr = 110 | new AllocaInst(Type::getInt32Ty(f->getContext())->getPointerTo(), 111 | DL.getAllocaAddrSpace(), "", oldTerm); 112 | 113 | // Remove jump 114 | oldTerm->eraseFromParent(); 115 | 116 | new StoreInst(ConstantInt::get(Type::getInt32Ty(f->getContext()), 117 | cryptoutils->scramble32(0, scrambling_key)), 118 | switchVar, insert); 119 | new StoreInst(switchVar, switchVarAddr, insert); 120 | 121 | // Create main loop 122 | loopEntry = BasicBlock::Create(f->getContext(), "loopEntry", f, insert); 123 | loopEnd = BasicBlock::Create(f->getContext(), "loopEnd", f, insert); 124 | 125 | load = new LoadInst(switchVar->getAllocatedType(), switchVar, "switchVar", 126 | loopEntry); 127 | 128 | // Move first BB on top 129 | insert->moveBefore(loopEntry); 130 | BranchInst::Create(loopEntry, insert); 131 | 132 | // loopEnd jump to loopEntry 133 | BranchInst::Create(loopEntry, loopEnd); 134 | 135 | BasicBlock *swDefault = 136 | BasicBlock::Create(f->getContext(), "switchDefault", f, loopEnd); 137 | BranchInst::Create(loopEnd, swDefault); 138 | 139 | // Create switch instruction itself and set condition 140 | switchI = SwitchInst::Create(&*f->begin(), swDefault, 0, loopEntry); 141 | switchI->setCondition(load); 142 | 143 | // Remove branch jump from 1st BB and make a jump to the while 144 | f->begin()->getTerminator()->eraseFromParent(); 145 | 146 | BranchInst::Create(loopEntry, &*f->begin()); 147 | 148 | // Put BB in the switch 149 | for (BasicBlock *i : origBB) { 150 | ConstantInt *numCase = nullptr; 151 | 152 | // Move the BB inside the switch (only visual, no code logic) 153 | i->moveBefore(loopEnd); 154 | 155 | // Add case to switch 156 | numCase = cast(ConstantInt::get( 157 | switchI->getCondition()->getType(), 158 | cryptoutils->scramble32(switchI->getNumCases(), scrambling_key))); 159 | switchI->addCase(numCase, i); 160 | } 161 | 162 | // Recalculate switchVar 163 | for (BasicBlock *i : origBB) { 164 | ConstantInt *numCase = nullptr; 165 | 166 | // If it's a non-conditional jump 167 | if (i->getTerminator()->getNumSuccessors() == 1) { 168 | // Get successor and delete terminator 169 | BasicBlock *succ = i->getTerminator()->getSuccessor(0); 170 | i->getTerminator()->eraseFromParent(); 171 | 172 | // Get next case 173 | numCase = switchI->findCaseDest(succ); 174 | 175 | // If next case == default case (switchDefault) 176 | if (!numCase) { 177 | numCase = cast( 178 | ConstantInt::get(switchI->getCondition()->getType(), 179 | cryptoutils->scramble32(switchI->getNumCases() - 1, 180 | scrambling_key))); 181 | } 182 | 183 | // Update switchVar and jump to the end of loop 184 | new StoreInst( 185 | numCase, 186 | new LoadInst(switchVarAddr->getAllocatedType(), switchVarAddr, "", i), 187 | i); 188 | BranchInst::Create(loopEnd, i); 189 | continue; 190 | } 191 | 192 | // If it's a conditional jump 193 | if (i->getTerminator()->getNumSuccessors() == 2) { 194 | // Get next cases 195 | ConstantInt *numCaseTrue = 196 | switchI->findCaseDest(i->getTerminator()->getSuccessor(0)); 197 | ConstantInt *numCaseFalse = 198 | switchI->findCaseDest(i->getTerminator()->getSuccessor(1)); 199 | 200 | // Check if next case == default case (switchDefault) 201 | if (!numCaseTrue) { 202 | numCaseTrue = cast( 203 | ConstantInt::get(switchI->getCondition()->getType(), 204 | cryptoutils->scramble32(switchI->getNumCases() - 1, 205 | scrambling_key))); 206 | } 207 | 208 | if (!numCaseFalse) { 209 | numCaseFalse = cast( 210 | ConstantInt::get(switchI->getCondition()->getType(), 211 | cryptoutils->scramble32(switchI->getNumCases() - 1, 212 | scrambling_key))); 213 | } 214 | 215 | // Create a SelectInst 216 | BranchInst *br = cast(i->getTerminator()); 217 | SelectInst *sel = 218 | SelectInst::Create(br->getCondition(), numCaseTrue, numCaseFalse, "", 219 | i->getTerminator()); 220 | 221 | // Erase terminator 222 | i->getTerminator()->eraseFromParent(); 223 | // Update switchVar and jump to the end of loop 224 | new StoreInst( 225 | sel, 226 | new LoadInst(switchVarAddr->getAllocatedType(), switchVarAddr, "", i), 227 | i); 228 | BranchInst::Create(loopEnd, i); 229 | continue; 230 | } 231 | } 232 | errs() << "Fixing Stack\n"; 233 | fixStack(f); 234 | errs() << "Fixed Stack\n"; 235 | } 236 | -------------------------------------------------------------------------------- /hikari/Flattening.h: -------------------------------------------------------------------------------- 1 | //===- FlatteningIncludes.h - Flattening Obfuscation pass------------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // This file contains includes and defines for the flattening pass 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | #ifndef _FLATTENING_H_ 15 | #define _FLATTENING_H_ 16 | 17 | #include "llvm/IR/PassManager.h" 18 | #include "llvm/Pass.h" 19 | 20 | namespace llvm { 21 | FunctionPass *createFlatteningPass(bool flag); 22 | void initializeFlatteningPass(PassRegistry &Registry); 23 | 24 | } // namespace llvm 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /hikari/FunctionCallObfuscate.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | #include "FunctionCallObfuscate.h" 5 | #include "json.hpp" 6 | #if LLVM_VERSION_MAJOR >= 17 7 | #include "llvm/ADT/SmallString.h" 8 | #include "llvm/TargetParser/Triple.h" 9 | #else 10 | #include "llvm/ADT/Triple.h" 11 | #endif 12 | #include "llvm/IR/Constants.h" 13 | #include "llvm/IR/IRBuilder.h" 14 | #include "llvm/IR/InstIterator.h" 15 | #include "llvm/IR/Instructions.h" 16 | #include "llvm/IR/Module.h" 17 | #include "llvm/IR/Value.h" 18 | #include "llvm/Support/CommandLine.h" 19 | #include "llvm/Support/Path.h" 20 | #include "llvm/Support/raw_ostream.h" 21 | #include "Utils.h" 22 | #include "compat/CallSite.h" 23 | #include 24 | #include 25 | 26 | using namespace llvm; 27 | 28 | static const int DARWIN_FLAG = 0x2 | 0x8; 29 | static const int ANDROID64_FLAG = 0x00002 | 0x100; 30 | static const int ANDROID32_FLAG = 0x0000 | 0x2; 31 | 32 | static cl::opt 33 | dlopen_flag("fco_flag", 34 | cl::desc("The value of RTLD_DEFAULT on your platform"), 35 | cl::value_desc("value"), cl::init(-1), cl::Optional); 36 | static cl::opt 37 | SymbolConfigPath("fcoconfig", 38 | cl::desc("FunctionCallObfuscate Configuration Path"), 39 | cl::value_desc("filename"), cl::init("+-x/")); 40 | namespace llvm { 41 | struct FunctionCallObfuscate : public FunctionPass { 42 | static char ID; 43 | nlohmann::json Configuration; 44 | bool flag; 45 | bool initialized; 46 | bool opaquepointers; 47 | Triple triple; 48 | FunctionCallObfuscate() : FunctionPass(ID) { 49 | this->flag = true; 50 | this->initialized = false; 51 | } 52 | FunctionCallObfuscate(bool flag) : FunctionPass(ID) { 53 | this->flag = flag; 54 | this->initialized = false; 55 | } 56 | StringRef getPassName() const override { return "FunctionCallObfuscate"; } 57 | bool initialize(Module &M) { 58 | // Basic Defs 59 | if (SymbolConfigPath == "+-x/") { 60 | SmallString<32> Path; 61 | if (sys::path::home_directory(Path)) { // Stolen from LineEditor.cpp 62 | sys::path::append(Path, "Hikari", "SymbolConfig.json"); 63 | SymbolConfigPath = Path.c_str(); 64 | } 65 | } 66 | std::ifstream infile(SymbolConfigPath); 67 | if (infile.good()) { 68 | errs() << "Loading Symbol Configuration From:" << SymbolConfigPath 69 | << "\n"; 70 | infile >> this->Configuration; 71 | } else { 72 | errs() << "Failed To Load Symbol Configuration From:" << SymbolConfigPath 73 | << "\n"; 74 | } 75 | this->triple = Triple(M.getTargetTriple()); 76 | if (triple.getVendor() == Triple::VendorType::Apple) { 77 | Type *Int8PtrTy = Type::getInt8Ty(M.getContext())->getPointerTo(); 78 | // Generic ObjC Runtime Declarations 79 | FunctionType *IMPType = 80 | FunctionType::get(Int8PtrTy, {Int8PtrTy, Int8PtrTy}, true); 81 | PointerType *IMPPointerType = PointerType::get(IMPType, 0); 82 | FunctionType *class_replaceMethod_type = FunctionType::get( 83 | IMPPointerType, {Int8PtrTy, Int8PtrTy, IMPPointerType, Int8PtrTy}, 84 | false); 85 | M.getOrInsertFunction("class_replaceMethod", class_replaceMethod_type); 86 | FunctionType *sel_registerName_type = 87 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); 88 | M.getOrInsertFunction("sel_registerName", sel_registerName_type); 89 | FunctionType *objc_getClass_type = 90 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); 91 | M.getOrInsertFunction("objc_getClass", objc_getClass_type); 92 | M.getOrInsertFunction("objc_getMetaClass", objc_getClass_type); 93 | FunctionType *class_getName_Type = 94 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); 95 | M.getOrInsertFunction("class_getName", class_getName_Type); 96 | FunctionType *objc_getMetaClass_Type = 97 | FunctionType::get(Int8PtrTy, {Int8PtrTy}, false); 98 | M.getOrInsertFunction("objc_getMetaClass", objc_getMetaClass_Type); 99 | } 100 | this->initialized = true; 101 | #if LLVM_VERSION_MAJOR >= 17 102 | this->opaquepointers = true; 103 | #else 104 | this->opaquepointers = !M.getContext().supportsTypedPointers(); 105 | #endif 106 | return true; 107 | } 108 | 109 | bool OnlyUsedByCompilerUsed(GlobalVariable *GV) { 110 | if (GV->getNumUses() == 1) { 111 | User *U = GV->user_back(); 112 | if (U->getNumUses() == 1) { 113 | if (GlobalVariable *GVU = dyn_cast(U->user_back())) { 114 | if (GVU->getName() == "llvm.compiler.used") 115 | return true; 116 | } 117 | } 118 | } 119 | return false; 120 | } 121 | 122 | void HandleObjC(Function *F) { 123 | SmallPtrSet objcclassgv, objcselgv, selnamegv; 124 | bool compilerUsedChanged = false; 125 | for (Instruction &I : instructions(F)) 126 | for (Value *Op : I.operands()) 127 | if (GlobalVariable *G = 128 | dyn_cast(Op->stripPointerCasts())) { 129 | if (!G->hasName() || !G->hasInitializer() || 130 | !G->getSection().contains("objc")) 131 | continue; 132 | #if LLVM_VERSION_MAJOR >= 18 133 | if (G->getName().starts_with("OBJC_CLASSLIST_REFERENCES")) 134 | objcclassgv.insert(G); 135 | else if (G->getName().starts_with("OBJC_SELECTOR_REFERENCES")) 136 | #else 137 | if (G->getName().startswith("OBJC_CLASSLIST_REFERENCES")) 138 | objcclassgv.insert(G); 139 | else if (G->getName().startswith("OBJC_SELECTOR_REFERENCES")) 140 | #endif 141 | objcselgv.insert(G); 142 | } 143 | Module *M = F->getParent(); 144 | SmallVector toErase; 145 | for (GlobalVariable *GV : objcclassgv) { 146 | // Iterate all CLASSREF uses and replace with objc_getClass() call 147 | // Strings are encrypted in other passes 148 | std::string className = GV->getInitializer()->getName().str(); 149 | className.replace(className.find("OBJC_CLASS_$_"), 150 | strlen("OBJC_CLASS_$_"), ""); 151 | for (User *U : GV->users()) 152 | if (Instruction *I = dyn_cast(U)) { 153 | IRBuilder<> builder(I); 154 | Function *objc_getClass_Func = 155 | cast(M->getFunction("objc_getClass")); 156 | Value *newClassName = 157 | builder.CreateGlobalStringPtr(StringRef(className)); 158 | CallInst *CI = builder.CreateCall(objc_getClass_Func, {newClassName}); 159 | // We need to bitcast it back to avoid IRVerifier 160 | Value *BCI = builder.CreateBitCast(CI, I->getType()); 161 | I->replaceAllUsesWith(BCI); 162 | toErase.emplace_back(I); // We cannot erase it directly or we will 163 | // have problems releasing the IRBuilder. 164 | } 165 | } 166 | for (GlobalVariable *GV : objcselgv) { 167 | // Selector Convert 168 | GlobalVariable *selgv = dyn_cast( 169 | opaquepointers 170 | ? GV->getInitializer() 171 | : cast(GV->getInitializer())->getOperand(0)); 172 | selnamegv.insert(selgv); 173 | ConstantDataArray *CDA = 174 | dyn_cast(selgv->getInitializer()); 175 | StringRef SELName = CDA->getAsString(); // This is REAL Selector Name 176 | for (User *U : GV->users()) 177 | if (Instruction *I = dyn_cast(U)) { 178 | IRBuilder<> builder(I); 179 | Function *sel_registerName_Func = 180 | cast(M->getFunction("sel_registerName")); 181 | Value *newGlobalSELName = builder.CreateGlobalStringPtr(SELName); 182 | CallInst *CI = 183 | builder.CreateCall(sel_registerName_Func, {newGlobalSELName}); 184 | // We need to bitcast it back to avoid IRVerifier 185 | Value *BCI = builder.CreateBitCast(CI, I->getType()); 186 | I->replaceAllUsesWith(BCI); 187 | toErase.emplace_back(I); // We cannot erase it directly or we will 188 | // have problems releasing the IRBuilder. 189 | } 190 | } 191 | for (Instruction *I : toErase) 192 | I->eraseFromParent(); 193 | for (GlobalVariable *GV : objcclassgv) { 194 | GV->removeDeadConstantUsers(); 195 | if (OnlyUsedByCompilerUsed(GV)) { 196 | compilerUsedChanged = true; 197 | GV->replaceAllUsesWith(Constant::getNullValue(GV->getType())); 198 | } 199 | if (GV->getNumUses() == 0) { 200 | GV->dropAllReferences(); 201 | GV->eraseFromParent(); 202 | continue; 203 | } 204 | } 205 | for (GlobalVariable *GV : objcselgv) { 206 | GV->removeDeadConstantUsers(); 207 | if (OnlyUsedByCompilerUsed(GV)) { 208 | compilerUsedChanged = true; 209 | GV->replaceAllUsesWith(Constant::getNullValue(GV->getType())); 210 | } 211 | if (GV->getNumUses() == 0) { 212 | GV->dropAllReferences(); 213 | GV->eraseFromParent(); 214 | } 215 | } 216 | for (GlobalVariable *GV : selnamegv) { 217 | GV->removeDeadConstantUsers(); 218 | if (OnlyUsedByCompilerUsed(GV)) { 219 | compilerUsedChanged = true; 220 | GV->replaceAllUsesWith(Constant::getNullValue(GV->getType())); 221 | } 222 | if (GV->getNumUses() == 0) { 223 | GV->dropAllReferences(); 224 | GV->eraseFromParent(); 225 | } 226 | } 227 | // Fixup llvm.compiler.used, so Verifier won't emit errors 228 | if (compilerUsedChanged) { 229 | GlobalVariable *CompilerUsedGV = 230 | F->getParent()->getGlobalVariable("llvm.compiler.used"); 231 | if (!CompilerUsedGV) 232 | return; 233 | ConstantArray *CompilerUsed = 234 | dyn_cast(CompilerUsedGV->getInitializer()); 235 | if (!CompilerUsed) { 236 | CompilerUsedGV->dropAllReferences(); 237 | CompilerUsedGV->eraseFromParent(); 238 | return; 239 | } 240 | std::vector elements = {}; 241 | for (unsigned int i = 0; i < CompilerUsed->getNumOperands(); i++) { 242 | Constant *Op = 243 | CompilerUsed->getAggregateElement(i); 244 | if (!Op->isNullValue()) 245 | elements.emplace_back(Op); 246 | } 247 | if (elements.size()) { 248 | ConstantArray *NewCA = cast( 249 | ConstantArray::get(CompilerUsed->getType(), elements)); 250 | CompilerUsedGV->setInitializer(NewCA); 251 | } 252 | else { 253 | CompilerUsedGV->dropAllReferences(); 254 | CompilerUsedGV->eraseFromParent(); 255 | } 256 | } 257 | } 258 | bool runOnFunction(Function &F) override { 259 | // Construct Function Prototypes 260 | if (!toObfuscate(flag, &F, "fco")) 261 | return false; 262 | errs() << "Running FunctionCallObfuscate On " << F.getName() << "\n"; 263 | Module *M = F.getParent(); 264 | if (!this->initialized) 265 | initialize(*M); 266 | if (!triple.isAndroid() && !triple.isOSDarwin()) { 267 | errs() << "Unsupported Target Triple: " << M->getTargetTriple() << "\n"; 268 | return false; 269 | } 270 | FixFunctionConstantExpr(&F); 271 | HandleObjC(&F); 272 | Type *Int32Ty = Type::getInt32Ty(M->getContext()); 273 | Type *Int8PtrTy = Type::getInt8Ty(M->getContext())->getPointerTo(); 274 | // ObjC Runtime Declarations 275 | FunctionType *dlopen_type = FunctionType::get( 276 | Int8PtrTy, {Int8PtrTy, Int32Ty}, 277 | false); // int has a length of 32 on both 32/64bit platform 278 | FunctionType *dlsym_type = 279 | FunctionType::get(Int8PtrTy, {Int8PtrTy, Int8PtrTy}, false); 280 | Function *dlopen_decl = cast( 281 | M->getOrInsertFunction("dlopen", dlopen_type).getCallee()); 282 | Function *dlsym_decl = 283 | cast(M->getOrInsertFunction("dlsym", dlsym_type).getCallee()); 284 | // Begin Iteration 285 | for (BasicBlock &BB : F) { 286 | for (Instruction &Inst : BB) { 287 | if (isa(&Inst) || isa(&Inst)) { 288 | CallSite CS(&Inst); 289 | Function *calledFunction = CS.getCalledFunction(); 290 | if (!calledFunction) { 291 | /* 292 | Note: 293 | For Indirect Calls: 294 | CalledFunction is NULL and calledValue is usually a bitcasted 295 | function pointer. We'll need to strip out the hiccups and obtain 296 | the called Function* from there 297 | */ 298 | calledFunction = 299 | dyn_cast(CS.getCalledValue()->stripPointerCasts()); 300 | } 301 | // Simple Extracting Failed 302 | // Use our own implementation 303 | if (!calledFunction) 304 | continue; 305 | #if LLVM_VERSION_MAJOR >= 18 306 | if (calledFunction->getName().starts_with("hikari_")) 307 | #else 308 | if (calledFunction->getName().startswith("hikari_")) 309 | #endif 310 | continue; 311 | 312 | // It's only safe to restrict our modification to external symbols 313 | // Otherwise stripped binary will crash 314 | if (!calledFunction->empty() || 315 | calledFunction->getName().equals("dlsym") || 316 | calledFunction->getName().equals("dlopen") || 317 | calledFunction->isIntrinsic()) 318 | continue; 319 | 320 | if (this->Configuration.find(calledFunction->getName().str()) != 321 | this->Configuration.end()) { 322 | std::string sname = 323 | this->Configuration[calledFunction->getName().str()] 324 | .get(); 325 | StringRef calledFunctionName = StringRef(sname); 326 | BasicBlock *EntryBlock = CS->getParent(); 327 | if (triple.isOSDarwin()) { 328 | dlopen_flag = DARWIN_FLAG; 329 | } else if (triple.isAndroid()) { 330 | if (triple.isArch64Bit()) 331 | dlopen_flag = ANDROID64_FLAG; 332 | else 333 | dlopen_flag = ANDROID32_FLAG; 334 | } else { 335 | errs() << "[FunctionCallObfuscate] Unsupported Target Triple:" 336 | << M->getTargetTriple() << "\n"; 337 | errs() << "[FunctionCallObfuscate] Applying Default Signature:" 338 | << dlopen_flag << "\n"; 339 | } 340 | IRBuilder<> IRB(EntryBlock, EntryBlock->getFirstInsertionPt()); 341 | Value *Handle = IRB.CreateCall( 342 | dlopen_decl, {Constant::getNullValue(Int8PtrTy), 343 | ConstantInt::get(Int32Ty, dlopen_flag)}); 344 | // Create dlsym call 345 | Value *fp = IRB.CreateCall( 346 | dlsym_decl, 347 | {Handle, IRB.CreateGlobalStringPtr(calledFunctionName)}); 348 | Value *bitCastedFunction = 349 | IRB.CreateBitCast(fp, CS.getCalledValue()->getType()); 350 | CS.setCalledFunction(bitCastedFunction); 351 | } 352 | } 353 | } 354 | } 355 | return true; 356 | } 357 | }; 358 | FunctionPass *createFunctionCallObfuscatePass(bool flag) { 359 | return new FunctionCallObfuscate(flag); 360 | } 361 | } // namespace llvm 362 | char FunctionCallObfuscate::ID = 0; 363 | INITIALIZE_PASS(FunctionCallObfuscate, "fcoobf", 364 | "Enable Function CallSite Obfuscation.", false, false) 365 | -------------------------------------------------------------------------------- /hikari/FunctionCallObfuscate.h: -------------------------------------------------------------------------------- 1 | #ifndef _FUNCTION_CALL_OBFUSCATION_H_ 2 | #define _FUNCTION_CALL_OBFUSCATION_H_ 3 | 4 | #include "llvm/IR/PassManager.h" 5 | #include "llvm/Pass.h" 6 | 7 | namespace llvm { 8 | 9 | FunctionPass *createFunctionCallObfuscatePass(bool flag); 10 | void initializeFunctionCallObfuscatePass(PassRegistry &Registry); 11 | 12 | } // namespace llvm 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /hikari/FunctionWrapper.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | 5 | #include "FunctionWrapper.h" 6 | #include "llvm/IR/Constants.h" 7 | #include "llvm/IR/IRBuilder.h" 8 | #include "llvm/IR/InstIterator.h" 9 | #include "llvm/IR/Instructions.h" 10 | #include "llvm/IR/Module.h" 11 | #include "llvm/IR/Value.h" 12 | #include "llvm/Support/CommandLine.h" 13 | #include "CryptoUtils.h" 14 | #include "Utils.h" 15 | #include "compat/CallSite.h" 16 | #include "llvm/Transforms/Utils/ModuleUtils.h" 17 | 18 | using namespace llvm; 19 | 20 | static cl::opt 21 | ProbRate("fw_prob", 22 | cl::desc("Choose the probability [%] For Each CallSite To Be " 23 | "Obfuscated By FunctionWrapper"), 24 | cl::value_desc("Probability Rate"), cl::init(30), cl::Optional); 25 | static uint32_t ProbRateTemp = 30; 26 | 27 | static cl::opt ObfTimes( 28 | "fw_times", 29 | cl::desc( 30 | "Choose how many time the FunctionWrapper pass loop on a CallSite"), 31 | cl::value_desc("Number of Times"), cl::init(2), cl::Optional); 32 | 33 | namespace llvm { 34 | struct FunctionWrapper : public ModulePass { 35 | static char ID; 36 | bool flag; 37 | FunctionWrapper() : ModulePass(ID) { this->flag = true; } 38 | FunctionWrapper(bool flag) : ModulePass(ID) { this->flag = flag; } 39 | StringRef getPassName() const override { return "FunctionWrapper"; } 40 | bool runOnModule(Module &M) override { 41 | SmallVector callsites; 42 | for (Function &F : M) { 43 | if (toObfuscate(flag, &F, "fw")) { 44 | errs() << "Running FunctionWrapper On " << F.getName() << "\n"; 45 | if (!toObfuscateUint32Option(&F, "fw_prob", &ProbRateTemp)) 46 | ProbRateTemp = ProbRate; 47 | if (ProbRateTemp > 100) { 48 | errs() << "FunctionWrapper application CallSite percentage " 49 | "-fw_prob=x must be 0 < x <= 100"; 50 | return false; 51 | } 52 | for (Instruction &Inst : instructions(F)) 53 | if ((isa(&Inst) || isa(&Inst))) 54 | if (cryptoutils->get_range(100) <= ProbRateTemp) 55 | callsites.emplace_back(new CallSite(&Inst)); 56 | } 57 | } 58 | for (CallSite *CS : callsites) 59 | for (uint32_t i = 0; i < ObfTimes && CS != nullptr; i++) 60 | CS = HandleCallSite(CS); 61 | return true; 62 | } // End of runOnModule 63 | CallSite *HandleCallSite(CallSite *CS) { 64 | Value *calledFunction = CS->getCalledFunction(); 65 | if (calledFunction == nullptr) 66 | calledFunction = CS->getCalledValue()->stripPointerCasts(); 67 | // Filter out IndirectCalls that depends on the context 68 | // Otherwise It'll be blantantly troublesome since you can't reference an 69 | // Instruction outside its BB Too much trouble for a hobby project 70 | // To be precise, we only keep CS that refers to a non-intrinsic function 71 | // either directly or through casting 72 | if (calledFunction == nullptr || 73 | (!isa(calledFunction) && 74 | !isa(calledFunction)) || 75 | CS->getIntrinsicID() != Intrinsic::not_intrinsic) 76 | return nullptr; 77 | SmallVector byvalArgNums; 78 | if (Function *tmp = dyn_cast(calledFunction)) { 79 | #if LLVM_VERSION_MAJOR >= 18 80 | if (tmp->getName().starts_with("clang.")) { 81 | #else 82 | if (tmp->getName().startswith("clang.")) { 83 | #endif 84 | // Clang Intrinsic 85 | return nullptr; 86 | } 87 | for (Argument &arg : tmp->args()) { 88 | if (arg.hasStructRetAttr() || arg.hasSwiftSelfAttr()) { 89 | return nullptr; 90 | } 91 | if (arg.hasByValAttr()) 92 | byvalArgNums.emplace_back(arg.getArgNo()); 93 | } 94 | } 95 | // Create a new function which in turn calls the actual function 96 | SmallVector types; 97 | for (unsigned int i = 0; i < CS->getNumArgOperands(); i++) 98 | types.emplace_back(CS->getArgOperand(i)->getType()); 99 | FunctionType *ft = 100 | FunctionType::get(CS->getType(), ArrayRef(types), false); 101 | Function *func = 102 | Function::Create(ft, GlobalValue::LinkageTypes::InternalLinkage, 103 | "HikariFunctionWrapper", CS->getParent()->getModule()); 104 | func->setCallingConv(CS->getCallingConv()); 105 | // Trolling was all fun and shit so old implementation forced this symbol to 106 | // exist in all objects 107 | appendToCompilerUsed(*func->getParent(), {func}); 108 | BasicBlock *BB = BasicBlock::Create(func->getContext(), "", func); 109 | SmallVector params; 110 | if (byvalArgNums.empty()) 111 | for (Argument &arg : func->args()) 112 | params.emplace_back(&arg); 113 | else 114 | for (Argument &arg : func->args()) { 115 | if (std::find(byvalArgNums.begin(), byvalArgNums.end(), 116 | arg.getArgNo()) != byvalArgNums.end()) { 117 | params.emplace_back(&arg); 118 | } else { 119 | AllocaInst *AI = nullptr; 120 | if (!BB->empty()) { 121 | BasicBlock::iterator InsertPoint = BB->begin(); 122 | while (isa(InsertPoint)) 123 | ++InsertPoint; 124 | AI = new AllocaInst(arg.getType(), 0, "", &*InsertPoint); 125 | } else 126 | AI = new AllocaInst(arg.getType(), 0, "", BB); 127 | new StoreInst(&arg, AI, BB); 128 | LoadInst *LI = new LoadInst(AI->getAllocatedType(), AI, "", BB); 129 | params.emplace_back(LI); 130 | } 131 | } 132 | Value *retval = CallInst::Create( 133 | CS->getFunctionType(), 134 | ConstantExpr::getBitCast(cast(calledFunction), 135 | CS->getCalledValue()->getType()), 136 | #if LLVM_VERSION_MAJOR >= 16 137 | ArrayRef(params), std::nullopt, "", BB); 138 | #else 139 | ArrayRef(params), None, "", BB); 140 | #endif 141 | if (ft->getReturnType()->isVoidTy()) { 142 | ReturnInst::Create(BB->getContext(), BB); 143 | } else { 144 | ReturnInst::Create(BB->getContext(), retval, BB); 145 | } 146 | CS->setCalledFunction(func); 147 | CS->mutateFunctionType(ft); 148 | Instruction *Inst = CS->getInstruction(); 149 | delete CS; 150 | return new CallSite(Inst); 151 | } 152 | }; 153 | 154 | ModulePass *createFunctionWrapperPass(bool flag) { 155 | return new FunctionWrapper(flag); 156 | } 157 | } // namespace llvm 158 | 159 | char FunctionWrapper::ID = 0; 160 | INITIALIZE_PASS(FunctionWrapper, "funcwra", "Enable FunctionWrapper.", false, 161 | false) 162 | -------------------------------------------------------------------------------- /hikari/FunctionWrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef _FUNCTION_WRAPPER_H_ 2 | #define _FUNCTION_WRAPPER_H_ 3 | 4 | #include "llvm/IR/PassManager.h" 5 | #include "llvm/Pass.h" 6 | 7 | namespace llvm { 8 | 9 | ModulePass *createFunctionWrapperPass(bool flag); 10 | void initializeFunctionWrapperPass(PassRegistry &Registry); 11 | 12 | } // namespace llvm 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /hikari/IndirectBranch.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | #include "IndirectBranch.h" 5 | #include "llvm/IR/Constants.h" 6 | #include "llvm/IR/IRBuilder.h" 7 | #include "llvm/IR/InstIterator.h" 8 | #include "llvm/IR/Instructions.h" 9 | #include "llvm/IR/Module.h" 10 | #include "llvm/IR/NoFolder.h" 11 | #include "llvm/Passes/PassBuilder.h" 12 | #include "llvm/Support/CommandLine.h" 13 | #include "llvm/Support/raw_ostream.h" 14 | #include "CryptoUtils.h" 15 | #include "Utils.h" 16 | #include "llvm/Transforms/Utils/BasicBlockUtils.h" 17 | #include "llvm/Transforms/Utils/LowerSwitch.h" 18 | #include "llvm/Transforms/Utils/ModuleUtils.h" 19 | 20 | using namespace llvm; 21 | 22 | static cl::opt 23 | UseStack("indibran-use-stack", cl::init(true), cl::NotHidden, 24 | cl::desc("[IndirectBranch]Stack-based indirect jumps")); 25 | static bool UseStackTemp = true; 26 | 27 | static cl::opt 28 | EncryptJumpTarget("indibran-enc-jump-target", cl::init(false), 29 | cl::NotHidden, 30 | cl::desc("[IndirectBranch]Encrypt jump target")); 31 | static bool EncryptJumpTargetTemp = false; 32 | 33 | namespace llvm { 34 | struct IndirectBranch : public FunctionPass { 35 | static char ID; 36 | bool flag; 37 | bool initialized; 38 | std::unordered_map indexmap; 39 | std::unordered_map encmap; 40 | std::set to_obf_funcs; 41 | IndirectBranch() : FunctionPass(ID) { 42 | this->flag = true; 43 | this->initialized = false; 44 | } 45 | IndirectBranch(bool flag) : FunctionPass(ID) { 46 | this->flag = flag; 47 | this->initialized = false; 48 | } 49 | StringRef getPassName() const override { return "IndirectBranch"; } 50 | bool initialize(Module &M) { 51 | PassBuilder PB; 52 | FunctionAnalysisManager FAM; 53 | FunctionPassManager FPM; 54 | PB.registerFunctionAnalyses(FAM); 55 | FPM.addPass(LowerSwitchPass()); 56 | 57 | SmallVector BBs; 58 | unsigned long long i = 0; 59 | for (Function &F : M) { 60 | if (!toObfuscate(flag, &F, "indibr")) 61 | continue; 62 | else 63 | to_obf_funcs.insert(&F); 64 | if (!toObfuscateBoolOption(&F, "indibran_use_stack", &UseStackTemp)) 65 | UseStackTemp = UseStack; 66 | 67 | // See https://github.com/61bcdefg/Hikari-LLVM15/issues/32 68 | FPM.run(F, FAM); 69 | 70 | if (!toObfuscateBoolOption(&F, "indibran_enc_jump_target", 71 | &EncryptJumpTargetTemp)) 72 | EncryptJumpTargetTemp = EncryptJumpTarget; 73 | 74 | if (EncryptJumpTargetTemp) 75 | encmap[&F] = ConstantInt::get( 76 | Type::getInt32Ty(M.getContext()), 77 | cryptoutils->get_range(UINT8_MAX, UINT16_MAX * 2) * 4); 78 | for (BasicBlock &BB : F) 79 | if (!BB.isEntryBlock()) { 80 | indexmap[&BB] = i++; 81 | BBs.emplace_back( 82 | EncryptJumpTargetTemp 83 | ? ConstantExpr::getGetElementPtr( 84 | Type::getInt8Ty(M.getContext()), 85 | ConstantExpr::getBitCast( 86 | BlockAddress::get(&BB), 87 | Type::getInt8Ty(M.getContext())->getPointerTo()), 88 | encmap[&F]) 89 | : BlockAddress::get(&BB)); 90 | } 91 | } 92 | if (to_obf_funcs.size()) { 93 | ArrayType *AT = ArrayType::get( 94 | Type::getInt8Ty(M.getContext())->getPointerTo(), BBs.size()); 95 | Constant *BlockAddressArray = 96 | ConstantArray::get(AT, ArrayRef(BBs)); 97 | GlobalVariable *Table = new GlobalVariable( 98 | M, AT, false, GlobalValue::LinkageTypes::PrivateLinkage, 99 | BlockAddressArray, "IndirectBranchingGlobalTable"); 100 | appendToCompilerUsed(M, {Table}); 101 | } 102 | this->initialized = true; 103 | return true; 104 | } 105 | bool runOnFunction(Function &Func) override { 106 | Module *M = Func.getParent(); 107 | if (!this->initialized) 108 | initialize(*M); 109 | if (std::find(to_obf_funcs.begin(), to_obf_funcs.end(), &Func) == 110 | to_obf_funcs.end()) 111 | return false; 112 | errs() << "Running IndirectBranch On " << Func.getName() << "\n"; 113 | SmallVector BIs; 114 | for (Instruction &Inst : instructions(Func)) 115 | if (BranchInst *BI = dyn_cast(&Inst)) 116 | BIs.emplace_back(BI); 117 | 118 | Type *Int8Ty = Type::getInt8Ty(M->getContext()); 119 | Type *Int32Ty = Type::getInt32Ty(M->getContext()); 120 | Type *Int8PtrTy = Type::getInt8Ty(M->getContext())->getPointerTo(); 121 | 122 | Value *zero = ConstantInt::get(Int32Ty, 0); 123 | 124 | IRBuilder *IRBEntry = 125 | new IRBuilder(&Func.getEntryBlock().front()); 126 | for (BranchInst *BI : BIs) { 127 | if (UseStackTemp && 128 | IRBEntry->GetInsertPoint() != 129 | (BasicBlock::iterator)Func.getEntryBlock().front()) 130 | IRBEntry->SetInsertPoint(Func.getEntryBlock().getTerminator()); 131 | IRBuilder *IRBBI = new IRBuilder(BI); 132 | SmallVector BBs; 133 | // We use the condition's evaluation result to generate the GEP 134 | // instruction False evaluates to 0 while true evaluates to 1. So here 135 | // we insert the false block first 136 | if (BI->isConditional() && !BI->getSuccessor(1)->isEntryBlock()) 137 | BBs.emplace_back(BI->getSuccessor(1)); 138 | if (!BI->getSuccessor(0)->isEntryBlock()) 139 | BBs.emplace_back(BI->getSuccessor(0)); 140 | 141 | GlobalVariable *LoadFrom = nullptr; 142 | if (BI->isConditional() || 143 | indexmap.find(BI->getSuccessor(0)) == indexmap.end()) { 144 | ArrayType *AT = ArrayType::get(Int8PtrTy, BBs.size()); 145 | SmallVector BlockAddresses; 146 | for (BasicBlock *BB : BBs) 147 | BlockAddresses.emplace_back( 148 | EncryptJumpTargetTemp ? ConstantExpr::getGetElementPtr( 149 | Int8Ty, 150 | ConstantExpr::getBitCast( 151 | BlockAddress::get(BB), Int8PtrTy), 152 | encmap[&Func]) 153 | : BlockAddress::get(BB)); 154 | // Create a new GV 155 | Constant *BlockAddressArray = 156 | ConstantArray::get(AT, ArrayRef(BlockAddresses)); 157 | LoadFrom = new GlobalVariable( 158 | *M, AT, false, GlobalValue::LinkageTypes::PrivateLinkage, 159 | BlockAddressArray, "HikariConditionalLocalIndirectBranchingTable"); 160 | appendToCompilerUsed(*Func.getParent(), {LoadFrom}); 161 | } else { 162 | LoadFrom = M->getGlobalVariable("IndirectBranchingGlobalTable", true); 163 | } 164 | AllocaInst *LoadFromAI = nullptr; 165 | if (UseStackTemp) { 166 | LoadFromAI = IRBEntry->CreateAlloca(LoadFrom->getType()); 167 | IRBEntry->CreateStore(LoadFrom, LoadFromAI); 168 | } 169 | Value *index, *RealIndex = nullptr; 170 | if (BI->isConditional()) { 171 | Value *condition = BI->getCondition(); 172 | Value *zext = IRBBI->CreateZExt(condition, Int32Ty); 173 | if (UseStackTemp) { 174 | AllocaInst *condAI = IRBEntry->CreateAlloca(Int32Ty); 175 | IRBBI->CreateStore(zext, condAI); 176 | index = condAI; 177 | } else { 178 | index = zext; 179 | } 180 | RealIndex = index; 181 | } else { 182 | Value *indexval = nullptr; 183 | ConstantInt *IndexEncKey = 184 | EncryptJumpTargetTemp ? cast(ConstantInt::get( 185 | Int32Ty, cryptoutils->get_uint32_t())) 186 | : nullptr; 187 | if (EncryptJumpTargetTemp) { 188 | GlobalVariable *indexgv = new GlobalVariable( 189 | *M, Int32Ty, false, GlobalValue::LinkageTypes::PrivateLinkage, 190 | ConstantInt::get(IndexEncKey->getType(), 191 | IndexEncKey->getValue() ^ 192 | indexmap[BI->getSuccessor(0)]), 193 | "IndirectBranchingIndex"); 194 | appendToCompilerUsed(*M, {indexgv}); 195 | indexval = (UseStackTemp ? IRBEntry : IRBBI) 196 | ->CreateLoad(indexgv->getValueType(), indexgv); 197 | } else { 198 | indexval = ConstantInt::get(Int32Ty, indexmap[BI->getSuccessor(0)]); 199 | if (UseStackTemp) { 200 | AllocaInst *indexAI = IRBEntry->CreateAlloca(Int32Ty); 201 | IRBEntry->CreateStore(indexval, indexAI); 202 | indexval = IRBBI->CreateLoad(indexAI->getAllocatedType(), indexAI); 203 | } 204 | } 205 | index = indexval; 206 | RealIndex = EncryptJumpTargetTemp ? IRBBI->CreateXor(index, IndexEncKey) 207 | : index; 208 | } 209 | Value *LI, *enckeyLoad, *gepptr = nullptr; 210 | if (UseStackTemp) { 211 | LoadInst *LILoadFrom = 212 | IRBBI->CreateLoad(LoadFrom->getType(), LoadFromAI); 213 | Value *GEP = IRBBI->CreateGEP( 214 | LoadFrom->getValueType(), LILoadFrom, 215 | {zero, BI->isConditional() ? IRBBI->CreateLoad(Int32Ty, RealIndex) 216 | : RealIndex}); 217 | if (!EncryptJumpTargetTemp) 218 | LI = IRBBI->CreateLoad(Int8PtrTy, GEP, 219 | "IndirectBranchingTargetAddress"); 220 | else 221 | gepptr = IRBBI->CreateLoad(Int8PtrTy, GEP); 222 | } else { 223 | Value *GEP = IRBBI->CreateGEP(LoadFrom->getValueType(), LoadFrom, 224 | {zero, RealIndex}); 225 | if (!EncryptJumpTargetTemp) 226 | LI = IRBBI->CreateLoad(Int8PtrTy, GEP, 227 | "IndirectBranchingTargetAddress"); 228 | else 229 | gepptr = IRBBI->CreateLoad(Int8PtrTy, GEP); 230 | } 231 | if (EncryptJumpTargetTemp) { 232 | ConstantInt *encenckey = cast( 233 | ConstantInt::get(Int32Ty, cryptoutils->get_uint32_t())); 234 | GlobalVariable *enckeyGV = new GlobalVariable( 235 | *M, Int32Ty, false, GlobalValue::LinkageTypes::PrivateLinkage, 236 | ConstantInt::get(Int32Ty, 237 | encenckey->getValue() ^ encmap[&Func]->getValue()), 238 | "IndirectBranchingAddressEncryptKey"); 239 | appendToCompilerUsed(*M, enckeyGV); 240 | enckeyLoad = IRBBI->CreateXor( 241 | IRBBI->CreateLoad(enckeyGV->getValueType(), enckeyGV), encenckey); 242 | LI = 243 | IRBBI->CreateGEP(Int8Ty, gepptr, IRBBI->CreateSub(zero, enckeyLoad), 244 | "IndirectBranchingTargetAddress"); 245 | } 246 | IndirectBrInst *indirBr = IndirectBrInst::Create(LI, BBs.size()); 247 | for (BasicBlock *BB : BBs) 248 | indirBr->addDestination(BB); 249 | ReplaceInstWithInst(BI, indirBr); 250 | } 251 | shuffleBasicBlocks(Func); 252 | return true; 253 | } 254 | void shuffleBasicBlocks(Function &F) { 255 | SmallVector blocks; 256 | for (BasicBlock &block : F) 257 | if (!block.isEntryBlock()) 258 | blocks.emplace_back(&block); 259 | 260 | if (blocks.size() < 2) 261 | return; 262 | 263 | for (size_t i = blocks.size() - 1; i > 0; i--) 264 | std::swap(blocks[i], blocks[cryptoutils->get_range(i + 1)]); 265 | 266 | Function::iterator fi = F.begin(); 267 | for (BasicBlock *block : blocks) { 268 | fi++; 269 | block->moveAfter(&*(fi)); 270 | } 271 | } 272 | }; 273 | } // namespace llvm 274 | 275 | FunctionPass *llvm::createIndirectBranchPass(bool flag) { 276 | return new IndirectBranch(flag); 277 | } 278 | char IndirectBranch::ID = 0; 279 | INITIALIZE_PASS(IndirectBranch, "indibran", "IndirectBranching", false, false) 280 | -------------------------------------------------------------------------------- /hikari/IndirectBranch.h: -------------------------------------------------------------------------------- 1 | #ifndef _INDIRECT_BRANCH_H_ 2 | #define _INDIRECT_BRANCH_H_ 3 | 4 | #include "llvm/IR/PassManager.h" 5 | #include "llvm/Pass.h" 6 | 7 | namespace llvm { 8 | 9 | FunctionPass *createIndirectBranchPass(bool flag); 10 | void initializeIndirectBranchPass(PassRegistry &Registry); 11 | 12 | } // namespace llvm 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /hikari/Makefile: -------------------------------------------------------------------------------- 1 | CC := xcrun clang 2 | CFLAGS := -I../llvm/include -I../Build/include -std=c++17 -fno-rtti -DGIT_COMMIT_HASH=20240825210000 #-O3 3 | LDFLAGS := -L../Build/lib -lstdc++ -lz -lzstd -lLLVMAsmParser -lLLVMCodeGenTypes -lLLVMDemangle -lLLVMHipStdPar -lLLVMLinker -lLLVMTargetParser -lLLVMBitstreamReader -lLLVMRemarks -lLLVMFrontendOffloading -lLLVMSupport -lLLVMBinaryFormat -lLLVMIRReader -lLLVMScalarOpts -lLLVMTarget -lLLVMIRPrinter -lLLVMMC -lLLVMBitReader -lLLVMProfileData -lLLVMInstCombine -lLLVMBitWriter -lLLVMObject -lLLVMVectorize -lLLVMTransformUtils -lLLVMipo -lLLVMObjCARCOpts -lLLVMMCParser -lLLVMAnalysis -lLLVMCodeGen -lLLVMInstrumentation -lLLVMFrontendOpenMP -lLLVMCoroutines -lLLVMCFGuard -lLLVMDebugInfoDWARF -lLLVMTextAPI -lLLVMAggressiveInstCombine -lLLVMCore -lLLVMPasses 4 | TARGET := hikari.dylib 5 | 6 | $(shell sed -i '' "s/llvm\/Transforms\/Obfuscation\///g" *.cpp *.h) 7 | 8 | SRCS := $(wildcard *.cpp) 9 | OBJS := $(patsubst %.cpp,%.o,$(SRCS)) 10 | 11 | all: $(TARGET) 12 | $(TARGET): $(OBJS) 13 | $(CC) -shared $(LDFLAGS) -o $@ $^ 14 | #strip -S -X -x $(TARGET) 15 | 16 | %.o: %.cpp 17 | $(CC) $(CFLAGS) -c $< 18 | 19 | clean: 20 | rm -rf $(TARGET) *.o 21 | 22 | .PHONY: all clean 23 | 24 | -------------------------------------------------------------------------------- /hikari/Obfuscation.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | /* 5 | Hikari 's own "Pass Scheduler". 6 | Because currently there is no way to add dependency to transform passes 7 | Ref : http://lists.llvm.org/pipermail/llvm-dev/2011-February/038109.html 8 | */ 9 | #include "Obfuscation.h" 10 | #include "llvm/Support/CommandLine.h" 11 | #include "Utils.h" 12 | 13 | using namespace llvm; 14 | 15 | // Begin Obfuscator Options 16 | static cl::opt AesSeed("aesSeed", cl::init(0x1337), 17 | cl::desc("seed for the PRNG")); 18 | static cl::opt EnableAntiClassDump("enable-acdobf", cl::init(false), 19 | cl::NotHidden, 20 | cl::desc("Enable AntiClassDump.")); 21 | static cl::opt EnableAntiHooking("enable-antihook", cl::init(false), 22 | cl::NotHidden, 23 | cl::desc("Enable AntiHooking.")); 24 | static cl::opt EnableAntiDebugging("enable-adb", cl::init(false), 25 | cl::NotHidden, 26 | cl::desc("Enable AntiDebugging.")); 27 | static cl::opt 28 | EnableBogusControlFlow("enable-bcfobf", cl::init(false), cl::NotHidden, 29 | cl::desc("Enable BogusControlFlow.")); 30 | static cl::opt EnableFlattening("enable-cffobf", cl::init(false), 31 | cl::NotHidden, 32 | cl::desc("Enable Flattening.")); 33 | static cl::opt 34 | EnableBasicBlockSplit("enable-splitobf", cl::init(false), cl::NotHidden, 35 | cl::desc("Enable BasicBlockSpliting.")); 36 | static cl::opt 37 | EnableSubstitution("enable-subobf", cl::init(false), cl::NotHidden, 38 | cl::desc("Enable Instruction Substitution.")); 39 | static cl::opt EnableAllObfuscation("enable-allobf", cl::init(false), 40 | cl::NotHidden, 41 | cl::desc("Enable All Obfuscation.")); 42 | static cl::opt EnableFunctionCallObfuscate( 43 | "enable-fco", cl::init(false), cl::NotHidden, 44 | cl::desc("Enable Function CallSite Obfuscation.")); 45 | static cl::opt 46 | EnableStringEncryption("enable-strcry", cl::init(false), cl::NotHidden, 47 | cl::desc("Enable String Encryption.")); 48 | static cl::opt 49 | EnableConstantEncryption("enable-constenc", cl::init(false), cl::NotHidden, 50 | cl::desc("Enable Constant Encryption.")); 51 | static cl::opt 52 | EnableIndirectBranching("enable-indibran", cl::init(false), cl::NotHidden, 53 | cl::desc("Enable Indirect Branching.")); 54 | static cl::opt 55 | EnableFunctionWrapper("enable-funcwra", cl::init(false), cl::NotHidden, 56 | cl::desc("Enable Function Wrapper.")); 57 | // End Obfuscator Options 58 | 59 | static void LoadEnv(void) { 60 | if (getenv("SPLITOBF")) { 61 | EnableBasicBlockSplit = true; 62 | } 63 | if (getenv("SUBOBF")) { 64 | EnableSubstitution = true; 65 | } 66 | if (getenv("ALLOBF")) { 67 | EnableAllObfuscation = true; 68 | } 69 | if (getenv("FCO")) { 70 | EnableFunctionCallObfuscate = true; 71 | } 72 | if (getenv("STRCRY")) { 73 | EnableStringEncryption = true; 74 | } 75 | if (getenv("INDIBRAN")) { 76 | EnableIndirectBranching = true; 77 | } 78 | if (getenv("FUNCWRA")) { 79 | EnableFunctionWrapper = true; // Broken 80 | } 81 | if (getenv("BCFOBF")) { 82 | EnableBogusControlFlow = true; 83 | } 84 | if (getenv("ACDOBF")) { 85 | EnableAntiClassDump = true; 86 | } 87 | if (getenv("CFFOBF")) { 88 | EnableFlattening = true; 89 | } 90 | if (getenv("CONSTENC")) { 91 | EnableConstantEncryption = true; 92 | } 93 | if (getenv("ANTIHOOK")) { 94 | EnableAntiHooking = true; 95 | } 96 | if (getenv("ADB")) { 97 | EnableAntiDebugging = true; 98 | } 99 | } 100 | namespace llvm { 101 | struct Obfuscation : public ModulePass { 102 | static char ID; 103 | Obfuscation() : ModulePass(ID) { 104 | initializeObfuscationPass(*PassRegistry::getPassRegistry()); 105 | } 106 | StringRef getPassName() const override { 107 | return "HikariObfuscationScheduler"; 108 | } 109 | bool runOnModule(Module &M) override { 110 | TimerGroup *tg = 111 | new TimerGroup("Obfuscation Timer Group", "Obfuscation Timer Group"); 112 | Timer *timer = new Timer("Obfuscation Timer", "Obfuscation Timer", *tg); 113 | timer->startTimer(); 114 | 115 | errs() << "Running Hikari On " << M.getSourceFileName() << "\n"; 116 | 117 | annotation2Metadata(M); 118 | 119 | ModulePass *MP = createAntiHookPass(EnableAntiHooking); 120 | MP->doInitialization(M); 121 | MP->runOnModule(M); 122 | delete MP; 123 | // Initial ACD Pass 124 | if (EnableAllObfuscation || EnableAntiClassDump) { 125 | ModulePass *P = createAntiClassDumpPass(); 126 | P->doInitialization(M); 127 | P->runOnModule(M); 128 | delete P; 129 | } 130 | // Now do FCO 131 | FunctionPass *FP = createFunctionCallObfuscatePass( 132 | EnableAllObfuscation || EnableFunctionCallObfuscate); 133 | for (Function &F : M) 134 | if (!F.isDeclaration()) 135 | FP->runOnFunction(F); 136 | delete FP; 137 | MP = createAntiDebuggingPass(EnableAntiDebugging); 138 | MP->runOnModule(M); 139 | delete MP; 140 | // Now Encrypt Strings 141 | MP = createStringEncryptionPass(EnableAllObfuscation || 142 | EnableStringEncryption); 143 | MP->runOnModule(M); 144 | delete MP; 145 | // Now perform Function-Level Obfuscation 146 | for (Function &F : M) 147 | if (!F.isDeclaration()) { 148 | FunctionPass *P = nullptr; 149 | P = createSplitBasicBlockPass(EnableAllObfuscation || 150 | EnableBasicBlockSplit); 151 | P->runOnFunction(F); 152 | delete P; 153 | P = createBogusControlFlowPass(EnableAllObfuscation || 154 | EnableBogusControlFlow); 155 | P->runOnFunction(F); 156 | delete P; 157 | P = createFlatteningPass(EnableAllObfuscation || EnableFlattening); 158 | P->runOnFunction(F); 159 | delete P; 160 | P = createSubstitutionPass(EnableAllObfuscation || EnableSubstitution); 161 | P->runOnFunction(F); 162 | delete P; 163 | } 164 | MP = createConstantEncryptionPass(EnableConstantEncryption); 165 | MP->runOnModule(M); 166 | delete MP; 167 | errs() << "Doing Post-Run Cleanup\n"; 168 | FunctionPass *P = createIndirectBranchPass(EnableAllObfuscation || 169 | EnableIndirectBranching); 170 | for (Function &F : M) 171 | if (!F.isDeclaration()) 172 | P->runOnFunction(F); 173 | delete P; 174 | MP = createFunctionWrapperPass(EnableAllObfuscation || 175 | EnableFunctionWrapper); 176 | MP->runOnModule(M); 177 | delete MP; 178 | // Cleanup Flags 179 | SmallVector toDelete; 180 | for (Function &F : M) 181 | if (F.isDeclaration() && F.hasName() && 182 | #if LLVM_VERSION_MAJOR >= 18 183 | F.getName().starts_with("hikari_")) { 184 | #else 185 | F.getName().startswith("hikari_")) { 186 | #endif 187 | for (User *U : F.users()) 188 | if (Instruction *Inst = dyn_cast(U)) 189 | Inst->eraseFromParent(); 190 | toDelete.emplace_back(&F); 191 | } 192 | for (Function *F : toDelete) 193 | F->eraseFromParent(); 194 | 195 | timer->stopTimer(); 196 | errs() << "Hikari Out\n"; 197 | errs() << "Spend Time: " 198 | << format("%.7f", timer->getTotalTime().getWallTime()) << "s" 199 | << "\n"; 200 | tg->clearAll(); 201 | return true; 202 | } // End runOnModule 203 | }; 204 | ModulePass *createObfuscationLegacyPass() { 205 | LoadEnv(); 206 | if (AesSeed != 0x1337) { 207 | cryptoutils->prng_seed(AesSeed); 208 | } else { 209 | cryptoutils->prng_seed(); 210 | } 211 | errs() << "Initializing Hikari Core with Revision ID:" << GIT_COMMIT_HASH 212 | << "\n"; 213 | return new Obfuscation(); 214 | } 215 | 216 | PreservedAnalyses ObfuscationPass::run(Module &M, ModuleAnalysisManager &MAM) { 217 | if (createObfuscationLegacyPass()->runOnModule(M)) { 218 | return PreservedAnalyses::none(); 219 | } 220 | return PreservedAnalyses::all(); 221 | } 222 | 223 | } // namespace llvm 224 | char Obfuscation::ID = 0; 225 | INITIALIZE_PASS_BEGIN(Obfuscation, "obfus", "Enable Obfuscation", false, false) 226 | INITIALIZE_PASS_DEPENDENCY(AntiClassDump); 227 | INITIALIZE_PASS_DEPENDENCY(BogusControlFlow); 228 | INITIALIZE_PASS_DEPENDENCY(Flattening); 229 | INITIALIZE_PASS_DEPENDENCY(FunctionCallObfuscate); 230 | INITIALIZE_PASS_DEPENDENCY(IndirectBranch); 231 | INITIALIZE_PASS_DEPENDENCY(SplitBasicBlock); 232 | INITIALIZE_PASS_DEPENDENCY(StringEncryption); 233 | INITIALIZE_PASS_DEPENDENCY(Substitution); 234 | INITIALIZE_PASS_END(Obfuscation, "obfus", "Enable Obfuscation", false, false) 235 | 236 | // add 237 | void hikariAddPass(ModulePassManager& MPM) { 238 | MPM.addPass(ObfuscationPass()); 239 | } 240 | 241 | -------------------------------------------------------------------------------- /hikari/Obfuscation.h: -------------------------------------------------------------------------------- 1 | #ifndef _OBFUSCATION_H_ 2 | #define _OBFUSCATION_H_ 3 | 4 | #include "llvm/Support/Timer.h" 5 | #include "AntiClassDump.h" 6 | #include "AntiDebugging.h" 7 | #include "AntiHook.h" 8 | #include "BogusControlFlow.h" 9 | #include "ConstantEncryption.h" 10 | #include "CryptoUtils.h" 11 | #include "Flattening.h" 12 | #include "FunctionCallObfuscate.h" 13 | #include "FunctionWrapper.h" 14 | #include "IndirectBranch.h" 15 | #include "Split.h" 16 | #include "StringEncryption.h" 17 | #include "Substitution.h" 18 | #include "llvm/Transforms/Utils/BasicBlockUtils.h" 19 | #include "llvm/Transforms/Utils/ModuleUtils.h" 20 | 21 | namespace llvm { 22 | 23 | class ObfuscationPass : public PassInfoMixin { 24 | public: 25 | ObfuscationPass() {} 26 | PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); 27 | static bool isRequired() { return true; } 28 | }; 29 | 30 | ModulePass *createObfuscationLegacyPass(); 31 | void initializeObfuscationPass(PassRegistry &Registry); 32 | 33 | } // namespace llvm 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /hikari/Split.h: -------------------------------------------------------------------------------- 1 | #ifndef _SPLIT_H_ 2 | #define _SPLIT_H_ 3 | 4 | #include "llvm/IR/PassManager.h" 5 | #include "llvm/Pass.h" 6 | 7 | namespace llvm { 8 | 9 | FunctionPass *createSplitBasicBlockPass(bool flag); 10 | void initializeSplitBasicBlockPass(PassRegistry &Registry); 11 | 12 | } // namespace llvm 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /hikari/SplitBasicBlocks.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | #include "llvm/IR/Instructions.h" 5 | #include "llvm/Support/CommandLine.h" 6 | #include "CryptoUtils.h" 7 | #include "Split.h" 8 | #include "Utils.h" 9 | 10 | using namespace llvm; 11 | 12 | static cl::opt SplitNum("split_num", cl::init(2), 13 | cl::desc("Split time each BB")); 14 | static uint32_t SplitNumTemp = 2; 15 | 16 | namespace { 17 | struct SplitBasicBlock : public FunctionPass { 18 | static char ID; // Pass identification, replacement for typeid 19 | bool flag; 20 | SplitBasicBlock() : FunctionPass(ID) { this->flag = true; } 21 | SplitBasicBlock(bool flag) : FunctionPass(ID) { this->flag = flag; } 22 | 23 | bool runOnFunction(Function &F) override { 24 | if (!toObfuscateUint32Option(&F, "split_num", &SplitNumTemp)) 25 | SplitNumTemp = SplitNum; 26 | 27 | // Check if the number of applications is correct 28 | if (!((SplitNumTemp > 1) && (SplitNumTemp <= 10))) { 29 | errs() 30 | << "Split application basic block percentage -split_num=x must be 1 " 31 | "< x <= 10"; 32 | return false; 33 | } 34 | 35 | // Do we obfuscate 36 | if (toObfuscate(flag, &F, "split")) { 37 | errs() << "Running BasicBlockSplit On " << F.getName() << "\n"; 38 | split(&F); 39 | } 40 | 41 | return true; 42 | } 43 | void split(Function *F) { 44 | SmallVector origBB; 45 | size_t split_ctr = 0; 46 | 47 | // Save all basic blocks 48 | for (BasicBlock &BB : *F) 49 | origBB.emplace_back(&BB); 50 | 51 | for (BasicBlock *currBB : origBB) { 52 | if (currBB->size() < 2 || containsPHI(currBB) || 53 | containsSwiftError(currBB)) 54 | continue; 55 | 56 | if ((size_t)SplitNumTemp > currBB->size() - 1) 57 | split_ctr = currBB->size() - 1; 58 | else 59 | split_ctr = (size_t)SplitNumTemp; 60 | 61 | // Generate splits point (count number of the LLVM instructions in the 62 | // current BB) 63 | SmallVector llvm_inst_ord; 64 | for (size_t i = 1; i < currBB->size(); ++i) 65 | llvm_inst_ord.emplace_back(i); 66 | 67 | // Shuffle 68 | split_point_shuffle(llvm_inst_ord); 69 | std::sort(llvm_inst_ord.begin(), llvm_inst_ord.begin() + split_ctr); 70 | 71 | // Split 72 | size_t llvm_inst_prev_offset = 0; 73 | BasicBlock::iterator curr_bb_it = currBB->begin(); 74 | BasicBlock *curr_bb_offset = currBB; 75 | 76 | for (size_t i = 0; i < split_ctr; ++i) { 77 | for (size_t j = 0; j < llvm_inst_ord[i] - llvm_inst_prev_offset; ++j) 78 | ++curr_bb_it; 79 | 80 | llvm_inst_prev_offset = llvm_inst_ord[i]; 81 | 82 | // https://github.com/eshard/obfuscator-llvm/commit/fff24c7a1555aa3ae7160056b06ba1e0b3d109db 83 | /* TODO: find a real fix or try with the probe-stack inline-asm when its 84 | * ready. See https://github.com/Rust-for-Linux/linux/issues/355. 85 | * Sometimes moving an alloca from the entry block to the second block 86 | * causes a segfault when using the "probe-stack" attribute (observed 87 | * with with Rust programs). To avoid this issue we just split the entry 88 | * block after the allocas in this case. 89 | */ 90 | if (F->hasFnAttribute("probe-stack") && currBB->isEntryBlock() && 91 | isa(curr_bb_it)) 92 | continue; 93 | 94 | curr_bb_offset = curr_bb_offset->splitBasicBlock( 95 | curr_bb_it, curr_bb_offset->getName() + ".split"); 96 | } 97 | } 98 | } 99 | 100 | bool containsPHI(BasicBlock *BB) { 101 | for (Instruction &I : *BB) 102 | if (isa(&I)) 103 | return true; 104 | return false; 105 | } 106 | 107 | bool containsSwiftError(BasicBlock *BB) { 108 | for (Instruction &I : *BB) 109 | if (AllocaInst *AI = dyn_cast(&I)) 110 | if (AI->isSwiftError()) 111 | return true; 112 | return false; 113 | } 114 | 115 | void split_point_shuffle(SmallVector &vec) { 116 | int n = vec.size(); 117 | for (int i = n - 1; i > 0; --i) 118 | std::swap(vec[i], vec[cryptoutils->get_range(i + 1)]); 119 | } 120 | }; 121 | } // namespace 122 | 123 | char SplitBasicBlock::ID = 0; 124 | INITIALIZE_PASS(SplitBasicBlock, "splitobf", "Enable BasicBlockSpliting.", 125 | false, false) 126 | 127 | FunctionPass *llvm::createSplitBasicBlockPass(bool flag) { 128 | return new SplitBasicBlock(flag); 129 | } 130 | -------------------------------------------------------------------------------- /hikari/StringEncryption.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | #include "StringEncryption.h" 5 | #include "llvm/IR/Constants.h" 6 | #include "llvm/IR/IRBuilder.h" 7 | #include "llvm/IR/InstIterator.h" 8 | #include "llvm/IR/Module.h" 9 | #include "llvm/IR/NoFolder.h" 10 | #include "llvm/Support/CommandLine.h" 11 | #include "CryptoUtils.h" 12 | #include "Obfuscation.h" 13 | #include "Utils.h" 14 | #include 15 | #include 16 | 17 | using namespace llvm; 18 | 19 | static cl::opt 20 | ElementEncryptProb("strcry_prob", cl::init(100), cl::NotHidden, 21 | cl::desc("Choose the probability [%] each element of " 22 | "ConstantDataSequential will be " 23 | "obfuscated by the -strcry pass")); 24 | static uint32_t ElementEncryptProbTemp = 100; 25 | 26 | namespace llvm { 27 | struct StringEncryption : public ModulePass { 28 | static char ID; 29 | bool flag; 30 | bool appleptrauth; 31 | bool opaquepointers; 32 | std::unordered_map 34 | encstatus; 35 | std::unordered_map> 36 | mgv2keys; 37 | std::unordered_map> 38 | unencryptedindex; 39 | SmallVector genedgv; 40 | StringEncryption() : ModulePass(ID) { this->flag = true; } 41 | 42 | StringEncryption(bool flag) : ModulePass(ID) { this->flag = flag; } 43 | 44 | StringRef getPassName() const override { return "StringEncryption"; } 45 | 46 | bool handleableGV(GlobalVariable *GV) { 47 | #if LLVM_VERSION_MAJOR >= 18 48 | if (GV->hasInitializer() && !GV->getSection().starts_with("llvm.") && 49 | #else 50 | if (GV->hasInitializer() && !GV->getSection().startswith("llvm.") && 51 | #endif 52 | !(GV->getSection().contains("__objc") && 53 | !GV->getSection().contains("array")) && 54 | !GV->getName().contains("OBJC") && 55 | std::find(genedgv.begin(), genedgv.end(), GV) == genedgv.end() && 56 | ((GV->getLinkage() == GlobalValue::LinkageTypes::PrivateLinkage || 57 | GV->getLinkage() == GlobalValue::LinkageTypes::InternalLinkage) && 58 | (flag || AreUsersInOneFunction(GV)))) 59 | return true; 60 | return false; 61 | } 62 | 63 | bool runOnModule(Module &M) override { 64 | // in runOnModule. We simple iterate function list and dispatch functions 65 | // to handlers 66 | this->appleptrauth = hasApplePtrauth(&M); 67 | #if LLVM_VERSION_MAJOR >= 17 68 | this->opaquepointers = true; 69 | #else 70 | this->opaquepointers = !M.getContext().supportsTypedPointers(); 71 | #endif 72 | 73 | for (Function &F : M) 74 | if (toObfuscate(flag, &F, "strenc")) { 75 | errs() << "Running StringEncryption On " << F.getName() << "\n"; 76 | 77 | if (!toObfuscateUint32Option(&F, "strcry_prob", 78 | &ElementEncryptProbTemp)) 79 | ElementEncryptProbTemp = ElementEncryptProb; 80 | 81 | // Check if the number of applications is correct 82 | if (!((ElementEncryptProbTemp > 0) && 83 | (ElementEncryptProbTemp <= 100))) { 84 | errs() << "StringEncryption application element percentage " 85 | "-strcry_prob=x must be 0 < x <= 100"; 86 | return false; 87 | } 88 | Constant *S = 89 | ConstantInt::getNullValue(Type::getInt32Ty(M.getContext())); 90 | GlobalVariable *GV = new GlobalVariable( 91 | M, S->getType(), false, GlobalValue::LinkageTypes::PrivateLinkage, 92 | S, "StringEncryptionEncStatus"); 93 | encstatus[&F] = GV; 94 | HandleFunction(&F); 95 | } 96 | return true; 97 | } 98 | 99 | void 100 | processConstantAggregate(GlobalVariable *strGV, ConstantAggregate *CA, 101 | std::set *rawStrings, 102 | SmallVector *unhandleablegvs, 103 | SmallVector *Globals, 104 | std::set *Users, bool *breakFor) { 105 | for (unsigned i = 0; i < CA->getNumOperands(); i++) { 106 | Constant *Op = CA->getOperand(i); 107 | if (GlobalVariable *GV = 108 | dyn_cast(Op->stripPointerCasts())) { 109 | if (!handleableGV(GV)) { 110 | unhandleablegvs->emplace_back(GV); 111 | continue; 112 | } 113 | Users->insert(opaquepointers ? CA : Op); 114 | if (std::find(Globals->begin(), Globals->end(), GV) == Globals->end()) { 115 | Globals->emplace_back(GV); 116 | *breakFor = true; 117 | } 118 | } else if (ConstantAggregate *NestedCA = 119 | dyn_cast(Op)) { 120 | processConstantAggregate(strGV, NestedCA, rawStrings, unhandleablegvs, 121 | Globals, Users, breakFor); 122 | } else if (isa(Op)) { 123 | if (CA->getNumOperands() != 1) 124 | continue; 125 | Users->insert(CA); 126 | rawStrings->insert(strGV); 127 | } 128 | } 129 | } 130 | 131 | void HandleUser(User *U, SmallVector &Globals, 132 | std::set &Users, 133 | std::unordered_set &VisitedUsers) { 134 | VisitedUsers.emplace(U); 135 | for (Value *Op : U->operands()) { 136 | if (GlobalVariable *G = 137 | dyn_cast(Op->stripPointerCasts())) { 138 | if (User *U2 = dyn_cast(Op)) 139 | Users.insert(U2); 140 | Users.insert(U); 141 | Globals.emplace_back(G); 142 | } else if (User *U = dyn_cast(Op)) { 143 | if (!VisitedUsers.count(U)) 144 | HandleUser(U, Globals, Users, VisitedUsers); 145 | } 146 | } 147 | } 148 | 149 | void HandleFunction(Function *Func) { 150 | FixFunctionConstantExpr(Func); 151 | SmallVector Globals; 152 | std::set Users; 153 | { 154 | std::unordered_set VisitedUsers; 155 | for (Instruction &I : instructions(Func)) 156 | HandleUser(&I, Globals, Users, VisitedUsers); 157 | } 158 | std::set rawStrings; 159 | std::set objCStrings; 160 | std::unordered_map> 162 | GV2Keys; 163 | std::unordered_map> 166 | old2new; 167 | 168 | auto end = Globals.end(); 169 | for (auto it = Globals.begin(); it != end; ++it) { 170 | end = std::remove(it + 1, end, *it); 171 | } 172 | Globals.erase(end, Globals.end()); 173 | 174 | Module *M = Func->getParent(); 175 | 176 | SmallVector transedGlobals, unhandleablegvs; 177 | 178 | do { 179 | for (GlobalVariable *GV : Globals) { 180 | if (std::find(transedGlobals.begin(), transedGlobals.end(), GV) == 181 | transedGlobals.end()) { 182 | bool breakThisFor = false; 183 | if (handleableGV(GV)) { 184 | if (GlobalVariable *CastedGV = dyn_cast( 185 | GV->getInitializer()->stripPointerCasts())) { 186 | if (std::find(Globals.begin(), Globals.end(), CastedGV) == 187 | Globals.end()) { 188 | Globals.emplace_back(CastedGV); 189 | ConstantExpr *CE = dyn_cast(GV->getInitializer()); 190 | Users.insert(CE ? CE : GV->getInitializer()); 191 | breakThisFor = true; 192 | } 193 | } 194 | if (GV->getInitializer()->getType() == 195 | StructType::getTypeByName(M->getContext(), 196 | "struct.__NSConstantString_tag")) { 197 | objCStrings.insert(GV); 198 | rawStrings.insert(cast( 199 | cast(GV->getInitializer()) 200 | ->getOperand(2) 201 | ->stripPointerCasts())); 202 | } else if (isa(GV->getInitializer())) { 203 | rawStrings.insert(GV); 204 | } else if (ConstantAggregate *CA = 205 | dyn_cast(GV->getInitializer())) { 206 | processConstantAggregate(GV, CA, &rawStrings, &unhandleablegvs, 207 | &Globals, &Users, &breakThisFor); 208 | } 209 | } else { 210 | unhandleablegvs.emplace_back(GV); 211 | } 212 | transedGlobals.emplace_back(GV); 213 | if (breakThisFor) 214 | break; 215 | } 216 | } // foreach loop 217 | } while (transedGlobals.size() != Globals.size()); 218 | for (GlobalVariable *ugv : unhandleablegvs) 219 | if (std::find(genedgv.begin(), genedgv.end(), ugv) != genedgv.end()) { 220 | std::pair mgv2keysval = mgv2keys[ugv]; 221 | if (ugv->getInitializer()->getType() == 222 | StructType::getTypeByName(M->getContext(), 223 | "struct.__NSConstantString_tag")) { 224 | GlobalVariable *rawgv = 225 | cast(cast(ugv->getInitializer()) 226 | ->getOperand(2) 227 | ->stripPointerCasts()); 228 | mgv2keysval = mgv2keys[rawgv]; 229 | if (mgv2keysval.first && mgv2keysval.second) { 230 | GV2Keys[rawgv] = mgv2keysval; 231 | } 232 | } else if (mgv2keysval.first && mgv2keysval.second) { 233 | GV2Keys[ugv] = mgv2keysval; 234 | } 235 | } 236 | for (GlobalVariable *GV : rawStrings) { 237 | if (GV->getInitializer()->isZeroValue() || 238 | GV->getInitializer()->isNullValue()) 239 | continue; 240 | ConstantDataSequential *CDS = 241 | dyn_cast(GV->getInitializer()); 242 | bool rust_string = !CDS; 243 | if (rust_string) 244 | CDS = cast( 245 | cast(GV->getInitializer())->getOperand(0)); 246 | Type *ElementTy = CDS->getElementType(); 247 | if (!ElementTy->isIntegerTy()) { 248 | continue; 249 | } 250 | IntegerType *intType = cast(ElementTy); 251 | Constant *KeyConst, *EncryptedConst, *DummyConst = nullptr; 252 | unencryptedindex[GV] = {}; 253 | if (intType == Type::getInt8Ty(M->getContext())) { 254 | std::vector keys, encry, dummy; 255 | for (unsigned i = 0; i < CDS->getNumElements(); i++) { 256 | if (cryptoutils->get_range(100) >= ElementEncryptProbTemp) { 257 | unencryptedindex[GV].emplace_back(i); 258 | keys.emplace_back(1); 259 | dummy.emplace_back(CDS->getElementAsInteger(i)); 260 | continue; 261 | } 262 | const uint8_t K = cryptoutils->get_uint8_t(); 263 | const uint64_t V = CDS->getElementAsInteger(i); 264 | keys.emplace_back(K); 265 | encry.emplace_back(K ^ V); 266 | dummy.emplace_back(cryptoutils->get_uint8_t()); 267 | } 268 | KeyConst = 269 | ConstantDataArray::get(M->getContext(), ArrayRef(keys)); 270 | EncryptedConst = 271 | ConstantDataArray::get(M->getContext(), ArrayRef(encry)); 272 | DummyConst = 273 | ConstantDataArray::get(M->getContext(), ArrayRef(dummy)); 274 | 275 | } else if (intType == Type::getInt16Ty(M->getContext())) { 276 | std::vector keys, encry, dummy; 277 | for (unsigned i = 0; i < CDS->getNumElements(); i++) { 278 | if (cryptoutils->get_range(100) >= ElementEncryptProbTemp) { 279 | unencryptedindex[GV].emplace_back(i); 280 | keys.emplace_back(1); 281 | dummy.emplace_back(CDS->getElementAsInteger(i)); 282 | continue; 283 | } 284 | const uint16_t K = cryptoutils->get_uint16_t(); 285 | const uint64_t V = CDS->getElementAsInteger(i); 286 | keys.emplace_back(K); 287 | encry.emplace_back(K ^ V); 288 | dummy.emplace_back(cryptoutils->get_uint16_t()); 289 | } 290 | KeyConst = 291 | ConstantDataArray::get(M->getContext(), ArrayRef(keys)); 292 | EncryptedConst = 293 | ConstantDataArray::get(M->getContext(), ArrayRef(encry)); 294 | DummyConst = 295 | ConstantDataArray::get(M->getContext(), ArrayRef(dummy)); 296 | } else if (intType == Type::getInt32Ty(M->getContext())) { 297 | std::vector keys, encry, dummy; 298 | for (unsigned i = 0; i < CDS->getNumElements(); i++) { 299 | if (cryptoutils->get_range(100) >= ElementEncryptProbTemp) { 300 | unencryptedindex[GV].emplace_back(i); 301 | keys.emplace_back(1); 302 | dummy.emplace_back(CDS->getElementAsInteger(i)); 303 | continue; 304 | } 305 | const uint32_t K = cryptoutils->get_uint32_t(); 306 | const uint64_t V = CDS->getElementAsInteger(i); 307 | keys.emplace_back(K); 308 | encry.emplace_back(K ^ V); 309 | dummy.emplace_back(cryptoutils->get_uint32_t()); 310 | } 311 | KeyConst = 312 | ConstantDataArray::get(M->getContext(), ArrayRef(keys)); 313 | EncryptedConst = 314 | ConstantDataArray::get(M->getContext(), ArrayRef(encry)); 315 | DummyConst = 316 | ConstantDataArray::get(M->getContext(), ArrayRef(dummy)); 317 | } else if (intType == Type::getInt64Ty(M->getContext())) { 318 | std::vector keys, encry, dummy; 319 | for (unsigned i = 0; i < CDS->getNumElements(); i++) { 320 | if (cryptoutils->get_range(100) >= ElementEncryptProbTemp) { 321 | unencryptedindex[GV].emplace_back(i); 322 | keys.emplace_back(1); 323 | dummy.emplace_back(CDS->getElementAsInteger(i)); 324 | continue; 325 | } 326 | const uint64_t K = cryptoutils->get_uint64_t(); 327 | const uint64_t V = CDS->getElementAsInteger(i); 328 | keys.emplace_back(K); 329 | encry.emplace_back(K ^ V); 330 | dummy.emplace_back(cryptoutils->get_uint64_t()); 331 | } 332 | KeyConst = 333 | ConstantDataArray::get(M->getContext(), ArrayRef(keys)); 334 | EncryptedConst = 335 | ConstantDataArray::get(M->getContext(), ArrayRef(encry)); 336 | DummyConst = 337 | ConstantDataArray::get(M->getContext(), ArrayRef(dummy)); 338 | } else { 339 | llvm_unreachable("Unsupported CDS Type"); 340 | } 341 | // Prepare new rawGV 342 | GlobalVariable *EncryptedRawGV = new GlobalVariable( 343 | *M, EncryptedConst->getType(), false, GV->getLinkage(), 344 | EncryptedConst, "EncryptedString", nullptr, GV->getThreadLocalMode(), 345 | GV->getType()->getAddressSpace()); 346 | genedgv.emplace_back(EncryptedRawGV); 347 | GlobalVariable *DecryptSpaceGV; 348 | if (rust_string) { 349 | ConstantAggregate *CA = cast(GV->getInitializer()); 350 | CA->setOperand(0, DummyConst); 351 | DecryptSpaceGV = new GlobalVariable( 352 | *M, GV->getValueType(), false, GV->getLinkage(), CA, 353 | "DecryptSpaceRust", nullptr, GV->getThreadLocalMode(), 354 | GV->getType()->getAddressSpace()); 355 | } else { 356 | DecryptSpaceGV = new GlobalVariable( 357 | *M, DummyConst->getType(), false, GV->getLinkage(), DummyConst, 358 | "DecryptSpace", nullptr, GV->getThreadLocalMode(), 359 | GV->getType()->getAddressSpace()); 360 | } 361 | genedgv.emplace_back(DecryptSpaceGV); 362 | old2new[GV] = std::make_pair(EncryptedRawGV, DecryptSpaceGV); 363 | GV2Keys[DecryptSpaceGV] = std::make_pair(KeyConst, EncryptedRawGV); 364 | mgv2keys[DecryptSpaceGV] = GV2Keys[DecryptSpaceGV]; 365 | unencryptedindex[KeyConst] = unencryptedindex[GV]; 366 | } 367 | // Now prepare ObjC new GV 368 | for (GlobalVariable *GV : objCStrings) { 369 | ConstantStruct *CS = cast(GV->getInitializer()); 370 | GlobalVariable *oldrawString = 371 | cast(CS->getOperand(2)->stripPointerCasts()); 372 | if (old2new.find(oldrawString) == 373 | old2new.end()) // Filter out zero initializers 374 | continue; 375 | GlobalVariable *EncryptedOCGV = ObjectiveCString( 376 | GV, "EncryptedStringObjC", old2new[oldrawString].first, CS); 377 | genedgv.emplace_back(EncryptedOCGV); 378 | GlobalVariable *DecryptSpaceOCGV = ObjectiveCString( 379 | GV, "DecryptSpaceObjC", old2new[oldrawString].second, CS); 380 | genedgv.emplace_back(DecryptSpaceOCGV); 381 | old2new[GV] = std::make_pair(EncryptedOCGV, DecryptSpaceOCGV); 382 | } // End prepare ObjC new GV 383 | if (GV2Keys.empty()) 384 | return; 385 | // Replace Uses 386 | for (User *U : Users) { 387 | for (std::unordered_map< 388 | GlobalVariable *, 389 | std::pair>::iterator iter = 390 | old2new.begin(); 391 | iter != old2new.end(); ++iter) { 392 | if (isa(U) && !isa(U)) { 393 | Constant *C = cast(U); 394 | for (Value *Op : C->operands()) 395 | if (Op == iter->first) { 396 | C->handleOperandChange(iter->first, iter->second.second); 397 | break; 398 | } 399 | } else 400 | U->replaceUsesOfWith(iter->first, iter->second.second); 401 | iter->first->removeDeadConstantUsers(); 402 | } 403 | } // End Replace Uses 404 | // CleanUp Old ObjC GVs 405 | for (GlobalVariable *GV : objCStrings) { 406 | GlobalVariable *PtrauthGV = nullptr; 407 | if (appleptrauth) { 408 | Constant *C = dyn_cast_or_null( 409 | opaquepointers 410 | ? GV->getInitializer() 411 | : cast(GV->getInitializer()->getOperand(0))); 412 | if (C) { 413 | PtrauthGV = dyn_cast(C->getOperand(0)); 414 | if (PtrauthGV->getSection() == "llvm.ptrauth") { 415 | if (ConstantExpr *CE = dyn_cast( 416 | PtrauthGV->getInitializer()->getOperand(2))) { 417 | if (GlobalVariable *GV2 = 418 | dyn_cast(CE->getOperand(0))) { 419 | if (GV->getNumUses() <= 1 && 420 | GV2->getGlobalIdentifier() == GV->getGlobalIdentifier()) 421 | PtrauthGV->getInitializer()->setOperand( 422 | 2, ConstantExpr::getPtrToInt( 423 | M->getGlobalVariable( 424 | "__CFConstantStringClassReference"), 425 | Type::getInt64Ty(M->getContext()))); 426 | } 427 | } else if (GlobalVariable *GV2 = dyn_cast( 428 | PtrauthGV->getInitializer()->getOperand(2))) 429 | if (GV->getNumUses() <= 1 && 430 | GV2->getGlobalIdentifier() == GV->getGlobalIdentifier()) 431 | PtrauthGV->getInitializer()->setOperand( 432 | 2, ConstantExpr::getPtrToInt( 433 | M->getGlobalVariable( 434 | "__CFConstantStringClassReference"), 435 | Type::getInt64Ty(M->getContext()))); 436 | } 437 | } 438 | } 439 | GV->removeDeadConstantUsers(); 440 | if (GV->getNumUses() == 0) { 441 | GV->dropAllReferences(); 442 | old2new.erase(GV); 443 | GV->eraseFromParent(); 444 | } 445 | if (PtrauthGV) { 446 | PtrauthGV->removeDeadConstantUsers(); 447 | if (PtrauthGV->getNumUses() == 0) { 448 | PtrauthGV->dropAllReferences(); 449 | PtrauthGV->eraseFromParent(); 450 | } 451 | } 452 | } 453 | // CleanUp Old Raw GVs 454 | for (std::unordered_map< 455 | GlobalVariable *, 456 | std::pair>::iterator iter = 457 | old2new.begin(); 458 | iter != old2new.end(); ++iter) { 459 | GlobalVariable *toDelete = iter->first; 460 | toDelete->removeDeadConstantUsers(); 461 | if (toDelete->getNumUses() == 0) { 462 | toDelete->dropAllReferences(); 463 | toDelete->eraseFromParent(); 464 | } 465 | } 466 | GlobalVariable *StatusGV = encstatus[Func]; 467 | /* 468 | - Split Original EntryPoint BB into A and C. 469 | - Create new BB as Decryption BB between A and C. Adjust the terminators 470 | into: A (Alloca a new array containing all) 471 | | 472 | B(If not decrypted) 473 | | 474 | C 475 | */ 476 | BasicBlock *A = &(Func->getEntryBlock()); 477 | BasicBlock *C = A->splitBasicBlock(A->getFirstNonPHIOrDbgOrLifetime()); 478 | C->setName("PrecedingBlock"); 479 | BasicBlock *B = 480 | BasicBlock::Create(Func->getContext(), "StringDecryptionBB", Func, C); 481 | // Change A's terminator to jump to B 482 | // We'll add new terminator to jump C later 483 | BranchInst *newBr = BranchInst::Create(B); 484 | ReplaceInstWithInst(A->getTerminator(), newBr); 485 | // Insert DecryptionCode 486 | HandleDecryptionBlock(B, C, GV2Keys); 487 | IRBuilder<> IRB(A->getFirstNonPHIOrDbgOrLifetime()); 488 | // Add atomic load checking status in A 489 | LoadInst *LI = IRB.CreateLoad(StatusGV->getValueType(), StatusGV, 490 | "LoadEncryptionStatus"); 491 | LI->setAtomic( 492 | AtomicOrdering::Acquire); // Will be released at the start of C 493 | LI->setAlignment(Align(4)); 494 | Value *condition = IRB.CreateICmpEQ( 495 | LI, ConstantInt::get(Type::getInt32Ty(Func->getContext()), 0)); 496 | A->getTerminator()->eraseFromParent(); 497 | BranchInst::Create(B, C, condition, A); 498 | // Add StoreInst atomically in C start 499 | // No matter control flow is coming from A or B, the GVs must be decrypted 500 | StoreInst *SI = 501 | new StoreInst(ConstantInt::get(Type::getInt32Ty(Func->getContext()), 1), 502 | StatusGV, C->getFirstNonPHIOrDbgOrLifetime()); 503 | SI->setAlignment(Align(4)); 504 | SI->setAtomic(AtomicOrdering::Release); // Release the lock acquired in LI 505 | } // End of HandleFunction 506 | 507 | GlobalVariable *ObjectiveCString(GlobalVariable *GV, std::string name, 508 | GlobalVariable *newString, 509 | ConstantStruct *CS) { 510 | Value *zero = ConstantInt::get(Type::getInt32Ty(GV->getContext()), 0); 511 | SmallVector vals; 512 | vals.emplace_back(CS->getOperand(0)); 513 | vals.emplace_back(CS->getOperand(1)); 514 | Constant *GEPed = ConstantExpr::getInBoundsGetElementPtr( 515 | newString->getValueType(), newString, {zero, zero}); 516 | if (GEPed->getType() == CS->getOperand(2)->getType()) { 517 | vals.emplace_back(GEPed); 518 | } else { 519 | Constant *BitCasted = 520 | ConstantExpr::getBitCast(newString, CS->getOperand(2)->getType()); 521 | vals.emplace_back(BitCasted); 522 | } 523 | vals.emplace_back(CS->getOperand(3)); 524 | Constant *newCS = 525 | ConstantStruct::get(CS->getType(), ArrayRef(vals)); 526 | GlobalVariable *ObjcGV = new GlobalVariable( 527 | *(GV->getParent()), newCS->getType(), false, GV->getLinkage(), newCS, 528 | name, nullptr, GV->getThreadLocalMode(), 529 | GV->getType()->getAddressSpace()); 530 | // for arm64e target on Apple LLVM 531 | if (appleptrauth) { 532 | Constant *C = dyn_cast_or_null( 533 | opaquepointers ? newCS : cast(newCS->getOperand(0))); 534 | GlobalVariable *PtrauthGV = dyn_cast(C->getOperand(0)); 535 | if (PtrauthGV && PtrauthGV->getSection() == "llvm.ptrauth") { 536 | GlobalVariable *NewPtrauthGV = new GlobalVariable( 537 | *PtrauthGV->getParent(), PtrauthGV->getValueType(), true, 538 | PtrauthGV->getLinkage(), 539 | ConstantStruct::getAnon( 540 | {(Constant *)PtrauthGV->getInitializer()->getOperand(0), 541 | (ConstantInt *)PtrauthGV->getInitializer()->getOperand(1), 542 | ConstantExpr::getPtrToInt( 543 | ObjcGV, Type::getInt64Ty(ObjcGV->getContext())), 544 | (ConstantInt *)PtrauthGV->getInitializer()->getOperand(3)}, 545 | false), 546 | PtrauthGV->getName(), nullptr, PtrauthGV->getThreadLocalMode()); 547 | NewPtrauthGV->setSection("llvm.ptrauth"); 548 | NewPtrauthGV->setAlignment(Align(8)); 549 | ObjcGV->getInitializer()->setOperand( 550 | 0, 551 | ConstantExpr::getBitCast( 552 | NewPtrauthGV, 553 | Type::getInt32Ty(NewPtrauthGV->getContext())->getPointerTo())); 554 | } 555 | } 556 | return ObjcGV; 557 | } 558 | 559 | void HandleDecryptionBlock( 560 | BasicBlock *B, BasicBlock *C, 561 | std::unordered_map> &GV2Keys) { 563 | IRBuilder<> IRB(B); 564 | Value *zero = ConstantInt::get(Type::getInt32Ty(B->getContext()), 0); 565 | for (std::unordered_map>::iterator 567 | iter = GV2Keys.begin(); 568 | iter != GV2Keys.end(); ++iter) { 569 | bool rust_string = 570 | !isa(iter->first->getInitializer()); 571 | ConstantAggregate *CA = 572 | rust_string ? cast(iter->first->getInitializer()) 573 | : nullptr; 574 | Constant *KeyConst = iter->second.first; 575 | ConstantDataArray *CastedCDA = cast(KeyConst); 576 | // Prevent optimization of encrypted data 577 | appendToCompilerUsed(*iter->second.second->getParent(), 578 | {iter->second.second}); 579 | // Element-By-Element XOR so the fucking verifier won't complain 580 | // Also, this hides keys 581 | uint64_t realkeyoff = 0; 582 | for (uint64_t i = 0; i < CastedCDA->getType()->getNumElements(); i++) { 583 | if (unencryptedindex[KeyConst].size() && 584 | std::find(unencryptedindex[KeyConst].begin(), 585 | unencryptedindex[KeyConst].end(), 586 | i) != unencryptedindex[KeyConst].end()) 587 | continue; 588 | Value *offset = 589 | ConstantInt::get(Type::getInt64Ty(B->getContext()), realkeyoff); 590 | Value *offset2 = ConstantInt::get(Type::getInt64Ty(B->getContext()), i); 591 | Value *EncryptedGEP = 592 | IRB.CreateGEP(iter->second.second->getValueType(), 593 | iter->second.second, {zero, offset}); 594 | Value *DecryptedGEP = 595 | rust_string 596 | ? IRB.CreateGEP( 597 | CA->getOperand(0)->getType(), 598 | IRB.CreateGEP( 599 | CA->getType(), iter->first, 600 | {zero, ConstantInt::getNullValue( 601 | Type::getInt64Ty(B->getContext()))}), 602 | {zero, offset2}) 603 | : IRB.CreateGEP(iter->first->getValueType(), iter->first, 604 | {zero, offset2}); 605 | LoadInst *LI = IRB.CreateLoad(CastedCDA->getElementType(), EncryptedGEP, 606 | "EncryptedChar"); 607 | Value *XORed = IRB.CreateXor(LI, CastedCDA->getElementAsConstant(i)); 608 | IRB.CreateStore(XORed, DecryptedGEP); 609 | realkeyoff++; 610 | } 611 | } 612 | IRB.CreateBr(C); 613 | } // End of HandleDecryptionBlock 614 | }; 615 | 616 | ModulePass *createStringEncryptionPass(bool flag) { 617 | return new StringEncryption(flag); 618 | } 619 | } // namespace llvm 620 | 621 | char StringEncryption::ID = 0; 622 | INITIALIZE_PASS(StringEncryption, "strcry", "Enable String Encryption", false, 623 | false) 624 | -------------------------------------------------------------------------------- /hikari/StringEncryption.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRING_ENCRYPTION_H_ 2 | #define _STRING_ENCRYPTION_H_ 3 | 4 | #include "llvm/IR/PassManager.h" 5 | #include "llvm/Pass.h" 6 | 7 | namespace llvm { 8 | 9 | ModulePass *createStringEncryptionPass(bool flag); 10 | void initializeStringEncryptionPass(PassRegistry &Registry); 11 | 12 | } // namespace llvm 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /hikari/SubstituteImpl.cpp: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | 3 | #include "SubstituteImpl.h" 4 | #include "llvm/IR/IRBuilder.h" 5 | #include "llvm/IR/NoFolder.h" 6 | #include "CryptoUtils.h" 7 | 8 | using namespace llvm; 9 | 10 | #define NUMBER_ADD_SUBST 7 11 | #define NUMBER_SUB_SUBST 6 12 | #define NUMBER_AND_SUBST 6 13 | #define NUMBER_OR_SUBST 6 14 | #define NUMBER_XOR_SUBST 6 15 | #define NUMBER_MUL_SUBST 2 16 | 17 | static void addNeg(BinaryOperator *bo); 18 | static void addDoubleNeg(BinaryOperator *bo); 19 | static void addRand(BinaryOperator *bo); 20 | static void addRand2(BinaryOperator *bo); 21 | static void addSubstitution(BinaryOperator *bo); 22 | static void addSubstitution2(BinaryOperator *bo); 23 | static void addSubstitution3(BinaryOperator *bo); 24 | 25 | static void subNeg(BinaryOperator *bo); 26 | static void subRand(BinaryOperator *bo); 27 | static void subRand2(BinaryOperator *bo); 28 | static void subSubstitution(BinaryOperator *bo); 29 | static void subSubstitution2(BinaryOperator *bo); 30 | static void subSubstitution3(BinaryOperator *bo); 31 | 32 | static void andSubstitution(BinaryOperator *bo); 33 | static void andSubstitution2(BinaryOperator *bo); 34 | static void andSubstitution3(BinaryOperator *bo); 35 | static void andSubstitutionRand(BinaryOperator *bo); 36 | static void andNor(BinaryOperator *bo); 37 | static void andNand(BinaryOperator *bo); 38 | 39 | static void orSubstitution(BinaryOperator *bo); 40 | static void orSubstitution2(BinaryOperator *bo); 41 | static void orSubstitution3(BinaryOperator *bo); 42 | static void orSubstitutionRand(BinaryOperator *bo); 43 | static void orNor(BinaryOperator *bo); 44 | static void orNand(BinaryOperator *bo); 45 | 46 | static void xorSubstitution(BinaryOperator *bo); 47 | static void xorSubstitution2(BinaryOperator *bo); 48 | static void xorSubstitution3(BinaryOperator *bo); 49 | static void xorSubstitutionRand(BinaryOperator *bo); 50 | static void xorNor(BinaryOperator *bo); 51 | static void xorNand(BinaryOperator *bo); 52 | 53 | static void mulSubstitution(BinaryOperator *bo); 54 | static void mulSubstitution2(BinaryOperator *bo); 55 | 56 | static void (*funcAdd[NUMBER_ADD_SUBST])(BinaryOperator *bo) = { 57 | &addNeg, &addDoubleNeg, &addRand, &addRand2, 58 | &addSubstitution, &addSubstitution2, &addSubstitution3}; 59 | static void (*funcSub[NUMBER_SUB_SUBST])(BinaryOperator *bo) = { 60 | &subNeg, &subRand, &subRand2, 61 | &subSubstitution, &subSubstitution2, &subSubstitution3}; 62 | static void (*funcAnd[NUMBER_AND_SUBST])(BinaryOperator *bo) = { 63 | &andSubstitution, &andSubstitution2, &andSubstitution3, 64 | &andSubstitutionRand, &andNor, &andNand}; 65 | static void (*funcOr[NUMBER_OR_SUBST])(BinaryOperator *bo) = { 66 | &orSubstitution, &orSubstitution2, &orSubstitution3, 67 | &orSubstitutionRand, &orNor, &orNand}; 68 | static void (*funcXor[NUMBER_XOR_SUBST])(BinaryOperator *bo) = { 69 | &xorSubstitution, &xorSubstitution2, xorSubstitution3, 70 | &xorSubstitutionRand, &xorNor, &xorNand}; 71 | static void (*funcMul[NUMBER_MUL_SUBST])(BinaryOperator *bo) = { 72 | &mulSubstitution, &mulSubstitution2}; 73 | 74 | void SubstituteImpl::substituteAdd(BinaryOperator *bo) { 75 | (*funcAdd[cryptoutils->get_range(NUMBER_ADD_SUBST)])(bo); 76 | } 77 | void SubstituteImpl::substituteSub(BinaryOperator *bo) { 78 | (*funcSub[cryptoutils->get_range(NUMBER_SUB_SUBST)])(bo); 79 | } 80 | void SubstituteImpl::substituteAnd(BinaryOperator *bo) { 81 | (*funcAnd[cryptoutils->get_range(NUMBER_AND_SUBST)])(bo); 82 | } 83 | void SubstituteImpl::substituteOr(BinaryOperator *bo) { 84 | (*funcOr[cryptoutils->get_range(NUMBER_OR_SUBST)])(bo); 85 | } 86 | void SubstituteImpl::substituteXor(BinaryOperator *bo) { 87 | (*funcXor[cryptoutils->get_range(NUMBER_XOR_SUBST)])(bo); 88 | } 89 | void SubstituteImpl::substituteMul(BinaryOperator *bo) { 90 | (*funcMul[cryptoutils->get_range(NUMBER_MUL_SUBST)])(bo); 91 | } 92 | 93 | // Implementation of ~(a | b) and ~a & ~b 94 | static BinaryOperator *buildNor(Value *a, Value *b, Instruction *insertBefore) { 95 | switch (cryptoutils->get_range(2)) { 96 | case 0: { 97 | // ~(a | b) 98 | BinaryOperator *op = 99 | BinaryOperator::Create(Instruction::Or, a, b, "", insertBefore); 100 | op = BinaryOperator::CreateNot(op, "", insertBefore); 101 | return op; 102 | } 103 | case 1: { 104 | // ~a & ~b 105 | BinaryOperator *nota = BinaryOperator::CreateNot(a, "", insertBefore); 106 | BinaryOperator *notb = BinaryOperator::CreateNot(b, "", insertBefore); 107 | BinaryOperator *op = 108 | BinaryOperator::Create(Instruction::And, nota, notb, "", insertBefore); 109 | return op; 110 | } 111 | default: 112 | llvm_unreachable("wtf?"); 113 | } 114 | } 115 | 116 | // Implementation of ~(a & b) and ~a | ~b 117 | static BinaryOperator *buildNand(Value *a, Value *b, 118 | Instruction *insertBefore) { 119 | switch (cryptoutils->get_range(2)) { 120 | case 0: { 121 | // ~(a & b) 122 | BinaryOperator *op = 123 | BinaryOperator::Create(Instruction::And, a, b, "", insertBefore); 124 | op = BinaryOperator::CreateNot(op, "", insertBefore); 125 | return op; 126 | } 127 | case 1: { 128 | // ~a | ~b 129 | BinaryOperator *nota = BinaryOperator::CreateNot(a, "", insertBefore); 130 | BinaryOperator *notb = BinaryOperator::CreateNot(b, "", insertBefore); 131 | BinaryOperator *op = 132 | BinaryOperator::Create(Instruction::Or, nota, notb, "", insertBefore); 133 | return op; 134 | } 135 | default: 136 | llvm_unreachable("wtf?"); 137 | } 138 | } 139 | 140 | // Implementation of a = b - (-c) 141 | static void addNeg(BinaryOperator *bo) { 142 | BinaryOperator *op = BinaryOperator::CreateNeg(bo->getOperand(1), "", bo); 143 | op = BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), op, "", bo); 144 | bo->replaceAllUsesWith(op); 145 | } 146 | 147 | // Implementation of a = -(-b + (-c)) 148 | static void addDoubleNeg(BinaryOperator *bo) { 149 | BinaryOperator *op = BinaryOperator::CreateNeg(bo->getOperand(0), "", bo); 150 | BinaryOperator *op2 = BinaryOperator::CreateNeg(bo->getOperand(1), "", bo); 151 | op = BinaryOperator::Create(Instruction::Add, op, op2, "", bo); 152 | op = BinaryOperator::CreateNeg(op, "", bo); 153 | bo->replaceAllUsesWith(op); 154 | } 155 | 156 | // Implementation of r = rand (); a = b + r; a = a + c; a = a - r 157 | static void addRand(BinaryOperator *bo) { 158 | ConstantInt *co = (ConstantInt *)ConstantInt::get( 159 | bo->getType(), cryptoutils->get_uint64_t()); 160 | BinaryOperator *op = 161 | BinaryOperator::Create(Instruction::Add, bo->getOperand(0), co, "", bo); 162 | op = BinaryOperator::Create(Instruction::Add, op, bo->getOperand(1), "", bo); 163 | op = BinaryOperator::Create(Instruction::Sub, op, co, "", bo); 164 | bo->replaceAllUsesWith(op); 165 | } 166 | 167 | // Implementation of r = rand (); a = b - r; a = a + b; a = a + r 168 | static void addRand2(BinaryOperator *bo) { 169 | ConstantInt *co = (ConstantInt *)ConstantInt::get( 170 | bo->getType(), cryptoutils->get_uint64_t()); 171 | BinaryOperator *op = 172 | BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), co, "", bo); 173 | op = BinaryOperator::Create(Instruction::Add, op, bo->getOperand(1), "", bo); 174 | op = BinaryOperator::Create(Instruction::Add, op, co, "", bo); 175 | bo->replaceAllUsesWith(op); 176 | } 177 | 178 | // Implementation of a = b + c => a = b - ~c - 1 179 | static void addSubstitution(BinaryOperator *bo) { 180 | ConstantInt *co = (ConstantInt *)ConstantInt::get(bo->getType(), 1); 181 | BinaryOperator *op = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 182 | BinaryOperator *op1 = BinaryOperator::CreateNeg(co, "", bo); 183 | op = BinaryOperator::Create(Instruction::Sub, op, op1, "", bo); 184 | op = BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), op, "", bo); 185 | bo->replaceAllUsesWith(op); 186 | } 187 | 188 | // Implementation of a = b + c => a = (b | c) + (b & c) 189 | static void addSubstitution2(BinaryOperator *bo) { 190 | BinaryOperator *op = BinaryOperator::Create( 191 | Instruction::And, bo->getOperand(0), bo->getOperand(1), "", bo); 192 | BinaryOperator *op1 = BinaryOperator::Create( 193 | Instruction::Or, bo->getOperand(0), bo->getOperand(1), "", bo); 194 | op = BinaryOperator::Create(Instruction::Add, op, op1, "", bo); 195 | bo->replaceAllUsesWith(op); 196 | } 197 | 198 | // Implementation of a = b + c => a = (b ^ c) + (b & c) * 2 199 | static void addSubstitution3(BinaryOperator *bo) { 200 | ConstantInt *co = (ConstantInt *)ConstantInt::get(bo->getType(), 2); 201 | BinaryOperator *op = BinaryOperator::Create( 202 | Instruction::And, bo->getOperand(0), bo->getOperand(1), "", bo); 203 | op = BinaryOperator::Create(Instruction::Mul, op, co, "", bo); 204 | BinaryOperator *op1 = BinaryOperator::Create( 205 | Instruction::Xor, bo->getOperand(0), bo->getOperand(1), "", bo); 206 | op = BinaryOperator::Create(Instruction::Add, op1, op, "", bo); 207 | bo->replaceAllUsesWith(op); 208 | } 209 | 210 | // Implementation of a = b + (-c) 211 | static void subNeg(BinaryOperator *bo) { 212 | BinaryOperator *op = BinaryOperator::CreateNeg(bo->getOperand(1), "", bo); 213 | op = BinaryOperator::Create(Instruction::Add, bo->getOperand(0), op, "", bo); 214 | bo->replaceAllUsesWith(op); 215 | } 216 | 217 | // Implementation of r = rand (); a = b + r; a = a - c; a = a - r 218 | static void subRand(BinaryOperator *bo) { 219 | ConstantInt *co = (ConstantInt *)ConstantInt::get( 220 | bo->getType(), cryptoutils->get_uint64_t()); 221 | BinaryOperator *op = 222 | BinaryOperator::Create(Instruction::Add, bo->getOperand(0), co, "", bo); 223 | op = BinaryOperator::Create(Instruction::Sub, op, bo->getOperand(1), "", bo); 224 | op = BinaryOperator::Create(Instruction::Sub, op, co, "", bo); 225 | bo->replaceAllUsesWith(op); 226 | } 227 | 228 | // Implementation of r = rand (); a = b - r; a = a - c; a = a + r 229 | static void subRand2(BinaryOperator *bo) { 230 | ConstantInt *co = (ConstantInt *)ConstantInt::get( 231 | bo->getType(), cryptoutils->get_uint64_t()); 232 | BinaryOperator *op = 233 | BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), co, "", bo); 234 | op = BinaryOperator::Create(Instruction::Sub, op, bo->getOperand(1), "", bo); 235 | op = BinaryOperator::Create(Instruction::Add, op, co, "", bo); 236 | bo->replaceAllUsesWith(op); 237 | } 238 | 239 | // Implementation of a = b - c => a = (b & ~c) - (~b & c) 240 | static void subSubstitution(BinaryOperator *bo) { 241 | BinaryOperator *op1 = BinaryOperator::CreateNot(bo->getOperand(0), "", bo); 242 | BinaryOperator *op = 243 | BinaryOperator::Create(Instruction::And, op1, bo->getOperand(1), "", bo); 244 | op1 = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 245 | BinaryOperator *op2 = 246 | BinaryOperator::Create(Instruction::And, bo->getOperand(0), op1, "", bo); 247 | op = BinaryOperator::Create(Instruction::Sub, op2, op, "", bo); 248 | bo->replaceAllUsesWith(op); 249 | } 250 | 251 | // Implementation of a = b - c => a = (2 * (b & ~c)) - (b ^ c) 252 | static void subSubstitution2(BinaryOperator *bo) { 253 | ConstantInt *co = (ConstantInt *)ConstantInt::get(bo->getType(), 2); 254 | BinaryOperator *op1 = BinaryOperator::Create( 255 | Instruction::Xor, bo->getOperand(0), bo->getOperand(1), "", bo); 256 | BinaryOperator *op = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 257 | op = BinaryOperator::Create(Instruction::And, bo->getOperand(0), op, "", bo); 258 | op = BinaryOperator::Create(Instruction::Mul, co, op, "", bo); 259 | op = BinaryOperator::Create(Instruction::Sub, op, op1, "", bo); 260 | bo->replaceAllUsesWith(op); 261 | } 262 | 263 | // Implementation of a = b - c => a = b + ~c + 1 264 | static void subSubstitution3(BinaryOperator *bo) { 265 | ConstantInt *co = (ConstantInt *)ConstantInt::get(bo->getType(), 1); 266 | BinaryOperator *op1 = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 267 | BinaryOperator *op = 268 | BinaryOperator::Create(Instruction::Add, bo->getOperand(0), op1, "", bo); 269 | op = BinaryOperator::Create(Instruction::Add, op, co, "", bo); 270 | bo->replaceAllUsesWith(op); 271 | } 272 | 273 | // Implementation of a = b & c => a = (b ^ ~c) & b 274 | static void andSubstitution(BinaryOperator *bo) { 275 | BinaryOperator *op = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 276 | BinaryOperator *op1 = 277 | BinaryOperator::Create(Instruction::Xor, bo->getOperand(0), op, "", bo); 278 | op = BinaryOperator::Create(Instruction::And, op1, bo->getOperand(0), "", bo); 279 | bo->replaceAllUsesWith(op); 280 | } 281 | 282 | // Implementation of a = b & c => a = (b | c) & ~(b ^ c) 283 | static void andSubstitution2(BinaryOperator *bo) { 284 | BinaryOperator *op1 = BinaryOperator::Create( 285 | Instruction::Xor, bo->getOperand(0), bo->getOperand(1), "", bo); 286 | op1 = BinaryOperator::CreateNot(op1, "", bo); 287 | BinaryOperator *op = BinaryOperator::Create( 288 | Instruction::Or, bo->getOperand(0), bo->getOperand(1), "", bo); 289 | op = BinaryOperator::Create(Instruction::And, op, op1, "", bo); 290 | bo->replaceAllUsesWith(op); 291 | } 292 | 293 | // Implementation of a = b & c => a = (~b | c) + (b + 1) 294 | static void andSubstitution3(BinaryOperator *bo) { 295 | ConstantInt *co = (ConstantInt *)ConstantInt::get(bo->getType(), 1); 296 | BinaryOperator *op1 = 297 | BinaryOperator::Create(Instruction::Add, bo->getOperand(0), co, "", bo); 298 | BinaryOperator *op = BinaryOperator::CreateNot(bo->getOperand(0), "", bo); 299 | op = BinaryOperator::Create(Instruction::Or, op, bo->getOperand(1), "", bo); 300 | op = BinaryOperator::Create(Instruction::Add, op, op1, "", bo); 301 | bo->replaceAllUsesWith(op); 302 | } 303 | 304 | // Implementation of a = a & b <=> ~(~a | ~b) & (r | ~r) 305 | static void andSubstitutionRand(BinaryOperator *bo) { 306 | ConstantInt *co = (ConstantInt *)ConstantInt::get( 307 | bo->getType(), cryptoutils->get_uint64_t()); 308 | BinaryOperator *op = BinaryOperator::CreateNot(bo->getOperand(0), "", bo); 309 | BinaryOperator *op1 = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 310 | BinaryOperator *opr = BinaryOperator::CreateNot(co, "", bo); 311 | BinaryOperator *opa = 312 | BinaryOperator::Create(Instruction::Or, op, op1, "", bo); 313 | opr = BinaryOperator::Create(Instruction::Or, co, opr, "", bo); 314 | op = BinaryOperator::CreateNot(opa, "", bo); 315 | op = BinaryOperator::Create(Instruction::And, op, opr, "", bo); 316 | bo->replaceAllUsesWith(op); 317 | } 318 | 319 | // Implementation of a = a & b => Nor(Nor(a, a), Nor(b, b)) 320 | static void andNor(BinaryOperator *bo) { 321 | BinaryOperator *noraa = buildNor(bo->getOperand(0), bo->getOperand(0), bo); 322 | BinaryOperator *norbb = buildNor(bo->getOperand(1), bo->getOperand(1), bo); 323 | bo->replaceAllUsesWith(buildNor(noraa, norbb, bo)); 324 | } 325 | 326 | // Implementation of a = a & b => Nand(Nand(a, b), Nand(a, b)) 327 | static void andNand(BinaryOperator *bo) { 328 | BinaryOperator *nandab = buildNand(bo->getOperand(0), bo->getOperand(1), bo); 329 | BinaryOperator *nandab2 = buildNand(bo->getOperand(0), bo->getOperand(1), bo); 330 | bo->replaceAllUsesWith(buildNand(nandab, nandab2, bo)); 331 | } 332 | 333 | // Implementation of a = a | b => a = (b & c) | (b ^ c) 334 | static void orSubstitution(BinaryOperator *bo) { 335 | BinaryOperator *op = BinaryOperator::Create( 336 | Instruction::And, bo->getOperand(0), bo->getOperand(1), "", bo); 337 | BinaryOperator *op1 = BinaryOperator::Create( 338 | Instruction::Xor, bo->getOperand(0), bo->getOperand(1), "", bo); 339 | op = BinaryOperator::Create(Instruction::Or, op, op1, "", bo); 340 | bo->replaceAllUsesWith(op); 341 | } 342 | 343 | // Implementation of a = a | b => a = (b + (b ^ c)) - (b & ~c) 344 | static void orSubstitution2(BinaryOperator *bo) { 345 | BinaryOperator *op1 = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 346 | op1 = 347 | BinaryOperator::Create(Instruction::And, bo->getOperand(0), op1, "", bo); 348 | BinaryOperator *op = BinaryOperator::Create( 349 | Instruction::Xor, bo->getOperand(0), bo->getOperand(1), "", bo); 350 | op = BinaryOperator::Create(Instruction::Add, bo->getOperand(0), op, "", bo); 351 | op = BinaryOperator::Create(Instruction::Sub, op, op1, "", bo); 352 | bo->replaceAllUsesWith(op); 353 | } 354 | 355 | // Implementation of a = a | b => a = (b + c + 1) + ~(c & b) 356 | static void orSubstitution3(BinaryOperator *bo) { 357 | ConstantInt *co = (ConstantInt *)ConstantInt::get(bo->getType(), 1); 358 | BinaryOperator *op1 = BinaryOperator::Create( 359 | Instruction::And, bo->getOperand(1), bo->getOperand(0), "", bo); 360 | op1 = BinaryOperator::CreateNot(op1, "", bo); 361 | BinaryOperator *op = BinaryOperator::Create( 362 | Instruction::Add, bo->getOperand(0), bo->getOperand(1), "", bo); 363 | op = BinaryOperator::Create(Instruction::Add, op, co, "", bo); 364 | op = BinaryOperator::Create(Instruction::Add, op, op1, "", bo); 365 | bo->replaceAllUsesWith(op); 366 | } 367 | 368 | // Implementation of a = b | c => a = (((~a & r) | (a & ~r)) ^ ((~b & r) | (b & 369 | // ~r))) | (~(~a | ~b) & (r | ~r)) 370 | static void orSubstitutionRand(BinaryOperator *bo) { 371 | ConstantInt *co = (ConstantInt *)ConstantInt::get( 372 | bo->getType(), cryptoutils->get_uint64_t()); 373 | BinaryOperator *op = BinaryOperator::CreateNot(bo->getOperand(0), "", bo); 374 | BinaryOperator *op1 = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 375 | BinaryOperator *op2 = BinaryOperator::CreateNot(co, "", bo); 376 | BinaryOperator *op3 = 377 | BinaryOperator::Create(Instruction::And, op, co, "", bo); 378 | BinaryOperator *op4 = 379 | BinaryOperator::Create(Instruction::And, bo->getOperand(0), op2, "", bo); 380 | BinaryOperator *op5 = 381 | BinaryOperator::Create(Instruction::And, op1, co, "", bo); 382 | BinaryOperator *op6 = 383 | BinaryOperator::Create(Instruction::And, bo->getOperand(1), op2, "", bo); 384 | op3 = BinaryOperator::Create(Instruction::Or, op3, op4, "", bo); 385 | op4 = BinaryOperator::Create(Instruction::Or, op5, op6, "", bo); 386 | op5 = BinaryOperator::Create(Instruction::Xor, op3, op4, "", bo); 387 | op3 = BinaryOperator::Create(Instruction::Or, op, op1, "", bo); 388 | op3 = BinaryOperator::CreateNot(op3, "", bo); 389 | op4 = BinaryOperator::Create(Instruction::Or, co, op2, "", bo); 390 | op4 = BinaryOperator::Create(Instruction::And, op3, op4, "", bo); 391 | op = BinaryOperator::Create(Instruction::Or, op5, op4, "", bo); 392 | bo->replaceAllUsesWith(op); 393 | } 394 | 395 | // Implementation of a = a | b => a = Nor(Nor(a, b), Nor(a, b)) 396 | static void orNor(BinaryOperator *bo) { 397 | BinaryOperator *norab = buildNor(bo->getOperand(0), bo->getOperand(1), bo); 398 | BinaryOperator *norab2 = buildNor(bo->getOperand(0), bo->getOperand(1), bo); 399 | BinaryOperator *op = buildNor(norab, norab2, bo); 400 | bo->replaceAllUsesWith(op); 401 | } 402 | 403 | // Implementation of a = a | b => a = Nand(Nand(a, a), Nand(b, b)) 404 | static void orNand(BinaryOperator *bo) { 405 | BinaryOperator *nandaa = buildNand(bo->getOperand(0), bo->getOperand(0), bo); 406 | BinaryOperator *nandbb = buildNand(bo->getOperand(1), bo->getOperand(1), bo); 407 | BinaryOperator *op = buildNand(nandaa, nandbb, bo); 408 | bo->replaceAllUsesWith(op); 409 | } 410 | 411 | // Implementation of a = a ^ b => a = (~a & b) | (a & ~b) 412 | static void xorSubstitution(BinaryOperator *bo) { 413 | BinaryOperator *op = BinaryOperator::CreateNot(bo->getOperand(0), "", bo); 414 | op = BinaryOperator::Create(Instruction::And, bo->getOperand(1), op, "", bo); 415 | BinaryOperator *op1 = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 416 | op1 = 417 | BinaryOperator::Create(Instruction::And, bo->getOperand(0), op1, "", bo); 418 | op = BinaryOperator::Create(Instruction::Or, op, op1, "", bo); 419 | bo->replaceAllUsesWith(op); 420 | } 421 | 422 | // Implementation of a = a ^ b => a = (b + c) - 2 * (b & c) 423 | static void xorSubstitution2(BinaryOperator *bo) { 424 | ConstantInt *co = (ConstantInt *)ConstantInt::get(bo->getType(), 2); 425 | BinaryOperator *op1 = BinaryOperator::Create( 426 | Instruction::And, bo->getOperand(0), bo->getOperand(1), "", bo); 427 | op1 = BinaryOperator::Create(Instruction::Mul, co, op1, "", bo); 428 | BinaryOperator *op = BinaryOperator::Create( 429 | Instruction::Add, bo->getOperand(0), bo->getOperand(1), "", bo); 430 | op = BinaryOperator::Create(Instruction::Sub, op, op1, "", bo); 431 | bo->replaceAllUsesWith(op); 432 | } 433 | 434 | // Implementation of a = a ^ b => a = b - (2 * (c & ~(b ^ c)) - c) 435 | static void xorSubstitution3(BinaryOperator *bo) { 436 | ConstantInt *co = (ConstantInt *)ConstantInt::get(bo->getType(), 2); 437 | BinaryOperator *op1 = BinaryOperator::Create( 438 | Instruction::Xor, bo->getOperand(0), bo->getOperand(1), "", bo); 439 | op1 = BinaryOperator::CreateNot(op1, "", bo); 440 | op1 = 441 | BinaryOperator::Create(Instruction::And, bo->getOperand(1), op1, "", bo); 442 | op1 = BinaryOperator::Create(Instruction::Mul, co, op1, "", bo); 443 | op1 = 444 | BinaryOperator::Create(Instruction::Sub, op1, bo->getOperand(1), "", bo); 445 | BinaryOperator *op = 446 | BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), op1, "", bo); 447 | bo->replaceAllUsesWith(op); 448 | } 449 | 450 | // Implementation of a = a ^ b <=> (a ^ r) ^ (b ^ r) <=> (~a & r | a & ~r) ^ (~b 451 | // & r | b & ~r) note : r is a random number 452 | static void xorSubstitutionRand(BinaryOperator *bo) { 453 | ConstantInt *co = (ConstantInt *)ConstantInt::get( 454 | bo->getType(), cryptoutils->get_uint64_t()); 455 | BinaryOperator *op = BinaryOperator::CreateNot(bo->getOperand(0), "", bo); 456 | op = BinaryOperator::Create(Instruction::And, co, op, "", bo); 457 | BinaryOperator *opr = BinaryOperator::CreateNot(co, "", bo); 458 | BinaryOperator *op1 = 459 | BinaryOperator::Create(Instruction::And, bo->getOperand(0), opr, "", bo); 460 | BinaryOperator *op2 = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 461 | op2 = BinaryOperator::Create(Instruction::And, op2, co, "", bo); 462 | BinaryOperator *op3 = 463 | BinaryOperator::Create(Instruction::And, bo->getOperand(1), opr, "", bo); 464 | op = BinaryOperator::Create(Instruction::Or, op, op1, "", bo); 465 | op1 = BinaryOperator::Create(Instruction::Or, op2, op3, "", bo); 466 | op = BinaryOperator::Create(Instruction::Xor, op, op1, "", bo); 467 | bo->replaceAllUsesWith(op); 468 | } 469 | 470 | // Implementation of a = a ^ b => a = Nor(Nor(Nor(a, a), Nor(b, b)), Nor(a, b)) 471 | static void xorNor(BinaryOperator *bo) { 472 | BinaryOperator *noraa = buildNor(bo->getOperand(0), bo->getOperand(0), bo); 473 | BinaryOperator *norbb = buildNor(bo->getOperand(1), bo->getOperand(1), bo); 474 | BinaryOperator *nornoraanorbb = buildNor(noraa, norbb, bo); 475 | BinaryOperator *norab = buildNor(bo->getOperand(0), bo->getOperand(1), bo); 476 | BinaryOperator *op = buildNor(nornoraanorbb, norab, bo); 477 | bo->replaceAllUsesWith(op); 478 | } 479 | 480 | // Implementation of a = a ^ b => a = Nand(Nand(Nand(a, a), b), Nand(a, Nand(b, 481 | // b))) 482 | static void xorNand(BinaryOperator *bo) { 483 | BinaryOperator *nandaa = buildNand(bo->getOperand(0), bo->getOperand(0), bo); 484 | BinaryOperator *nandnandaab = buildNand(nandaa, bo->getOperand(1), bo); 485 | BinaryOperator *nandbb = buildNand(bo->getOperand(1), bo->getOperand(1), bo); 486 | BinaryOperator *nandanandbb = buildNand(bo->getOperand(0), nandbb, bo); 487 | BinaryOperator *op = buildNand(nandnandaab, nandanandbb, bo); 488 | bo->replaceAllUsesWith(op); 489 | } 490 | 491 | // Implementation of a = b * c => a = (((b | c) * (b & c)) + ((b & ~c) * (c & 492 | // ~b))) 493 | static void mulSubstitution(BinaryOperator *bo) { 494 | BinaryOperator *op1 = BinaryOperator::CreateNot(bo->getOperand(0), "", bo); 495 | op1 = 496 | BinaryOperator::Create(Instruction::And, bo->getOperand(1), op1, "", bo); 497 | BinaryOperator *op2 = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 498 | op2 = 499 | BinaryOperator::Create(Instruction::And, bo->getOperand(0), op2, "", bo); 500 | BinaryOperator *op = 501 | BinaryOperator::Create(Instruction::Mul, op2, op1, "", bo); 502 | op1 = BinaryOperator::Create(Instruction::And, bo->getOperand(0), 503 | bo->getOperand(1), "", bo); 504 | op2 = BinaryOperator::Create(Instruction::Or, bo->getOperand(0), 505 | bo->getOperand(1), "", bo); 506 | BinaryOperator *op3 = 507 | BinaryOperator::Create(Instruction::Mul, op2, op1, "", bo); 508 | op = BinaryOperator::Create(Instruction::Add, op3, op, "", bo); 509 | bo->replaceAllUsesWith(op); 510 | } 511 | 512 | // Implementation of a = b * c => a = (((b | c) * (b & c)) + ((~(b | ~c)) * (b & 513 | // ~c))) 514 | static void mulSubstitution2(BinaryOperator *bo) { 515 | BinaryOperator *op1 = BinaryOperator::CreateNot(bo->getOperand(1), "", bo); 516 | BinaryOperator *op2 = 517 | BinaryOperator::Create(Instruction::And, bo->getOperand(0), op1, "", bo); 518 | BinaryOperator *op3 = 519 | BinaryOperator::Create(Instruction::Or, bo->getOperand(0), op1, "", bo); 520 | op3 = BinaryOperator::CreateNot(op3, "", bo); 521 | op3 = BinaryOperator::Create(Instruction::Mul, op3, op2, "", bo); 522 | BinaryOperator *op4 = BinaryOperator::Create( 523 | Instruction::And, bo->getOperand(0), bo->getOperand(1), "", bo); 524 | BinaryOperator *op5 = BinaryOperator::Create( 525 | Instruction::Or, bo->getOperand(0), bo->getOperand(1), "", bo); 526 | op5 = BinaryOperator::Create(Instruction::Mul, op5, op4, "", bo); 527 | BinaryOperator *op = 528 | BinaryOperator::Create(Instruction::Add, op5, op3, "", bo); 529 | bo->replaceAllUsesWith(op); 530 | } 531 | -------------------------------------------------------------------------------- /hikari/SubstituteImpl.h: -------------------------------------------------------------------------------- 1 | #ifndef _SUBSTITUTE_IMPL_H 2 | #define _SUBSTITUTE_IMPL_H 3 | 4 | #include "llvm/IR/InstrTypes.h" 5 | 6 | namespace llvm { 7 | 8 | namespace SubstituteImpl { 9 | 10 | void substituteAdd(BinaryOperator *bo); 11 | void substituteSub(BinaryOperator *bo); 12 | void substituteAnd(BinaryOperator *bo); 13 | void substituteOr(BinaryOperator *bo); 14 | void substituteXor(BinaryOperator *bo); 15 | void substituteMul(BinaryOperator *bo); 16 | 17 | } // namespace SubstituteImpl 18 | 19 | } // namespace llvm 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /hikari/Substitution.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | #include "Substitution.h" 5 | #include "llvm/ADT/Statistic.h" 6 | #include "llvm/IR/InstIterator.h" 7 | #include "llvm/IR/Intrinsics.h" 8 | #include "llvm/Support/CommandLine.h" 9 | #include "CryptoUtils.h" 10 | #include "SubstituteImpl.h" 11 | #include "Utils.h" 12 | 13 | using namespace llvm; 14 | 15 | #define DEBUG_TYPE "substitution" 16 | 17 | static cl::opt 18 | ObfTimes("sub_loop", 19 | cl::desc("Choose how many time the -sub pass loops on a function"), 20 | cl::value_desc("number of times"), cl::init(1), cl::Optional); 21 | static uint32_t ObfTimesTemp = 1; 22 | 23 | static cl::opt 24 | ObfProbRate("sub_prob", 25 | cl::desc("Choose the probability [%] each instructions will be " 26 | "obfuscated by the InstructionSubstitution pass"), 27 | cl::value_desc("probability rate"), cl::init(50), cl::Optional); 28 | static uint32_t ObfProbRateTemp = 50; 29 | 30 | // Stats 31 | STATISTIC(Add, "Add substitued"); 32 | STATISTIC(Sub, "Sub substitued"); 33 | STATISTIC(Mul, "Mul substitued"); 34 | // STATISTIC(Div, "Div substitued"); 35 | // STATISTIC(Rem, "Rem substitued"); 36 | // STATISTIC(Shi, "Shift substitued"); 37 | STATISTIC(And, "And substitued"); 38 | STATISTIC(Or, "Or substitued"); 39 | STATISTIC(Xor, "Xor substitued"); 40 | 41 | namespace { 42 | 43 | struct Substitution : public FunctionPass { 44 | static char ID; // Pass identification, replacement for typeid 45 | bool flag; 46 | Substitution(bool flag) : Substitution() { this->flag = flag; } 47 | Substitution() : FunctionPass(ID) { this->flag = true; } 48 | 49 | bool runOnFunction(Function &F) override { 50 | if (!toObfuscateUint32Option(&F, "sub_loop", &ObfTimesTemp)) 51 | ObfTimesTemp = ObfTimes; 52 | if (!toObfuscateUint32Option(&F, "sub_prob", &ObfProbRateTemp)) 53 | ObfProbRateTemp = ObfProbRate; 54 | 55 | // Check if the percentage is correct 56 | if (ObfTimesTemp <= 0) { 57 | errs() << "Substitution application number -sub_loop=x must be x > 0"; 58 | return false; 59 | } 60 | if (ObfProbRateTemp > 100) { 61 | errs() << "InstructionSubstitution application instruction percentage " 62 | "-sub_prob=x must be 0 < x <= 100"; 63 | return false; 64 | } 65 | 66 | Function *tmp = &F; 67 | // Do we obfuscate 68 | if (toObfuscate(flag, tmp, "sub")) { 69 | errs() << "Running Instruction Substitution On " << F.getName() << "\n"; 70 | substitute(tmp); 71 | return true; 72 | } 73 | 74 | return false; 75 | }; 76 | bool substitute(Function *f) { 77 | // Loop for the number of time we run the pass on the function 78 | uint32_t times = ObfTimesTemp; 79 | do { 80 | for (Instruction &inst : instructions(f)) 81 | if (inst.isBinaryOp() && 82 | cryptoutils->get_range(100) <= ObfProbRateTemp) { 83 | switch (inst.getOpcode()) { 84 | case BinaryOperator::Add: 85 | // case BinaryOperator::FAdd: 86 | SubstituteImpl::substituteAdd(cast(&inst)); 87 | ++Add; 88 | break; 89 | case BinaryOperator::Sub: 90 | // case BinaryOperator::FSub: 91 | SubstituteImpl::substituteSub(cast(&inst)); 92 | ++Sub; 93 | break; 94 | case BinaryOperator::Mul: 95 | // case BinaryOperator::FMul: 96 | SubstituteImpl::substituteMul(cast(&inst)); 97 | ++Mul; 98 | break; 99 | case BinaryOperator::UDiv: 100 | case BinaryOperator::SDiv: 101 | case BinaryOperator::FDiv: 102 | //++Div; 103 | break; 104 | case BinaryOperator::URem: 105 | case BinaryOperator::SRem: 106 | case BinaryOperator::FRem: 107 | //++Rem; 108 | break; 109 | case Instruction::Shl: 110 | //++Shi; 111 | break; 112 | case Instruction::LShr: 113 | //++Shi; 114 | break; 115 | case Instruction::AShr: 116 | //++Shi; 117 | break; 118 | case Instruction::And: 119 | SubstituteImpl::substituteAnd(cast(&inst)); 120 | ++And; 121 | break; 122 | case Instruction::Or: 123 | SubstituteImpl::substituteOr(cast(&inst)); 124 | ++Or; 125 | break; 126 | case Instruction::Xor: 127 | SubstituteImpl::substituteXor(cast(&inst)); 128 | ++Xor; 129 | break; 130 | default: 131 | break; 132 | } // End switch 133 | } // End isBinaryOp 134 | } while (--times); // for times 135 | return true; 136 | } 137 | }; 138 | } // namespace 139 | 140 | char Substitution::ID = 0; 141 | INITIALIZE_PASS(Substitution, "subobf", "Enable Instruction Substitution.", 142 | false, false) 143 | FunctionPass *llvm::createSubstitutionPass(bool flag) { 144 | return new Substitution(flag); 145 | } 146 | -------------------------------------------------------------------------------- /hikari/Substitution.h: -------------------------------------------------------------------------------- 1 | //===- Substitution.h - Substitution Obfuscation 2 | // pass-------------------------===// 3 | // 4 | // The LLVM Compiler Infrastructure 5 | // 6 | // This file is distributed under the University of Illinois Open Source 7 | // License. See LICENSE.TXT for details. 8 | // 9 | //===----------------------------------------------------------------------===// 10 | // 11 | // This file contains includes and defines for the substitution pass 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | #ifndef _SUBSTITUTIONS_H_ 16 | #define _SUBSTITUTIONS_H_ 17 | 18 | #include "llvm/IR/PassManager.h" 19 | #include "llvm/Pass.h" 20 | 21 | namespace llvm { 22 | 23 | FunctionPass *createSubstitutionPass(bool flag); 24 | void initializeSubstitutionPass(PassRegistry &Registry); 25 | 26 | } // namespace llvm 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /hikari/Utils.cpp: -------------------------------------------------------------------------------- 1 | // For open-source license, please refer to 2 | // [License](https://github.com/HikariObfuscator/Hikari/wiki/License). 3 | //===----------------------------------------------------------------------===// 4 | #include "Utils.h" 5 | #include "llvm/IR/IRBuilder.h" 6 | #include "llvm/IR/InstIterator.h" 7 | #include "llvm/IR/IntrinsicInst.h" 8 | #include "llvm/IR/MDBuilder.h" 9 | #include "llvm/IR/Module.h" 10 | #include "llvm/IR/NoFolder.h" 11 | #include "llvm/Support/raw_ostream.h" 12 | #include "llvm/Transforms/Utils/Local.h" 13 | #include 14 | #include 15 | 16 | using namespace llvm; 17 | 18 | namespace llvm { 19 | 20 | // Shamefully borrowed from ../Scalar/RegToMem.cpp :( 21 | bool valueEscapes(Instruction *Inst) { 22 | BasicBlock *BB = Inst->getParent(); 23 | for (Value::use_iterator UI = Inst->use_begin(), E = Inst->use_end(); UI != E; 24 | ++UI) { 25 | Instruction *I = cast(*UI); 26 | if (I->getParent() != BB || isa(I)) { 27 | return true; 28 | } 29 | } 30 | return false; 31 | } 32 | 33 | void fixStack(Function *f) { 34 | // Try to remove phi node and demote reg to stack 35 | SmallVector tmpPhi; 36 | SmallVector tmpReg; 37 | BasicBlock *bbEntry = &*f->begin(); 38 | // Find first non-alloca instruction and create insertion point. This is 39 | // safe if block is well-formed: it always have terminator, otherwise 40 | // we'll get and assertion. 41 | BasicBlock::iterator I = bbEntry->begin(); 42 | while (isa(I)) 43 | ++I; 44 | Instruction *AllocaInsertionPoint = &*I; 45 | do { 46 | tmpPhi.clear(); 47 | tmpReg.clear(); 48 | for (BasicBlock &i : *f) { 49 | for (Instruction &j : i) { 50 | if (isa(&j)) { 51 | PHINode *phi = cast(&j); 52 | tmpPhi.emplace_back(phi); 53 | continue; 54 | } 55 | if (!(isa(&j) && j.getParent() == bbEntry) && 56 | (valueEscapes(&j) || j.isUsedOutsideOfBlock(&i))) { 57 | tmpReg.emplace_back(&j); 58 | continue; 59 | } 60 | } 61 | } 62 | #if LLVM_VERSION_MAJOR >= 19 63 | for (Instruction *I : tmpReg) 64 | DemoteRegToStack(*I, false, AllocaInsertionPoint->getIterator()); 65 | for (PHINode *P : tmpPhi) 66 | DemotePHIToStack(P, AllocaInsertionPoint->getIterator()); 67 | #else 68 | for (Instruction *I : tmpReg) 69 | DemoteRegToStack(*I, false, AllocaInsertionPoint); 70 | for (PHINode *P : tmpPhi) 71 | DemotePHIToStack(P, AllocaInsertionPoint); 72 | #endif 73 | } while (tmpReg.size() != 0 || tmpPhi.size() != 0); 74 | } 75 | 76 | // Unlike O-LLVM which uses __attribute__ that is not supported by the ObjC 77 | // CFE. We use a dummy call here and remove the call later Very dumb and 78 | // definitely slower than the function attribute method Merely a hack 79 | bool readFlag(Function *f, std::string attribute) { 80 | for (Instruction &I : instructions(f)) { 81 | Instruction *Inst = &I; 82 | if (CallInst *CI = dyn_cast(Inst)) { 83 | if (CI->getCalledFunction() != nullptr && 84 | #if LLVM_VERSION_MAJOR >= 18 85 | CI->getCalledFunction()->getName().starts_with("hikari_" + 86 | attribute)) { 87 | #else 88 | CI->getCalledFunction()->getName().startswith("hikari_" + 89 | attribute)) { 90 | #endif 91 | CI->eraseFromParent(); 92 | return true; 93 | } 94 | } 95 | if (InvokeInst *II = dyn_cast(Inst)) { 96 | if (II->getCalledFunction() != nullptr && 97 | #if LLVM_VERSION_MAJOR >= 18 98 | II->getCalledFunction()->getName().starts_with("hikari_" + 99 | attribute)) { 100 | #else 101 | II->getCalledFunction()->getName().startswith("hikari_" + 102 | attribute)) { 103 | #endif 104 | BasicBlock *normalDest = II->getNormalDest(); 105 | BasicBlock *unwindDest = II->getUnwindDest(); 106 | BasicBlock *parent = II->getParent(); 107 | if (parent->size() == 1) { 108 | parent->replaceAllUsesWith(normalDest); 109 | II->eraseFromParent(); 110 | parent->eraseFromParent(); 111 | } else { 112 | BranchInst::Create(normalDest, II); 113 | II->eraseFromParent(); 114 | } 115 | if (pred_size(unwindDest) == 0) 116 | unwindDest->eraseFromParent(); 117 | return true; 118 | } 119 | } 120 | } 121 | return false; 122 | } 123 | 124 | bool toObfuscate(bool flag, Function *f, std::string attribute) { 125 | // Check if declaration and external linkage 126 | if (f->isDeclaration() || f->hasAvailableExternallyLinkage()) { 127 | return false; 128 | } 129 | std::string attr = attribute; 130 | std::string attrNo = "no" + attr; 131 | if (readAnnotationMetadata(f, attrNo) || readFlag(f, attrNo)) { 132 | return false; 133 | } 134 | if (readAnnotationMetadata(f, attr) || readFlag(f, attr)) { 135 | return true; 136 | } 137 | return flag; 138 | } 139 | 140 | bool toObfuscateBoolOption(Function *f, std::string option, bool *val) { 141 | std::string opt = option; 142 | std::string optDisable = "no" + option; 143 | if (readAnnotationMetadata(f, optDisable) || readFlag(f, optDisable)) { 144 | *val = false; 145 | return true; 146 | } 147 | if (readAnnotationMetadata(f, opt) || readFlag(f, opt)) { 148 | *val = true; 149 | return true; 150 | } 151 | return false; 152 | } 153 | 154 | static const char obfkindid[] = "MD_obf"; 155 | 156 | bool readAnnotationMetadataUint32OptVal(Function *f, std::string opt, 157 | uint32_t *val) { 158 | MDNode *Existing = f->getMetadata(obfkindid); 159 | if (Existing) { 160 | MDTuple *Tuple = cast(Existing); 161 | for (auto &N : Tuple->operands()) { 162 | StringRef mdstr = cast(N.get())->getString(); 163 | std::string estr = opt + "="; 164 | #if LLVM_VERSION_MAJOR >= 18 165 | if (mdstr.starts_with(estr)) { 166 | #else 167 | if (mdstr.startswith(estr)) { 168 | #endif 169 | *val = atoi(mdstr.substr(strlen(estr.c_str())).str().c_str()); 170 | return true; 171 | } 172 | } 173 | } 174 | return false; 175 | } 176 | 177 | bool readFlagUint32OptVal(Function *f, std::string opt, uint32_t *val) { 178 | for (Instruction &I : instructions(f)) { 179 | Instruction *Inst = &I; 180 | if (CallInst *CI = dyn_cast(Inst)) { 181 | if (CI->getCalledFunction() != nullptr && 182 | #if LLVM_VERSION_MAJOR >= 18 183 | CI->getCalledFunction()->getName().starts_with("hikari_" + opt)) { 184 | #else 185 | CI->getCalledFunction()->getName().startswith("hikari_" + opt)) { 186 | #endif 187 | if (ConstantInt *C = dyn_cast(CI->getArgOperand(0))) { 188 | *val = (uint32_t)C->getValue().getZExtValue(); 189 | CI->eraseFromParent(); 190 | return true; 191 | } 192 | } 193 | } 194 | if (InvokeInst *II = dyn_cast(Inst)) { 195 | if (II->getCalledFunction() != nullptr && 196 | #if LLVM_VERSION_MAJOR >= 18 197 | II->getCalledFunction()->getName().starts_with("hikari_" + opt)) { 198 | #else 199 | II->getCalledFunction()->getName().startswith("hikari_" + opt)) { 200 | #endif 201 | if (ConstantInt *C = dyn_cast(II->getArgOperand(0))) { 202 | *val = (uint32_t)C->getValue().getZExtValue(); 203 | BasicBlock *normalDest = II->getNormalDest(); 204 | BasicBlock *unwindDest = II->getUnwindDest(); 205 | BasicBlock *parent = II->getParent(); 206 | if (parent->size() == 1) { 207 | parent->replaceAllUsesWith(normalDest); 208 | II->eraseFromParent(); 209 | parent->eraseFromParent(); 210 | } else { 211 | BranchInst::Create(normalDest, II); 212 | II->eraseFromParent(); 213 | } 214 | if (pred_size(unwindDest) == 0) 215 | unwindDest->eraseFromParent(); 216 | return true; 217 | } 218 | } 219 | } 220 | } 221 | return false; 222 | } 223 | 224 | bool toObfuscateUint32Option(Function *f, std::string option, uint32_t *val) { 225 | if (readAnnotationMetadataUint32OptVal(f, option, val) || 226 | readFlagUint32OptVal(f, option, val)) 227 | return true; 228 | return false; 229 | } 230 | 231 | bool hasApplePtrauth(Module *M) { 232 | for (GlobalVariable &GV : M->globals()) 233 | if (GV.getSection() == "llvm.ptrauth") 234 | return true; 235 | return false; 236 | } 237 | 238 | void FixBasicBlockConstantExpr(BasicBlock *BB) { 239 | // Replace ConstantExpr with equal instructions 240 | // Otherwise replacing on Constant will crash the compiler 241 | // Things to note: 242 | // - Phis must be placed at BB start so CEs must be placed prior to current BB 243 | assert(!BB->empty() && "BasicBlock is empty!"); 244 | assert(BB->getParent() && "BasicBlock must be in a Function!"); 245 | Instruction *FunctionInsertPt = 246 | &*(BB->getParent()->getEntryBlock().getFirstInsertionPt()); 247 | 248 | for (Instruction &I : *BB) { 249 | if (isa(I) || isa(I) || 250 | isa(I)) 251 | continue; 252 | for (unsigned int i = 0; i < I.getNumOperands(); i++) 253 | if (ConstantExpr *C = dyn_cast(I.getOperand(i))) { 254 | IRBuilder IRB(&I); 255 | if (isa(I)) 256 | IRB.SetInsertPoint(FunctionInsertPt); 257 | Instruction *Inst = IRB.Insert(C->getAsInstruction()); 258 | I.setOperand(i, Inst); 259 | } 260 | } 261 | } 262 | 263 | void FixFunctionConstantExpr(Function *Func) { 264 | // Replace ConstantExpr with equal instructions 265 | // Otherwise replacing on Constant will crash the compiler 266 | for (BasicBlock &BB : *Func) 267 | FixBasicBlockConstantExpr(&BB); 268 | } 269 | 270 | void turnOffOptimization(Function *f) { 271 | f->removeFnAttr(Attribute::AttrKind::MinSize); 272 | f->removeFnAttr(Attribute::AttrKind::OptimizeForSize); 273 | if (!f->hasFnAttribute(Attribute::AttrKind::OptimizeNone) && 274 | !f->hasFnAttribute(Attribute::AttrKind::AlwaysInline)) { 275 | f->addFnAttr(Attribute::AttrKind::OptimizeNone); 276 | f->addFnAttr(Attribute::AttrKind::NoInline); 277 | } 278 | } 279 | 280 | static inline std::vector splitString(std::string str) { 281 | std::stringstream ss(str); 282 | std::string word; 283 | std::vector words; 284 | while (ss >> word) 285 | words.emplace_back(word); 286 | return words; 287 | } 288 | 289 | void annotation2Metadata(Module &M) { 290 | GlobalVariable *Annotations = M.getGlobalVariable("llvm.global.annotations"); 291 | if (!Annotations) 292 | return; 293 | auto *C = dyn_cast(Annotations->getInitializer()); 294 | if (!C) 295 | return; 296 | for (unsigned int i = 0; i < C->getNumOperands(); i++) 297 | if (ConstantStruct *CS = dyn_cast(C->getOperand(i))) { 298 | GlobalValue *StrC = 299 | dyn_cast(CS->getOperand(1)->stripPointerCasts()); 300 | if (!StrC) 301 | continue; 302 | ConstantDataSequential *StrData = 303 | dyn_cast(StrC->getOperand(0)); 304 | if (!StrData) 305 | continue; 306 | Function *Fn = dyn_cast(CS->getOperand(0)->stripPointerCasts()); 307 | if (!Fn) 308 | continue; 309 | 310 | // Add annotation to the function. 311 | std::vector strs = 312 | splitString(StrData->getAsCString().str()); 313 | for (std::string str : strs) 314 | writeAnnotationMetadata(Fn, str); 315 | } 316 | } 317 | 318 | bool readAnnotationMetadata(Function *f, std::string annotation) { 319 | MDNode *Existing = f->getMetadata(obfkindid); 320 | if (Existing) { 321 | MDTuple *Tuple = cast(Existing); 322 | for (auto &N : Tuple->operands()) 323 | if (cast(N.get())->getString() == annotation) 324 | return true; 325 | } 326 | return false; 327 | } 328 | 329 | void writeAnnotationMetadata(Function *f, std::string annotation) { 330 | LLVMContext &Context = f->getContext(); 331 | MDBuilder MDB(Context); 332 | 333 | MDNode *Existing = f->getMetadata(obfkindid); 334 | SmallVector Names; 335 | bool AppendName = true; 336 | if (Existing) { 337 | MDTuple *Tuple = cast(Existing); 338 | for (auto &N : Tuple->operands()) { 339 | if (cast(N.get())->getString() == annotation) 340 | AppendName = false; 341 | Names.emplace_back(N.get()); 342 | } 343 | } 344 | if (AppendName) 345 | Names.emplace_back(MDB.createString(annotation)); 346 | 347 | MDNode *MD = MDTuple::get(Context, Names); 348 | f->setMetadata(obfkindid, MD); 349 | } 350 | 351 | bool AreUsersInOneFunction(GlobalVariable *GV) { 352 | SmallPtrSet userFunctions; 353 | for (User *U : GV->users()) { 354 | if (Instruction *I = dyn_cast(U)) { 355 | userFunctions.insert(I->getFunction()); 356 | } else if (ConstantExpr *CE = dyn_cast(U)) { 357 | for (User *U2 : CE->users()) { 358 | if (Instruction *I = dyn_cast(U2)) { 359 | userFunctions.insert(I->getFunction()); 360 | } 361 | } 362 | } else { 363 | return false; 364 | } 365 | } 366 | return userFunctions.size() <= 1; 367 | } 368 | 369 | #if 0 370 | std::map BuildAnnotateMap(Module &M) { 371 | std::map VAMap; 372 | GlobalVariable *glob = M.getGlobalVariable("llvm.global.annotations"); 373 | if (glob != nullptr && glob->hasInitializer()) { 374 | ConstantArray *CDA = cast(glob->getInitializer()); 375 | for (Value *op : CDA->operands()) { 376 | ConstantStruct *anStruct = cast(op); 377 | /* 378 | Structure: [Value,Annotation,SourceFilePath,LineNumber] 379 | Usually wrapped inside GEP/BitCast 380 | We only care about Value and Annotation Here 381 | */ 382 | GlobalValue *Value = 383 | cast(anStruct->getOperand(0)->getOperand(0)); 384 | GlobalVariable *Annotation = 385 | cast(anStruct->getOperand(1)->getOperand(0)); 386 | if (Annotation->hasInitializer()) { 387 | VAMap[Value] = 388 | cast(Annotation->getInitializer()) 389 | ->getAsCString(); 390 | } 391 | } 392 | } 393 | return VAMap; 394 | } 395 | #endif 396 | 397 | } // namespace llvm 398 | -------------------------------------------------------------------------------- /hikari/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H_ 2 | #define _UTILS_H_ 3 | 4 | #include "llvm/IR/Module.h" 5 | #include 6 | 7 | namespace llvm { 8 | 9 | void fixStack(Function *f); 10 | bool toObfuscate(bool flag, Function *f, std::string attribute); 11 | bool toObfuscateBoolOption(Function *f, std::string option, bool *val); 12 | bool toObfuscateUint32Option(Function *f, std::string option, uint32_t *val); 13 | bool hasApplePtrauth(Module *M); 14 | void FixFunctionConstantExpr(Function *Func); 15 | void turnOffOptimization(Function *f); 16 | void annotation2Metadata(Module &M); 17 | bool readAnnotationMetadata(Function *f, std::string annotation); 18 | void writeAnnotationMetadata(Function *f, std::string annotation); 19 | bool AreUsersInOneFunction(GlobalVariable *GV); 20 | #if 0 21 | std::map BuildAnnotateMap(Module& M); 22 | #endif 23 | 24 | } // namespace llvm 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /hikari/diff.md: -------------------------------------------------------------------------------- 1 | llvm/include/llvm/InitializePasses.h: 2 | void initializeObfuscationPass(PassRegistry&); 3 | llvm/include/llvm/LinkAllPasses.h: 4 | (void)llvm::createObfuscationLegacyPass() 5 | llvm/lib/Passes/PassBuilderPipelines.cpp 6 | if (!LTOPreLink) 7 | MPM.addPass(ObfuscationPass()); 8 | MPM.addPass(ObfuscationPass()); 9 | MPM.addPass(ObfuscationPass()); 10 | MPM.addPass(ObfuscationPass()); 11 | MPM.addPass(ObfuscationPass()); 12 | MPM.addPass(ObfuscationPass()); 13 | if (!LTOPreLink) 14 | MPM.addPass(ObfuscationPass()); 15 | llvm/lib/Passes/CMakeLists.txt: 16 | Obfuscation 17 | llvm/lib/Transforms/CMakeLists.txt: 18 | add_subdirectory(Obfuscation) 19 | 20 | --------------------------------------------------------------------------------