├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── include └── Limoncello │ ├── Config │ ├── Config.h │ ├── Pass │ │ ├── ArithmeticMangler.h │ │ ├── Bloater.h │ │ ├── ConstantMangler.h │ │ ├── Flattener.h │ │ └── StringObfuscator.h │ └── PassConfig.h │ ├── Pass │ ├── ArithmeticMangler.h │ ├── Bloater.h │ ├── ConstantMangler.h │ ├── Flattener.h │ └── StringObfuscator.h │ └── Support │ ├── Function.h │ ├── Module.h │ └── Random.h ├── lib ├── Config │ ├── Config.cpp │ └── PassConfig.cpp ├── Pass │ ├── ArithmeticMangler.cpp │ ├── Bloater.cpp │ ├── ConstantMangler.cpp │ ├── Flattener.cpp │ ├── StringObfuscator.cpp │ └── StringObfuscatorBitcode.h └── Support │ ├── Function.cpp │ ├── Module.cpp │ └── Random.cpp ├── src └── Plugin.cpp └── test ├── BuildSamples.py ├── Configs ├── ArithmeticMangler.yml ├── Bloater.yml ├── ConstantMangler.yml ├── Everything.yml ├── Flattener.yml ├── FlattenerRandomIDs.yml ├── None.yml └── StringObfuscator.yml └── Samples ├── ArithmeticBonanza.c ├── ConstantPaloozaRedux.c ├── DoubleSwitch.c ├── Hello.c ├── NumberClassifier.c ├── SayHello.c ├── SayHelloLibrary.c └── SimpleBlocks.c /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | ... 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/Samples/Output/ 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(Limoncello LANGUAGES C CXX) 4 | 5 | find_package(LLVM 17 REQUIRED CONFIG) 6 | include(AddLLVM) 7 | 8 | # TODO: Create proper sub-libraries and remove globbing & filtering. 9 | file(GLOB_RECURSE LIMONCELLO_CORE_SOURCE include/*.h lib/*.h lib/*.cpp) 10 | 11 | add_llvm_library(LimoncelloCore ${LIMONCELLO_CORE_SOURCE}) 12 | target_compile_features(LimoncelloCore PRIVATE cxx_std_20) 13 | target_compile_definitions(LimoncelloCore PRIVATE ${LLVM_DEFINITIONS}) 14 | target_include_directories(LimoncelloCore PUBLIC include ${LLVM_INCLUDE_DIRS}) 15 | 16 | add_llvm_library(Limoncello MODULE src/Plugin.cpp PLUGIN_TOOL opt) 17 | target_link_libraries(Limoncello PRIVATE LimoncelloCore) 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Jon Palmisciano. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Limoncello 2 | 3 | > Yet another LLVM-based obfuscation pipeline. 4 | 5 | **This is an incomplete project from 2023.** I've decided to release it almost 6 | a year later as I have not worked on it in months, and others might find the 7 | source interesting or helpful to browse. Feel free to reach out if you have any 8 | questions about how it works, but support, etc. will not be provided and I 9 | don't have any intent to continue working on this project publicly. 10 | 11 | ## License 12 | 13 | Copyright © 2023 Jon Palmisciano. All rights reserved. 14 | 15 | Use of Limoncello and its source code is governed by the BSD 3-Clause license; 16 | the full terms of the license can be found in [LICENSE.txt](LICENSE.txt). 17 | -------------------------------------------------------------------------------- /include/Limoncello/Config/Config.h: -------------------------------------------------------------------------------- 1 | //===-- Config/Config.h - Top-level configuration structure ---------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_CONFIG_CONFIG_H 11 | #define LIMONCELLO_CONFIG_CONFIG_H 12 | 13 | #include "Limoncello/Config/Pass/ArithmeticMangler.h" 14 | #include "Limoncello/Config/Pass/Bloater.h" 15 | #include "Limoncello/Config/Pass/ConstantMangler.h" 16 | #include "Limoncello/Config/Pass/Flattener.h" 17 | #include "Limoncello/Config/Pass/StringObfuscator.h" 18 | 19 | /// Top-level obfuscator configuration structure. 20 | class Config { 21 | /// Create the default configuration. 22 | Config(); 23 | 24 | /// Load a config from the file at \p path. 25 | /// 26 | /// Will return an invalid config if \p path is a file that does not exist, 27 | /// or if the file at \p path fails to parse as configuration YAML. 28 | explicit Config(std::string const &path); 29 | 30 | /// Update the current config by loading values from YAML string \p yaml. 31 | void loadFromYAML(std::string const &yaml); 32 | 33 | public: 34 | bool isValid; 35 | 36 | /// Controls obfuscator output, including presence of symbol names, etc. 37 | bool debug; 38 | 39 | /// Seed for the obfuscator's RNG; random if not provided. 40 | unsigned seed; 41 | 42 | ArithmeticManglerConfig arithmeticMangler; 43 | BloaterConfig bloater; 44 | ConstantManglerConfig constantMangler; 45 | FlattenerConfig flattener; 46 | StringObfuscatorConfig stringObfuscator; 47 | 48 | /// Load the global config from \p path. 49 | static Config *load(std::string path = ""); 50 | 51 | /// Get the global configuration. 52 | static Config *get(); 53 | 54 | /// Get the default linkage for Limoncello-related symbols and functions. 55 | llvm::GlobalValue::LinkageTypes getDefaultLinkage() const { 56 | return debug ? llvm::GlobalValue::InternalLinkage 57 | : llvm::GlobalValue::PrivateLinkage; 58 | } 59 | }; 60 | 61 | template <> struct llvm::yaml::MappingTraits { 62 | static void mapping(llvm::yaml::IO &io, Config &config) { 63 | io.mapOptional("debug", config.debug); 64 | io.mapOptional("seed", config.seed); 65 | 66 | io.mapOptional("arithmetic-mangler", config.arithmeticMangler); 67 | io.mapOptional("bloater", config.bloater); 68 | io.mapOptional("constant-mangler", config.constantMangler); 69 | io.mapOptional("flattener", config.flattener); 70 | io.mapOptional("string-obfuscator", config.stringObfuscator); 71 | } 72 | }; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /include/Limoncello/Config/Pass/ArithmeticMangler.h: -------------------------------------------------------------------------------- 1 | //===-- Config/Pass/ArithmeticMangler.h - Arithmetic mangler config -------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_CONFIG_PASS_ARITHMETICMANGLER_H 11 | #define LIMONCELLO_CONFIG_PASS_ARITHMETICMANGLER_H 12 | 13 | #include "Limoncello/Config/PassConfig.h" 14 | 15 | class ArithmeticManglerConfig : public PassConfig { 16 | public: 17 | int rounds = 2; 18 | }; 19 | 20 | template <> struct llvm::yaml::MappingTraits { 21 | static void mapping(IO &io, ArithmeticManglerConfig &config) { 22 | io.mapOptional("enabled", config.isEnabled); 23 | io.mapOptional("patterns", config.patterns); 24 | 25 | io.mapOptional("rounds", config.rounds); 26 | } 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/Limoncello/Config/Pass/Bloater.h: -------------------------------------------------------------------------------- 1 | //===-- Config/Pass/Bloater.h - Bloater config ----------------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_CONFIG_PASS_BLOATER_H 11 | #define LIMONCELLO_CONFIG_PASS_BLOATER_H 12 | 13 | #include "Limoncello/Config/PassConfig.h" 14 | 15 | class BloaterConfig : public PassConfig { 16 | public: 17 | int rounds = 1; 18 | int probability = 50; 19 | }; 20 | 21 | template <> struct llvm::yaml::MappingTraits { 22 | static void mapping(IO &io, BloaterConfig &config) { 23 | io.mapOptional("enabled", config.isEnabled); 24 | io.mapOptional("patterns", config.patterns); 25 | 26 | io.mapOptional("rounds", config.rounds); 27 | io.mapOptional("probability", config.probability); 28 | } 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/Limoncello/Config/Pass/ConstantMangler.h: -------------------------------------------------------------------------------- 1 | //===-- Config/Pass/ConstantMangler.h - Constant mangler config -----------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_CONFIG_PASS_CONSTANTMANGLER_H 11 | #define LIMONCELLO_CONFIG_PASS_CONSTANTMANGLER_H 12 | 13 | #include "Limoncello/Config/PassConfig.h" 14 | 15 | class ConstantManglerConfig : public PassConfig {}; 16 | 17 | template <> struct llvm::yaml::MappingTraits { 18 | static void mapping(IO &io, ConstantManglerConfig &config) { 19 | io.mapOptional("enabled", config.isEnabled); 20 | io.mapOptional("patterns", config.patterns); 21 | } 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/Limoncello/Config/Pass/Flattener.h: -------------------------------------------------------------------------------- 1 | //===-- Config/Pass/Flattener.h - Control flow flattener config -----------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_CONFIG_PASS_FLATTENER_H 11 | #define LIMONCELLO_CONFIG_PASS_FLATTENER_H 12 | 13 | #include "Limoncello/Config/PassConfig.h" 14 | 15 | class FlattenerConfig : public PassConfig { 16 | public: 17 | bool useRandomCaseIds = false; 18 | int maxRandomCases = 768; 19 | }; 20 | 21 | template <> struct llvm::yaml::MappingTraits { 22 | static void mapping(IO &io, FlattenerConfig &config) { 23 | io.mapOptional("enabled", config.isEnabled); 24 | io.mapOptional("patterns", config.patterns); 25 | 26 | io.mapOptional("random-case-ids", config.useRandomCaseIds); 27 | io.mapOptional("max-random-cases", config.maxRandomCases); 28 | } 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/Limoncello/Config/Pass/StringObfuscator.h: -------------------------------------------------------------------------------- 1 | //===-- Config/Pass/StringObfuscator.h - String obfuscator config ---------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_CONFIG_PASS_STRINGOBFUSCATOR_H 11 | #define LIMONCELLO_CONFIG_PASS_STRINGOBFUSCATOR_H 12 | 13 | #include "Limoncello/Config/PassConfig.h" 14 | 15 | class StringObfuscatorConfig : public PassConfig {}; 16 | 17 | template <> struct llvm::yaml::MappingTraits { 18 | static void mapping(IO &io, StringObfuscatorConfig &config) { 19 | io.mapOptional("enabled", config.isEnabled); 20 | io.mapOptional("patterns", config.patterns); 21 | } 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/Limoncello/Config/PassConfig.h: -------------------------------------------------------------------------------- 1 | //===-- Config/PassConfig.h - Base pass configuration structure -----------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_CONFIG_PASSCONFIG_H 11 | #define LIMONCELLO_CONFIG_PASSCONFIG_H 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | /// Base pass configuration structure. 19 | class PassConfig { 20 | public: 21 | bool isEnabled = false; 22 | std::vector patterns{}; 23 | 24 | /// Tells whether \p function is matched by \p patterns. 25 | bool shouldRunOnFunction(llvm::Function const &function) const; 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/Limoncello/Pass/ArithmeticMangler.h: -------------------------------------------------------------------------------- 1 | //===-- Pass/ArithmeticMangler.h - Arithmetic-mangling pass ---------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_PASS_ARITHMETICMANGLER_H 11 | #define LIMONCELLO_PASS_ARITHMETICMANGLER_H 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /// Instruction visitor which mangles arithmetic expressions. 19 | /// 20 | /// The visit functions will return a new (top-level) instruction if the 21 | /// instruction visited can be replaced with a MBA expression; other 22 | /// instructions will have already been created and parented to the returned 23 | /// instruction in this case. The caller is responsible for replacing the 24 | /// originally visited instruction with the returned result. 25 | /// 26 | /// This is overwhelmingly NOT read-only and should be used with care. It WILL 27 | /// mess with the IR and will NOT clean up after itself! 28 | class ManglingVisitor 29 | : public llvm::InstVisitor { 30 | /// Internal IR builder. 31 | /// 32 | /// It is critically important that it is of the "NoFolder" variety as LLVM 33 | /// is capable of simplifying some MBA expressions on its own before the 34 | /// optimizer is even invoked. 35 | llvm::IRBuilder m_builder; 36 | 37 | public: 38 | explicit ManglingVisitor(llvm::BasicBlock *block); 39 | 40 | /// Set the insertion point of the internal IR builder. 41 | /// 42 | /// This should be called every time a new instruction is inspected. 43 | void setInsertPoint(llvm::Instruction *inst); 44 | 45 | llvm::Instruction *visitAdd(llvm::BinaryOperator &addOp); 46 | llvm::Instruction *visitSub(llvm::BinaryOperator &subOp); 47 | llvm::Instruction *visitAnd(llvm::BinaryOperator &andOp); 48 | llvm::Instruction *visitOr(llvm::BinaryOperator &orOp); 49 | llvm::Instruction *visitXor(llvm::BinaryOperator &xorOp); 50 | 51 | static llvm::Instruction *visitInstruction(llvm::Instruction &) { 52 | return nullptr; 53 | } 54 | }; 55 | 56 | /// Pass for replacing normal arithmetic expressions with equivalent mixed 57 | /// boolean-arithmetic expressions. 58 | class ArithmeticManglerPass 59 | : public llvm::PassInfoMixin { 60 | 61 | /// Indicates if an operation can be mangled by this pass. 62 | static bool canMangleOperation(llvm::BinaryOperator const *op); 63 | 64 | /// Create a stub function to perform \p binaryOp between \p lhs and \p rhs. 65 | static llvm::Function *createBinaryOpStub(llvm::Module *module, 66 | llvm::BinaryOperator *binaryOp, 67 | llvm::Value *lhs, llvm::Value *rhs); 68 | 69 | /// Replace all (obfuscatable) arithmetic expressions in \p func with calls 70 | /// to generated mixed boolean-arithmetic stub functions. 71 | /// 72 | /// Any stub functions created will be inserted into \p stubs. 73 | static void insertStubs(llvm::Function &func, 74 | std::vector &stubs); 75 | 76 | public: 77 | static llvm::PreservedAnalyses run(llvm::Module &module, 78 | llvm::ModuleAnalysisManager &); 79 | static bool isRequired() { return true; } 80 | }; 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/Limoncello/Pass/Bloater.h: -------------------------------------------------------------------------------- 1 | //===-- Pass/Bloater.h - Function-bloating pass ---------------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_PASS_BLOATER_H 11 | #define LIMONCELLO_PASS_BLOATER_H 12 | 13 | #include 14 | 15 | /// Pass for performing function bloating. 16 | class BloaterPass : public llvm::PassInfoMixin { 17 | /// Get the opaque global variable used by the bloater. 18 | static llvm::Constant *getOpaqueGlobal(llvm::Module &module); 19 | 20 | /// Get the opaque "always true" function. 21 | static llvm::Function *getOpaqueTrueFunction(llvm::Module &module); 22 | 23 | /// Perform bloating on a function. Does NOT leave the function in a sound 24 | /// state, i.e. SSA repairs, etc. will still need to be done after. 25 | static void bloatFunction(llvm::Function &func); 26 | 27 | public: 28 | static llvm::PreservedAnalyses run(llvm::Module &module, 29 | llvm::ModuleAnalysisManager &); 30 | static bool isRequired() { return true; } 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/Limoncello/Pass/ConstantMangler.h: -------------------------------------------------------------------------------- 1 | //===-- Pass/ConstantMangler.h - Constant-mangling pass -------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_PASS_CONSTANTMANGLER_H 11 | #define LIMONCELLO_PASS_CONSTANTMANGLER_H 12 | 13 | #include 14 | 15 | class ConstantManglerPass : public llvm::PassInfoMixin { 16 | /// Indicates if an instruction can be descended into and have it's operands 17 | /// modified as part of this pass. 18 | static bool canMangleInstruction(llvm::Instruction const &insn); 19 | 20 | /// Mangle all of the constants in \p func. 21 | static bool mangleFunctionConstants(llvm::Function &func); 22 | 23 | public: 24 | static llvm::PreservedAnalyses run(llvm::Module &module, 25 | llvm::ModuleAnalysisManager &); 26 | static bool isRequired() { return true; } 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/Limoncello/Pass/Flattener.h: -------------------------------------------------------------------------------- 1 | //===-- Pass/Flattener.h - Control flow flattening pass -------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_PASS_FLATTENER_H 11 | #define LIMONCELLO_PASS_FLATTENER_H 12 | 13 | #include 14 | #include 15 | 16 | /// Intermediary "state machine" structure used to simplify the control flow 17 | /// flattening process. 18 | class StateMachine { 19 | llvm::BasicBlock *m_entryBlock = nullptr; 20 | llvm::SwitchInst *m_switchInst = nullptr; 21 | llvm::Value *m_stateVar = nullptr; 22 | llvm::BasicBlock *m_switchBlock = nullptr; 23 | llvm::BasicBlock *m_defaultBlock = nullptr; 24 | llvm::BasicBlock *m_endBlock = nullptr; 25 | 26 | static bool shouldIgnoreTerminator(llvm::Instruction *instruction); 27 | 28 | void rewriteBlock(llvm::BasicBlock *block); 29 | void rewriteBranch(llvm::BranchInst *branchInst); 30 | 31 | public: 32 | explicit StateMachine(llvm::BasicBlock *entryBlock); 33 | 34 | void addState(llvm::BasicBlock *block) const; 35 | void finalize(llvm::BasicBlock *firstBlock, 36 | llvm::SmallVector const &flatteningSet); 37 | }; 38 | 39 | using BlockPair = std::pair; 40 | 41 | /// Pass for performing control flow flattening. 42 | class FlattenerPass : public llvm::PassInfoMixin { 43 | /// Get the set of blocks in \p func which should be flattened. 44 | static llvm::SmallVector 45 | getFlatteningSet(llvm::Function &func); 46 | 47 | /// Remove the terminator from \p block, including the condition instruction 48 | /// if the terminator is a conditional branch. 49 | static BlockPair splitConditionalPart(llvm::BasicBlock *block); 50 | 51 | public: 52 | static llvm::PreservedAnalyses run(llvm::Function &func, 53 | llvm::FunctionAnalysisManager &); 54 | static bool isRequired() { return true; } 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /include/Limoncello/Pass/StringObfuscator.h: -------------------------------------------------------------------------------- 1 | //===-- Pass/StringObfuscator.h - String obfuscation pass -----------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_PASS_STRINGOBFUSCATOR_H 11 | #define LIMONCELLO_PASS_STRINGOBFUSCATOR_H 12 | 13 | #include 14 | 15 | /// Record of a string that has been obfuscated during this pass. 16 | class ObfuscatedStringRecord { 17 | public: 18 | /// Global variable associated with the string. 19 | llvm::GlobalVariable *handle; 20 | 21 | /// The size of the string in bytes. 22 | size_t size; 23 | 24 | /// The XOR key used to obfuscate the string. 25 | uint8_t key; 26 | 27 | // XXX: This exists purely to appease `clangd` when trying to construct these 28 | // in place during a call to `emplace_back`. 29 | ObfuscatedStringRecord(llvm::GlobalVariable *handle, size_t size, uint8_t key) 30 | : handle(handle), size(size), key(key) {} 31 | }; 32 | 33 | class StringObfuscatorPass : public llvm::PassInfoMixin { 34 | /// Get a local copy of the deobfuscation routine. 35 | /// 36 | /// Guaranteed to return a valid function (or panic). 37 | static llvm::Function *getDeobfuscateFunction(llvm::Module &module); 38 | 39 | /// Create a function to deobfuscate all strings in \p records. 40 | static llvm::Function *createDeobfuscateAllFunction( 41 | llvm::Module &module, std::vector const &records); 42 | 43 | /// Create a new strings constant with the obfuscated version of \p content 44 | /// by XOR-ing each byte with \p key. 45 | static llvm::Constant *createObfuscatedString(llvm::LLVMContext &context, 46 | llvm::StringRef content, 47 | uint8_t key); 48 | 49 | /// Obfuscate all globally-defined strings in \p module. 50 | static std::vector 51 | obfuscateStrings(llvm::Module &module); 52 | 53 | public: 54 | static llvm::PreservedAnalyses run(llvm::Module &module, 55 | llvm::ModuleAnalysisManager &); 56 | static bool isRequired() { return true; } 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /include/Limoncello/Support/Function.h: -------------------------------------------------------------------------------- 1 | //===-- Support/Function.h - Function-related helpers ---------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_SUPPORT_FUNCTION_H 11 | #define LIMONCELLO_SUPPORT_FUNCTION_H 12 | 13 | #include 14 | 15 | /// Make \p function local to \p module (optionally with a \p newName) by 16 | /// cloning it into a new function in \p module. 17 | /// 18 | /// The name of the existing function will be used if \p newName is empty. 19 | llvm::Function *localizeFunction(llvm::Module &module, llvm::Function &function, 20 | llvm::StringRef newName = ""); 21 | 22 | using BuildStubCallback = 23 | std::function &)>; 24 | 25 | /// Create a "stub function" which will always be inlined and never be 26 | /// optimized with the given \p name and \p type inside of \p module. 27 | /// 28 | /// The body of the function (expected to be one block) will be populated by 29 | /// the \p build callback. 30 | llvm::Function *createStubFunction(llvm::Module &module, llvm::StringRef name, 31 | llvm::FunctionType *type, 32 | BuildStubCallback build); 33 | 34 | /// Tells whether the value of \p inst escapes its parent block. 35 | bool valueEscapesLocalBlock(llvm::Instruction &value); 36 | 37 | /// Perform repairs to the IR for \p func as to not break SSA rules. 38 | void repairSSA(llvm::Function &func); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/Limoncello/Support/Module.h: -------------------------------------------------------------------------------- 1 | //===-- Support/Module.h - Module-related helpers -------------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_SUPPORT_MODULE_H 11 | #define LIMONCELLO_SUPPORT_MODULE_H 12 | 13 | #include 14 | 15 | /// Make \p name unique by "salting" it with a module-specific suffix. 16 | std::string getModuleSpecificName(llvm::Module &module, llvm::StringRef name); 17 | 18 | /// Create a new global variable inside \p module called \p name with the given 19 | /// initial \p value. 20 | llvm::Constant *getOrInsertGlobal(llvm::Module &module, llvm::StringRef name, 21 | llvm::Constant *value); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /include/Limoncello/Support/Random.h: -------------------------------------------------------------------------------- 1 | //===-- Support/Random.h - Random number generation shorthand(s) ----------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_SUPPORT_RANDOM_H 11 | #define LIMONCELLO_SUPPORT_RANDOM_H 12 | 13 | #include 14 | 15 | /// Seed the backing random number generator. 16 | void setRandomSeed(unsigned seed); 17 | 18 | /// Get a random 8-bit value. 19 | uint8_t getRandomInt8(); 20 | 21 | /// Get a random 32-bit value. 22 | uint32_t getRandomInt32(); 23 | 24 | /// Get a random 64-bit value. 25 | uint64_t getRandomInt64(); 26 | 27 | /// Get a random item from a container. 28 | template 29 | typename ContainerTy::value_type getRandomItem(ContainerTy container) { 30 | return container[getRandomInt64() % container.size()]; 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /lib/Config/Config.cpp: -------------------------------------------------------------------------------- 1 | //===-- Config/Config.cpp - Top-level configuration structure -------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Config/Config.h" 11 | 12 | #include 13 | #include 14 | 15 | using namespace llvm; 16 | 17 | Config::Config() : isValid(true), debug(false), seed(0) {} 18 | 19 | Config::Config(std::string const &path) : Config() { 20 | std::string yaml; 21 | try { 22 | std::ifstream inputFile(path); 23 | yaml = std::string(std::istreambuf_iterator(inputFile), 24 | std::istreambuf_iterator()); 25 | } catch (...) { 26 | isValid = false; 27 | return; 28 | } 29 | 30 | loadFromYAML(yaml); 31 | } 32 | 33 | void Config::loadFromYAML(std::string const &yaml) { 34 | yaml::Input yamlParser(yaml); 35 | yamlParser >> *this; 36 | 37 | if (yamlParser.error()) 38 | isValid = false; 39 | } 40 | 41 | static Config *g_config = nullptr; 42 | 43 | Config *Config::load(std::string path) { 44 | g_config = path.empty() ? new Config : new Config(path); 45 | return g_config; 46 | } 47 | 48 | Config *Config::get() { 49 | if (!g_config) 50 | g_config = new Config; 51 | 52 | return g_config; 53 | } 54 | -------------------------------------------------------------------------------- /lib/Config/PassConfig.cpp: -------------------------------------------------------------------------------- 1 | //===-- Config/PassConfig.cpp - Base pass configuration structure ---------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Config/PassConfig.h" 11 | 12 | #include 13 | 14 | using namespace llvm; 15 | 16 | bool PassConfig::shouldRunOnFunction(Function const &function) const { 17 | if (!isEnabled) 18 | return false; 19 | 20 | // If no patterns are specified, but the pass is nevertheless enabled, all 21 | // functions are assumed to be targeted. As soon as one pattern is given, 22 | // matching behavior will work as expected. 23 | if (patterns.empty()) 24 | return true; 25 | 26 | for (auto pattern : patterns) { 27 | // As a cheap hack, let patterns be negated by prepending a tilde; since 28 | // this isn't actually valid regex, we'll need to note that the pattern 29 | // should be negated, then erase the tilde. 30 | bool negate = false; 31 | if (pattern.starts_with("~")) { 32 | pattern.erase(0, 1); 33 | negate = true; 34 | } 35 | 36 | // TODO: Regexes should be checked for validity when the config is loaded 37 | // and errors should be reported then, rather than silently failing here. 38 | Regex regex(pattern); 39 | if (!regex.isValid()) 40 | continue; 41 | 42 | // If the regex matched, this is either a function we want to include, or a 43 | // function we want to exclude; in either of these cases, we have an 44 | // answer. It's important not to simply return the value of the match, 45 | // since other patterns may match this function even if this one does not. 46 | if (regex.match(function.getName())) 47 | return true && !negate; 48 | } 49 | 50 | return false; 51 | } 52 | -------------------------------------------------------------------------------- /lib/Pass/ArithmeticMangler.cpp: -------------------------------------------------------------------------------- 1 | //===-- Pass/ArithmeticMangler.cpp - Arithmetic-mangling pass -------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Pass/ArithmeticMangler.h" 11 | 12 | #include "Limoncello/Config/Config.h" 13 | #include "Limoncello/Support/Function.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace llvm; 22 | 23 | // TOOD: Replace this with template magic. 24 | #define MATCH(Op, PredicateFn, Lhs, Rhs) \ 25 | (PatternMatch::match(Op, PredicateFn(PatternMatch::m_Value(Lhs), \ 26 | PatternMatch::m_Value(Rhs)))) 27 | 28 | ManglingVisitor::ManglingVisitor(BasicBlock *block) 29 | : m_builder(IRBuilder(block)) {} 30 | 31 | void ManglingVisitor::setInsertPoint(Instruction *inst) { 32 | m_builder.SetInsertPoint(inst); 33 | } 34 | 35 | Instruction *ManglingVisitor::visitAdd(BinaryOperator &addOp) { 36 | Value *lhs, *rhs; 37 | if (!MATCH(&addOp, PatternMatch::m_Add, lhs, rhs)) { 38 | return nullptr; 39 | } 40 | 41 | return BinaryOperator::CreateAdd(m_builder.CreateAnd(lhs, rhs), 42 | m_builder.CreateOr(lhs, rhs)); 43 | } 44 | 45 | Instruction *ManglingVisitor::visitSub(BinaryOperator &subOp) { 46 | Value *lhs, *rhs; 47 | if (!MATCH(&subOp, PatternMatch::m_Sub, lhs, rhs)) { 48 | return nullptr; 49 | } 50 | 51 | return BinaryOperator::CreateAdd( 52 | m_builder.CreateXor(lhs, m_builder.CreateNeg(rhs)), 53 | m_builder.CreateMul(ConstantInt::get(lhs->getType(), 2), 54 | m_builder.CreateAnd(lhs, m_builder.CreateNeg(rhs)))); 55 | } 56 | 57 | Instruction *ManglingVisitor::visitAnd(BinaryOperator &andOp) { 58 | Value *lhs, *rhs; 59 | if (!MATCH(&andOp, PatternMatch::m_And, lhs, rhs)) { 60 | return nullptr; 61 | } 62 | 63 | return BinaryOperator::CreateSub(m_builder.CreateAdd(lhs, rhs), 64 | m_builder.CreateOr(lhs, rhs)); 65 | } 66 | 67 | Instruction *ManglingVisitor::visitOr(BinaryOperator &orOp) { 68 | Value *lhs, *rhs; 69 | if (!MATCH(&orOp, PatternMatch::m_Or, lhs, rhs)) { 70 | return nullptr; 71 | } 72 | 73 | return BinaryOperator::CreateAdd( 74 | m_builder.CreateAdd(m_builder.CreateAdd(lhs, rhs), 75 | ConstantInt::get(lhs->getType(), 1)), 76 | m_builder.CreateOr(m_builder.CreateNot(lhs), m_builder.CreateNot(rhs))); 77 | } 78 | 79 | Instruction *ManglingVisitor::visitXor(BinaryOperator &xorOp) { 80 | Value *lhs, *rhs; 81 | if (!MATCH(&xorOp, PatternMatch::m_Xor, lhs, rhs)) { 82 | return nullptr; 83 | } 84 | 85 | return BinaryOperator::CreateSub(m_builder.CreateOr(lhs, rhs), 86 | m_builder.CreateAnd(lhs, rhs)); 87 | } 88 | 89 | bool ArithmeticManglerPass::canMangleOperation(BinaryOperator const *op) { 90 | switch (op->getOpcode()) { 91 | case Instruction::Add: 92 | case Instruction::Sub: 93 | case Instruction::And: 94 | case Instruction::Or: 95 | case Instruction::Xor: 96 | return true; 97 | default: 98 | return false; 99 | } 100 | } 101 | 102 | Function *ArithmeticManglerPass::createBinaryOpStub(Module *module, 103 | BinaryOperator *op, 104 | Value *lhs, Value *rhs) { 105 | auto type = FunctionType::get(op->getType(), {lhs->getType(), rhs->getType()}, 106 | /*isVarArg=*/false); 107 | return createStubFunction(*module, "__lmcoArithmeticStub", type, 108 | [=](Function *f, IRBuilder<> &entryBuilder) { 109 | entryBuilder.CreateRet(entryBuilder.CreateBinOp( 110 | op->getOpcode(), f->getArg(0), f->getArg(1))); 111 | }); 112 | } 113 | 114 | void ArithmeticManglerPass::insertStubs(Function &func, 115 | std::vector &stubs) { 116 | SmallVector replacedInstructions; 117 | for (auto &inst : instructions(func)) { 118 | auto binaryOp = dyn_cast(&inst); 119 | if (!binaryOp || !canMangleOperation(binaryOp)) 120 | continue; 121 | 122 | auto lhs = binaryOp->getOperand(0); 123 | auto rhs = binaryOp->getOperand(1); 124 | auto stub = createBinaryOpStub(func.getParent(), binaryOp, lhs, rhs); 125 | 126 | IRBuilder<> builder(&inst); 127 | auto result = builder.CreateCall(stub->getFunctionType(), stub, {lhs, rhs}); 128 | inst.replaceAllUsesWith(result); 129 | replacedInstructions.emplace_back(&inst); 130 | 131 | stubs.emplace_back(stub); 132 | } 133 | 134 | for (auto inst : replacedInstructions) 135 | inst->eraseFromParent(); 136 | } 137 | 138 | PreservedAnalyses ArithmeticManglerPass::run(Module &module, 139 | ModuleAnalysisManager &) { 140 | auto config = Config::get(); 141 | 142 | // Since the MBA stub functions are created on-demand (and parented to the 143 | // current module) the module's function list cannot be used directly as the 144 | // iterator will become invalid. 145 | // 146 | // To avoid inevitable crashes due to invalid iterators (and to avoid 147 | // recursively processing MBA stubs), the original list of functions needs to 148 | // be backed up. 149 | std::vector originalFunctions; 150 | originalFunctions.reserve(module.getFunctionList().size()); 151 | std::transform( 152 | module.getFunctionList().begin(), module.getFunctionList().end(), 153 | std::back_inserter(originalFunctions), [](Function &f) { return &f; }); 154 | 155 | // Iterate through all of the module's functions (that match the filtering 156 | // criteria for the pass), replacing obfuscatable arithmetic expressions with 157 | // calls to MBA stub functions. 158 | std::vector stubFunctions; 159 | for (auto function : originalFunctions) { 160 | if (!config->arithmeticMangler.shouldRunOnFunction(*function)) 161 | continue; 162 | 163 | insertStubs(*function, stubFunctions); 164 | } 165 | 166 | // The stub functions created simply extracted the original arithmetic 167 | // expression into a separate function; MBA obfuscation has not been applied 168 | // yet. 169 | // 170 | // Each stub function needs to be processed and have MBA equivalents of its 171 | // operations inserted where appropriate. 172 | for (auto stub : stubFunctions) { 173 | for (int i = 0; i < config->arithmeticMangler.rounds; ++i) { 174 | for (auto &block : *stub) { 175 | ManglingVisitor visitor(&block); 176 | 177 | SmallVector replacedInstructions; 178 | for (auto &inst : block) { 179 | visitor.setInsertPoint(&inst); 180 | auto replacement = visitor.visit(inst); 181 | if (!replacement || replacement == &inst) 182 | continue; 183 | 184 | replacement->takeName(&inst); 185 | replacement->insertInto(&block, inst.getIterator()); 186 | inst.replaceAllUsesWith(replacement); 187 | replacedInstructions.push_back(&inst); 188 | } 189 | 190 | for (auto inst : replacedInstructions) 191 | inst->eraseFromParent(); 192 | } 193 | } 194 | } 195 | 196 | return stubFunctions.empty() ? PreservedAnalyses::none() 197 | : PreservedAnalyses::all(); 198 | } 199 | -------------------------------------------------------------------------------- /lib/Pass/Bloater.cpp: -------------------------------------------------------------------------------- 1 | //===-- Pass/Bloater.cpp - Function-bloating pass -------------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Pass/Bloater.h" 11 | 12 | #include "Limoncello/Config/Config.h" 13 | #include "Limoncello/Support/Function.h" 14 | #include "Limoncello/Support/Module.h" 15 | #include "Limoncello/Support/Random.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace llvm; 23 | 24 | /// Magic value returned from the "opaque true" function. 25 | static constexpr uint64_t BloaterMagic = 0x3636365f4f434d4c; 26 | static constexpr uint64_t BloaterMagicSafeMask = 0xfffffffffffff; 27 | 28 | /// Set of predicates that will always result in a truthy value when used to 29 | /// compare a random 32-bit number to the magic value using a comparison in the 30 | /// form of `magic random`. 31 | static constexpr std::array truePredicates = { 32 | CmpInst::Predicate::ICMP_NE, 33 | CmpInst::Predicate::ICMP_UGE, 34 | CmpInst::Predicate::ICMP_UGT, 35 | }; 36 | 37 | /// Set of predicates to use in comparisons that do not matter; these are used 38 | /// in blocks that are already unreachable (LLVM just can't prove it). 39 | static constexpr std::array fakePredicates = { 40 | CmpInst::Predicate::ICMP_SGE, CmpInst::Predicate::ICMP_SGT, 41 | CmpInst::Predicate::ICMP_SLE, CmpInst::Predicate::ICMP_SLT, 42 | CmpInst::Predicate::ICMP_UGE, CmpInst::Predicate::ICMP_UGT, 43 | CmpInst::Predicate::ICMP_ULE, CmpInst::Predicate::ICMP_ULT, 44 | }; 45 | 46 | static constexpr std::array bogusBinaryOps = { 47 | BinaryOperator::BinaryOps::Add, BinaryOperator::BinaryOps::Sub, 48 | BinaryOperator::BinaryOps::And, BinaryOperator::BinaryOps::Or, 49 | BinaryOperator::BinaryOps::Xor, 50 | }; 51 | 52 | constexpr auto OpaqueGlobalName = "__lmcoBloaterOpaqueGlobal"; 53 | 54 | Constant *BloaterPass::getOpaqueGlobal(Module &module) { 55 | return getOrInsertGlobal( 56 | module, OpaqueGlobalName, 57 | ConstantInt::get(Type::getInt64Ty(module.getContext()), 0)); 58 | } 59 | 60 | constexpr auto OpaqueTrueFunctionName = "__lmcoBloaterOpaqueTrue"; 61 | 62 | Function *BloaterPass::getOpaqueTrueFunction(Module &module) { 63 | auto callee = module.getOrInsertFunction( 64 | getModuleSpecificName(module, OpaqueTrueFunctionName), 65 | FunctionType::get(Type::getInt64Ty(module.getContext()), {})); 66 | auto func = cast(callee.getCallee()); 67 | 68 | // If we just created the function, it's body needs to be populated. 69 | if (func->empty()) { 70 | func->setLinkage(Config::get()->getDefaultLinkage()); 71 | func->addFnAttr(Attribute::OptimizeNone); 72 | func->addFnAttr(Attribute::NoInline); 73 | func->setCallingConv(CallingConv::C); 74 | 75 | IRBuilder<> builder(BasicBlock::Create(func->getContext(), "entry", func)); 76 | auto var = builder.CreateAlloca(builder.getInt64Ty()); 77 | builder.CreateStore(builder.getInt64(BloaterMagic), var); 78 | builder.CreateRet(builder.CreateOr( 79 | builder.CreateLoad(builder.getInt64Ty(), var), 80 | builder.CreateLoad(builder.getInt64Ty(), getOpaqueGlobal(module)))); 81 | } 82 | 83 | return func; 84 | } 85 | 86 | void BloaterPass::bloatFunction(llvm::Function &func) { 87 | auto config = Config::get(); 88 | auto &module = *func.getParent(); 89 | auto opaqueGlobal = getOpaqueGlobal(module); 90 | 91 | SmallVector bloatingSet; 92 | for (auto &block : func) { 93 | // We don't want the entry block in the bloating set because it creates the 94 | // potential for the garbage block to branch back to the entry block and 95 | // the entry block is forbidden from having predecessors. 96 | if (&block == &func.getEntryBlock()) 97 | continue; 98 | 99 | bloatingSet.emplace_back(&block); 100 | } 101 | 102 | for (auto block : bloatingSet) { 103 | auto term = block->getTerminator(); 104 | if (!term) 105 | continue; 106 | auto branch = dyn_cast(term); 107 | if (!branch || branch->isConditional()) 108 | continue; 109 | 110 | // Bloating every branch matching the conditions above might be a bit too 111 | // aggressive; roll a die and randomly decide if this branch should be 112 | // bloated with respect to the configured probability. 113 | // 114 | // This comparison looks backwards at first but it makes sense, I promise. 115 | if (getRandomInt32() % 100 > config->bloater.probability) 116 | continue; 117 | 118 | // Find where this block is supposed to branch to, then erase the branch. 119 | auto nextBlock = branch->getSuccessor(0); 120 | branch->eraseFromParent(); 121 | 122 | // Clone the intended destination block in order to create the starting 123 | // basis for our "garbage block", insert it into the function, then erase 124 | // the branch to the following block. 125 | ValueToValueMapTy map; 126 | auto garbageBlock = CloneBasicBlock(nextBlock, map); 127 | garbageBlock->insertInto(&func, nextBlock); 128 | if (garbageBlock->getTerminator()) 129 | garbageBlock->getTerminator()->eraseFromParent(); 130 | 131 | // Create a "dispatch block" which really only exists to add complexity to 132 | // the CFG (this is handy when pairing this pass with the flattener pass) 133 | // while still serving the purpose of branching to the next block. 134 | auto dispatchBlock = BasicBlock::Create(func.getContext(), "dispatch"); 135 | dispatchBlock->insertInto(&func, nextBlock); 136 | 137 | // Populate the dispatch block with some garbage code followed by a call 138 | // to the opaque "always true" function, then use the result to branch to 139 | // the real block (but retain the garbage block as a possible destination, 140 | // as far as LLVM is concerned). 141 | IRBuilder<> dispatchBuilder(dispatchBlock); 142 | auto opaqueTrue = dispatchBuilder.CreateCmp( 143 | getRandomItem(truePredicates), 144 | dispatchBuilder.CreateCall(getOpaqueTrueFunction(module)), 145 | dispatchBuilder.getInt64(getRandomInt32())); 146 | dispatchBuilder.CreateCondBr(opaqueTrue, nextBlock, garbageBlock); 147 | 148 | // Add a meaningless store to the opaque global value at the start of the 149 | // copied block and a meaningless load of the same value at the end. 150 | IRBuilder<> garbageBuilder(garbageBlock); 151 | garbageBuilder.SetInsertPoint(garbageBlock, garbageBlock->begin()); 152 | auto bogusArithmetic = garbageBuilder.CreateBinOp( 153 | getRandomItem(bogusBinaryOps), 154 | garbageBuilder.CreateLoad(garbageBuilder.getInt64Ty(), opaqueGlobal), 155 | garbageBuilder.getInt64(getRandomInt32())); 156 | garbageBuilder.CreateStore( 157 | garbageBuilder.CreateAnd(bogusArithmetic, 158 | garbageBuilder.getInt64(BloaterMagicSafeMask)), 159 | opaqueGlobal, /*isVolatile=*/true); 160 | garbageBuilder.SetInsertPoint(garbageBlock); 161 | 162 | // Insert a conditional branch so that the block has a valid terminator; 163 | // the destinations are irrelevant (since this block is unreachable in 164 | // practice) and were picked to optimize for the nastiest decompilation. 165 | auto fakeCond = garbageBuilder.CreateCmp( 166 | getRandomItem(fakePredicates), 167 | garbageBuilder.CreateLoad(garbageBuilder.getInt64Ty(), opaqueGlobal), 168 | garbageBuilder.getInt64(getRandomInt32())); 169 | garbageBuilder.CreateCondBr(fakeCond, block, dispatchBlock); 170 | 171 | // Create an unconditional branch to the dispatch block created above. 172 | IRBuilder<> opaqueBuilder(block); 173 | opaqueBuilder.CreateBr(dispatchBlock); 174 | } 175 | } 176 | 177 | PreservedAnalyses BloaterPass::run(Module &module, ModuleAnalysisManager &) { 178 | auto config = Config::get(); 179 | 180 | bool changed = false; 181 | for (auto &func : module.getFunctionList()) { 182 | if (!config->bloater.shouldRunOnFunction(func)) 183 | continue; 184 | 185 | for (int i = 0; i < config->bloater.rounds; ++i) 186 | bloatFunction(func); 187 | 188 | repairSSA(func); 189 | changed |= true; 190 | } 191 | 192 | return changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); 193 | } 194 | -------------------------------------------------------------------------------- /lib/Pass/ConstantMangler.cpp: -------------------------------------------------------------------------------- 1 | //===-- Pass/ConstantMangler.cpp - Constant-mangling pass -----------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Pass/ConstantMangler.h" 11 | 12 | #include "Limoncello/Config/Config.h" 13 | #include "Limoncello/Support/Module.h" 14 | #include "Limoncello/Support/Random.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace llvm; 23 | 24 | bool ConstantManglerPass::canMangleInstruction(Instruction const &insn) { 25 | if (isa(insn) || isa(insn) || 26 | isa(insn) || isa(insn) || insn.isAtomic()) 27 | return false; 28 | 29 | return true; 30 | } 31 | 32 | bool ConstantManglerPass::mangleFunctionConstants(Function &func) { 33 | auto module = func.getParent(); 34 | auto int64Ty = Type::getInt64Ty(module->getContext()); 35 | auto opaqueGlobal = getOrInsertGlobal(*module, "__lmcoOpaqueGlobal", 36 | ConstantInt::get(int64Ty, 0)); 37 | 38 | bool changed = false; 39 | for (auto &inst : instructions(func)) { 40 | if (!canMangleInstruction(inst)) 41 | continue; 42 | 43 | for (auto &op : inst.operands()) { 44 | if (auto *intOp = dyn_cast(op)) { 45 | auto intType = intOp->getType(); 46 | auto xorKey = getRandomInt64(); 47 | 48 | IRBuilder irb(&inst); 49 | auto rawOpaqueValue = irb.CreateLoad(int64Ty, opaqueGlobal); 50 | auto opaqueValue = irb.CreateTrunc(rawOpaqueValue, intType); 51 | auto mangledConstant = 52 | ConstantInt::get(intType, intOp->getValue() ^ xorKey); 53 | auto opacifiedKey = 54 | irb.CreateOr(opaqueValue, ConstantInt::get(intType, xorKey)); 55 | auto xorExpr = irb.CreateXor(mangledConstant, opacifiedKey); 56 | 57 | op.set(xorExpr); 58 | changed |= true; 59 | } 60 | } 61 | } 62 | 63 | return changed; 64 | } 65 | 66 | PreservedAnalyses ConstantManglerPass::run(Module &module, 67 | ModuleAnalysisManager &) { 68 | bool changed = false; 69 | for (auto &func : module.getFunctionList()) { 70 | if (!Config::get()->constantMangler.shouldRunOnFunction(func)) 71 | continue; 72 | 73 | mangleFunctionConstants(func); 74 | changed |= true; 75 | } 76 | 77 | return changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); 78 | } 79 | -------------------------------------------------------------------------------- /lib/Pass/Flattener.cpp: -------------------------------------------------------------------------------- 1 | //===-- Pass/Flattener.cpp - Control flow flattening pass -----------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Pass/Flattener.h" 11 | 12 | #include "Limoncello/Config/Config.h" 13 | #include "Limoncello/Support/Function.h" 14 | #include "Limoncello/Support/Random.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace llvm; 23 | 24 | StateMachine::StateMachine(BasicBlock *eb) : m_entryBlock(eb) { 25 | auto &ctx = m_entryBlock->getContext(); 26 | auto func = m_entryBlock->getParent(); 27 | 28 | m_entryBlock->getTerminator()->eraseFromParent(); 29 | IRBuilder<> entryBuilder(m_entryBlock); 30 | m_stateVar = 31 | entryBuilder.CreateAlloca(entryBuilder.getInt32Ty(), nullptr, "fsmState"); 32 | m_endBlock = BasicBlock::Create(ctx, "fsmEnd", func, m_entryBlock); 33 | m_defaultBlock = BasicBlock::Create(ctx, "fsmDefault", func, m_endBlock); 34 | m_switchBlock = BasicBlock::Create(ctx, "fsmStart", func, m_entryBlock); 35 | 36 | m_defaultBlock->moveBefore(m_endBlock); 37 | m_switchBlock->moveBefore(m_defaultBlock); 38 | m_entryBlock->moveBefore(m_switchBlock); 39 | 40 | IRBuilder<> terminalBuilder(m_endBlock); 41 | terminalBuilder.CreateBr(m_switchBlock); 42 | 43 | IRBuilder<> defaultBuilder(m_defaultBlock); 44 | defaultBuilder.CreateUnreachable(); 45 | 46 | IRBuilder<> dispatchBuilder(m_switchBlock); 47 | auto switchValue = dispatchBuilder.CreateLoad(dispatchBuilder.getInt32Ty(), 48 | m_stateVar, "state"); 49 | m_switchInst = dispatchBuilder.CreateSwitch(switchValue, m_defaultBlock); 50 | } 51 | 52 | bool StateMachine::shouldIgnoreTerminator(Instruction *instruction) { 53 | if (isa(instruction)) 54 | return true; 55 | 56 | return false; 57 | } 58 | 59 | /// Maximum number of times a case ID should be generated before giving up. 60 | constexpr auto MaxRandomCaseIDAttempts = 3; 61 | 62 | void StateMachine::addState(BasicBlock *block) const { 63 | auto config = Config::get(); 64 | 65 | auto getConstantInt32 = [this](uint32_t value) { 66 | auto &context = m_switchInst->getContext(); 67 | return ConstantInt::get(Type::getInt32Ty(context), value); 68 | }; 69 | 70 | auto stateId = m_switchInst->getNumCases() + 1; 71 | if (config->flattener.useRandomCaseIds) { 72 | // We need to make sure that the state ID we just generated are unique, 73 | // otherwise we'll end up creating two cases with the same ID, which 74 | // becomes more likely in sufficiently large functions. 75 | // 76 | // That said, an infinite loop here would be dangerous and who doesn't like 77 | // a bit of gambling; will the RNG succeed within the number of allowed 78 | // attempts or will you have to recompile? 79 | int attempts = 0; 80 | while (attempts < MaxRandomCaseIDAttempts) { 81 | stateId = getRandomInt32() % config->flattener.maxRandomCases; 82 | 83 | auto state = getConstantInt32(stateId); 84 | if (m_switchInst->findCaseValue(state) != m_switchInst->case_default()) { 85 | ++attempts; 86 | continue; 87 | } 88 | 89 | break; 90 | } 91 | 92 | // If this is a debug build then we'll get a nice message here, otherwise 93 | // the verifier pass (or some other part of LLVM) will eventually cry about 94 | // this; in either case, compiliation won't succeed and this will 95 | // eventually be communicated to the user. 96 | if (attempts == MaxRandomCaseIDAttempts) { 97 | assert(0 && "Failed to generate unique case ID for switch"); 98 | } 99 | } 100 | 101 | block->moveBefore(m_endBlock); 102 | m_switchInst->addCase(getConstantInt32(stateId), block); 103 | } 104 | 105 | void StateMachine::rewriteBranch(BranchInst *branchInst) { 106 | auto block = branchInst->getParent(); 107 | 108 | if (branchInst->isConditional()) { 109 | auto trueDest = branchInst->getSuccessor(0); 110 | auto falseDest = branchInst->getSuccessor(1); 111 | 112 | auto trueDestId = m_switchInst->findCaseDest(trueDest); 113 | auto falseDestId = m_switchInst->findCaseDest(falseDest); 114 | 115 | IRBuilder<> builder(block); 116 | auto nextStateId = builder.CreateSelect(branchInst->getCondition(), 117 | trueDestId, falseDestId); 118 | builder.CreateStore(nextStateId, m_stateVar); 119 | builder.CreateBr(m_endBlock); 120 | } else { 121 | auto dest = branchInst->getSuccessor(0); 122 | if (dest == m_endBlock) 123 | return; 124 | 125 | auto destId = m_switchInst->findCaseDest(dest); 126 | 127 | IRBuilder<> builder(block); 128 | builder.CreateStore(destId, m_stateVar); 129 | builder.CreateBr(m_endBlock); 130 | } 131 | 132 | branchInst->eraseFromParent(); 133 | } 134 | 135 | void StateMachine::rewriteBlock(BasicBlock *block) { 136 | auto termInst = block->getTerminator(); 137 | if (shouldIgnoreTerminator(termInst)) 138 | return; 139 | 140 | if (isa(termInst)) 141 | assert(1 && "Failed to filter out exception-using function"); 142 | 143 | if (auto branchInst = dyn_cast(termInst)) 144 | rewriteBranch(branchInst); 145 | 146 | assert("Got unhandled block terminator!"); 147 | } 148 | 149 | void StateMachine::finalize(BasicBlock *firstBlock, 150 | SmallVector const &flatteningSet) { 151 | IRBuilder<> entryBuilder(m_entryBlock); 152 | 153 | auto stateId = m_switchInst->findCaseDest(firstBlock); 154 | entryBuilder.CreateStore(stateId, m_stateVar); 155 | 156 | for (auto block : flatteningSet) 157 | rewriteBlock(block); 158 | 159 | entryBuilder.CreateBr(m_switchBlock); 160 | } 161 | 162 | SmallVector FlattenerPass::getFlatteningSet(Function &func) { 163 | auto entryBlock = &func.getEntryBlock(); 164 | 165 | SmallVector result; 166 | for (auto &block : func) { 167 | if (&block == entryBlock) 168 | continue; 169 | 170 | result.emplace_back(&block); 171 | } 172 | 173 | return result; 174 | } 175 | 176 | BlockPair FlattenerPass::splitConditionalPart(BasicBlock *block) { 177 | auto terminator = block->getTerminator(); 178 | 179 | Instruction *splitPoint = nullptr; 180 | if (auto branchInst = dyn_cast(terminator)) { 181 | if (branchInst->isConditional()) { 182 | auto condition = branchInst->getCondition(); 183 | auto conditionInst = dyn_cast(condition); 184 | assert(conditionInst && "Found conditional branch without condition"); 185 | 186 | splitPoint = conditionInst; 187 | } else { 188 | splitPoint = terminator; 189 | } 190 | } else if (auto switchInst = dyn_cast(terminator)) { 191 | splitPoint = switchInst; 192 | } 193 | 194 | if (!splitPoint) 195 | return {block, nullptr}; 196 | 197 | auto conditionalPart = block->splitBasicBlock(splitPoint, "conditionalPart"); 198 | return {block, conditionalPart}; 199 | } 200 | 201 | PreservedAnalyses FlattenerPass::run(Function &func, 202 | FunctionAnalysisManager &) { 203 | if (!Config::get()->flattener.shouldRunOnFunction(func)) 204 | return PreservedAnalyses::all(); 205 | 206 | // TODO: Support C++ exceptions. 207 | for (auto &block : func) { 208 | if (block.isLandingPad()) { 209 | return PreservedAnalyses::all(); 210 | } 211 | } 212 | 213 | auto [entryBlock, trailingConditionalBlock] = 214 | splitConditionalPart(&func.getEntryBlock()); 215 | auto flatteningSet = getFlatteningSet(func); 216 | if (flatteningSet.size() < 2) 217 | return PreservedAnalyses::none(); 218 | 219 | StateMachine stateMachine(entryBlock); 220 | for (auto block : flatteningSet) 221 | stateMachine.addState(block); 222 | 223 | stateMachine.finalize(trailingConditionalBlock, flatteningSet); 224 | 225 | repairSSA(func); 226 | 227 | return PreservedAnalyses::none(); 228 | } 229 | -------------------------------------------------------------------------------- /lib/Pass/StringObfuscator.cpp: -------------------------------------------------------------------------------- 1 | //===-- Pass/StringObfuscator.cpp - String obfuscation pass ---------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Pass/StringObfuscator.h" 11 | 12 | #include "Limoncello/Support/Function.h" 13 | #include "Limoncello/Support/Module.h" 14 | #include "Limoncello/Support/Random.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "StringObfuscatorBitcode.h" 27 | 28 | constexpr auto FunctionNameDeobfuscate = "__lmcoStringDeobfuscateXOR"; 29 | constexpr auto FunctionNameDeobfuscateAll = "__lmcoStringDeobfuscateAll"; 30 | 31 | using namespace llvm; 32 | 33 | Constant *StringObfuscatorPass::createObfuscatedString(LLVMContext &context, 34 | StringRef content, 35 | uint8_t key) { 36 | std::string result(content.size(), 0); 37 | std::transform(content.begin(), content.end() - 1, result.begin(), 38 | [key](auto c) { return c ^ key; }); 39 | 40 | return ConstantDataArray::getString(context, StringRef(result), false); 41 | } 42 | 43 | std::vector 44 | StringObfuscatorPass::obfuscateStrings(Module &module) { 45 | std::vector modifiedGlobals; 46 | for (auto &var : module.globals()) { 47 | // Can't operate on uninitialized or imported globals. 48 | if (!var.hasInitializer() || var.hasExternalLinkage()) 49 | continue; 50 | 51 | // Initializer must be both a constant data array and a string. 52 | auto initializer = var.getInitializer(); 53 | auto data = dyn_cast(initializer); 54 | if (!data || !data->isString()) 55 | continue; 56 | 57 | auto content = data->getAsString(); 58 | uint8_t xorKey = getRandomInt8(); 59 | 60 | var.setInitializer( 61 | createObfuscatedString(module.getContext(), content, xorKey)); 62 | var.setConstant(false); 63 | 64 | // The null byte is included in `content.size()` and should not be touched 65 | // when deobfuscating strings. 66 | modifiedGlobals.emplace_back(&var, content.size() - 1, xorKey); 67 | } 68 | 69 | return modifiedGlobals; 70 | } 71 | 72 | Function *StringObfuscatorPass::getDeobfuscateFunction(Module &module) { 73 | SMDiagnostic unusedError; 74 | 75 | auto runtimeModule = 76 | parseIR(getDeobfuscateBitcode(), unusedError, module.getContext()); 77 | assert(runtimeModule && "Failed to parse string obfuscator runtime IR!"); 78 | 79 | // TODO: Ideally the name of the runtime function doesn't live here as a 80 | // magic string and is defined elsewhere. 81 | auto deobfuscateFn = runtimeModule->getFunction("deobfuscateXOR"); 82 | assert(deobfuscateFn && "String deobfuscation function missing in runtime!"); 83 | 84 | return localizeFunction( 85 | module, *deobfuscateFn, 86 | getModuleSpecificName(module, FunctionNameDeobfuscate)); 87 | } 88 | 89 | Function *StringObfuscatorPass::createDeobfuscateAllFunction( 90 | Module &module, std::vector const &records) { 91 | auto &context = module.getContext(); 92 | 93 | auto callee = module.getOrInsertFunction( 94 | getModuleSpecificName(module, FunctionNameDeobfuscateAll), 95 | Type::getVoidTy(context)); 96 | auto result = cast(callee.getCallee()); 97 | 98 | auto deobfuscateFn = getDeobfuscateFunction(module); 99 | 100 | // Build a huge block with a call to the deobfuscate function for every 101 | // single obfuscated string in the binary. 102 | auto body = BasicBlock::Create(context, "", result); 103 | IRBuilder<> builder(body); 104 | for (auto string : records) { 105 | builder.CreateCall(deobfuscateFn, {/*pointer=*/string.handle, 106 | /*size=*/builder.getInt64(string.size), 107 | /*key=*/builder.getInt8(string.key)}); 108 | } 109 | builder.CreateRetVoid(); 110 | 111 | // TODO: This used to be done to prevent the optimizer from deleting these 112 | // function (calls) from the module. Now, commenting out these lines does not 113 | // break the string obfuscation, but the routines do tend to get inlined 114 | // which is debatably desirable. This should be looked into further. 115 | result->addFnAttr(Attribute::get(context, Attribute::NoInline)); 116 | result->addFnAttr(Attribute::get(context, Attribute::OptimizeNone)); 117 | 118 | return result; 119 | } 120 | 121 | PreservedAnalyses StringObfuscatorPass::run(Module &module, 122 | ModuleAnalysisManager &) { 123 | auto obfuscatedStrings = obfuscateStrings(module); 124 | auto deobfuscateAllFn = 125 | createDeobfuscateAllFunction(module, obfuscatedStrings); 126 | 127 | if (auto mainFn = module.getFunction("main")) { 128 | auto &entryBlock = mainFn->getEntryBlock(); 129 | auto callBlock = BasicBlock::Create(mainFn->getContext(), "", 130 | entryBlock.getParent(), &entryBlock); 131 | 132 | // If this is an executable, it's easy enough (and less suspicious) to shim 133 | // the deobfuscation routine into the start of `main`. 134 | IRBuilder<> builder(callBlock); 135 | builder.CreateCall(deobfuscateAllFn); 136 | builder.CreateBr(&entryBlock); 137 | } else { 138 | // However, if `main` isn't present (e.g. this is a library), we can add 139 | // the "deobfuscate all strings" function as a global constructor to ensure 140 | // it is run. Setting the priority to 0 here is important as there is a 141 | // potential for a data hazard if other global constructors depend on 142 | // strings which have yet to be deobfuscated. 143 | appendToGlobalCtors(module, deobfuscateAllFn, /*priority=*/0); 144 | } 145 | 146 | return PreservedAnalyses::none(); 147 | } 148 | -------------------------------------------------------------------------------- /lib/Pass/StringObfuscatorBitcode.h: -------------------------------------------------------------------------------- 1 | //===-- Pass/StringObfuscatorBitcode.h ------------------------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef LIMONCELLO_PASS_STRINGOBFUSCATORBITCODE_H 11 | #define LIMONCELLO_PASS_STRINGOBFUSCATORBITCODE_H 12 | 13 | #include 14 | 15 | // void (char *data, size_t size, uint8_t key) { 16 | // for (size_t i = 0; i < size; ++i) 17 | // data[i] ^= key; 18 | // } 19 | // 20 | // define void @(ptr noundef %0, i64 noundef %1, i8 noundef zeroext %2) { 21 | // %4 = icmp eq i64 %1, 0 22 | // br i1 %4, label %5, label %6 23 | // 24 | // 5: ; preds = %6, %3 25 | // ret void 26 | // 27 | // 6: ; preds = %3, %6 28 | // %7 = phi i64 [ %11, %6 ], [ 0, %3 ] 29 | // %8 = getelementptr inbounds i8, ptr %0, i64 %7 30 | // %9 = load i8, ptr %8, align 1 31 | // %10 = xor i8 %9, %2 32 | // store i8 %10, ptr %8, align 1 33 | // %11 = add nuw i64 %7, 1 34 | // %12 = icmp eq i64 %11, %1 35 | // br i1 %12, label %5, label %6 36 | // } 37 | static unsigned char const deobfuscateBitcode[] = { 38 | 0xde, 0xc0, 0x17, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 39 | 0xf8, 0x09, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x42, 0x43, 0xc0, 0xde, 40 | 0x35, 0x14, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x62, 0x0c, 0x30, 0x24, 41 | 0x98, 0x96, 0xa6, 0xa5, 0xf7, 0xd7, 0x7f, 0x8d, 0xd3, 0xb7, 0x4f, 0xfb, 42 | 0xb6, 0xed, 0xd7, 0xb8, 0x5f, 0xff, 0xb4, 0x10, 0x05, 0xc8, 0x14, 0x00, 43 | 0x00, 0x00, 0x00, 0x00, 0x21, 0x0c, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 44 | 0x0b, 0x02, 0x21, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 45 | 0x07, 0x81, 0x23, 0x91, 0x41, 0xc8, 0x04, 0x49, 0x06, 0x10, 0x32, 0x39, 46 | 0x92, 0x01, 0x84, 0x0c, 0x25, 0x05, 0x08, 0x19, 0x1e, 0x04, 0x8b, 0x62, 47 | 0x80, 0x10, 0x45, 0x02, 0x42, 0x92, 0x0b, 0x42, 0x84, 0x10, 0x32, 0x14, 48 | 0x38, 0x08, 0x18, 0x4b, 0x0a, 0x32, 0x42, 0x88, 0x48, 0x70, 0xc4, 0x21, 49 | 0x23, 0x44, 0x12, 0x87, 0x8c, 0x10, 0x41, 0x92, 0x02, 0x64, 0xc8, 0x08, 50 | 0xb1, 0x14, 0x20, 0x43, 0x46, 0x88, 0x20, 0xc9, 0x01, 0x32, 0x42, 0x84, 51 | 0x18, 0x2a, 0x28, 0x2a, 0x90, 0x31, 0x7c, 0xb0, 0x5c, 0x91, 0x20, 0xc4, 52 | 0xc8, 0x00, 0x00, 0x00, 0x89, 0x20, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 53 | 0x32, 0x22, 0x08, 0x09, 0x20, 0x62, 0x46, 0x00, 0x21, 0x2b, 0x24, 0x98, 54 | 0x10, 0x21, 0x25, 0x24, 0x98, 0x10, 0x19, 0x27, 0x0c, 0x85, 0xa4, 0x90, 55 | 0x60, 0x42, 0x64, 0x5c, 0x20, 0x24, 0x64, 0x82, 0x60, 0x19, 0x01, 0x98, 56 | 0x23, 0x40, 0x28, 0xe6, 0x08, 0x40, 0xc1, 0x10, 0x20, 0x13, 0x44, 0x03, 57 | 0x01, 0x73, 0x04, 0x60, 0xa0, 0x02, 0x37, 0x47, 0x10, 0x4c, 0x01, 0x00, 58 | 0x51, 0x18, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x1b, 0xe6, 0x24, 0xf8, 59 | 0xff, 0xff, 0xff, 0xff, 0x01, 0x68, 0x03, 0xc0, 0x0f, 0x00, 0x38, 0x00, 60 | 0xfe, 0x00, 0x90, 0x80, 0x09, 0xd0, 0x82, 0x30, 0x08, 0x08, 0x73, 0x90, 61 | 0x87, 0x70, 0x68, 0x87, 0x72, 0x68, 0x03, 0x78, 0x78, 0x87, 0x74, 0x70, 62 | 0x07, 0x7a, 0x28, 0x07, 0x79, 0x00, 0xdc, 0xe1, 0x1d, 0xdc, 0xa1, 0x0d, 63 | 0xd8, 0xa1, 0x1c, 0xc2, 0xc1, 0x1c, 0x00, 0xa2, 0x1d, 0xd2, 0xc1, 0x1d, 64 | 0xda, 0x80, 0x1d, 0xca, 0xe1, 0x1c, 0xc2, 0x81, 0x1d, 0xda, 0xc0, 0x1e, 65 | 0xca, 0x61, 0x1c, 0xe8, 0xe1, 0x1d, 0xe4, 0xa1, 0x0d, 0xee, 0x21, 0x1d, 66 | 0xc8, 0x81, 0x1e, 0xd0, 0x01, 0x80, 0x03, 0x80, 0x70, 0x87, 0x77, 0x68, 67 | 0x03, 0x7a, 0x90, 0x87, 0x70, 0x80, 0x07, 0x78, 0x48, 0x07, 0x77, 0x38, 68 | 0x87, 0x36, 0x68, 0x87, 0x70, 0xa0, 0x07, 0x74, 0x00, 0xe8, 0x41, 0x1e, 69 | 0xea, 0xa1, 0x1c, 0x00, 0x02, 0x1e, 0xe4, 0xe1, 0x1d, 0xc4, 0xa1, 0x1c, 70 | 0xda, 0x60, 0x1e, 0xe8, 0x21, 0x1c, 0xc6, 0x61, 0x1d, 0x80, 0x5f, 0xf8, 71 | 0x85, 0x71, 0x40, 0x87, 0x75, 0x98, 0x07, 0x7a, 0x58, 0x87, 0x5f, 0x20, 72 | 0x87, 0x70, 0x90, 0x87, 0x7b, 0x48, 0x07, 0x77, 0x00, 0x88, 0x79, 0xa0, 73 | 0x87, 0x70, 0x18, 0x87, 0x75, 0x68, 0x03, 0x78, 0x90, 0x87, 0x77, 0xa0, 74 | 0x87, 0x72, 0x18, 0x07, 0x7a, 0x78, 0x07, 0x79, 0x68, 0x03, 0x71, 0xa8, 75 | 0x07, 0x73, 0x30, 0x87, 0x72, 0x90, 0x87, 0x36, 0x98, 0x87, 0x74, 0xd0, 76 | 0x87, 0x72, 0x00, 0xf0, 0x00, 0x20, 0xe8, 0x21, 0x1c, 0xe4, 0xe1, 0x1c, 77 | 0xca, 0x81, 0x1e, 0xda, 0x60, 0x1c, 0xe0, 0xa1, 0x1e, 0x80, 0x70, 0x80, 78 | 0x07, 0x78, 0x60, 0x87, 0x72, 0x68, 0x83, 0x76, 0x88, 0x03, 0x80, 0xa0, 79 | 0x87, 0x70, 0x90, 0x87, 0x73, 0x28, 0x07, 0x7a, 0x68, 0x03, 0x73, 0x28, 80 | 0x87, 0x70, 0xa0, 0x87, 0x7a, 0x90, 0x87, 0x72, 0x98, 0x07, 0x60, 0x0d, 81 | 0xc2, 0xa1, 0x1c, 0xe6, 0x81, 0x0d, 0xd6, 0x60, 0x1c, 0xe4, 0x61, 0x1c, 82 | 0xd8, 0x60, 0x0d, 0xc6, 0x41, 0x1e, 0xf2, 0x01, 0x1e, 0xe8, 0xe1, 0x1d, 83 | 0xd8, 0x60, 0x0d, 0xc8, 0xe1, 0x1d, 0xe8, 0x01, 0x1e, 0xe4, 0xe1, 0x1d, 84 | 0xc8, 0x81, 0x0d, 0xd6, 0xc0, 0x1c, 0xe0, 0xa1, 0x0d, 0xc2, 0x41, 0x1e, 85 | 0xda, 0xc1, 0x1e, 0xf0, 0x80, 0x0d, 0xd6, 0xc0, 0x1c, 0xe0, 0x21, 0x0e, 86 | 0xec, 0xc0, 0x1c, 0xda, 0x81, 0x1d, 0xd8, 0x60, 0x0d, 0xcc, 0xa1, 0x1e, 87 | 0xd8, 0x81, 0x1d, 0xcc, 0x01, 0x1e, 0xe2, 0xc0, 0x0e, 0xd8, 0x60, 0x0d, 88 | 0xd8, 0x61, 0x1e, 0xca, 0x81, 0x0d, 0xd6, 0xc0, 0x1d, 0xca, 0xe1, 0x1d, 89 | 0xdc, 0x81, 0x0d, 0xd6, 0x40, 0x1e, 0xc2, 0x61, 0x1e, 0xd8, 0x60, 0x0d, 90 | 0xe4, 0x61, 0x1c, 0xe0, 0x61, 0x1c, 0xd8, 0x60, 0x0d, 0xe4, 0x81, 0x1c, 91 | 0xda, 0x81, 0x0d, 0xd6, 0x60, 0x1e, 0xd0, 0x21, 0x1c, 0xe4, 0x80, 0x0d, 92 | 0xd6, 0x60, 0x1e, 0xd0, 0x21, 0x1c, 0xe6, 0x80, 0x0d, 0xd6, 0x60, 0x1e, 93 | 0xda, 0x81, 0x0e, 0xd8, 0x60, 0x0d, 0xec, 0x01, 0x0f, 0xdc, 0xa0, 0x0e, 94 | 0xc2, 0x81, 0x0d, 0xd6, 0x40, 0x1f, 0xc6, 0xa1, 0x1d, 0xd8, 0x60, 0x0d, 95 | 0xf4, 0x61, 0x1c, 0xf4, 0x01, 0xd8, 0x60, 0x08, 0x01, 0xb0, 0x00, 0xa4, 96 | 0xb0, 0x81, 0x18, 0x04, 0x80, 0x14, 0x36, 0x18, 0xc4, 0x00, 0x90, 0x02, 97 | 0x20, 0x06, 0x00, 0x00, 0x49, 0x18, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 98 | 0x13, 0x88, 0x40, 0x18, 0x08, 0x00, 0x00, 0x00, 0x13, 0xb0, 0x70, 0x90, 99 | 0x87, 0x76, 0xb0, 0x03, 0x3a, 0x68, 0x83, 0x70, 0x80, 0x07, 0x78, 0x60, 100 | 0x87, 0x72, 0x68, 0x83, 0x76, 0x08, 0x87, 0x71, 0x78, 0x87, 0x79, 0xc0, 101 | 0x87, 0x38, 0x98, 0x03, 0x37, 0x80, 0x03, 0x37, 0x80, 0x83, 0x0d, 0x61, 102 | 0x50, 0x0e, 0x6d, 0xd0, 0x0e, 0x7a, 0xf0, 0x0e, 0x6d, 0x90, 0x0e, 0x76, 103 | 0x40, 0x07, 0x7a, 0x60, 0x07, 0x74, 0xd0, 0x06, 0xe9, 0x10, 0x07, 0x72, 104 | 0x80, 0x07, 0x7a, 0x10, 0x07, 0x72, 0x80, 0x07, 0x6d, 0xe0, 0x0e, 0x73, 105 | 0x20, 0x07, 0x7a, 0x60, 0x07, 0x74, 0xd0, 0x06, 0xb3, 0x10, 0x07, 0x72, 106 | 0x80, 0x07, 0x1a, 0x21, 0x4c, 0x0e, 0x3d, 0x72, 0x98, 0x1e, 0x7e, 0x4d, 107 | 0xe9, 0xf2, 0xb4, 0xfb, 0xfc, 0x14, 0x9b, 0xeb, 0xf3, 0x71, 0x98, 0xfe, 108 | 0x96, 0x4b, 0xeb, 0x6e, 0x7a, 0xba, 0x5d, 0x76, 0x8d, 0x87, 0x54, 0x80, 109 | 0x43, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 110 | 0x00, 0x38, 0x80, 0xc4, 0x06, 0x81, 0xa2, 0x3b, 0x02, 0x00, 0x00, 0x59, 111 | 0x20, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x32, 0x1e, 0x98, 0x10, 112 | 0x19, 0x11, 0x4c, 0x90, 0x8c, 0x09, 0x26, 0x47, 0xc6, 0x04, 0x43, 0xba, 113 | 0x22, 0x20, 0x1c, 0x4b, 0x68, 0x06, 0xba, 0x12, 0x28, 0x84, 0x82, 0x18, 114 | 0x01, 0x28, 0x07, 0x9a, 0x11, 0x00, 0x00, 0x00, 0xb1, 0x18, 0x00, 0x00, 115 | 0x97, 0x00, 0x00, 0x00, 0x33, 0x08, 0x80, 0x1c, 0xc4, 0xe1, 0x1c, 0x66, 116 | 0x14, 0x01, 0x3d, 0x88, 0x43, 0x38, 0x84, 0xc3, 0x8c, 0x42, 0x80, 0x07, 117 | 0x79, 0x78, 0x07, 0x73, 0x98, 0x71, 0x0c, 0xe6, 0x00, 0x0f, 0xed, 0x10, 118 | 0x0e, 0xf4, 0x80, 0x0e, 0x33, 0x0c, 0x42, 0x1e, 0xc2, 0xc1, 0x1d, 0xce, 119 | 0xa1, 0x1c, 0x66, 0x30, 0x05, 0x3d, 0x88, 0x43, 0x38, 0x84, 0x83, 0x1b, 120 | 0xcc, 0x03, 0x3d, 0xc8, 0x43, 0x3d, 0x8c, 0x03, 0x3d, 0xcc, 0x78, 0x8c, 121 | 0x74, 0x70, 0x07, 0x7b, 0x08, 0x07, 0x79, 0x48, 0x87, 0x70, 0x70, 0x07, 122 | 0x7a, 0x70, 0x03, 0x76, 0x78, 0x87, 0x70, 0x20, 0x87, 0x19, 0xcc, 0x11, 123 | 0x0e, 0xec, 0x90, 0x0e, 0xe1, 0x30, 0x0f, 0x6e, 0x30, 0x0f, 0xe3, 0xf0, 124 | 0x0e, 0xf0, 0x50, 0x0e, 0x33, 0x10, 0xc4, 0x1d, 0xde, 0x21, 0x1c, 0xd8, 125 | 0x21, 0x1d, 0xc2, 0x61, 0x1e, 0x66, 0x30, 0x89, 0x3b, 0xbc, 0x83, 0x3b, 126 | 0xd0, 0x43, 0x39, 0xb4, 0x03, 0x3c, 0xbc, 0x83, 0x3c, 0x84, 0x03, 0x3b, 127 | 0xcc, 0xf0, 0x14, 0x76, 0x60, 0x07, 0x7b, 0x68, 0x07, 0x37, 0x68, 0x87, 128 | 0x72, 0x68, 0x07, 0x37, 0x80, 0x87, 0x70, 0x90, 0x87, 0x70, 0x60, 0x07, 129 | 0x76, 0x28, 0x07, 0x76, 0xf8, 0x05, 0x76, 0x78, 0x87, 0x77, 0x80, 0x87, 130 | 0x5f, 0x08, 0x87, 0x71, 0x18, 0x87, 0x72, 0x98, 0x87, 0x79, 0x98, 0x81, 131 | 0x2c, 0xee, 0xf0, 0x0e, 0xee, 0xe0, 0x0e, 0xf5, 0xc0, 0x0e, 0xec, 0x30, 132 | 0x03, 0x62, 0xc8, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xcc, 0xa1, 0x1c, 0xe4, 133 | 0xa1, 0x1c, 0xdc, 0x61, 0x1c, 0xca, 0x21, 0x1c, 0xc4, 0x81, 0x1d, 0xca, 134 | 0x61, 0x06, 0xd6, 0x90, 0x43, 0x39, 0xc8, 0x43, 0x39, 0x98, 0x43, 0x39, 135 | 0xc8, 0x43, 0x39, 0xb8, 0xc3, 0x38, 0x94, 0x43, 0x38, 0x88, 0x03, 0x3b, 136 | 0x94, 0xc3, 0x2f, 0xbc, 0x83, 0x3c, 0xfc, 0x82, 0x3b, 0xd4, 0x03, 0x3b, 137 | 0xb0, 0xc3, 0x0c, 0xc7, 0x69, 0x87, 0x70, 0x58, 0x87, 0x72, 0x70, 0x83, 138 | 0x74, 0x68, 0x07, 0x78, 0x60, 0x87, 0x74, 0x18, 0x87, 0x74, 0xa0, 0x87, 139 | 0x19, 0xce, 0x53, 0x0f, 0xee, 0x00, 0x0f, 0xf2, 0x50, 0x0e, 0xe4, 0x90, 140 | 0x0e, 0xe3, 0x40, 0x0f, 0xe1, 0x20, 0x0e, 0xec, 0x50, 0x0e, 0x33, 0x20, 141 | 0x28, 0x1d, 0xdc, 0xc1, 0x1e, 0xc2, 0x41, 0x1e, 0xd2, 0x21, 0x1c, 0xdc, 142 | 0x81, 0x1e, 0xdc, 0xe0, 0x1c, 0xe4, 0xe1, 0x1d, 0xea, 0x01, 0x1e, 0x66, 143 | 0x18, 0x51, 0x38, 0xb0, 0x43, 0x3a, 0x9c, 0x83, 0x3b, 0xcc, 0x50, 0x24, 144 | 0x76, 0x60, 0x07, 0x7b, 0x68, 0x07, 0x37, 0x60, 0x87, 0x77, 0x78, 0x07, 145 | 0x78, 0x98, 0x51, 0x4c, 0xf4, 0x90, 0x0f, 0xf0, 0x50, 0x0e, 0x33, 0x1e, 146 | 0x6a, 0x1e, 0xca, 0x61, 0x1c, 0xe8, 0x21, 0x1d, 0xde, 0xc1, 0x1d, 0x7e, 147 | 0x01, 0x1e, 0xe4, 0xa1, 0x1c, 0xcc, 0x21, 0x1d, 0xf0, 0x61, 0x06, 0x54, 148 | 0x85, 0x83, 0x38, 0xcc, 0xc3, 0x3b, 0xb0, 0x43, 0x3d, 0xd0, 0x43, 0x39, 149 | 0xfc, 0xc2, 0x3c, 0xe4, 0x43, 0x3b, 0x88, 0xc3, 0x3b, 0xb0, 0xc3, 0x8c, 150 | 0xc5, 0x0a, 0x87, 0x79, 0x98, 0x87, 0x77, 0x18, 0x87, 0x74, 0x08, 0x07, 151 | 0x7a, 0x28, 0x07, 0x72, 0x98, 0x81, 0x5c, 0xe3, 0x10, 0x0e, 0xec, 0xc0, 152 | 0x0e, 0xe5, 0x50, 0x0e, 0xf3, 0x30, 0x23, 0xc1, 0xd2, 0x41, 0x1e, 0xe4, 153 | 0xe1, 0x17, 0xd8, 0xe1, 0x1d, 0xde, 0x01, 0x1e, 0x66, 0x48, 0x19, 0x3b, 154 | 0xb0, 0x83, 0x3d, 0xb4, 0x83, 0x1b, 0x84, 0xc3, 0x38, 0x8c, 0x43, 0x39, 155 | 0xcc, 0xc3, 0x3c, 0xb8, 0xc1, 0x39, 0xc8, 0xc3, 0x3b, 0xd4, 0x03, 0x3c, 156 | 0xcc, 0x48, 0xb4, 0x71, 0x08, 0x07, 0x76, 0x60, 0x07, 0x71, 0x08, 0x87, 157 | 0x71, 0x58, 0x87, 0x19, 0xdb, 0xc6, 0x0e, 0xec, 0x60, 0x0f, 0xed, 0xe0, 158 | 0x06, 0xf0, 0x20, 0x0f, 0xe5, 0x30, 0x0f, 0xe5, 0x20, 0x0f, 0xf6, 0x50, 159 | 0x0e, 0x6e, 0x10, 0x0e, 0xe3, 0x30, 0x0e, 0xe5, 0x30, 0x0f, 0xf3, 0xe0, 160 | 0x06, 0xe9, 0xe0, 0x0e, 0xe4, 0x50, 0x0e, 0xf8, 0x30, 0x23, 0xe2, 0xec, 161 | 0x61, 0x1c, 0xc2, 0x81, 0x1d, 0xd8, 0xe1, 0x17, 0xec, 0x21, 0x1d, 0xe6, 162 | 0x21, 0x1d, 0xc4, 0x21, 0x1d, 0xd8, 0x21, 0x1d, 0xe8, 0x21, 0x1f, 0x66, 163 | 0x20, 0x9d, 0x3b, 0xbc, 0x43, 0x3d, 0xb8, 0x03, 0x39, 0x94, 0x83, 0x39, 164 | 0xcc, 0x58, 0xbc, 0x70, 0x70, 0x07, 0x77, 0x78, 0x07, 0x7a, 0x08, 0x07, 165 | 0x7a, 0x48, 0x87, 0x77, 0x70, 0x07, 0x00, 0x00, 0x79, 0x20, 0x00, 0x00, 166 | 0x59, 0x00, 0x00, 0x00, 0x72, 0x1e, 0x48, 0x20, 0x43, 0x88, 0x0c, 0x19, 167 | 0x09, 0x72, 0x32, 0x48, 0x20, 0x23, 0x81, 0x8c, 0x91, 0x91, 0xd1, 0x44, 168 | 0xa0, 0x10, 0x28, 0x64, 0x3c, 0x31, 0x32, 0x42, 0x8e, 0x90, 0x21, 0xa3, 169 | 0xa8, 0x30, 0xaf, 0x01, 0x8b, 0x92, 0x4d, 0x57, 0x97, 0x1c, 0x4d, 0x1c, 170 | 0x00, 0x00, 0x00, 0x00, 0x53, 0x44, 0x4b, 0x20, 0x56, 0x65, 0x72, 0x73, 171 | 0x69, 0x6f, 0x6e, 0x77, 0x63, 0x68, 0x61, 0x72, 0x5f, 0x73, 0x69, 0x7a, 172 | 0x65, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x2d, 0x74, 0x61, 0x72, 0x67, 173 | 0x65, 0x74, 0x2d, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 174 | 0x6e, 0x74, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x72, 0x65, 0x74, 0x75, 0x72, 175 | 0x6e, 0x2d, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x73, 0x69, 0x67, 176 | 0x6e, 0x2d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x2d, 0x61, 0x64, 0x64, 177 | 0x72, 0x65, 0x73, 0x73, 0x2d, 0x61, 0x6c, 0x6c, 0x73, 0x69, 0x67, 0x6e, 178 | 0x2d, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x2d, 0x61, 0x64, 0x64, 0x72, 179 | 0x65, 0x73, 0x73, 0x2d, 0x77, 0x69, 0x74, 0x68, 0x2d, 0x62, 0x6b, 0x65, 180 | 0x79, 0x50, 0x49, 0x43, 0x20, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x75, 0x77, 181 | 0x74, 0x61, 0x62, 0x6c, 0x65, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x2d, 0x70, 182 | 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 183 | 0x63, 0x6c, 0x61, 0x6e, 0x67, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 184 | 0x6e, 0x20, 0x31, 0x34, 0x2e, 0x30, 0x2e, 0x33, 0x20, 0x28, 0x63, 0x6c, 185 | 0x61, 0x6e, 0x67, 0x2d, 0x31, 0x34, 0x30, 0x33, 0x2e, 0x30, 0x2e, 0x32, 186 | 0x32, 0x2e, 0x31, 0x34, 0x2e, 0x31, 0x29, 0x00, 0x23, 0x08, 0x47, 0x30, 187 | 0x82, 0x80, 0x08, 0x23, 0x08, 0xc7, 0x30, 0x82, 0x70, 0x10, 0x23, 0x08, 188 | 0x47, 0x31, 0x82, 0x70, 0x18, 0x23, 0x08, 0xc7, 0x31, 0xc3, 0xb0, 0x04, 189 | 0xcc, 0x0c, 0x43, 0x23, 0x38, 0x33, 0x0c, 0xcf, 0x00, 0xcd, 0x30, 0x3c, 190 | 0x04, 0x34, 0xc3, 0xf0, 0x14, 0xd0, 0x0c, 0xc3, 0x63, 0x40, 0x33, 0x0c, 191 | 0xd1, 0xb1, 0xcc, 0x30, 0x44, 0x48, 0x33, 0xc3, 0x10, 0x25, 0xcd, 0x0c, 192 | 0x81, 0x22, 0x23, 0x81, 0x09, 0xca, 0x88, 0x8d, 0xcd, 0xae, 0xcd, 0xa5, 193 | 0xed, 0x8d, 0xac, 0x8e, 0xad, 0xcc, 0xc5, 0x8c, 0x2d, 0xec, 0x6c, 0x6e, 194 | 0x94, 0x24, 0x92, 0x26, 0xaa, 0xb2, 0x2e, 0x2c, 0x4b, 0x85, 0x8d, 0xcd, 195 | 0xae, 0xcd, 0x25, 0x8d, 0xac, 0xcc, 0x8d, 0x6e, 0x94, 0x40, 0x03, 0x00, 196 | 0xa9, 0x18, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x0b, 0x0a, 0x72, 0x28, 197 | 0x87, 0x77, 0x80, 0x07, 0x7a, 0x58, 0x70, 0x98, 0x43, 0x3d, 0xb8, 0xc3, 198 | 0x38, 0xb0, 0x43, 0x39, 0xd0, 0xc3, 0x82, 0xe6, 0x1c, 0xc6, 0xa1, 0x0d, 199 | 0xe8, 0x41, 0x1e, 0xc2, 0xc1, 0x1d, 0xe6, 0x21, 0x1d, 0xe8, 0x21, 0x1d, 200 | 0xde, 0xc1, 0x1d, 0x16, 0x34, 0xe3, 0x60, 0x0e, 0xe7, 0x50, 0x0f, 0xe1, 201 | 0x20, 0x0f, 0xe4, 0x40, 0x0f, 0xe1, 0x20, 0x0f, 0xe7, 0x50, 0x0e, 0xf4, 202 | 0xb0, 0x80, 0x81, 0x07, 0x79, 0x28, 0x87, 0x70, 0x60, 0x07, 0x76, 0x78, 203 | 0x87, 0x71, 0x08, 0x07, 0x7a, 0x28, 0x07, 0x72, 0x58, 0x70, 0x9c, 0xc3, 204 | 0x38, 0xb4, 0x01, 0x3b, 0xa4, 0x83, 0x3d, 0x94, 0xc3, 0x02, 0x6b, 0x1c, 205 | 0xd8, 0x21, 0x1c, 0xdc, 0xe1, 0x1c, 0xdc, 0x20, 0x1c, 0xe4, 0x61, 0x1c, 206 | 0xdc, 0x20, 0x1c, 0xe8, 0x81, 0x1e, 0xc2, 0x61, 0x1c, 0xd0, 0xa1, 0x1c, 207 | 0xc8, 0x61, 0x1c, 0xc2, 0x81, 0x1d, 0xd8, 0x61, 0xc1, 0x01, 0x0f, 0xf4, 208 | 0x20, 0x0f, 0xe1, 0x50, 0x0f, 0xf4, 0x80, 0x0e, 0x00, 0x00, 0x00, 0x00, 209 | 0xd1, 0x10, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0xcc, 0x3c, 0xa4, 210 | 0x83, 0x3b, 0x9c, 0x03, 0x3b, 0x94, 0x03, 0x3d, 0xa0, 0x83, 0x3c, 0x94, 211 | 0x43, 0x38, 0x90, 0xc3, 0x01, 0x00, 0x00, 0x00, 0x61, 0x20, 0x00, 0x00, 212 | 0x35, 0x00, 0x00, 0x00, 0x13, 0x04, 0x43, 0x2c, 0x10, 0x00, 0x00, 0x00, 213 | 0x01, 0x00, 0x00, 0x00, 0x34, 0x25, 0x00, 0x00, 0xf1, 0x30, 0x00, 0x00, 214 | 0x19, 0x00, 0x00, 0x00, 0x22, 0x47, 0xc8, 0x90, 0x51, 0x0e, 0x84, 0x1e, 215 | 0x00, 0x00, 0x00, 0x00, 0x4f, 0x64, 0x01, 0x00, 0x6f, 0x6d, 0x6e, 0x69, 216 | 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x53, 217 | 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x43, 0x2f, 0x43, 0x2b, 0x2b, 0x20, 218 | 0x54, 0x42, 0x41, 0x41, 0x6c, 0x6c, 0x76, 0x6d, 0x2e, 0x6c, 0x6f, 0x6f, 219 | 0x70, 0x2e, 0x6d, 0x75, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 220 | 0x73, 0x73, 0x00, 0x00, 0x13, 0x84, 0x01, 0x59, 0x21, 0x80, 0x01, 0x19, 221 | 0x6c, 0x08, 0xba, 0x0d, 0x03, 0x17, 0x06, 0xdf, 0x86, 0x41, 0x0c, 0xc4, 222 | 0xe0, 0xdb, 0x10, 0x78, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x0d, 0x43, 0x01, 223 | 0x06, 0xb3, 0x0c, 0x81, 0x10, 0xe8, 0x40, 0xc5, 0x90, 0x08, 0x0c, 0xd0, 224 | 0x43, 0x30, 0x02, 0x0a, 0x22, 0x5c, 0x60, 0xf0, 0xb0, 0x01, 0x31, 0x04, 225 | 0x01, 0x80, 0x84, 0x01, 0x01, 0xc3, 0x0d, 0x41, 0x02, 0x06, 0xb3, 0x0c, 226 | 0x81, 0x10, 0x04, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 227 | 0x5b, 0x86, 0x22, 0x10, 0x83, 0x2d, 0xc3, 0x11, 0x88, 0xc1, 0x96, 0x41, 228 | 0x91, 0x3e, 0x00, 0x00, 0x21, 0x31, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 229 | 0x13, 0x86, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 230 | 0x71, 0x20, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x32, 0x0e, 0x10, 0x22, 231 | 0x84, 0x00, 0x84, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 232 | 0x65, 0x0c, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x12, 0x03, 0x94, 0xf0, 233 | 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 234 | 0x06, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 235 | 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 236 | 0x01, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 237 | 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 238 | 0x1e, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 239 | 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 240 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 241 | 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 242 | 0xff, 0xff, 0xff, 0xff, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 243 | 0x5d, 0x0c, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x12, 0x03, 0x94, 0xb9, 244 | 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x6f, 0x62, 0x66, 0x75, 0x73, 0x63, 245 | 0x61, 0x74, 0x65, 0x58, 0x4f, 0x52, 0x31, 0x34, 0x2e, 0x30, 0x2e, 0x33, 246 | 0x61, 0x72, 0x6d, 0x36, 0x34, 0x2d, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2d, 247 | 0x6d, 0x61, 0x63, 0x6f, 0x73, 0x78, 0x31, 0x33, 0x2e, 0x30, 0x2e, 0x30, 248 | 0x64, 0x61, 0x74, 0x61, 0x2f, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4f, 249 | 0x62, 0x66, 0x75, 0x73, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x75, 0x6e, 250 | 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x63, 0x5f, 0x64, 0x65, 0x6f, 0x62, 0x66, 251 | 0x75, 0x73, 0x63, 0x61, 0x74, 0x65, 0x58, 0x4f, 0x52, 0x00, 0x00, 0x00, 252 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 253 | }; 254 | 255 | inline llvm::MemoryBufferRef getDeobfuscateBitcode() { 256 | return {{reinterpret_cast(deobfuscateBitcode), 257 | sizeof(deobfuscateBitcode)}, 258 | "deobfuscate"}; 259 | } 260 | 261 | #endif -------------------------------------------------------------------------------- /lib/Support/Function.cpp: -------------------------------------------------------------------------------- 1 | //===-- Support/Function.cpp - Function-related helpers -------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Support/Function.h" 11 | 12 | #include "Limoncello/Config/Config.h" 13 | #include "Limoncello/Support/Module.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace llvm; 20 | 21 | Function *localizeFunction(Module &module, Function &function, 22 | StringRef newName) { 23 | // Default the new local function's name to the external function's name if 24 | // an override is not provided. 25 | if (newName.empty()) 26 | newName = function.getName(); 27 | 28 | auto localCallee = 29 | module.getOrInsertFunction(newName, function.getFunctionType()); 30 | auto localFunction = cast(localCallee.getCallee()); 31 | 32 | auto localArg = localFunction->arg_begin(); 33 | auto arg = function.arg_begin(); 34 | 35 | ValueToValueMapTy argMap; 36 | for (auto argsEnd = function.arg_end(); arg != argsEnd; ++localArg, ++arg) 37 | argMap[&*arg] = &*localArg; 38 | 39 | SmallVector returnValues; 40 | CloneFunctionInto(localFunction, &function, argMap, 41 | CloneFunctionChangeType::DifferentModule, returnValues); 42 | 43 | return localFunction; 44 | } 45 | 46 | Function *createStubFunction(Module &module, StringRef name, FunctionType *type, 47 | BuildStubCallback build) { 48 | auto stub = Function::Create(type, Config::get()->getDefaultLinkage(), 49 | getModuleSpecificName(module, name), module); 50 | stub->addFnAttr(Attribute::AlwaysInline); 51 | stub->addFnAttr(Attribute::OptimizeNone); 52 | stub->setCallingConv(CallingConv::C); 53 | 54 | IRBuilder<> builder(BasicBlock::Create(stub->getContext(), "entry", stub)); 55 | build(stub, builder); 56 | 57 | return stub; 58 | } 59 | 60 | bool valueEscapesLocalBlock(Instruction &value) { 61 | for (auto const &use : value.uses()) { 62 | auto inst = cast(&use); 63 | if (inst->getParent() != inst->getParent() || isa(inst)) 64 | return true; 65 | } 66 | 67 | return false; 68 | } 69 | 70 | void repairSSA(Function &func) { 71 | auto entryBlock = &func.getEntryBlock(); 72 | 73 | std::vector phis; 74 | std::vector regs; 75 | do { 76 | phis.clear(); 77 | regs.clear(); 78 | 79 | for (auto &block : func) { 80 | for (auto &inst : block) { 81 | if (isa(inst)) { 82 | phis.emplace_back(cast(&inst)); 83 | continue; 84 | } 85 | 86 | bool canIgnore = 87 | isa(&inst) && inst.getParent() == entryBlock; 88 | bool needsFixing = 89 | valueEscapesLocalBlock(inst) || inst.isUsedOutsideOfBlock(&block); 90 | if (!canIgnore && needsFixing) 91 | regs.emplace_back(&inst); 92 | } 93 | } 94 | 95 | for (auto reg : regs) 96 | DemoteRegToStack(*reg, entryBlock->getTerminator()); 97 | for (auto phi : phis) 98 | DemotePHIToStack(phi, entryBlock->getTerminator()); 99 | } while (!regs.empty() || !phis.empty()); 100 | } 101 | -------------------------------------------------------------------------------- /lib/Support/Module.cpp: -------------------------------------------------------------------------------- 1 | //===-- Support/Module.cpp - Module-related helpers -----------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Support/Module.h" 11 | 12 | #include "Limoncello/Config/Config.h" 13 | 14 | #include 15 | #include 16 | 17 | using namespace llvm; 18 | 19 | std::string getModuleSpecificName(Module &module, StringRef name) { 20 | auto salt = arrayRefFromStringRef(module.getModuleIdentifier()); 21 | return name.str() + "$" + 22 | toHex(MD5::hash(salt), /*lowerCase=*/true).substr(0, 16); 23 | } 24 | 25 | Constant *getOrInsertGlobal(Module &module, StringRef name, Constant *value) { 26 | return module.getOrInsertGlobal(name, value->getType(), [&] { 27 | return new GlobalVariable(module, value->getType(), /*isConstant=*/false, 28 | Config::get()->getDefaultLinkage(), value, name); 29 | }); 30 | } -------------------------------------------------------------------------------- /lib/Support/Random.cpp: -------------------------------------------------------------------------------- 1 | //===-- Support/Random.cpp - Random number generation shorthand(s) --------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Support/Random.h" 11 | 12 | #include 13 | 14 | static std::mt19937 g_mt{std::random_device()()}; 15 | static std::uniform_int_distribution 16 | g_rng(0, std::numeric_limits::max()); 17 | 18 | void setRandomSeed(unsigned seed) { g_mt.seed(seed); } 19 | 20 | uint8_t getRandomInt8() { return static_cast(g_rng(g_mt)); } 21 | uint32_t getRandomInt32() { return static_cast(g_rng(g_mt)); } 22 | uint64_t getRandomInt64() { return static_cast(g_rng(g_mt)); } 23 | -------------------------------------------------------------------------------- /src/Plugin.cpp: -------------------------------------------------------------------------------- 1 | //===-- Plugin.cpp - Limoncello plugin entry point ------------------------===// 2 | // 3 | // Copyright (c) 2023 Jon Palmisciano. All rights reserved. 4 | // 5 | // Use of this source code is governed by the BSD 3-Clause license; a full copy 6 | // of the license can be found in the LICENSE.txt file. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "Limoncello/Config/Config.h" 11 | #include "Limoncello/Pass/ArithmeticMangler.h" 12 | #include "Limoncello/Pass/Bloater.h" 13 | #include "Limoncello/Pass/ConstantMangler.h" 14 | #include "Limoncello/Pass/Flattener.h" 15 | #include "Limoncello/Pass/StringObfuscator.h" 16 | #include "Limoncello/Support/Random.h" 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace llvm; 23 | 24 | static cl::opt configPath("limoncello-config", cl::init(""), 25 | cl::desc("Limoncello config path")); 26 | 27 | void registerCallbacks(PassBuilder &pb) { 28 | auto config = Config::load(configPath); 29 | if (!config || !config->isValid) { 30 | errs() << "Limoncello: Invalid configuration; skipping registration...\n"; 31 | return; 32 | } 33 | 34 | if (config->seed) 35 | setRandomSeed(config->seed); 36 | 37 | auto addVerifierPass = [&config](ModulePassManager &pm) { 38 | #if 0 39 | if (config->debug || !NDEBUG) 40 | pm.addPass(VerifierPass()); 41 | #endif 42 | }; 43 | 44 | pb.registerPipelineStartEPCallback([=](ModulePassManager &manager, auto) { 45 | if (config->stringObfuscator.isEnabled) { 46 | manager.addPass(StringObfuscatorPass()); 47 | addVerifierPass(manager); 48 | } 49 | if (config->bloater.isEnabled) { 50 | manager.addPass(BloaterPass()); 51 | addVerifierPass(manager); 52 | } 53 | if (config->flattener.isEnabled) { 54 | manager.addPass(createModuleToFunctionPassAdaptor(FlattenerPass())); 55 | addVerifierPass(manager); 56 | } 57 | if (config->constantMangler.isEnabled) { 58 | manager.addPass(ConstantManglerPass()); 59 | addVerifierPass(manager); 60 | } 61 | if (config->arithmeticMangler.isEnabled) { 62 | manager.addPass(ArithmeticManglerPass()); 63 | 64 | // XXX: Do not add a verifier pass after arithmetic mangling; the 65 | // verifier will whine about using `AlwaysInline` and `OptimizeNone` 66 | // together on the stub functions created in this pass. 67 | } 68 | }); 69 | } 70 | 71 | PassPluginLibraryInfo getPassPluginInfo() { 72 | return {LLVM_PLUGIN_API_VERSION, "Limoncello", LLVM_VERSION_STRING, 73 | registerCallbacks}; 74 | }; 75 | 76 | extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo llvmGetPassPluginInfo() { 77 | return getPassPluginInfo(); 78 | } 79 | -------------------------------------------------------------------------------- /test/BuildSamples.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from argparse import ArgumentParser 4 | from dataclasses import dataclass 5 | from enum import Enum 6 | from fnmatch import fnmatch 7 | import multiprocessing 8 | import subprocess 9 | from typing import Tuple 10 | 11 | SOURCE_DIR = "test/Samples" 12 | CONFIG_DIR = "test/Configs" 13 | BUILD_DIR = "test/Samples/Output" 14 | 15 | 16 | class SampleType(Enum): 17 | EXECUTABLE = 0 18 | LIBRARY = 1 19 | 20 | 21 | @dataclass 22 | class Context: 23 | clang_path: str 24 | plugin_path: str 25 | 26 | 27 | @dataclass 28 | class Sample: 29 | type: SampleType 30 | source: str 31 | config: str 32 | 33 | def name(self): 34 | return self.source.split(".", 1)[0] 35 | 36 | def output_name(self, opt_type: str, with_obfuscation: bool = True): 37 | tag = opt_type 38 | if with_obfuscation: 39 | tag += f",{self.config}" 40 | 41 | return f"{self.name()}.{tag}" 42 | 43 | def build(self, context: Context, opt_type: str, with_obfuscation: bool = True): 44 | output_path = f"{BUILD_DIR}/{self.output_name(opt_type, with_obfuscation)}" 45 | source_path = f"{SOURCE_DIR}/{self.source}" 46 | 47 | args = [context.clang_path] 48 | if self.type == SampleType.LIBRARY: 49 | args += ["-shared"] 50 | if with_obfuscation: 51 | args += [ 52 | f"-fplugin={context.plugin_path}", 53 | f"-fpass-plugin={context.plugin_path}", 54 | "-mllvm", 55 | f"-limoncello-config={CONFIG_DIR}/{self.config}.yml", 56 | ] 57 | args += ["-" + opt_type, "-o", output_path, source_path] 58 | 59 | subprocess.run(args) 60 | 61 | 62 | ALL_SAMPLES = [ 63 | Sample(SampleType.EXECUTABLE, "ArithmeticBonanza.c", "ArithmeticMangler"), 64 | Sample(SampleType.EXECUTABLE, "ConstantPaloozaRedux.c", "ConstantMangler"), 65 | Sample(SampleType.EXECUTABLE, "DoubleSwitch.c", "Flattener"), 66 | Sample(SampleType.EXECUTABLE, "Hello.c", "Default"), 67 | Sample(SampleType.EXECUTABLE, "NumberClassifier.c", "Flattener"), 68 | Sample(SampleType.EXECUTABLE, "NumberClassifier.c", "FlattenerRandomIDs"), 69 | Sample(SampleType.EXECUTABLE, "SimpleBlocks.c", "Bloater"), 70 | Sample(SampleType.EXECUTABLE, "SayHello.c", "StringObfuscator"), 71 | Sample(SampleType.LIBRARY, "SayHelloLibrary.c", "StringObfuscator"), 72 | Sample( 73 | SampleType.EXECUTABLE, 74 | "NumberClassifier.c", 75 | "Everything", 76 | ), 77 | ] 78 | 79 | 80 | def build_sample(sample_with_context: Tuple[Sample, Context]): 81 | (sample, context) = sample_with_context 82 | 83 | for opt_type in ["O0", "O2"]: 84 | print(f"Building sample {sample.output_name(opt_type, False)}...") 85 | sample.build(context, opt_type, False) 86 | 87 | print(f"Building sample {sample.output_name(opt_type)}...") 88 | sample.build(context, opt_type) 89 | 90 | 91 | if __name__ == "__main__": 92 | # NOTE: I hate Python's argument parser (irrationally so, due to it not 93 | # using capitalization anywhere) but I care more about this project not 94 | # having any Pip dependencies than I do fixing it. 95 | parser = ArgumentParser() 96 | parser.add_argument( 97 | "-c", 98 | dest="clang", 99 | type=str, 100 | help="path to Clang", 101 | metavar="CLANG", 102 | required=True, 103 | ) 104 | parser.add_argument( 105 | "-p", 106 | dest="plugin", 107 | type=str, 108 | help="path to Limoncello plugin", 109 | metavar="PLUGIN", 110 | required=True, 111 | ) 112 | parser.add_argument( 113 | "-f", dest="filter", type=str, help="filter samples to build", metavar="FILTER", default="*" 114 | ) 115 | parser.add_argument( 116 | "-j", 117 | dest="jobs", 118 | type=int, 119 | help="number of threads to use", 120 | metavar="N", 121 | default=8, 122 | ) 123 | 124 | args = parser.parse_args() 125 | 126 | context = Context(clang_path=args.clang, plugin_path=args.plugin) 127 | samples = [(s, context) for s in ALL_SAMPLES if fnmatch(s.name(), args.filter)] 128 | 129 | build_pool = multiprocessing.Pool(processes=args.jobs) 130 | build_pool.map(build_sample, samples) # pyright: ignore 131 | -------------------------------------------------------------------------------- /test/Configs/ArithmeticMangler.yml: -------------------------------------------------------------------------------- 1 | arithmetic-mangler: 2 | enabled: true 3 | -------------------------------------------------------------------------------- /test/Configs/Bloater.yml: -------------------------------------------------------------------------------- 1 | bloater: 2 | enabled: true 3 | rounds: 3 4 | probability: 60 5 | patterns: 6 | - ~main 7 | - .* 8 | -------------------------------------------------------------------------------- /test/Configs/ConstantMangler.yml: -------------------------------------------------------------------------------- 1 | constant-mangler: 2 | enabled: true 3 | -------------------------------------------------------------------------------- /test/Configs/Everything.yml: -------------------------------------------------------------------------------- 1 | arithmetic-mangler: 2 | enabled: true 3 | bloater: 4 | enabled: true 5 | constant-mangler: 6 | enabled: true 7 | flattener: 8 | enabled: true 9 | string-obfuscator: 10 | enabled: true 11 | -------------------------------------------------------------------------------- /test/Configs/Flattener.yml: -------------------------------------------------------------------------------- 1 | flattener: 2 | enabled: true 3 | patterns: 4 | - ~main 5 | - .* 6 | 7 | -------------------------------------------------------------------------------- /test/Configs/FlattenerRandomIDs.yml: -------------------------------------------------------------------------------- 1 | flattener: 2 | enabled: true 3 | patterns: 4 | - ~main 5 | - .* 6 | 7 | random-case-ids: true 8 | max-random-cases: 256 9 | -------------------------------------------------------------------------------- /test/Configs/None.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonpalmisc/limoncello/69710087901da405220b812d64628b7747a98a8e/test/Configs/None.yml -------------------------------------------------------------------------------- /test/Configs/StringObfuscator.yml: -------------------------------------------------------------------------------- 1 | string-obfuscator: 2 | enabled: true 3 | patterns: 4 | - sensitive_.* 5 | -------------------------------------------------------------------------------- /test/Samples/ArithmeticBonanza.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | volatile int n = 0; 5 | 6 | extern void doArithmetic(int m) { 7 | n = m; 8 | 9 | int o = n; 10 | o ^= 2; 11 | o += 17; 12 | n = o; 13 | 14 | int p = n; 15 | p -= 2; 16 | p &= 0x5555; 17 | n = p; 18 | } 19 | 20 | int main(int argc, char **argv) { 21 | if (argc < 2) { 22 | fprintf(stderr, "Usage: %s \n", argv[0]); 23 | return 1; 24 | } 25 | 26 | int m = atoi(argv[1]); 27 | doArithmetic(m); 28 | 29 | printf("%d\n", n); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /test/Samples/ConstantPaloozaRedux.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void caesar(char *data) { 4 | while (*data != 0) { 5 | if (*data >= 'A' && *data <= 'X') 6 | *data += 2; 7 | if (*data >= 'a' && *data <= 'x') 8 | *data += 1; 9 | 10 | ++data; 11 | } 12 | } 13 | 14 | char greeting[] = "Hello, world!"; 15 | 16 | int main(int argc, char **argv) { 17 | (void)argc; 18 | (void)argv; 19 | 20 | puts(greeting); 21 | caesar(greeting); 22 | puts(greeting); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /test/Samples/DoubleSwitch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | volatile int a = 0; 6 | volatile int b = 0; 7 | volatile int c = 0; 8 | volatile int d = 0; 9 | volatile int e = 0; 10 | volatile int f = 0; 11 | volatile int g = 0; 12 | volatile int h = 0; 13 | 14 | extern void classifyString(char const *string) { 15 | switch (strlen(string)) { 16 | case 0: 17 | a = 7832; 18 | puts("String is empty."); 19 | break; 20 | case 1: 21 | b = 12; 22 | puts("String is one character."); 23 | break; 24 | case 2: 25 | c = 212321; 26 | puts("String is two characters."); 27 | break; 28 | case 3: 29 | d = 3123; 30 | puts("String is three characters."); 31 | break; 32 | case 4: 33 | e = 48832; 34 | puts("String is four characters."); 35 | break; 36 | case 5: 37 | f = 5123; 38 | puts("String is five characters."); 39 | break; 40 | case 6: 41 | g = 677123; 42 | puts("String is six characters."); 43 | break; 44 | default: 45 | h = -1312; 46 | puts("String is seven or more characters."); 47 | break; 48 | } 49 | 50 | switch (string[0]) { 51 | case 'a': 52 | a = 12458; 53 | puts("String starts with A."); 54 | break; 55 | case 'b': 56 | b = 177723; 57 | puts("String starts with B."); 58 | break; 59 | case 'c': 60 | c = 265987; 61 | puts("String starts with C."); 62 | break; 63 | case 'd': 64 | d = 3987956; 65 | puts("String starts with D."); 66 | break; 67 | case 'e': 68 | e = 49072134; 69 | puts("String starts with E."); 70 | break; 71 | case 'f': 72 | f = 50751; 73 | puts("String starts with F."); 74 | break; 75 | case 'g': 76 | g = 68412; 77 | puts("String starts with G."); 78 | break; 79 | default: 80 | h = -1; 81 | puts("String starts with another letter."); 82 | break; 83 | } 84 | } 85 | 86 | int main(int argc, char **argv) { 87 | if (argc < 2) { 88 | fprintf(stderr, "Usage: %s \n", argv[0]); 89 | return 1; 90 | } 91 | 92 | classifyString(argv[1]); 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /test/Samples/Hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) { 4 | puts("Hello, world!"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /test/Samples/NumberClassifier.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern void classifyNumber(int number) { 5 | if (number % 2 == 0) { 6 | puts("Number is even."); 7 | 8 | if (number % 12 == 0) 9 | puts("Number is a multiple of twelve."); 10 | if (number > 100) 11 | puts("Number is a greater than one hundred."); 12 | if (number > 1000) 13 | puts("Number is a greater than one thousand."); 14 | } else { 15 | if (number % 7 == 0) 16 | puts("Number is an odd multiple of seven."); 17 | if (number < 10) 18 | puts("Number is a less than ten."); 19 | } 20 | } 21 | 22 | extern void classifyNumber2(int number) { 23 | int i = 0; 24 | for (i = 0; i < 10; ++i) { 25 | if (number % i == 0) { 26 | printf("Number is a multiple of %d.\n", i); 27 | } 28 | 29 | if (i == 2) { 30 | printf("Number is even."); 31 | } 32 | 33 | if (i + number == 18) 34 | break; 35 | 36 | if (i == 5) { 37 | printf("Boom!"); 38 | } 39 | } 40 | 41 | if (i != 10) 42 | printf("Bang!"); 43 | } 44 | 45 | int main(int argc, char **argv) { 46 | if (argc < 2) { 47 | fprintf(stderr, "Usage: %s \n", argv[0]); 48 | return 1; 49 | } 50 | 51 | int number = atoi(argv[1]); 52 | classifyNumber(number); 53 | classifyNumber2(number); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /test/Samples/SayHello.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) { 4 | (void)argc; 5 | (void)argv; 6 | 7 | puts("Hello, world!"); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /test/Samples/SayHelloLibrary.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void sayHello(void) { puts("Hello, world!"); } 4 | -------------------------------------------------------------------------------- /test/Samples/SimpleBlocks.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern void processNumber(int n) { 5 | if (n == 2) { 6 | puts("A"); 7 | } 8 | 9 | puts("B"); 10 | 11 | if (n == 3) { 12 | puts("C"); 13 | } 14 | 15 | puts("D"); 16 | 17 | if (n == 4) { 18 | puts("C"); 19 | } 20 | 21 | puts("E"); 22 | 23 | if (n == 5) { 24 | puts("C"); 25 | } 26 | } 27 | 28 | int main(int argc, char *argv[]) { 29 | if (argc < 2) { 30 | fprintf(stderr, "Usage: %s \n", argv[0]); 31 | return 1; 32 | } 33 | 34 | int n = atoi(argv[1]); 35 | 36 | processNumber(n); 37 | 38 | return 0; 39 | } 40 | --------------------------------------------------------------------------------