diff --git a/.github/scripts/s3-deploy.py b/.github/scripts/s3-deploy.py index 64b1e733..be77dec1 100755 --- a/.github/scripts/s3-deploy.py +++ b/.github/scripts/s3-deploy.py @@ -1,4 +1,9 @@ #!/usr/bin/env python3 + +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import sys import os import logging diff --git a/README.md b/README.md index 2df3fdb4..d4ca4005 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ # O-MVLL O-MVLL (in reference to [O-LLVM](https://github.com/obfuscator-llvm/obfuscator)) is a LLVM-based obfuscator -driven by a Python API and by new LLVM pass manager: +driven by Python and the LLVM pass manager. It can be run as follows: ```bash -clang++ -fpass-plugin=omvll.so main.cpp -o main +clang++ -fpass-plugin=libOMVLL.dylib main.cpp -o main ``` ```python @@ -51,7 +51,7 @@ class MyConfig(omvll.ObfuscationConfig): O-MVLL can be used with the Android NDK and an iOS toolchain but **it only supports the AArch64 architecture**. -For the details, you can checkout the documentation: [obfuscator.re/omvll](https://obfuscator.re/omvll) +For more details, please check out the documentation at [obfuscator.re/omvll](https://obfuscator.re/omvll). ### Download @@ -60,7 +60,7 @@ For the details, you can checkout the documentation: [obfuscator.re/omvll](https ### Contact -You can reach out by email at this address: `ping@obfuscator.re` +Feel free to reach out at `ping@obfuscator.re` for any doubt, issue, bug you may encounter. #### Maintainers @@ -68,7 +68,7 @@ You can reach out by email at this address: `ping@obfuscator.re` #### Author -- [Romain Thomas](https://www.romainthomas.fr): [@rh0main](https://twitter.com/rh0main) - `me@romainthomas.fr` +- [Romain Thomas](https://www.romainthomas.fr): [@rh0main](https://twitter.com/rh0main) (`me@romainthomas.fr`) #### Credits @@ -80,7 +80,9 @@ You can reach out by email at this address: `ping@obfuscator.re` ### License -O-MVLL is released under the same License as LLVM: [Apache License, Version 2.0](./LICENSE) +O-MVLL is released under the same License as LLVM: [Apache License, Version 2.0](./LICENSE). + +This project is partly funded by the EU and the European Cybersecurity Competence Center.
EU Co-funding Logo diff --git a/doc/conf.py b/doc/conf.py index 8afe619e..2078c8bb 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import os import sphinx import sys diff --git a/scripts/deps/common/compile_cpython310.sh b/scripts/deps/common/compile_cpython310.sh index 1878fc49..3db57b9e 100755 --- a/scripts/deps/common/compile_cpython310.sh +++ b/scripts/deps/common/compile_cpython310.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash + +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + # This script is used to compile cpython + set -e curl -LO https://www.python.org/ftp/python/3.10.7/Python-3.10.7.tgz diff --git a/scripts/deps/common/compile_pybind11.sh b/scripts/deps/common/compile_pybind11.sh index 58b0d61b..3de4c641 100755 --- a/scripts/deps/common/compile_pybind11.sh +++ b/scripts/deps/common/compile_pybind11.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash + +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + # This script is used to compile pybind11 + set -e curl -LO https://github.com/pybind/pybind11/archive/refs/tags/v2.10.3.tar.gz diff --git a/scripts/deps/common/compile_spdlog.sh b/scripts/deps/common/compile_spdlog.sh index 4a758b36..ab60d167 100755 --- a/scripts/deps/common/compile_spdlog.sh +++ b/scripts/deps/common/compile_spdlog.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash + +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + # This script is used to compile cpython + set -e curl -LO https://github.com/gabime/spdlog/archive/refs/tags/v1.10.0.tar.gz diff --git a/scripts/deps/generate_deps.sh b/scripts/deps/generate_deps.sh index baee36b3..217c4d26 100755 --- a/scripts/deps/generate_deps.sh +++ b/scripts/deps/generate_deps.sh @@ -1,4 +1,9 @@ #!/usr/bin/env bash + +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + set -e SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" diff --git a/scripts/deps/ndk/compile_llvm_r26d.sh b/scripts/deps/ndk/compile_llvm_r26d.sh index 47ab3ac5..c35a2d3e 100755 --- a/scripts/deps/ndk/compile_llvm_r26d.sh +++ b/scripts/deps/ndk/compile_llvm_r26d.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash + +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + # This script is used to compile the Android NDK r26d LLVM toolchain + set -e host=$(uname) diff --git a/scripts/docker/doc.sh b/scripts/docker/doc.sh index 983677c0..b1194cf0 100755 --- a/scripts/docker/doc.sh +++ b/scripts/docker/doc.sh @@ -1,4 +1,9 @@ #!/usr/bin/sh + +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + set -ex mkdir -p /deps && cd /deps diff --git a/scripts/docker/ndk_r26_compile.sh b/scripts/docker/ndk_r26_compile.sh index c468e739..9602e1cb 100755 --- a/scripts/docker/ndk_r26_compile.sh +++ b/scripts/docker/ndk_r26_compile.sh @@ -1,6 +1,10 @@ #!/usr/bin/sh -set -ex +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + +set -ex mkdir -p /data && cd /data cp /third-party/omvll-deps-ndk-*/android-llvm-toolchain-r26d.tar.gz . @@ -16,7 +20,6 @@ tar xzvf Python-slim.tar.gz tar xzvf pybind11.tar.gz tar xzvf spdlog-1.10.0-Linux.tar.gz - # Android NDK is bootstrapped in a so-called 2-stage process. To avoid ABI incompatibilities, # we build our plugin with the same toolchain used to build the NDK itself (stage-1). Then, # we link the plugin against stage-2 build artifacts. diff --git a/scripts/docker/xcode_15_compile.sh b/scripts/docker/xcode_15_compile.sh index 610efa92..b27c28c4 100755 --- a/scripts/docker/xcode_15_compile.sh +++ b/scripts/docker/xcode_15_compile.sh @@ -1,5 +1,11 @@ #!/usr/bin/sh + +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + set -ex + mkdir -p /deps && cd /deps cp /third-party/omvll-deps-xcode-*/LLVM-16.0.0git-arm64-Darwin.tar.gz . diff --git a/scripts/package.py b/scripts/package.py index 7e9ee913..75a9e6e1 100644 --- a/scripts/package.py +++ b/scripts/package.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import argparse import sys from pathlib import Path diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee98a103..aa2007f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,7 +75,6 @@ else() set(OMVLL_DEBUG 1) endif() - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/omvll/config.hpp.in" "${CMAKE_CURRENT_BINARY_DIR}/include/omvll/config.hpp") @@ -133,7 +132,6 @@ endif() target_link_options(OMVLL PUBLIC ${OMVLL_LINK_OPT}) - target_link_libraries(OMVLL PRIVATE spdlog::spdlog pybind11::headers @@ -147,7 +145,6 @@ if(NOT APPLE) ) endif() - set(LLVM_LIBS_DEP demangle OrcJIT diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9d78d410..5a9fb1a4 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,10 +1,9 @@ - target_sources(OMVLL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/omvll_config.cpp ${CMAKE_CURRENT_SOURCE_DIR}/log.cpp ${CMAKE_CURRENT_SOURCE_DIR}/plugin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Jitter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/jitter.cpp ) add_subdirectory("python") diff --git a/src/core/Jitter.cpp b/src/core/Jitter.cpp deleted file mode 100644 index e98dba48..00000000 --- a/src/core/Jitter.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "omvll/Jitter.hpp" - -#include "omvll/utils.hpp" -#include "omvll/log.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace llvm; - -namespace omvll { - -Jitter::Jitter(const std::string &Triple) - : Triple_{Triple}, Ctx_{new LLVMContext{}} { - InitializeNativeTarget(); - InitializeNativeTargetAsmParser(); - InitializeNativeTargetAsmPrinter(); -} - -std::unique_ptr Jitter::compile(llvm::Module& M) { - static ExitOnError ExitOnErr; - auto JITB = ExitOnErr(orc::LLJITBuilder().create()); - - std::unique_ptr Mod = CloneModule(M); - - auto Context = std::make_unique(); - ExitOnErr(JITB->addIRModule(llvm::orc::ThreadSafeModule(std::move(Mod), std::move(Context)))); - return JITB; -} - -size_t Jitter::getFunctionSize(llvm::object::ObjectFile& Obj, llvm::StringRef Name) { - if (auto* ELF = dyn_cast(&Obj)) { - for (const object::ELFSymbolRef& Sym : ELF->symbols()) { - if (Sym.getELFType() != ELF::STT_FUNC) { - continue; - } - if (auto Str = Sym.getName()) { - if (*Str == Name) { - return Sym.getSize(); - } - } - } - fatalError("jitAsm: Unable to find symbol '" + Name.str() + "'"); - } - //if (auto* MachO = dyn_cast(&Obj)) { - // for (const object::SymbolRef Sym : MachO->symbols()) { - // object::DataRefImpl Impl = Sym.getRawDataRefImpl(); - // if (auto SName = MachO->getSymbolName(Impl)) { - // if (*SName != Name) { - // continue; - // } - // if (auto ItSec = MachO->getSymbolSection(Impl)) { - // uint64_t size = (*ItSec)->getSize(); - // } - // //return - // } - // } - // fatalError("jitAsm: Unable to find symbol '" + Name.str() + "'"); - //} - fatalError("jitAsm: Unsupported file format"); -} - -std::unique_ptr Jitter::jitAsm(const std::string& Asm, size_t Size) { - static constexpr const char FNAME[] = "__omvll_asm_func"; - - static ExitOnError ExitOnErr; - - auto Context = std::make_unique(); - auto M = std::make_unique("__omvll_asm_jit", *Context); - - Function *F = Function::Create(llvm::FunctionType::get(llvm::Type::getVoidTy(*Context), {}, false), - Function::ExternalLinkage, FNAME, M.get()); - - llvm::BasicBlock *BB = llvm::BasicBlock::Create(*Context, "EntryBlock", F); - IRBuilder<> builder(BB); - - auto* FType = llvm::FunctionType::get(builder.getVoidTy(), false); - InlineAsm* rawAsm = InlineAsm::get(FType, Asm, "", - /* hasSideEffects */ true, - /* isStackAligned */ true - ); - - builder.CreateCall(FType, rawAsm); - builder.CreateRetVoid(); - - - orc::LLJITBuilder Builder; - std::string TT = Triple_; - orc::JITTargetMachineBuilder JTMB{llvm::Triple(TT)}; - JTMB.setRelocationModel(Reloc::Model::PIC_); - JTMB.setCodeModel(CodeModel::Large); - JTMB.setCodeGenOptLevel(CodeGenOpt::Level::None); - - Builder - .setPlatformSetUp(orc::setUpInactivePlatform) // /!\Only for iOS??? - .setJITTargetMachineBuilder(JTMB); - - Builder - .setJITTargetMachineBuilder(std::move(JTMB)); - - std::unique_ptr JITB = ExitOnErr(Builder.create()); - - if (!JITB) { - fatalError("JITB is null"); - } - - M->setDataLayout(JITB->getDataLayout()); - - auto& IRC = JITB->getIRCompileLayer(); - orc::IRCompileLayer::IRCompiler& Compiler = IRC.getCompiler(); - if (auto Res = Compiler(*M)) { - std::unique_ptr Buff = std::move(*Res); - size_t FunSize = Size * /* Sizeof AArch64 inst=*/ 4; - - //if (auto E = object::ObjectFile::createObjectFile(*Buff)) { - // std::unique_ptr Obj = std::move(E.get()); - // FunSize = getFunctionSize(*Obj, FNAME); - //} - - if (FunSize == 0) { - fatalError("Can't get the function size"); - } - - ExitOnErr(JITB->addObjectFile(std::move(Buff))); - - if (auto L = JITB->lookup(FNAME)) { - auto *ptr = reinterpret_cast(L->getValue()); - return MemoryBuffer::getMemBufferCopy({ptr, FunSize}); - } - } - fatalError("Can't compile: " + Asm); -} - -} diff --git a/src/core/jitter.cpp b/src/core/jitter.cpp new file mode 100644 index 00000000..dad7db17 --- /dev/null +++ b/src/core/jitter.cpp @@ -0,0 +1,152 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include "llvm/AsmParser/Parser.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Transforms/Utils/Cloning.h" + +#include "omvll/jitter.hpp" +#include "omvll/utils.hpp" + +using namespace llvm; + +static constexpr auto AsmFunctionName = "__omvll_asm_func"; + +namespace llvm { +namespace object { +class MachOObjectFile; +} // end namespace object +} // end namespace llvm + +namespace omvll { + +Jitter::Jitter(const std::string &Triple) + : Triple{Triple}, Ctx{new LLVMContext{}} { + InitializeNativeTarget(); + InitializeNativeTargetAsmParser(); + InitializeNativeTargetAsmPrinter(); +} + +std::unique_ptr Jitter::compile(Module &M) { + static ExitOnError ExitOnErr; + auto JITB = ExitOnErr(orc::LLJITBuilder().create()); + std::unique_ptr ClonedM = CloneModule(M); + auto Context = std::make_unique(); + + ExitOnErr(JITB->addIRModule( + orc::ThreadSafeModule(std::move(ClonedM), std::move(Context)))); + return JITB; +} + +size_t Jitter::getFunctionSize(object::ObjectFile &Obj, StringRef Name) { + if (auto *ELF = dyn_cast(&Obj)) { + for (const object::ELFSymbolRef &Sym : ELF->symbols()) { + if (Sym.getELFType() != ELF::STT_FUNC) + continue; + if (auto Str = Sym.getName()) + if (*Str == Name) + return Sym.getSize(); + } + fatalError("jitAsm: Unable to find symbol " + Name.str()); + } + +#if 0 + if (auto *MachO = dyn_cast(&Obj)) { + for (const object::SymbolRef Sym : MachO->symbols()) { + object::DataRefImpl Impl = Sym.getRawDataRefImpl(); + if (auto SName = MachO->getSymbolName(Impl)) { + if (*SName != Name) + continue; + } + if (auto ItSec = MachO->getSymbolSection(Impl)) { + uint64_t Size = (*ItSec)->getSize(); + return Size; + } + } + fatalError("jitAsm: Unable to find symbol '" + Name.str() + "'"); + } +#endif + + fatalError("jitAsm: Unsupported file format"); +} + +std::unique_ptr Jitter::jitAsm(const std::string &Asm, + size_t Size) { + static ExitOnError ExitOnErr; + + auto Context = std::make_unique(); + auto M = std::make_unique("__omvll_asm_jit", *Context); + + Function *F = + Function::Create(FunctionType::get(Type::getVoidTy(*Context), {}, false), + Function::ExternalLinkage, AsmFunctionName, M.get()); + + BasicBlock *BB = BasicBlock::Create(*Context, "EntryBlock", F); + IRBuilder<> IRB(BB); + + auto *FType = FunctionType::get(IRB.getVoidTy(), false); + InlineAsm *RawAsm = InlineAsm::get(FType, Asm, "", /* hasSideEffects */ true, + /* isStackAligned */ true); + IRB.CreateCall(FType, RawAsm); + IRB.CreateRetVoid(); + + orc::LLJITBuilder Builder; + std::string TT = Triple; + orc::JITTargetMachineBuilder JTMB{llvm::Triple(TT)}; + JTMB.setRelocationModel(Reloc::Model::PIC_); + JTMB.setCodeModel(CodeModel::Large); + JTMB.setCodeGenOptLevel(CodeGenOpt::Level::None); + + Builder.setPlatformSetUp(orc::setUpInactivePlatform) + .setJITTargetMachineBuilder(JTMB); + Builder.setJITTargetMachineBuilder(std::move(JTMB)); + + std::unique_ptr JITB = ExitOnErr(Builder.create()); + if (!JITB) + fatalError("JITB is null"); + + M->setDataLayout(JITB->getDataLayout()); + + auto &IRC = JITB->getIRCompileLayer(); + orc::IRCompileLayer::IRCompiler &Compiler = IRC.getCompiler(); + if (auto Res = Compiler(*M)) { + std::unique_ptr Buff = std::move(*Res); + size_t FunSize = Size * /* Sizeof AArch64 inst=*/4; + +#if 0 + if (auto E = object::ObjectFile::createObjectFile(*Buff)) { + std::unique_ptr Obj = std::move(E.get()); + FunSize = getFunctionSize(*Obj, AsmFunctionName); + } +#endif + + if (FunSize == 0) + fatalError("Cannot retrieve function size"); + + ExitOnErr(JITB->addObjectFile(std::move(Buff))); + if (auto L = JITB->lookup(AsmFunctionName)) { + auto *Ptr = reinterpret_cast(L->getValue()); + return MemoryBuffer::getMemBufferCopy({Ptr, FunSize}); + } + } + + fatalError("Cannot compile " + Asm); +} + +} // end namespace omvll diff --git a/src/core/log.cpp b/src/core/log.cpp index b027b91f..43b8fe38 100644 --- a/src/core/log.cpp +++ b/src/core/log.cpp @@ -1,61 +1,74 @@ -#include "omvll/log.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// #include "spdlog/spdlog.h" #include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/android_sink.h" -Logger::Logger(Logger&&) = default; -Logger& Logger::operator=(Logger&&) = default; +#include "omvll/log.hpp" + +static constexpr auto EnvLogName = "OMVLL_TRUNCATE_LOG"; +static constexpr auto LogFileName = "omvll.log"; +static constexpr auto LogName = "omvll"; + +Logger::Logger(Logger &&) = default; +Logger &Logger::operator=(Logger &&) = default; Logger::~Logger() = default; Logger::Logger() { - bool truncate = false; - if (getenv("OMVLL_TRUNCATE_LOG")) - truncate = true; - - sink_ = spdlog::basic_logger_mt("omvll", "omvll.log", truncate); - //sink_ = spdlog::stdout_color_mt("omvll"); - sink_->set_pattern("%v"); - sink_->set_level(spdlog::level::debug); - sink_->flush_on(spdlog::level::debug); + bool Truncate = false; + if (getenv(EnvLogName)) + Truncate = true; + + Sink = spdlog::basic_logger_mt(LogName, LogFileName, Truncate); + Sink->set_pattern("%v"); + Sink->set_level(spdlog::level::debug); + Sink->flush_on(spdlog::level::debug); } -Logger& Logger::instance() { - if (instance_ == nullptr) { - instance_ = new Logger{}; +Logger &Logger::instance() { + if (!Instance) { + Instance = new Logger{}; std::atexit(destroy); } - return *instance_; + return *Instance; } - -void Logger::destroy() { - delete instance_; -} +void Logger::destroy() { delete Instance; } void Logger::disable() { - Logger::instance().sink_->set_level(spdlog::level::off); + Logger::instance().Sink->set_level(spdlog::level::off); } void Logger::enable() { - Logger::instance().sink_->set_level(spdlog::level::warn); + Logger::instance().Sink->set_level(spdlog::level::warn); } -void Logger::set_level(spdlog::level::level_enum lvl) { - Logger& instance = Logger::instance(); - instance.sink_->set_level(lvl); - instance.sink_->flush_on(lvl); +void Logger::set_level(spdlog::level::level_enum Level) { + Logger &Instance = Logger::instance(); + Instance.Sink->set_level(Level); + Instance.Sink->flush_on(Level); } -void Logger::set_level(LOG_LEVEL lvl) { - switch (lvl) { - case LOG_LEVEL::DEBUG: Logger::set_level(spdlog::level::debug); return; - case LOG_LEVEL::TRACE: Logger::set_level(spdlog::level::trace); return; - case LOG_LEVEL::INFO: Logger::set_level(spdlog::level::info); return; - case LOG_LEVEL::WARN: Logger::set_level(spdlog::level::warn); return; - case LOG_LEVEL::ERR: Logger::set_level(spdlog::level::err); return; +void Logger::set_level(LogLevel Level) { + switch (Level) { + case LogLevel::Debug: + Logger::set_level(spdlog::level::debug); + return; + case LogLevel::Trace: + Logger::set_level(spdlog::level::trace); + return; + case LogLevel::Info: + Logger::set_level(spdlog::level::info); + return; + case LogLevel::Warn: + Logger::set_level(spdlog::level::warn); + return; + case LogLevel::Err: + Logger::set_level(spdlog::level::err); + return; } } - - diff --git a/src/core/omvll_config.cpp b/src/core/omvll_config.cpp index 763db84c..894be4db 100644 --- a/src/core/omvll_config.cpp +++ b/src/core/omvll_config.cpp @@ -1,28 +1,34 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "omvll/omvll_config.hpp" #include "omvll/passes.hpp" namespace omvll { -config_t config; -void init_default_config() { - config.passes = { - AntiHook::name().str(), - StringEncoding::name().str(), - OpaqueFieldAccess::name().str(), - ControlFlowFlattening::name().str(), - BreakControlFlow::name().str(), +OMVLLConfig Config; + +void initDefaultConfig() { + Config.Passes = { + AntiHook::name().str(), + StringEncoding::name().str(), + + OpaqueFieldAccess::name().str(), + ControlFlowFlattening::name().str(), + BreakControlFlow::name().str(), - OpaqueConstants::name().str(), - Arithmetic::name().str(), + OpaqueConstants::name().str(), + Arithmetic::name().str(), - // Last pass - Cleaning::name().str(), + // Last pass. + Cleaning::name().str(), }; - config.cleaning = true; - config.inline_jni_wrappers = true; - config.shuffle_functions = true; + Config.Cleaning = true; + Config.InlineJniWrappers = true; + Config.ShuffleFunctions = true; } -} - +} // end namespace omvll diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp index f004f1c9..b62f61e8 100644 --- a/src/core/plugin.cpp +++ b/src/core/plugin.cpp @@ -1,157 +1,145 @@ -#include "omvll/passes.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include + +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" + #include "omvll/PyConfig.hpp" +#include "omvll/jitter.hpp" #include "omvll/log.hpp" +#include "omvll/passes.hpp" #include "omvll/utils.hpp" -#include "omvll/Jitter.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include using namespace llvm; -#define REGISTER_PASS(X) \ - do { \ - if (pass == X::name()) { \ - SDEBUG("Registering {}", pass); \ - MPM.addPass(X()); \ - continue; \ - } \ - } while (0) - -template <> -struct yaml::MappingTraits { - static void mapping(IO& io, omvll::yaml_config_t& config) { - io.mapOptional("OMVLL_PYTHONPATH", config.PYTHONPATH, ""); - io.mapOptional("OMVLL_CONFIG", config.OMVLL_CONFIG, ""); - } +template <> struct yaml::MappingTraits { + static void mapping(IO &IO, omvll::YamlConfig &Config) { + IO.mapOptional("OMVLL_PYTHONPATH", Config.PythonPath, ""); + IO.mapOptional("OMVLL_CONFIG", Config.OMVLLConfig, ""); + } }; -std::string expand_abs_path(StringRef path, StringRef base) { - if (sys::path::is_absolute(path)) { - if (!sys::fs::exists(path)) - SWARN("Absolute path from doesn't exist!"); - return path.str(); +static std::string expandAbsPath(StringRef Path, StringRef Base) { + if (sys::path::is_absolute(Path)) { + if (!sys::fs::exists(Path)) + SWARN("Absolute path from does not exist"); + return Path.str(); } - SmallString<256> YConfigAbs = base; - sys::path::append(YConfigAbs, path); + SmallString<256> YConfigAbs = Base; + sys::path::append(YConfigAbs, Path); if (!sys::fs::exists(YConfigAbs)) - SWARN("Relative path doesn't exist!"); + SWARN("Relative path does not exist"); + return YConfigAbs.str().str(); } -bool load_yamlconfig(StringRef dir, StringRef filename) { - SmallString<256> YConfig = dir; - sys::path::append(YConfig, filename); +static bool loadYamlConfig(StringRef Dir, StringRef FileName) { + SmallString<256> YConfig = Dir; + sys::path::append(YConfig, FileName); if (!sys::fs::exists(YConfig)) return false; SINFO("Loading omvll.yml from {}", YConfig.str()); auto Buffer = MemoryBuffer::getFile(YConfig); if (!Buffer) { - SERR("Can't read '{}': {}", YConfig.str(), Buffer.getError().message()); + SERR("Cannot read '{}': {}", YConfig.str(), Buffer.getError().message()); return false; } - yaml::Input yin(**Buffer); - omvll::yaml_config_t yconfig; - yin >> yconfig; - SINFO("OMVLL_PYTHONPATH = {}", yconfig.PYTHONPATH); - yconfig.PYTHONPATH = expand_abs_path(yconfig.PYTHONPATH, dir); + yaml::Input Input(**Buffer); + omvll::YamlConfig Config; + Input >> Config; + + SINFO("OMVLL_PYTHONPATH = {}", Config.PythonPath); + Config.PythonPath = expandAbsPath(Config.PythonPath, Dir); - SINFO("OMVLL_CONFIG = {}", yconfig.OMVLL_CONFIG); - yconfig.OMVLL_CONFIG = expand_abs_path(yconfig.OMVLL_CONFIG, dir); + SINFO("OMVLL_CONFIG = {}", Config.OMVLLConfig); + Config.OMVLLConfig = expandAbsPath(Config.OMVLLConfig, Dir); - omvll::PyConfig::yconfig = yconfig; + omvll::PyConfig::YConfig = Config; return true; } -bool find_yamlconfig(std::string dir) { +static bool findYamlConfig(std::string Dir) { while (true) { - SINFO("Looking for omvll.yml in {}", dir); - if (load_yamlconfig(dir, omvll::PyConfig::YAML_FILE)) + SINFO("Looking for omvll.yml in {}", Dir); + if (loadYamlConfig(Dir, omvll::PyConfig::YamlFile)) return true; - if (sys::path::has_parent_path(dir)) { - dir = sys::path::parent_path(dir); + if (sys::path::has_parent_path(Dir)) { + Dir = sys::path::parent_path(Dir); } else { return false; } } } -void omvll::init_yamlconfig() { - SmallString<256> cwd; - if (auto err = sys::fs::current_path(cwd)) { - SERR("Can't determine the current path: '{}''", err.message()); +void omvll::initYamlConfig() { + SmallString<256> CurrentPath; + if (auto Err = sys::fs::current_path(CurrentPath)) { + SERR("Cannot determine the current path: '{}'", Err.message()); } else { - if (find_yamlconfig(cwd.str().str())) + if (findYamlConfig(CurrentPath.str().str())) return; } - Dl_info info; - if (!dladdr((void*)find_yamlconfig, &info)) { - SERR("Can't determine plugin file path"); + Dl_info Info; + if (!dladdr((void *)findYamlConfig, &Info)) { + SERR("Cannot determine plugin file path"); } else { - SmallString<256> PluginDir = StringRef(info.dli_fname); + SmallString<256> PluginDir = StringRef(Info.dli_fname); sys::path::remove_filename(PluginDir); - if (find_yamlconfig(PluginDir.str().str())) + if (findYamlConfig(PluginDir.str().str())) return; } - SINFO("Didn't find omvll.yml"); + SINFO("Could not find omvll.yml"); } - PassPluginLibraryInfo getOMVLLPluginInfo() { - static std::atomic ONCE_FLAG = false; + static std::atomic Once = false; Logger::set_level(spdlog::level::level_enum::debug); - omvll::init_yamlconfig(); - omvll::init_pythonpath(); - return {LLVM_PLUGIN_API_VERSION, "OMVLL", "0.0.1", - [](PassBuilder &PB) { + omvll::initYamlConfig(); + omvll::initPythonpath(); + return {LLVM_PLUGIN_API_VERSION, "OMVLL", "1.1.0", [](PassBuilder &PB) { try { - auto& instance = omvll::PyConfig::instance(); - SDEBUG("OMVLL Path: {}", instance.config_path()); + auto &Instance = omvll::PyConfig::instance(); + SDEBUG("Found OMVLL at: {}", Instance.configPath()); PB.registerPipelineEarlySimplificationEPCallback( - [&] (ModulePassManager &MPM, OptimizationLevel opt) { - if (ONCE_FLAG) { - return true; - } - for (const std::string& pass : instance.get_passes()) { - REGISTER_PASS(omvll::AntiHook); - REGISTER_PASS(omvll::StringEncoding); - - REGISTER_PASS(omvll::OpaqueFieldAccess); - REGISTER_PASS(omvll::ControlFlowFlattening); - REGISTER_PASS(omvll::BreakControlFlow); - - REGISTER_PASS(omvll::OpaqueConstants); - REGISTER_PASS(omvll::Arithmetic); - + [&](ModulePassManager &MPM, OptimizationLevel Opt) { + if (Once) + return true; + + MPM.addPass(omvll::AntiHook()); + MPM.addPass(omvll::StringEncoding()); + MPM.addPass(omvll::OpaqueFieldAccess()); + MPM.addPass(omvll::ControlFlowFlattening()); + MPM.addPass(omvll::BreakControlFlow()); + MPM.addPass(omvll::OpaqueConstants()); + MPM.addPass(omvll::Arithmetic()); #ifdef OMVLL_EXPERIMENTAL - /* ObjCleaner must be the last pass as function's name could be - * changed, which can be confusing of the user - */ - REGISTER_PASS(omvll::ObjCleaner); + // ObjCleaner must be the last pass as function's name could + // be changed, which can be confusing for the user. + MPM.addPass(omvll::ObjCleaner()); #endif - REGISTER_PASS(omvll::Cleaning); - } - ONCE_FLAG = true; - return true; - } - ); - } catch (const std::exception& e) { - omvll::fatalError(e.what()); + MPM.addPass(omvll::Cleaning()); + + Once = true; + return true; + }); + } catch (const std::exception &Exc) { + omvll::fatalError(Exc.what()); } }}; } @@ -161,5 +149,3 @@ extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() { return getOMVLLPluginInfo(); } - - diff --git a/src/core/python/PyConfig.cpp b/src/core/python/PyConfig.cpp index 74ff4c22..05b30823 100644 --- a/src/core/python/PyConfig.cpp +++ b/src/core/python/PyConfig.cpp @@ -1,73 +1,76 @@ -#include "omvll/PyConfig.hpp" -#include "omvll/passes.hpp" -#include "omvll/utils.hpp" -#include "omvll/log.hpp" -#include "omvll/versioning.hpp" -#include "omvll/omvll_config.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// -#include "PyObfuscationConfig.hpp" -#include "init.hpp" +#include +#include #include #include -#include -#include +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" -#include -#include -#include +#include "omvll/PyConfig.hpp" +#include "omvll/omvll_config.hpp" +#include "omvll/utils.hpp" +#include "omvll/versioning.hpp" + +#include "PyObfuscationConfig.hpp" +#include "init.hpp" namespace py = pybind11; -namespace omvll { +using namespace pybind11::literals; +namespace omvll { -void init_pythonpath() { - if (char* config = getenv(PyConfig::PYENV_KEY)) { - Py_SetPath(Py_DecodeLocale(config, nullptr)); - setenv("PYTHONHOME", config, true); +void initPythonpath() { + if (char *Config = getenv(PyConfig::PyEnv_Key)) { + Py_SetPath(Py_DecodeLocale(Config, nullptr)); + setenv("PYTHONHOME", Config, true); return; } - if (!PyConfig::yconfig.PYTHONPATH.empty()) { - Py_SetPath(Py_DecodeLocale(PyConfig::yconfig.PYTHONPATH.c_str(), nullptr)); - setenv("PYTHONHOME", PyConfig::yconfig.PYTHONPATH.c_str(), true); + if (!PyConfig::YConfig.PythonPath.empty()) { + Py_SetPath(Py_DecodeLocale(PyConfig::YConfig.PythonPath.c_str(), nullptr)); + setenv("PYTHONHOME", PyConfig::YConfig.PythonPath.c_str(), true); return; } - #if defined(__linux__) - if (auto* hdl = dlopen("libpython3.10.so", RTLD_LAZY)) { - char PATH[400]; - int ret = dlinfo(hdl, RTLD_DI_ORIGIN, PATH); - if (ret != 0) { - return; - } - std::string PythonPath = PATH; - PythonPath.append("/python3.10"); - Py_SetPath(Py_DecodeLocale(PythonPath.c_str(), nullptr)); - setenv("PYTHONHOME", PythonPath.c_str(), true); + +#if defined(__linux__) + if (auto *Hdl = dlopen("libpython3.10.so", RTLD_LAZY)) { + char Path[400]; + int Ret = dlinfo(Hdl, RTLD_DI_ORIGIN, Path); + if (Ret != 0) return; - } - #endif + + std::string PythonPath = Path; + PythonPath.append("/python3.10"); + Py_SetPath(Py_DecodeLocale(PythonPath.c_str(), nullptr)); + setenv("PYTHONHOME", PythonPath.c_str(), true); + return; + } +#endif } -void omvll_ctor(py::module_& m) { - init_default_config(); +void OMVLLCtor(py::module_ &m) { + initDefaultConfig(); m.attr("LLVM_VERSION") = OMVLL_LLVM_VERSION_STRING; m.attr("OMVLL_VERSION") = OMVLL_VERSION; m.attr("OMVLL_VERSION_FULL") = "OMVLL Version: " OMVLL_VERSION " / " OMVLL_LLVM_VERSION_STRING " (" OMVLL_LLVM_VERSION ")"; - py::class_(m, "config_t", - R"delim( + py::class_(m, "OMVLLConfig", + R"delim( This class is used to configure the global behavior of O-MVLL. It can be accessed through the global :attr:`omvll.config` attribute )delim") - .def_readwrite("passes", - &config_t::passes, - R"delim( + .def_readwrite("passes", &OMVLLConfig::Passes, + R"delim( This **ordered** list contains the sequence of the obfuscation passes that must be used. It should not be modified unless you know what you do. @@ -78,9 +81,8 @@ void omvll_ctor(py::module_& m) { )delim") - .def_readwrite("inline_jni_wrappers", - &config_t::inline_jni_wrappers, - R"delim( + .def_readwrite("inline_jni_wrappers", &OMVLLConfig::InlineJniWrappers, + R"delim( This boolean attribute is used to force inlining JNI C++ wrapper. For instance ``GetStringChars``: @@ -92,9 +94,8 @@ void omvll_ctor(py::module_& m) { The default value is ``True``. )delim") - .def_readwrite("shuffle_functions", - &config_t::shuffle_functions, - R"delim( + .def_readwrite("shuffle_functions", &OMVLLConfig::ShuffleFunctions, + R"delim( Whether the postition of Module's functions should be shuffled. This randomization is used to avoid the same (relative) position of the functions @@ -119,22 +120,21 @@ void omvll_ctor(py::module_& m) { If this value is set to ``True`` (which is the default value), the sequence is randomized. )delim"); - m.attr("config") = &config; + m.attr("config") = &Config; py_init_obf_opt(m); py_init_llvm_bindings(m); py_init_log(m); py::class_(m, "ObfuscationConfig", - R"delim( + R"delim( This class must be inherited by the user to define where and how the obfuscation passes must be enabled. )delim") - .def(py::init<>()) + .def(py::init<>()) - .def("obfuscate_string", - &ObfuscationConfig::obfuscate_string, - R"delim( + .def("obfuscate_string", &ObfuscationConfig::obfuscateString, + R"delim( The default user-callback used to configure strings obfuscation. In addition to the associated class options, O-MVLL interprets these return values as follows: @@ -154,11 +154,11 @@ void omvll_ctor(py::module_& m) { +--------------+-------------------------------------+ See the :omvll:`strings-encoding` documentation. - )delim", "module"_a, "function"_a, "string"_a) + )delim", + "module"_a, "function"_a, "string"_a) - .def("break_control_flow", - &ObfuscationConfig::break_control_flow, - R"delim( + .def("break_control_flow", &ObfuscationConfig::breakControlFlow, + R"delim( The default user-callback for the pass that breaks the control flow. @@ -175,11 +175,11 @@ void omvll_ctor(py::module_& m) { +--------------+-------------------------------------------------+ See the :omvll:`control-flow-breaking` documentation. - )delim", "module"_a, "function"_a) + )delim", + "module"_a, "function"_a) - .def("flatten_cfg", - &ObfuscationConfig::flatten_cfg, - R"delim( + .def("flatten_cfg", &ObfuscationConfig::controlFlowGraphFlattening, + R"delim( The default user-callback used to configure the control-flow flattening pass. @@ -196,11 +196,11 @@ void omvll_ctor(py::module_& m) { +--------------+------------------------------------------------------+ See the :omvll:`control-flow-flattening` documentation. - )delim", "module"_a, "function"_a) + )delim", + "module"_a, "function"_a) - .def("obfuscate_struct_access", - &ObfuscationConfig::obfuscate_struct_access, - R"delim( + .def("obfuscate_struct_access", &ObfuscationConfig::obfuscateStructAccess, + R"delim( The default user-callback when obfuscating structures accesses. In addition to the associated class options, O-MVLL interprets these return values as follows: @@ -216,11 +216,12 @@ void omvll_ctor(py::module_& m) { +--------------+---------------------------------------------+ See the :omvll:`opaque-fields-access` documentation. - )delim", "module"_a, "function"_a, "struct"_a) + )delim", + "module"_a, "function"_a, "struct"_a) - .def("obfuscate_variable_access", - &ObfuscationConfig::obfuscate_variable_access, - R"delim( + .def("obfuscate_variable_access", + &ObfuscationConfig::obfuscateVariableAccess, + R"delim( The default user-callback when obfuscating global variables access. In addition to the associated class options, O-MVLL interprets these return values as follows: @@ -236,11 +237,11 @@ void omvll_ctor(py::module_& m) { +--------------+------------------------------------------+ See the :omvll:`opaque-fields-access` documentation. - )delim", "module"_a, "function"_a, "variable"_a) + )delim", + "module"_a, "function"_a, "variable"_a) - .def("obfuscate_constants", - &ObfuscationConfig::obfuscate_constants, - R"delim( + .def("obfuscate_constants", &ObfuscationConfig::obfuscateConstants, + R"delim( The default user-callback to obfuscate constants. In addition to the associated class options, O-MVLL interprets these return values as follows: @@ -258,11 +259,11 @@ void omvll_ctor(py::module_& m) { +-------------------+--------------------------------------------------------+ See the :omvll:`opaque-constants` documentation. - )delim", "module"_a, "function"_a) + )delim", + "module"_a, "function"_a) - .def("obfuscate_arithmetic", - &ObfuscationConfig::obfuscate_arithmetic, - R"delim( + .def("obfuscate_arithmetic", &ObfuscationConfig::obfuscateArithmetics, + R"delim( The default user-callback when obfuscating arithmetic operations. In addition to the associated class options, O-MVLL interprets these return values as follows: @@ -278,11 +279,11 @@ void omvll_ctor(py::module_& m) { +--------------+-------------------------------------------+ See the :omvll:`arithmetic` documentation. - )delim", "module"_a, "function"_a) + )delim", + "module"_a, "function"_a) - .def("anti_hooking", - &ObfuscationConfig::anti_hooking, - R"delim( + .def("anti_hooking", &ObfuscationConfig::antiHooking, + R"delim( The default user-callback to enable hooking protection. In addition to the associated class options, O-MVLL interprets these return values as follows: @@ -298,101 +299,91 @@ void omvll_ctor(py::module_& m) { +--------------+-----------------------------------------+ See the :omvll:`anti-hook` documentation. - )delim", "module"_a, "function"_a) + )delim", + "module"_a, "function"_a) - .def("report_diff", - &ObfuscationConfig::report_diff, - R"delim( + .def("report_diff", &ObfuscationConfig::reportDiff, + R"delim( User-callback to monitor IR-level changes from individual obfuscation passes. - )delim", "pass_name"_a, "original"_a, "obfuscated"_a); - + )delim", + "pass_name"_a, "original"_a, "obfuscated"_a); } -std::unique_ptr init_omvll_core(py::dict modules) { - auto m = std::make_unique(py::module_::create_extension_module("omvll", "", new PyModuleDef())); - omvll_ctor(*m); - modules["omvll"] = *m; - return m; +std::unique_ptr initOMVLLCore(py::dict Modules) { + auto M = std::make_unique( + py::module_::create_extension_module("omvll", "", new PyModuleDef())); + OMVLLCtor(*M); + Modules["omvll"] = *M; + return M; } - PyConfig::~PyConfig() = default; -PyConfig& PyConfig::instance() { - if (instance_ == nullptr) { - instance_ = new PyConfig{}; +PyConfig &PyConfig::instance() { + if (!Instance) { + Instance = new PyConfig{}; std::atexit(destroy); } - return *instance_; + return *Instance; } -ObfuscationConfig* PyConfig::getUserConfig() { +ObfuscationConfig *PyConfig::getUserConfig() { try { llvm::LLVMContext Ctx; - if (!py::hasattr(*mod_, "omvll_get_config")) { + if (!py::hasattr(*Mod, "omvll_get_config")) fatalError("Missing omvll_get_config"); - } - auto py_user_config = mod_->attr("omvll_get_config"); - if (py_user_config.is_none()) { - fatalError("Missing omvll_get_config"); - } - py::object result = py_user_config(); - auto* conf = result.cast(); - return conf; - } catch (const std::exception& e) { - fatalError(e.what()); - } -} + auto PyUserConfig = Mod->attr("omvll_get_config"); + if (PyUserConfig.is_none()) + fatalError("Missing omvll_get_config"); -const std::vector& PyConfig::get_passes() { - return config.passes; + py::object Result = PyUserConfig(); + return Result.cast(); + } catch (const std::exception &Exc) { + fatalError(Exc.what()); + } } PyConfig::PyConfig() { py::initialize_interpreter(); - py::module_ sys_mod = py::module_::import("sys"); - py::module_ pathlib = py::module_::import("pathlib"); - - py::dict modules = sys_mod.attr("modules"); - core_mod_ = init_omvll_core(modules); - - llvm::StringRef configpath; - - if (char* config = getenv(ENV_KEY)) { - configpath = config; - } else if (!PyConfig::yconfig.OMVLL_CONFIG.empty()) { - configpath = PyConfig::yconfig.OMVLL_CONFIG; - } - std::string modname = DEFAULT_FILE_NAME; - if (!configpath.empty()) { - std::string config = configpath.str(); - - auto pypath = pathlib.attr("Path")(config); - py::list path = sys_mod.attr("path"); - path.insert(0, pypath.attr("parent").attr("as_posix")()); - std::string name = pypath.attr("stem").cast(); - modname = name; + py::module_ SysMod = py::module_::import("sys"); + py::module_ PathLib = py::module_::import("pathlib"); + py::dict Modules = SysMod.attr("modules"); + + CoreMod = initOMVLLCore(Modules); + + llvm::StringRef ConfigPath; + if (char *Config = getenv(EnvKey)) + ConfigPath = Config; + else if (!PyConfig::YConfig.OMVLLConfig.empty()) + ConfigPath = PyConfig::YConfig.OMVLLConfig; + + std::string ModName = DefaultFileName; + if (!ConfigPath.empty()) { + std::string Config = ConfigPath.str(); + auto PyPath = PathLib.attr("Path")(Config); + py::list Path = SysMod.attr("path"); + Path.insert(0, PyPath.attr("parent").attr("as_posix")()); + std::string Name = PyPath.attr("stem").cast(); + ModName = Name; } try { - mod_ = std::make_unique(py::module_::import(modname.c_str())); - } catch (const std::exception& e) { - fatalError(e.what()); + Mod = std::make_unique(py::module_::import(ModName.c_str())); + } catch (const std::exception &Exc) { + fatalError(Exc.what()); } } -std::string PyConfig::config_path() { - return mod_->attr("__file__").cast(); +std::string PyConfig::configPath() { + return Mod->attr("__file__").cast(); } void PyConfig::destroy() { - delete instance_; + delete Instance; py::finalize_interpreter(); } -} +} // end namespace omvll -PYBIND11_MODULE(omvll, m) { - omvll::omvll_ctor(m); -} +PYBIND11_MODULE(omvll, m) { omvll::OMVLLCtor(m); } diff --git a/src/core/python/PyObfuscationConfig.cpp b/src/core/python/PyObfuscationConfig.cpp index 49998629..5be4377e 100644 --- a/src/core/python/PyObfuscationConfig.cpp +++ b/src/core/python/PyObfuscationConfig.cpp @@ -1,269 +1,295 @@ -#include "PyObfuscationConfig.hpp" -#include "omvll/utils.hpp" -#include "omvll/log.hpp" - -#include +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// #include #include #include -#include -#include -#include +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" + +#include "omvll/utils.hpp" + +#include "PyObfuscationConfig.hpp" namespace py = pybind11; +namespace detail = py::detail; + using namespace std::string_literals; namespace omvll { -StringEncodingOpt PyObfuscationConfig::obfuscate_string(llvm::Module* mod, llvm::Function* func, - const std::string& str) -{ +StringEncodingOpt PyObfuscationConfig::obfuscateString(llvm::Module *M, + llvm::Function *F, + const std::string &Str) { py::gil_scoped_acquire gil; - py::function override = py::get_override(static_cast(this), "obfuscate_string"); + const auto *Base = static_cast(this); + py::function override = py::get_override(Base, "obfuscate_string"); if (override) { try { - py::bytes bytes_str(str); - py::object out = override(mod, func, bytes_str); - if (out.is_none()) { + py::bytes bytes_str(Str); + py::object out = override(M, F, bytes_str); + if (out.is_none()) return StringEncOptSkip(); - } + if (py::isinstance(out)) { - bool val = out.cast(); - if (!val) { + bool Value = out.cast(); + if (!Value) return StringEncOptSkip(); - } else { + else return StringEncOptDefault(); - } } if (py::isinstance(out)) { - auto val = out.cast(); - return StringEncOptReplace(std::move(val)); + auto Value = out.cast(); + return StringEncOptReplace(std::move(Value)); } if (py::isinstance(out)) { - auto val = out.cast(); - return StringEncOptReplace(std::move(val)); + auto Value = out.cast(); + return StringEncOptReplace(std::move(Value)); } - if (py::detail::cast_is_temporary_value_reference::value) { - static pybind11::detail::override_caster_t caster; - return pybind11::detail::cast_ref(std::move(out), caster); + bool is_temp = + detail::cast_is_temporary_value_reference::value; + if (is_temp) { + static detail::override_caster_t caster; + return detail::cast_ref(std::move(out), caster); } - return pybind11::detail::cast_safe(std::move(out)); - } catch (const std::exception& e) { - fatalError("Error in 'obfuscate_string': '"s + e.what() + "'"); + + return detail::cast_safe(std::move(out)); + } catch (const std::exception &Exc) { + fatalError("Error in obfuscate_string: '"s + Exc.what() + "'"); } } - return StringEncOptSkip(); } -BreakControlFlowOpt PyObfuscationConfig::break_control_flow(llvm::Module* mod, llvm::Function* func) { +BreakControlFlowOpt PyObfuscationConfig::breakControlFlow(llvm::Module *M, + llvm::Function *F) { py::gil_scoped_acquire gil; - py::function override = py::get_override(static_cast(this), "break_control_flow"); + const auto *Base = static_cast(this); + py::function override = py::get_override(Base, "break_control_flow"); if (override) { try { - py::object out = override(mod, func); - if (out.is_none()) { + py::object out = override(M, F); + if (out.is_none()) return false; - } + if (py::isinstance(out)) { - bool val = out.cast(); - return val; + bool Value = out.cast(); + return Value; } - if (py::detail::cast_is_temporary_value_reference::value) { - static pybind11::detail::override_caster_t caster; - return pybind11::detail::cast_ref(std::move(out), caster); + + bool is_temp = + detail::cast_is_temporary_value_reference::value; + if (is_temp) { + static detail::override_caster_t caster; + return detail::cast_ref(std::move(out), caster); } - return pybind11::detail::cast_safe(std::move(out)); - } catch (const std::exception& e) { - fatalError("Error in 'break_control_flow': '"s + e.what() + "'"); + + return detail::cast_safe(std::move(out)); + } catch (const std::exception &Exc) { + fatalError("Error in break_control_flow: '"s + Exc.what() + "'"); } } return false; } -ControlFlowFlatteningOpt PyObfuscationConfig::flatten_cfg(llvm::Module* mod, llvm::Function* func) { +ControlFlowFlatteningOpt +PyObfuscationConfig::controlFlowGraphFlattening(llvm::Module *M, + llvm::Function *F) { py::gil_scoped_acquire gil; - py::function override = py::get_override(static_cast(this), "flatten_cfg"); + const auto *Base = static_cast(this); + py::function override = py::get_override(Base, "flatten_cfg"); if (override) { try { - py::object out = override(mod, func); - if (out.is_none()) { + py::object out = override(M, F); + if (out.is_none()) return false; - } + if (py::isinstance(out)) { - bool val = out.cast(); - return val; + bool Value = out.cast(); + return Value; } - if (py::detail::cast_is_temporary_value_reference::value) { - static pybind11::detail::override_caster_t caster; - return pybind11::detail::cast_ref(std::move(out), caster); + + bool is_temp = detail::cast_is_temporary_value_reference< + ControlFlowFlatteningOpt>::value; + if (is_temp) { + static detail::override_caster_t caster; + return detail::cast_ref(std::move(out), + caster); } - return pybind11::detail::cast_safe(std::move(out)); - } catch (const std::exception& e) { - fatalError("Error in 'flatten_cfg': '"s + e.what() + "'"); + + return detail::cast_safe(std::move(out)); + } catch (const std::exception &Exc) { + fatalError("Error in flatten_cfg: '"s + Exc.what() + "'"); } } return false; } -StructAccessOpt PyObfuscationConfig::obfuscate_struct_access(llvm::Module* mod, llvm::Function* func, - llvm::StructType* S) -{ +StructAccessOpt +PyObfuscationConfig::obfuscateStructAccess(llvm::Module *M, llvm::Function *F, + llvm::StructType *S) { py::gil_scoped_acquire gil; - py::function override = py::get_override(static_cast(this), "obfuscate_struct_access"); + const auto *Base = static_cast(this); + py::function override = py::get_override(Base, "obfuscate_struct_access"); if (override) { try { - py::object out = override(mod, func, S); - - if (out.is_none()) { + py::object out = override(M, F, S); + if (out.is_none()) return false; - } if (py::isinstance(out)) { - bool val = out.cast(); - return val; + bool Value = out.cast(); + return Value; } - if (py::detail::cast_is_temporary_value_reference::value) { - static pybind11::detail::override_caster_t caster; - return pybind11::detail::cast_ref(std::move(out), caster); + bool is_temp = + detail::cast_is_temporary_value_reference::value; + if (is_temp) { + static detail::override_caster_t caster; + return detail::cast_ref(std::move(out), caster); } - return pybind11::detail::cast_safe(std::move(out)); - } catch (const std::exception& e) { - fatalError("Error in 'obfuscate_struct_access': '"s + e.what() + "'"); + + return detail::cast_safe(std::move(out)); + } catch (const std::exception &Exc) { + fatalError("Error in obfuscate_struct_access: '"s + Exc.what() + "'"); } } return false; } -VarAccessOpt PyObfuscationConfig::obfuscate_variable_access(llvm::Module* mod, llvm::Function* func, - llvm::GlobalVariable* GV) -{ +VarAccessOpt +PyObfuscationConfig::obfuscateVariableAccess(llvm::Module *M, llvm::Function *F, + llvm::GlobalVariable *GV) { py::gil_scoped_acquire gil; - py::function override = py::get_override(static_cast(this), "obfuscate_variable_access"); + const auto *Base = static_cast(this); + py::function override = py::get_override(Base, "obfuscate_variable_access"); if (override) { try { - py::object out = override(mod, func, GV); - - if (out.is_none()) { + py::object out = override(M, F, GV); + if (out.is_none()) return false; - } if (py::isinstance(out)) { bool val = out.cast(); return val; } - if (py::detail::cast_is_temporary_value_reference::value) { - static pybind11::detail::override_caster_t caster; - return pybind11::detail::cast_ref(std::move(out), caster); + if (detail::cast_is_temporary_value_reference::value) { + static detail::override_caster_t caster; + return detail::cast_ref(std::move(out), caster); } - return pybind11::detail::cast_safe(std::move(out)); - } catch (const std::exception& e) { - fatalError("Error in 'obfuscate_variable_access': '"s + e.what() + "'"); + + return detail::cast_safe(std::move(out)); + } catch (const std::exception &Exc) { + fatalError("Error in obfuscate_variable_access: '"s + Exc.what() + "'"); } } return false; } -AntiHookOpt PyObfuscationConfig::anti_hooking(llvm::Module* mod, llvm::Function* func) -{ +AntiHookOpt PyObfuscationConfig::antiHooking(llvm::Module *M, + llvm::Function *F) { py::gil_scoped_acquire gil; - py::function override = py::get_override(static_cast(this), "anti_hooking"); + const auto *Base = static_cast(this); + py::function override = py::get_override(Base, "anti_hooking"); if (override) { try { - py::object out = override(mod, func); - if (out.is_none()) { + py::object out = override(M, F); + if (out.is_none()) return false; - } if (py::isinstance(out)) { - bool res = out.cast(); - return res; + bool Res = out.cast(); + return Res; } - if (py::detail::cast_is_temporary_value_reference::value) { - static pybind11::detail::override_caster_t caster; - return pybind11::detail::cast_ref(std::move(out), caster); + + if (detail::cast_is_temporary_value_reference::value) { + static detail::override_caster_t caster; + return detail::cast_ref(std::move(out), caster); } - return pybind11::detail::cast_safe(std::move(out)); - } catch (const std::exception& e) { - fatalError("Error in 'anti_hooking': '"s + e.what() + "'"); + + return detail::cast_safe(std::move(out)); + } catch (const std::exception &Exc) { + fatalError("Error in anti_hooking: '"s + Exc.what() + "'"); } } return false; } -bool PyObfuscationConfig::has_report_diff_override() { - std::call_once(overrides_report_diff_checked_, [this]() { - const auto *base = static_cast(this); - overrides_report_diff_ = - static_cast(py::get_override(base, "report_diff")); +bool PyObfuscationConfig::hasReportDiffOverride() { + std::call_once(OverridesReportDiffChecked, [this]() { + const auto *Base = static_cast(this); + OverridesReportDiff = + static_cast(py::get_override(Base, "report_diff")); }); - return overrides_report_diff_; + return OverridesReportDiff; } -void PyObfuscationConfig::report_diff(const std::string &pass, - const std::string &original, - const std::string &obfuscated) { - if (has_report_diff_override()) { +void PyObfuscationConfig::reportDiff(const std::string &Pass, + const std::string &Original, + const std::string &Obfuscated) { + if (hasReportDiffOverride()) { py::gil_scoped_acquire gil; - py::function override = py::get_override( - static_cast(this), "report_diff"); + const auto *Base = static_cast(this); + py::function override = py::get_override(Base, "report_diff"); assert(override && "Checked once in ctor"); try { - override(pass, original, obfuscated); - } catch (const std::exception &e) { - fatalError("Error in 'report_diff': '"s + e.what() + "'"); + override(Pass, Original, Obfuscated); + } catch (const std::exception &Exc) { + fatalError("Error in report_diff: '"s + Exc.what() + "'"); } } } -ArithmeticOpt PyObfuscationConfig::obfuscate_arithmetic(llvm::Module* mod, llvm::Function* func) -{ +ArithmeticOpt PyObfuscationConfig::obfuscateArithmetics(llvm::Module *M, + llvm::Function *F) { py::gil_scoped_acquire gil; - py::function override = py::get_override(static_cast(this), "obfuscate_arithmetic"); + const auto *Base = static_cast(this); + py::function override = py::get_override(Base, "obfuscate_arithmetic"); if (override) { try { - py::object out = override(mod, func); - if (out.is_none()) { + py::object out = override(M, F); + if (out.is_none()) return false; - } if (py::isinstance(out)) { bool res = out.cast(); return res; } - if (py::detail::cast_is_temporary_value_reference::value) { - static pybind11::detail::override_caster_t caster; - return pybind11::detail::cast_ref(std::move(out), caster); + + if (detail::cast_is_temporary_value_reference::value) { + static detail::override_caster_t caster; + return detail::cast_ref(std::move(out), caster); } - return pybind11::detail::cast_safe(std::move(out)); - } catch (const std::exception& e) { - fatalError("Error in 'obfuscate_arithmetic': '"s + e.what() + "'"); + + return detail::cast_safe(std::move(out)); + } catch (const std::exception &Exc) { + fatalError("Error in obfuscate_arithmetic: '"s + Exc.what() + "'"); } } return false; } -OpaqueConstantsOpt PyObfuscationConfig::obfuscate_constants(llvm::Module* mod, llvm::Function* func) -{ +OpaqueConstantsOpt PyObfuscationConfig::obfuscateConstants(llvm::Module *M, + llvm::Function *F) { py::gil_scoped_acquire gil; - py::function override = py::get_override(static_cast(this), "obfuscate_constants"); + const auto *Base = static_cast(this); + py::function override = py::get_override(Base, "obfuscate_constants"); if (override) { try { - py::object out = override(mod, func); - if (out.is_none()) { + py::object out = override(M, F); + if (out.is_none()) return OpaqueConstantsSkip(); - } if (py::isinstance(out)) { - bool res = out.cast(); - return OpaqueConstantsBool(res); + bool Res = out.cast(); + return OpaqueConstantsBool(Res); } if (py::isinstance(out)) { @@ -276,18 +302,19 @@ OpaqueConstantsOpt PyObfuscationConfig::obfuscate_constants(llvm::Module* mod, l return OpaqueConstantsSet(std::move(values)); } - if (py::detail::cast_is_temporary_value_reference::value) { - static pybind11::detail::override_caster_t caster; - return pybind11::detail::cast_ref(std::move(out), caster); + bool is_temp = + detail::cast_is_temporary_value_reference::value; + if (is_temp) { + static detail::override_caster_t caster; + return detail::cast_ref(std::move(out), caster); } - return pybind11::detail::cast_safe(std::move(out)); - } catch (const std::exception& e) { - fatalError("Error in 'obfuscate_constants': '"s + e.what() + "'"); + + return detail::cast_safe(std::move(out)); + } catch (const std::exception &Exc) { + fatalError("Error in obfuscate_constants: '"s + Exc.what() + "'"); } } return OpaqueConstantsSkip(); } -} - - +} // end namespace omvll diff --git a/src/core/python/PyObfuscationConfig.hpp b/src/core/python/PyObfuscationConfig.hpp index 4a37a3a6..9d65be42 100644 --- a/src/core/python/PyObfuscationConfig.hpp +++ b/src/core/python/PyObfuscationConfig.hpp @@ -1,33 +1,49 @@ -#ifndef OMVLL_PY_OBFUSCATION_CONFIG_H -#define OMVLL_PY_OBFUSCATION_CONFIG_H -#include "omvll/ObfuscationConfig.hpp" +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include +#include "omvll/ObfuscationConfig.hpp" + namespace omvll { class PyObfuscationConfig : public ObfuscationConfig { using ObfuscationConfig::ObfuscationConfig; - StringEncodingOpt obfuscate_string(llvm::Module* mod, llvm::Function* func, - const std::string& str) override; - BreakControlFlowOpt break_control_flow(llvm::Module* mod, llvm::Function* func) override; - ControlFlowFlatteningOpt flatten_cfg(llvm::Module* mod, llvm::Function* func) override; + StringEncodingOpt obfuscateString(llvm::Module *M, llvm::Function *F, + const std::string &Str) override; + + BreakControlFlowOpt breakControlFlow(llvm::Module *M, + llvm::Function *F) override; - StructAccessOpt obfuscate_struct_access(llvm::Module* mod, llvm::Function* func, - llvm::StructType* S) override; - VarAccessOpt obfuscate_variable_access(llvm::Module* mod, llvm::Function* func, - llvm::GlobalVariable* S) override; - AntiHookOpt anti_hooking(llvm::Module* mod, llvm::Function* func) override; - ArithmeticOpt obfuscate_arithmetic(llvm::Module* mod, llvm::Function* func) override; - OpaqueConstantsOpt obfuscate_constants(llvm::Module* mod, llvm::Function* func) override; + ControlFlowFlatteningOpt + controlFlowGraphFlattening(llvm::Module *M, llvm::Function *F) override; - bool has_report_diff_override() override; - void report_diff(const std::string &pass, const std::string &original, - const std::string &obfuscated) override; + StructAccessOpt obfuscateStructAccess(llvm::Module *M, llvm::Function *F, + llvm::StructType *S) override; + + VarAccessOpt obfuscateVariableAccess(llvm::Module *M, llvm::Function *F, + llvm::GlobalVariable *S) override; + + AntiHookOpt antiHooking(llvm::Module *M, llvm::Function *F) override; + + ArithmeticOpt obfuscateArithmetics(llvm::Module *M, + llvm::Function *F) override; + + OpaqueConstantsOpt obfuscateConstants(llvm::Module *M, + llvm::Function *F) override; + + bool hasReportDiffOverride() override; + void reportDiff(const std::string &Pass, const std::string &Original, + const std::string &Obfuscated) override; private: - bool overrides_report_diff_ = false; - std::once_flag overrides_report_diff_checked_; + bool OverridesReportDiff = false; + std::once_flag OverridesReportDiffChecked; }; -} -#endif + +} // end namespace omvll diff --git a/src/core/python/init.hpp b/src/core/python/init.hpp index ca049dc5..c7f3f38f 100644 --- a/src/core/python/init.hpp +++ b/src/core/python/init.hpp @@ -1,16 +1,18 @@ -#ifndef OMVLL_PY_INIT_H -#define OMVLL_PY_INIT_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include #include #include -namespace py = pybind11; -using namespace pybind11::literals; - namespace omvll { -py::module_& py_init_log(py::module_& m); -py::module_& py_init_llvm_bindings(py::module_& m); -py::module_& py_init_obf_opt(py::module_& m); -} -#endif +pybind11::module_ &py_init_log(pybind11::module_ &M); +pybind11::module_ &py_init_llvm_bindings(pybind11::module_ &M); +pybind11::module_ &py_init_obf_opt(pybind11::module_ &M); + +} // end namespace omvll diff --git a/src/core/python/pyllvm.cpp b/src/core/python/pyllvm.cpp index 7492ef8e..9aaf5b5e 100644 --- a/src/core/python/pyllvm.cpp +++ b/src/core/python/pyllvm.cpp @@ -1,18 +1,28 @@ -#include -#include -#include +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include "llvm/Demangle/Demangle.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" #include "omvll/utils.hpp" #include "init.hpp" +namespace py = pybind11; + +using namespace pybind11::literals; + namespace omvll { -py::module_& py_init_llvm_bindings(py::module_& m) { - py::class_(m, "Struct", "This class mirrors ``llvm::StructType``") - .def_property_readonly("name", - [] (const llvm::StructType& S) { return S.getName().str(); }, - R"delim( +py::module_ &py_init_llvm_bindings(py::module_ &m) { + py::class_(m, "Struct", + "This class mirrors ``llvm::StructType``") + .def_property_readonly( + "name", [](const llvm::StructType &S) { return S.getName().str(); }, + R"delim( The name of the structure or the class. For instance: @@ -24,60 +34,63 @@ py::module_& py_init_llvm_bindings(py::module_& m) { )delim"); py::class_(m, "Module", "This class mirrors ``llvm::Module``") - .def_property_readonly("identifier", &llvm::Module::getModuleIdentifier) + .def_property_readonly("identifier", &llvm::Module::getModuleIdentifier) - .def_property_readonly("instruction_count", - &llvm::Module::getInstructionCount, - R"delim( + .def_property_readonly("instruction_count", + &llvm::Module::getInstructionCount, + R"delim( The number of non-debug IR instructions in the module. )delim") - .def_property_readonly("source_filename", - &llvm::Module::getSourceFileName, - R"delim( + .def_property_readonly("source_filename", + &llvm::Module::getSourceFileName, + R"delim( The source filename of the module )delim") - .def_property_readonly("name", - [] (const llvm::Module& module) { return module.getName().str(); }, - R"delim( + .def_property_readonly( + "name", + [](const llvm::Module &Module) { return Module.getName().str(); }, + R"delim( The short "name" of this module )delim") - .def_property_readonly("data_layout", - &llvm::Module::getDataLayoutStr, - R"delim( + .def_property_readonly("data_layout", &llvm::Module::getDataLayoutStr, + R"delim( Get the data layout string for the module's target platform. )delim") - .def("dump", [] (llvm::Module& self, const std::string& path) { - dump(self, path); - }, - R"delim( + .def( + "dump", + [](llvm::Module &Self, const std::string &Path) { dump(Self, Path); }, + R"delim( This function dumps the IR instructions of the current module in the file provided in the second parameter. - )delim", "file"_a); - - - py::class_(m, "Function", "This class mirrors ``llvm::Function``") - .def_property_readonly("nb_instructions", - &llvm::Function::getInstructionCount, - R"delim( + )delim", + "file"_a); + + py::class_(m, "Function", + "This class mirrors ``llvm::Function``") + .def_property_readonly("nb_instructions", + &llvm::Function::getInstructionCount, + R"delim( Return the number of IR instructions. )delim") - .def_property_readonly("name", - [] (const llvm::Function& func) { - return func.getName().str(); - }, R"delim( + .def_property_readonly( + "name", + [](const llvm::Function &Func) { return Func.getName().str(); }, + R"delim( The (mangled) name of the function. For instance: - ``_ZN7_JNIEnv12NewStringUTFEPKc`` - ``main`` )delim") - .def_property_readonly("demangled_name", - [] (const llvm::Function& func) { - return llvm::demangle(func.getName().str()); - }, R"delim( + .def_property_readonly( + "demangled_name", + [](const llvm::Function &Func) { + return llvm::demangle(Func.getName().str()); + }, + R"delim( The demangled name of the function. For instance: @@ -85,21 +98,24 @@ py::module_& py_init_llvm_bindings(py::module_& m) { - ``main`` )delim"); - py::class_(m, "GlobalVariable", "This class mirrors ``llvm::GlobalVariable``") - .def_property_readonly("name", - [] (const llvm::GlobalVariable& GV) { - return GV.getName().str(); - }, R"delim( + py::class_( + m, "GlobalVariable", "This class mirrors ``llvm::GlobalVariable``") + .def_property_readonly( + "name", + [](const llvm::GlobalVariable &GV) { return GV.getName().str(); }, + R"delim( The mangled name of the global variable. )delim") - .def_property_readonly("demangled_name", - [] (const llvm::GlobalVariable& GV) { - return llvm::demangle(GV.getName().str()); - }, R"delim( + .def_property_readonly( + "demangled_name", + [](const llvm::GlobalVariable &GV) { + return llvm::demangle(GV.getName().str()); + }, + R"delim( The demangled name of the global variable. )delim"); return m; } -} +} // end namespace omvll diff --git a/src/core/python/pylog.cpp b/src/core/python/pylog.cpp index 241b610b..9655fed3 100644 --- a/src/core/python/pylog.cpp +++ b/src/core/python/pylog.cpp @@ -1,19 +1,26 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "omvll/log.hpp" + #include "init.hpp" -namespace omvll { +namespace py = pybind11; -py::module_& py_init_log(py::module_& m) { +namespace omvll { - py::enum_(m, "LOG_LEVEL") - .value("DEBUG", LOG_LEVEL::DEBUG) - .value("TRACE", LOG_LEVEL::TRACE) - .value("INFO", LOG_LEVEL::INFO) - .value("WARN", LOG_LEVEL::WARN) - .value("ERR", LOG_LEVEL::ERR); +py::module_ &py_init_log(py::module_ &m) { + py::enum_(m, "LogLevel") + .value("DEBUG", LogLevel::Debug) + .value("TRACE", LogLevel::Trace) + .value("INFO", LogLevel::Info) + .value("WARN", LogLevel::Warn) + .value("ERR", LogLevel::Err); - m.def("set_log_level", py::overload_cast(&Logger::set_level)); + m.def("set_log_level", py::overload_cast(&Logger::set_level)); return m; } -} +} // end namespace omvll diff --git a/src/core/python/pyobf_opt.cpp b/src/core/python/pyobf_opt.cpp index 0a57f387..bba804db 100644 --- a/src/core/python/pyobf_opt.cpp +++ b/src/core/python/pyobf_opt.cpp @@ -1,9 +1,19 @@ -#include "init.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "omvll/passes/ObfuscationOpt.hpp" +#include "init.hpp" + +namespace py = pybind11; + +using namespace pybind11::literals; + namespace omvll { -py::module_& py_init_obf_opt(py::module_& m) { +py::module_ &py_init_obf_opt(py::module_ &m) { // Strings Encoding py::class_(m, "StringEncOptSkip", R"delim( @@ -153,4 +163,4 @@ py::module_& py_init_obf_opt(py::module_& m) { return m; } -} +} // end namespace omvll diff --git a/src/core/utils.cpp b/src/core/utils.cpp index 5c7f5803..f79bda04 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -1,28 +1,33 @@ -#include "omvll/utils.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include + +#include "llvm/ADT/Hashing.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" + #include "omvll/ObfuscationConfig.hpp" #include "omvll/PyConfig.hpp" #include "omvll/log.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include "omvll/utils.hpp" using namespace llvm; @@ -97,7 +102,7 @@ runClangExecutable(StringRef Code, StringRef Dashx, const Triple &Triple, for (const auto &Arg : ExtraArgs) Args.push_back(Arg); - // Create the input C file and choose macthing output file name + // Create the input C file and choose macthing output file name. int InFileFD; SmallString<128> InFileName; std::string Prefix = "omvll-" + Triple.getTriple(); @@ -110,16 +115,16 @@ runClangExecutable(StringRef Code, StringRef Dashx, const Triple &Triple, Args.push_back(OutFileName); Args.push_back(InFileName); - // Write the given C code to the input file + // Write the given C code to the input file. { std::error_code EC; - raw_fd_ostream inFileOS(InFileName, EC); + raw_fd_ostream OS(InFileName, EC); if (EC) return errorCodeToError(EC); - inFileOS << Code; + OS << Code; } - // TODO: Write to omvll debug log instead of stderr + // TODO: Write to omvll debug log instead of stderr. for (StringRef Entry : Args) errs() << Entry << " "; errs() << "\n"; @@ -131,7 +136,7 @@ runClangExecutable(StringRef Code, StringRef Dashx, const Triple &Triple, return OutFileName; } -static Error createSMDiagnosticError(llvm::SMDiagnostic &Diag) { +static Error createSMDiagnosticError(SMDiagnostic &Diag) { std::string Msg; { raw_string_ostream OS(Msg); @@ -140,8 +145,8 @@ static Error createSMDiagnosticError(llvm::SMDiagnostic &Diag) { return make_error(std::move(Msg), inconvertibleErrorCode()); } -static Expected> loadModule(StringRef Path, - LLVMContext &Ctx) { +static Expected> loadModule(StringRef Path, + LLVMContext &Ctx) { SMDiagnostic Err; auto M = parseIRFile(Path, Err, Ctx); if (!M) @@ -149,54 +154,53 @@ static Expected> loadModule(StringRef Path, return M; } -} // namespace detail +} // end namespace detail namespace omvll { -std::string ToString(const Module& M) { - std::error_code ec; - std::string out; - raw_string_ostream os(out); - M.print(os, nullptr); - return out; +std::string ToString(const Module &M) { + std::error_code EC; + std::string Out; + raw_string_ostream OS(Out); + M.print(OS, nullptr); + return Out; } -std::string ToString(const Instruction& I) { - std::string out; - raw_string_ostream(out) << I; - return out; +std::string ToString(const Instruction &I) { + std::string Out; + raw_string_ostream(Out) << I; + return Out; } -std::string ToString(const BasicBlock& BB) { - std::string out; - raw_string_ostream os(out); - BB.printAsOperand(os, true); - return out; +std::string ToString(const BasicBlock &BB) { + std::string Out; + raw_string_ostream OS(Out); + BB.printAsOperand(OS, true); + return Out; } -std::string ToString(const Type& Ty) { - std::string out; - raw_string_ostream os(out); - os << TypeIDStr(Ty) << ": " << Ty; - return out; +std::string ToString(const Type &Ty) { + std::string Out; + raw_string_ostream OS(Out); + OS << TypeIDStr(Ty) << ": " << Ty; + return Out; } -std::string ToString(const Value& V) { - std::string out; - raw_string_ostream os(out); - os << ValueIDStr(V) << ": " << V; - return out; +std::string ToString(const Value &V) { + std::string Out; + raw_string_ostream OS(Out); + OS << ValueIDStr(V) << ": " << V; + return Out; } - -std::string ToString(const MDNode& N) { - std::string out; - raw_string_ostream os(out); - N.printTree(os); - return out; +std::string ToString(const MDNode &N) { + std::string Out; + raw_string_ostream OS(Out); + N.printTree(OS); + return Out; } -std::string TypeIDStr(const Type& Ty) { +std::string TypeIDStr(const Type &Ty) { switch (Ty.getTypeID()) { case Type::TypeID::HalfTyID: return "HalfTyID"; case Type::TypeID::BFloatTyID: return "BFloatTyID"; @@ -225,8 +229,7 @@ std::string TypeIDStr(const Type& Ty) { } } -std::string ValueIDStr(const Value& V) { - +std::string ValueIDStr(const Value &V) { #define HANDLE_VALUE(ValueName) case Value::ValueTy::ValueName ## Val: return #ValueName; //#define HANDLE_INSTRUCTION(Name) /* nothing */ @@ -241,40 +244,38 @@ std::string ValueIDStr(const Value& V) { return std::to_string(V.getValueID()); } -void dump(Module& M, const std::string& file) { - std::error_code ec; - raw_fd_ostream fd(file, ec); - M.print(fd, nullptr); +void dump(Module &M, const std::string &File) { + std::error_code EC; + raw_fd_ostream FD(File, EC); + M.print(FD, nullptr); } -void dump(Function& F, const std::string& file) { - std::error_code ec; - raw_fd_ostream fd(file, ec); - F.print(fd, nullptr); +void dump(Function &F, const std::string &File) { + std::error_code EC; + raw_fd_ostream FD(File, EC); + F.print(FD, nullptr); } -void dump(const MemoryBuffer& MB, const std::string& file) { - std::error_code ec; - raw_fd_ostream fd(file, ec); - fd << MB.getBuffer(); +void dump(const MemoryBuffer &MB, const std::string &File) { + std::error_code EC; + raw_fd_ostream FD(File, EC); + FD << MB.getBuffer(); } -size_t demotePHINode(Function& F) { - size_t count = 0; - std::vector phiNodes; +size_t demotePHINode(Function &F) { + size_t Count = 0; + std::vector PhiNodes; do { - phiNodes.clear(); - for (auto& BB : F) { - for (auto& I : BB.phis()) { - phiNodes.push_back(&I); - } - } - count += phiNodes.size(); - for (PHINode* phi : phiNodes) { - DemotePHIToStack(phi, F.begin()->getTerminator()); - } - } while (!phiNodes.empty()); - return count; + PhiNodes.clear(); + for (auto &BB : F) + for (auto &I : BB.phis()) + PhiNodes.push_back(&I); + + Count += PhiNodes.size(); + for (PHINode *Phi : PhiNodes) + DemotePHIToStack(Phi, F.begin()->getTerminator()); + } while (!PhiNodes.empty()); + return Count; } static bool valueEscapes(const Instruction &Inst) { @@ -287,28 +288,26 @@ static bool valueEscapes(const Instruction &Inst) { return false; } -size_t demoteRegs(Function& F) { - size_t count = 0; - std::list WorkList; - BasicBlock* BBEntry = &F.getEntryBlock(); +size_t demoteRegs(Function &F) { + size_t Count = 0; + std::list WorkList; + BasicBlock *BBEntry = &F.getEntryBlock(); do { WorkList.clear(); - for (BasicBlock& BB : F) { - for (Instruction& I : BB) { - if (!(isa(I) && I.getParent() == BBEntry) && valueEscapes(I)) { + for (BasicBlock &BB : F) + for (Instruction &I : BB) + if (!(isa(I) && I.getParent() == BBEntry) && + valueEscapes(I)) WorkList.push_front(&I); - } - } - } - count += WorkList.size(); - for (Instruction* I : WorkList) { + + Count += WorkList.size(); + for (Instruction *I : WorkList) DemoteRegToStack(*I, false, F.begin()->getTerminator()); - } } while (!WorkList.empty()); - return count; + return Count; } -size_t reg2mem(Function& F) { +size_t reg2mem(Function &F) { /* The code of this function comes from the pass Reg2Mem.cpp * Note(Romain): I tried to run this pass using the PassManager with the following code: * @@ -324,7 +323,7 @@ size_t reg2mem(Function& F) { * It might be worth investigating why it crashes. */ SplitAllCriticalEdges(F, CriticalEdgeSplittingOptions()); - size_t count = 0; + size_t Count = 0; // Insert all new allocas into entry block. BasicBlock *BBEntry = &F.getEntryBlock(); assert(pred_empty(BBEntry) && @@ -342,32 +341,32 @@ size_t reg2mem(Function& F) { // Find the escaped instructions. But don't create stack slots for // allocas in entry block. - std::list WorkList; + std::list WorkList; for (Instruction &I : instructions(F)) if (!(isa(I) && I.getParent() == BBEntry) && valueEscapes(I)) WorkList.push_front(&I); - // Demote escaped instructions - count += WorkList.size(); + // Demote escaped instructions. + Count += WorkList.size(); for (Instruction *I : WorkList) DemoteRegToStack(*I, false, AllocaInsertionPoint); WorkList.clear(); - // Find all phi's + // Find all phi's. for (BasicBlock &BB : F) - for (auto &Phi : BB.phis()) + for (PHINode &Phi : BB.phis()) WorkList.push_front(&Phi); - // Demote phi nodes - count += WorkList.size(); + // Demote phi nodes. + Count += WorkList.size(); for (Instruction *I : WorkList) DemotePHIToStack(cast(I), AllocaInsertionPoint); - return count; + return Count; } -void shuffleFunctions(Module& M) { +void shuffleFunctions(Module &M) { /* * The iterator associated getFunctionList() is not "random" * so we can't std::shuffle() the list. @@ -375,53 +374,52 @@ void shuffleFunctions(Module& M) { * On the other hand, getFunctionList() has a sort method that we can * use to (randomly?) shuffle in list in place. */ - DenseMap Values; + DenseMap Values; DenseSet Taken; - std::mt19937_64 gen64; - std::uniform_int_distribution Dist(0, std::numeric_limits::max() - 1); + std::mt19937_64 Gen64; + size_t Max = std::numeric_limits::max(); + std::uniform_int_distribution Dist(0, Max - 1); + + for (Function &F : M) { + uint64_t ID = Dist(Gen64); + while (!Taken.insert(ID).second) + ID = Dist(Gen64); - for (Function& F : M) { - uint64_t ID = Dist(gen64); - while (!Taken.insert(ID).second) { - ID = Dist(gen64); - } Values[&F] = ID; } - auto& List = M.getFunctionList(); - List.sort([&Values] (Function& LHS, Function& RHS) { - return Values[&LHS] < Values[&RHS]; - }); + auto &List = M.getFunctionList(); + List.sort([&Values](Function &LHS, Function &RHS) { + return Values[&LHS] < Values[&RHS]; + }); } -void fatalError(const std::string& msg) { - fatalError(msg.c_str()); -} +void fatalError(const std::string &Msg) { fatalError(Msg.c_str()); } -void fatalError(const char* msg) { +void fatalError(const char *Msg) { static LLVMContext Ctx; - Ctx.emitError(msg); + Ctx.emitError(Msg); - // emitError could return, so we make sure that we stop the execution - SERR("Error: {}", msg); + // emitError could return, so we make sure that we stop the execution. + SERR("Error: {}", Msg); std::abort(); } -Expected> +Expected> generateModule(StringRef Routine, const Triple &Triple, StringRef Extension, LLVMContext &Ctx, ArrayRef ExtraArgs) { using namespace ::detail; - llvm::hash_code HashValue = llvm::hash_combine(Routine, Triple.getTriple()); + hash_code HashValue = hash_combine(Routine, Triple.getTriple()); SmallString<128> TempPath; - llvm::sys::path::system_temp_directory(true, TempPath); - llvm::sys::path::append(TempPath, "omvll-cache-" + Triple.getTriple() + "-" + - std::to_string(HashValue) + ".ll"); + sys::path::system_temp_directory(true, TempPath); + sys::path::append(TempPath, "omvll-cache-" + Triple.getTriple() + "-" + + std::to_string(HashValue) + ".ll"); std::string IRModuleFilename = std::string(TempPath.str()); - if (llvm::sys::fs::exists(IRModuleFilename)) { + if (sys::fs::exists(IRModuleFilename)) { return loadModule(IRModuleFilename, Ctx); } else { auto MaybePath = runClangExecutable(Routine, Extension, Triple, ExtraArgs); @@ -433,8 +431,7 @@ generateModule(StringRef Routine, const Triple &Triple, StringRef Extension, return MaybeModule.takeError(); std::error_code EC; - llvm::raw_fd_ostream IRModuleFile(IRModuleFilename, EC, - llvm::sys::fs::OF_None); + raw_fd_ostream IRModuleFile(IRModuleFilename, EC, sys::fs::OF_None); if (EC) return errorCodeToError(EC); @@ -444,23 +441,22 @@ generateModule(StringRef Routine, const Triple &Triple, StringRef Extension, } } -IRChangesMonitor::IRChangesMonitor(const llvm::Module &M, - llvm::StringRef PassName) - : Mod(M), UserConfig(PyConfig::instance().getUserConfig()), +IRChangesMonitor::IRChangesMonitor(const Module &M, StringRef PassName) + : M(M), UserConfig(PyConfig::instance().getUserConfig()), PassName(PassName), ChangeReported(false) { - if (UserConfig->has_report_diff_override()) - llvm::raw_string_ostream(OriginalIR) << Mod; + if (UserConfig->hasReportDiffOverride()) + raw_string_ostream(OriginalIR) << M; } PreservedAnalyses IRChangesMonitor::report() { - if (ChangeReported && UserConfig->has_report_diff_override()) { + if (ChangeReported && UserConfig->hasReportDiffOverride()) { std::string ObfuscatedIR; - llvm::raw_string_ostream(ObfuscatedIR) << Mod; + raw_string_ostream(ObfuscatedIR) << M; if (OriginalIR != ObfuscatedIR) - UserConfig->report_diff(PassName, OriginalIR, ObfuscatedIR); + UserConfig->reportDiff(PassName, OriginalIR, ObfuscatedIR); } return ChangeReported ? PreservedAnalyses::none() : PreservedAnalyses::all(); } -} // namespace omvll +} // end namespace omvll diff --git a/src/include/omvll/Jitter.hpp b/src/include/omvll/Jitter.hpp deleted file mode 100644 index c7f51d73..00000000 --- a/src/include/omvll/Jitter.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef OMVLL_JITTER_H -#define OMVLL_JITTER_H -#include -#include - -#include -#include -#include - -namespace llvm { -class LLVMContext; -class Module; -class MemoryBuffer; -namespace orc { -class LLJIT; -} - -namespace object { -class ObjectFile; -} -} - -namespace omvll { -class Jitter { -public: - Jitter(const std::string &Triple); - - llvm::LLVMContext &getContext() { return *Ctx_; } - - llvm::Expected> - loadModule(llvm::StringRef Path, llvm::LLVMContext &Ctx); - - std::unique_ptr compile(llvm::Module& M); - - std::unique_ptr jitAsm(const std::string& Asm, size_t Size); - -protected: - size_t getFunctionSize(llvm::object::ObjectFile& Obj, llvm::StringRef Name); - -private: - std::string Triple_; - std::unique_ptr Ctx_; -}; -} -#endif diff --git a/src/include/omvll/ObfuscationConfig.hpp b/src/include/omvll/ObfuscationConfig.hpp index 59ae520b..a5654d82 100644 --- a/src/include/omvll/ObfuscationConfig.hpp +++ b/src/include/omvll/ObfuscationConfig.hpp @@ -1,46 +1,56 @@ -#ifndef OMVLL_OBF_CONFIG_H -#define OMVLL_OBF_CONFIG_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// #include "omvll/passes/ObfuscationOpt.hpp" +// Forward declarations namespace llvm { class Module; class Function; class StructType; class GlobalVariable; -} - +} // end namespace llvm namespace omvll { + struct ObfuscationConfig { ObfuscationConfig() = default; virtual ~ObfuscationConfig() = default; - ObfuscationConfig(const ObfuscationConfig&) = delete; - ObfuscationConfig& operator=(const ObfuscationConfig&) = delete; + ObfuscationConfig(const ObfuscationConfig &) = delete; + ObfuscationConfig &operator=(const ObfuscationConfig &) = delete; - virtual StringEncodingOpt obfuscate_string(llvm::Module* mod, llvm::Function* func, - const std::string& str) = 0; + virtual StringEncodingOpt obfuscateString(llvm::Module *M, llvm::Function *F, + const std::string &Str) = 0; - virtual StructAccessOpt obfuscate_struct_access(llvm::Module* mod, llvm::Function* func, - llvm::StructType* S) = 0; + virtual StructAccessOpt obfuscateStructAccess(llvm::Module *M, + llvm::Function *F, + llvm::StructType *S) = 0; - virtual VarAccessOpt obfuscate_variable_access(llvm::Module* mod, llvm::Function* func, - llvm::GlobalVariable* S) = 0; + virtual VarAccessOpt obfuscateVariableAccess(llvm::Module *M, + llvm::Function *F, + llvm::GlobalVariable *S) = 0; - virtual BreakControlFlowOpt break_control_flow(llvm::Module* mod, llvm::Function* func) = 0; + virtual BreakControlFlowOpt breakControlFlow(llvm::Module *M, + llvm::Function *F) = 0; - virtual ControlFlowFlatteningOpt flatten_cfg(llvm::Module* mod, llvm::Function* func) = 0; + virtual ControlFlowFlatteningOpt + controlFlowGraphFlattening(llvm::Module *M, llvm::Function *F) = 0; - virtual OpaqueConstantsOpt obfuscate_constants(llvm::Module* mod, llvm::Function* func) = 0; + virtual OpaqueConstantsOpt obfuscateConstants(llvm::Module *M, + llvm::Function *F) = 0; - virtual ArithmeticOpt obfuscate_arithmetic(llvm::Module* mod, llvm::Function* func) = 0; + virtual ArithmeticOpt obfuscateArithmetics(llvm::Module *M, + llvm::Function *F) = 0; - virtual AntiHookOpt anti_hooking(llvm::Module* mod, llvm::Function* func) = 0; + virtual AntiHookOpt antiHooking(llvm::Module *M, llvm::Function *F) = 0; - virtual bool has_report_diff_override() { return false; }; - virtual void report_diff(const std::string &pass, const std::string &original, - const std::string &obfuscated) = 0; + virtual bool hasReportDiffOverride() { return false; }; + virtual void reportDiff(const std::string &Pass, const std::string &Original, + const std::string &Obfuscated) = 0; }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/PyConfig.hpp b/src/include/omvll/PyConfig.hpp index 5d16188c..960b7901 100644 --- a/src/include/omvll/PyConfig.hpp +++ b/src/include/omvll/PyConfig.hpp @@ -1,53 +1,61 @@ -#ifndef OMVLL_PYCONFIG_H -#define OMVLL_PYCONFIG_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include #include #include + #include "omvll/passes/ObfuscationOpt.hpp" +// Forward declarations namespace pybind11 { class module_; -} +} // end namespace pybind11 + +// Forward declarations namespace llvm { class Module; class Function; class StructType; -} +} // end namespace llvm namespace omvll { + struct ObfuscationConfig; -struct yaml_config_t { - std::string PYTHONPATH; - std::string OMVLL_CONFIG; +struct YamlConfig { + std::string PythonPath; + std::string OMVLLConfig; }; -void init_pythonpath(); -void init_yamlconfig(); - +void initPythonpath(); +void initYamlConfig(); class PyConfig { - public: +public: + static PyConfig &instance(); + ObfuscationConfig *getUserConfig(); + std::string configPath(); - static PyConfig& instance(); - ObfuscationConfig* getUserConfig(); - const std::vector& get_passes(); - std::string config_path(); + static constexpr auto DefaultFileName = "omvll_config"; + static constexpr auto EnvKey = "OMVLL_CONFIG"; + static constexpr auto PyEnv_Key = "OMVLL_PYTHONPATH"; + static constexpr auto YamlFile = "omvll.yml"; - static inline const char DEFAULT_FILE_NAME[] = "omvll_config"; - static inline const char ENV_KEY[] = "OMVLL_CONFIG"; - static inline const char PYENV_KEY[] = "OMVLL_PYTHONPATH"; - static inline const char YAML_FILE[] = "omvll.yml"; + static inline YamlConfig YConfig; - inline static yaml_config_t yconfig; - private: +private: PyConfig(); ~PyConfig(); static void destroy(); - std::unique_ptr mod_; - std::unique_ptr core_mod_; - inline static PyConfig* instance_ = nullptr; + std::unique_ptr Mod; + std::unique_ptr CoreMod; + static inline PyConfig *Instance = nullptr; }; -} -#endif + +} // end namespace omvll diff --git a/src/include/omvll/config.hpp.in b/src/include/omvll/config.hpp.in index 26ae19a6..43cd0e27 100644 --- a/src/include/omvll/config.hpp.in +++ b/src/include/omvll/config.hpp.in @@ -1,5 +1,9 @@ -#ifndef OMVLL_CONFIG_H -#define OMVLL_CONFIG_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for details. +// + #cmakedefine OMVLL_DEBUG @OMVLL_DEBUG@ #cmakedefine01 OMVLL_PY_STANDALONE @@ -15,5 +19,3 @@ static constexpr bool is_debug = true; #else static constexpr bool is_debug = false; #endif - -#endif diff --git a/src/include/omvll/jitter.hpp b/src/include/omvll/jitter.hpp new file mode 100644 index 00000000..8392128c --- /dev/null +++ b/src/include/omvll/jitter.hpp @@ -0,0 +1,52 @@ +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include +#include + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +// Forward declarations +namespace llvm { +class LLVMContext; +class Module; +class MemoryBuffer; + +namespace orc { +class LLJIT; +} // end namespace orc + +namespace object { +class ObjectFile; +} // end namespace object +} // end namespace llvm + +namespace omvll { + +class Jitter { +public: + Jitter(const std::string &Triple); + + llvm::LLVMContext &getContext() { return *Ctx; } + + llvm::Expected> + loadModule(llvm::StringRef Path, llvm::LLVMContext &Ctx); + + std::unique_ptr compile(llvm::Module &M); + std::unique_ptr jitAsm(const std::string &Asm, + size_t Size); + +protected: + size_t getFunctionSize(llvm::object::ObjectFile &Obj, llvm::StringRef Name); + +private: + std::string Triple; + std::unique_ptr Ctx; +}; + +} // end namespace omvll diff --git a/src/include/omvll/log.hpp b/src/include/omvll/log.hpp index b26f35b7..a9216124 100644 --- a/src/include/omvll/log.hpp +++ b/src/include/omvll/log.hpp @@ -1,10 +1,15 @@ #pragma once +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include +#include +#include #include #include -#include -#include -#include #include "omvll/config.hpp" @@ -19,20 +24,20 @@ #define SWARN(...) Logger::warn(__VA_ARGS__) #define SERR(...) Logger::err(__VA_ARGS__) -enum class LOG_LEVEL { - DEBUG, - TRACE, - INFO, - WARN, - ERR, +enum class LogLevel { + Debug, + Trace, + Info, + Warn, + Err, }; class Logger { - public: - Logger(const Logger&) = delete; - Logger& operator=(const Logger&) = delete; +public: + Logger(const Logger &) = delete; + Logger &operator=(const Logger &) = delete; - static Logger& instance(); + static Logger &instance(); //! @brief Disable the logging module static void disable(); @@ -40,44 +45,44 @@ class Logger { //! @brief Enable the logging module static void enable(); - static void set_level(spdlog::level::level_enum lvl); - static void set_level(LOG_LEVEL lvl); + static void set_level(spdlog::level::level_enum Level); + static void set_level(LogLevel Level); - template - static void trace(const char *fmt, const Args &... args) { - Logger::instance().sink_->trace(fmt, args...); + template + static void trace(const char *Fmt, const Ts &...Args) { + Logger::instance().Sink->trace(Fmt, Args...); } - template - static void debug(const char *fmt, const Args &... args) { - #ifdef OMVLL_DEBUG - Logger::instance().sink_->debug(fmt, args...); - #endif + template + static void debug(const char *Fmt, const Ts &...Args) { +#ifdef OMVLL_DEBUG + Logger::instance().Sink->debug(Fmt, Args...); +#endif } - template - static void info(const char *fmt, const Args &... args) { - Logger::instance().sink_->info(fmt, args...); + template + static void info(const char *Fmt, const Ts &...Args) { + Logger::instance().Sink->info(Fmt, Args...); } - template - static void err(const char *fmt, const Args &... args) { - Logger::instance().sink_->error(fmt, args...); + template + static void err(const char *Fmt, const Ts &...Args) { + Logger::instance().Sink->error(Fmt, Args...); } - template - static void warn(const char *fmt, const Args &... args) { - Logger::instance().sink_->warn(fmt, args...); + template + static void warn(const char *Fmt, const Ts &...Args) { + Logger::instance().Sink->warn(Fmt, Args...); } ~Logger(); - private: + +private: Logger(void); - Logger(Logger&&); - Logger& operator=(Logger&&); + Logger(Logger &&); + Logger &operator=(Logger &&); static void destroy(); - inline static Logger* instance_ = nullptr; - std::shared_ptr sink_; + static inline Logger *Instance = nullptr; + std::shared_ptr Sink; }; - diff --git a/src/include/omvll/omvll_config.hpp b/src/include/omvll/omvll_config.hpp index 1d2bddd5..1bbb3bcc 100644 --- a/src/include/omvll/omvll_config.hpp +++ b/src/include/omvll/omvll_config.hpp @@ -1,20 +1,24 @@ -#ifndef OMVLL_CONFIG_ALT_H -#define OMVLL_CONFIG_ALT_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include #include namespace omvll { -struct config_t { - std::vector passes; - bool cleaning; - bool shuffle_functions; - bool inline_jni_wrappers; -}; -// Defined in omvll_config.cpp -extern config_t config; -void init_default_config(); +struct OMVLLConfig { + std::vector Passes; + bool Cleaning; + bool ShuffleFunctions; + bool InlineJniWrappers; +}; -} +// Defined in omvll_config.cpp. +extern OMVLLConfig Config; +void initDefaultConfig(); -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes.hpp b/src/include/omvll/passes.hpp index a64be654..74d2417d 100644 --- a/src/include/omvll/passes.hpp +++ b/src/include/omvll/passes.hpp @@ -1,15 +1,17 @@ -#ifndef OMVLL_PASSES_H -#define OMVLL_PASSES_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// #include "omvll/passes/ObfuscationOpt.hpp" +#include "omvll/passes/anti-hook/AntiHook.hpp" #include "omvll/passes/arithmetic/Arithmetic.hpp" -#include "omvll/passes/string-encoding/StringEncoding.hpp" -#include "omvll/passes/flattening/ControlFlowFlattening.hpp" -#include "omvll/passes/opaque-field-access/OpaqueFieldAccess.hpp" -#include "omvll/passes/opaque-constants/OpaqueConstants.hpp" #include "omvll/passes/break-cfg/BreakControlFlow.hpp" -#include "omvll/passes/anti-hook/AntiHook.hpp" -#include "omvll/passes/objcleaner/ObjCleaner.hpp" +#include "omvll/passes/cfg-flattening/ControlFlowFlattening.hpp" #include "omvll/passes/cleaning/Cleaning.hpp" - -#endif +#include "omvll/passes/objc-cleaner/ObjCleaner.hpp" +#include "omvll/passes/opaque-constants/OpaqueConstants.hpp" +#include "omvll/passes/opaque-field-access/OpaqueFieldAccess.hpp" +#include "omvll/passes/string-encoding/StringEncoding.hpp" diff --git a/src/include/omvll/passes/Metadata.hpp b/src/include/omvll/passes/Metadata.hpp index a8282b5b..13f0ec44 100644 --- a/src/include/omvll/passes/Metadata.hpp +++ b/src/include/omvll/passes/Metadata.hpp @@ -1,55 +1,56 @@ -#ifndef OMVLL_METADATA_H -#define OMVLL_METADATA_H -#include -#include -#include +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// #include +#include + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +// Forward declarations namespace llvm { class Instruction; -} +} // end namespace llvm namespace omvll { -enum MObfTy { - NONE = 0, - OPAQUE_CST, // Force to apply opaque constants - OPAQUE_OP, // Force to apply MBA-like - OPAQUE_COND, // Force to apply opaque conditions - - PROTECT_FIELD_ACCESS, // Force to apply opaque field access +enum MetaObfTy { + None = 0, + OpaqueCst, // Force to apply opaque constants. + OpaqueOp, // Force to apply MBA-like. + OpaqueCond, // Force to apply opaque conditions. + ProtectFieldAccess, // Force to apply opaque field access. }; -using MObfVal = std::variant< - std::monostate, - uint64_t ->; +using MetaObfVal = std::variant; struct MetaObf { - inline static constexpr MObfTy None = NONE; - MetaObf() = default; - MetaObf(MObfTy Ty) : type(Ty) {} - MetaObf(MObfTy Ty, MObfVal val) : type(Ty), value(std::move(val)) {} - MObfTy type = MObfTy::NONE; - MObfVal value; - inline bool isNone() { return type == MObfTy::NONE; } - inline bool hasValue() { return !std::holds_alternative(value); } - template - const T* get() { - if (const T* v = std::get_if(&value)) { - return v; - } + MetaObf(MetaObfTy Ty) : Type(Ty) {} + MetaObf(MetaObfTy Ty, MetaObfVal Value) : Type(Ty), Value(std::move(Value)) {} + MetaObfTy Type = MetaObfTy::None; + MetaObfVal Value; + + inline bool isNone() { return Type == MetaObfTy::None; } + inline bool hasValue() { + return !std::holds_alternative(Value); + } + + template const T *get() { + if (const T *V = std::get_if(&Value)) + return V; return nullptr; } }; -void addMetadata(llvm::Instruction& I, MetaObf M); -void addMetadata(llvm::Instruction& I, llvm::ArrayRef M); -llvm::SmallVector getObfMetadata(llvm::Instruction& I); -std::optional getObf(llvm::Instruction &I, MObfTy M); -bool hasObf(llvm::Instruction& I, MObfTy M); +void addMetadata(llvm::Instruction &I, MetaObf M); +void addMetadata(llvm::Instruction &I, llvm::ArrayRef M); +llvm::SmallVector getObfMetadata(llvm::Instruction &I); +std::optional getObf(llvm::Instruction &I, MetaObfTy M); +bool hasObf(llvm::Instruction &I, MetaObfTy M); -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/ObfuscationOpt.hpp b/src/include/omvll/passes/ObfuscationOpt.hpp index 9e3c11f9..92b82979 100644 --- a/src/include/omvll/passes/ObfuscationOpt.hpp +++ b/src/include/omvll/passes/ObfuscationOpt.hpp @@ -1,12 +1,14 @@ -#ifndef OMVLL_OBFUSCATION_OPT_H -#define OMVLL_OBFUSCATION_OPT_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// -#include "omvll/passes/flattening/ControlFlowFlatteningOpt.hpp" -#include "omvll/passes/string-encoding/StringEncodingOpt.hpp" -#include "omvll/passes/opaque-field-access/OpaqueFieldAccessOpt.hpp" -#include "omvll/passes/break-cfg/BreakControlFlowOpt.hpp" #include "omvll/passes/anti-hook/AntiHookOpt.hpp" #include "omvll/passes/arithmetic/ArithmeticOpt.hpp" +#include "omvll/passes/break-cfg/BreakControlFlowOpt.hpp" +#include "omvll/passes/cfg-flattening/ControlFlowFlatteningOpt.hpp" #include "omvll/passes/opaque-constants/OpaqueConstantsOpt.hpp" - -#endif +#include "omvll/passes/opaque-field-access/OpaqueFieldAccessOpt.hpp" +#include "omvll/passes/string-encoding/StringEncodingOpt.hpp" diff --git a/src/include/omvll/passes/anti-hook/AntiHook.hpp b/src/include/omvll/passes/anti-hook/AntiHook.hpp index 94ed8c50..9629b16d 100644 --- a/src/include/omvll/passes/anti-hook/AntiHook.hpp +++ b/src/include/omvll/passes/anti-hook/AntiHook.hpp @@ -1,17 +1,24 @@ -#ifndef OMVLL_ANTI_HOOK_H -#define OMVLL_ANTI_HOOK_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "llvm/IR/PassManager.h" #include "llvm/Support/RandomNumberGenerator.h" +// Forward declarations namespace llvm { class Function; -} +} // end namespace llvm namespace omvll { + class Jitter; -//! Frida anti-hooking -//! See: https://obfuscator.re/omvll/passes/anti-hook/ for the details +// Frida Anti-Hooking. +// See https://obfuscator.re/omvll/passes/anti-hook/ for details. struct AntiHook : llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &FAM); @@ -19,9 +26,8 @@ struct AntiHook : llvm::PassInfoMixin { bool runOnFunction(llvm::Function &F); private: - std::unique_ptr RNG_; - std::unique_ptr jitter_; + std::unique_ptr RNG; + std::unique_ptr JIT; }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/anti-hook/AntiHookOpt.hpp b/src/include/omvll/passes/anti-hook/AntiHookOpt.hpp index 3edc587b..01e20f55 100644 --- a/src/include/omvll/passes/anti-hook/AntiHookOpt.hpp +++ b/src/include/omvll/passes/anti-hook/AntiHookOpt.hpp @@ -1,12 +1,16 @@ -#ifndef OMVLL_ANTI_HOOK_OPT_H -#define OMVLL_ANTI_HOOK_OPT_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// namespace omvll { + struct AntiHookOpt { - AntiHookOpt(bool val) : value(val) {} - operator bool() const { return value; } - bool value = false; + AntiHookOpt(bool Value) : Value(Value) {} + operator bool() const { return Value; } + bool Value = false; }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/arithmetic/Arithmetic.hpp b/src/include/omvll/passes/arithmetic/Arithmetic.hpp index e5e84e38..80716362 100644 --- a/src/include/omvll/passes/arithmetic/Arithmetic.hpp +++ b/src/include/omvll/passes/arithmetic/Arithmetic.hpp @@ -1,5 +1,10 @@ -#ifndef OMVLL_ARITHMETIC_H -#define OMVLL_ARITHMETIC_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "llvm/IR/PassManager.h" #include "llvm/IR/InstVisitor.h" #include "llvm/ADT/DenseMap.h" @@ -7,29 +12,30 @@ #include "omvll/passes/arithmetic/ArithmeticOpt.hpp" +// Forward declarations namespace llvm { class Function; class Module; class Value; class BinaryOperator; -} +} // end namespace llvm namespace omvll { -// See: https://obfuscator.re/omvll/passes/arithmetic/ for the details +// See https://obfuscator.re/omvll/passes/arithmetic/ for details. struct Arithmetic : llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &FAM); bool runOnBasicBlock(llvm::BasicBlock &BB); - llvm::Function* injectFunWrapper(llvm::Module& M, llvm::BinaryOperator& Op, - llvm::Value& Lhs, llvm::Value& Rhs); + llvm::Function *injectFunWrapper(llvm::Module &M, llvm::BinaryOperator &Op, + llvm::Value &Lhs, llvm::Value &Rhs); + + static bool isSupported(const llvm::BinaryOperator &Op); - static bool isSupported(const llvm::BinaryOperator& Op); - private: - llvm::DenseMap opts_; - std::unique_ptr RNG_; +private: + llvm::DenseMap Opts; + std::unique_ptr RNG; }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/arithmetic/ArithmeticOpt.hpp b/src/include/omvll/passes/arithmetic/ArithmeticOpt.hpp index 9e81622e..dbcef0bf 100644 --- a/src/include/omvll/passes/arithmetic/ArithmeticOpt.hpp +++ b/src/include/omvll/passes/arithmetic/ArithmeticOpt.hpp @@ -1,20 +1,22 @@ -#ifndef OMVLL_ARITHMETIC_OPT_H -#define OMVLL_ARITHMETIC_OPT_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include #include namespace omvll { struct ArithmeticOpt { - static constexpr size_t DEFAULT_NB_ROUND = 3; + static constexpr size_t DefaultNumRounds = 3; ArithmeticOpt() = default; - ArithmeticOpt(uint8_t it) : iterations(it) {} - ArithmeticOpt(bool val) : iterations(val ? DEFAULT_NB_ROUND : 0) {} - operator bool() const { return iterations > 0; } - uint8_t iterations = DEFAULT_NB_ROUND; + ArithmeticOpt(uint8_t Iterations) : Iterations(Iterations) {} + ArithmeticOpt(bool Value) : Iterations(Value ? DefaultNumRounds : 0) {} + operator bool() const { return Iterations > 0; } + uint8_t Iterations = DefaultNumRounds; }; - -} - -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/break-cfg/BreakControlFlow.hpp b/src/include/omvll/passes/break-cfg/BreakControlFlow.hpp index 22d23895..0169f508 100644 --- a/src/include/omvll/passes/break-cfg/BreakControlFlow.hpp +++ b/src/include/omvll/passes/break-cfg/BreakControlFlow.hpp @@ -1,27 +1,34 @@ -#ifndef OMVLL_BREAK_CONTROL_FLOW_H -#define OMVLL_BREAK_CONTROL_FLOW_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "llvm/IR/PassManager.h" +// Forward declarations namespace llvm { class Function; class RandomNumberGenerator; class BasicBlock; -} +} // end namespace llvm namespace omvll { + class Jitter; -// See: https://obfuscator.re/omvll/passes/control-flow-breaking/ for the details +// See https://obfuscator.re/omvll/passes/control-flow-breaking/ for details. struct BreakControlFlow : llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &FAM); - bool runOnEntryBlock(llvm::Function &F, llvm::BasicBlock& EntryBlock); - bool runOnTerminators(llvm::Function &F, llvm::BasicBlock& TBlock); + + bool runOnEntryBlock(llvm::Function &F, llvm::BasicBlock &EntryBlock); + bool runOnTerminators(llvm::Function &F, llvm::BasicBlock &TBlock); bool runOnFunction(llvm::Function &F); - std::unique_ptr Jitter_; - std::unique_ptr RNG_; + std::unique_ptr JIT; + std::unique_ptr RNG; }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/break-cfg/BreakControlFlowOpt.hpp b/src/include/omvll/passes/break-cfg/BreakControlFlowOpt.hpp index 1e07829c..48e1a3ad 100644 --- a/src/include/omvll/passes/break-cfg/BreakControlFlowOpt.hpp +++ b/src/include/omvll/passes/break-cfg/BreakControlFlowOpt.hpp @@ -1,17 +1,16 @@ -#ifndef OMVLL_BREAK_CONTROL_FLOW_OPT_H -#define OMVLL_BREAK_CONTROL_FLOW_OPT_H -#include -#include -#include +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// namespace omvll { struct BreakControlFlowOpt { - BreakControlFlowOpt(bool val) : value(val) {} - operator bool() const { return value; } - bool value = false; + BreakControlFlowOpt(bool Value) : Value(Value) {} + operator bool() const { return Value; } + bool Value = false; }; -} - -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/flattening/ControlFlowFlattening.hpp b/src/include/omvll/passes/cfg-flattening/ControlFlowFlattening.hpp similarity index 53% rename from src/include/omvll/passes/flattening/ControlFlowFlattening.hpp rename to src/include/omvll/passes/cfg-flattening/ControlFlowFlattening.hpp index bd2bac43..3cc2ac9a 100644 --- a/src/include/omvll/passes/flattening/ControlFlowFlattening.hpp +++ b/src/include/omvll/passes/cfg-flattening/ControlFlowFlattening.hpp @@ -1,16 +1,20 @@ -#ifndef OMVLL_CFG_FLAT_H -#define OMVLL_CFG_FLAT_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "llvm/IR/PassManager.h" namespace omvll { -// The classical control-flow flattening pass -// See: https://obfuscator.re/omvll/passes/control-flow-flattening/ for the details +// The classical control-flow flattening pass. +// See https://obfuscator.re/omvll/passes/control-flow-flattening/ for details. struct ControlFlowFlattening : llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &FAM); - bool runOnFunction(llvm::Function &F, llvm::RandomNumberGenerator& RNG); + bool runOnFunction(llvm::Function &F, llvm::RandomNumberGenerator &RNG); }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/cfg-flattening/ControlFlowFlatteningOpt.hpp b/src/include/omvll/passes/cfg-flattening/ControlFlowFlatteningOpt.hpp new file mode 100644 index 00000000..81ac70d3 --- /dev/null +++ b/src/include/omvll/passes/cfg-flattening/ControlFlowFlatteningOpt.hpp @@ -0,0 +1,16 @@ +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +namespace omvll { + +struct ControlFlowFlatteningOpt { + ControlFlowFlatteningOpt(bool Value) : Value(Value) {} + operator bool() const { return Value; } + bool Value = false; +}; + +} // end namespace omvll diff --git a/src/include/omvll/passes/cleaning/Cleaning.hpp b/src/include/omvll/passes/cleaning/Cleaning.hpp index b83dd29c..9343f5c5 100644 --- a/src/include/omvll/passes/cleaning/Cleaning.hpp +++ b/src/include/omvll/passes/cleaning/Cleaning.hpp @@ -1,9 +1,11 @@ -#ifndef OMVLL_CLEANING_H -#define OMVLL_CLEANING_H -#include "llvm/IR/PassManager.h" +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// -namespace llvm { -} +#include "llvm/IR/PassManager.h" namespace omvll { @@ -11,6 +13,5 @@ struct Cleaning : llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &FAM); }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/flattening/ControlFlowFlatteningOpt.hpp b/src/include/omvll/passes/flattening/ControlFlowFlatteningOpt.hpp deleted file mode 100644 index eacceaac..00000000 --- a/src/include/omvll/passes/flattening/ControlFlowFlatteningOpt.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef OMVLL_CFG_FLAT_OPT_H -#define OMVLL_CFG_FLAT_OPT_H -#include - -namespace omvll { - -struct ControlFlowFlatteningOpt { - ControlFlowFlatteningOpt(bool val) : value(val) {} - operator bool() const { return value; } - bool value = false; -}; - -} - -#endif diff --git a/src/include/omvll/passes/objcleaner/ObjCleaner.hpp b/src/include/omvll/passes/objc-cleaner/ObjCleaner.hpp similarity index 52% rename from src/include/omvll/passes/objcleaner/ObjCleaner.hpp rename to src/include/omvll/passes/objc-cleaner/ObjCleaner.hpp index 9bb5cbb3..fb4300f5 100644 --- a/src/include/omvll/passes/objcleaner/ObjCleaner.hpp +++ b/src/include/omvll/passes/objc-cleaner/ObjCleaner.hpp @@ -1,23 +1,25 @@ -#ifndef OMVLL_OBJCLEANER_H -#define OMVLL_OBJCLEANER_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "llvm/IR/PassManager.h" +// Forward declarations namespace llvm { class GlobalVariable; -} +} // end namespace llvm namespace omvll { -/* - * /!\ THIS PASS IS NOT YET FULLY IMPLEMENTED /!\ - * - * This pass aims at mangling/obfuscating objective-C symbols - */ +// This pass aims at mangling/obfuscating Objective-C symbols. +// Note: this pass is incomplete. struct ObjCleaner : llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &FAM); bool runGlobalVariable(llvm::Function &F); }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/objc-cleaner/ObjCleanerOpt.hpp b/src/include/omvll/passes/objc-cleaner/ObjCleanerOpt.hpp new file mode 100644 index 00000000..7ab26998 --- /dev/null +++ b/src/include/omvll/passes/objc-cleaner/ObjCleanerOpt.hpp @@ -0,0 +1,16 @@ +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +namespace omvll { + +struct ObjCleanerOpt { + ObjCleanerOpt(bool Value) : Value(Value) {} + operator bool() const { return Value; } + bool Value = false; +}; + +} // end namespace omvll diff --git a/src/include/omvll/passes/objcleaner/ObjCleanerOpt.hpp b/src/include/omvll/passes/objcleaner/ObjCleanerOpt.hpp deleted file mode 100644 index 4354ab2e..00000000 --- a/src/include/omvll/passes/objcleaner/ObjCleanerOpt.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef OMVLL_OBJCLEANER_OPT_H -#define OMVLL_OBJCLEANER_OPT_H -#include - -namespace omvll { - -struct ObjCleanerOpt { - ObjCleanerOpt(bool val) : value(val) {} - operator bool() const { return value; } - bool value = false; -}; - -} - -#endif diff --git a/src/include/omvll/passes/opaque-constants/OpaqueConstants.hpp b/src/include/omvll/passes/opaque-constants/OpaqueConstants.hpp index dfd7c168..5915eeab 100644 --- a/src/include/omvll/passes/opaque-constants/OpaqueConstants.hpp +++ b/src/include/omvll/passes/opaque-constants/OpaqueConstants.hpp @@ -1,10 +1,16 @@ -#ifndef OMVLL_OPAQUE_CONSTANTS_H -#define OMVLL_OPAQUE_CONSTANTS_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "llvm/IR/PassManager.h" #include "llvm/ADT/DenseMap.h" #include "omvll/passes/opaque-constants/OpaqueConstantsOpt.hpp" +// Forward declarations namespace llvm { class Instruction; class ConstantInt; @@ -12,34 +18,38 @@ class Value; class Use; class AllocaInst; class Type; -} +} // end namespace llvm namespace omvll { + struct OpaqueContext { - llvm::AllocaInst* T1; - llvm::AllocaInst* T2; + llvm::AllocaInst *T1; + llvm::AllocaInst *T2; }; -// See: https://obfuscator.re/omvll/passes/opaque-constants for the details +// See https://obfuscator.re/omvll/passes/opaque-constants for details. struct OpaqueConstants : llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &FAM); - bool runOnBasicBlock(llvm::BasicBlock &BB, OpaqueConstantsOpt* opt); - bool Process(llvm::Instruction& I, OpaqueConstantsOpt* opt); - bool Process(llvm::Instruction& I, llvm::Use& Op, llvm::ConstantInt& CI, OpaqueConstantsOpt* opt); + bool runOnBasicBlock(llvm::BasicBlock &BB, OpaqueConstantsOpt *Opt); - OpaqueContext* getOrCreateContext(llvm::BasicBlock& BB); + bool process(llvm::Instruction &I, OpaqueConstantsOpt *Opt); + bool process(llvm::Instruction &I, llvm::Use &Op, llvm::ConstantInt &CI, + OpaqueConstantsOpt *Opt); - llvm::Value* GetOpaqueZero(llvm::Instruction& I, OpaqueContext& C, llvm::Type* Ty); - llvm::Value* GetOpaqueOne(llvm::Instruction& I, OpaqueContext& C, llvm::Type* Ty); - llvm::Value* GetOpaqueCst(llvm::Instruction& I, OpaqueContext& C, const llvm::ConstantInt& Val); + OpaqueContext *getOrCreateContext(llvm::BasicBlock &BB); - private: - std::unique_ptr RNG_; - llvm::DenseMap ctx_; - llvm::DenseMap opts_; + llvm::Value *getOpaqueZero(llvm::Instruction &I, OpaqueContext &C, + llvm::Type *Ty); + llvm::Value *getOpaqueOne(llvm::Instruction &I, OpaqueContext &C, + llvm::Type *Ty); + llvm::Value *getOpaqueCst(llvm::Instruction &I, OpaqueContext &C, + const llvm::ConstantInt &Val); +private: + std::unique_ptr RNG; + llvm::DenseMap Ctx; + llvm::DenseMap Opts; }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/opaque-constants/OpaqueConstantsOpt.hpp b/src/include/omvll/passes/opaque-constants/OpaqueConstantsOpt.hpp index ce5cfdd2..da20c330 100644 --- a/src/include/omvll/passes/opaque-constants/OpaqueConstantsOpt.hpp +++ b/src/include/omvll/passes/opaque-constants/OpaqueConstantsOpt.hpp @@ -1,36 +1,40 @@ -#ifndef OMVLL_OPAQUE_CST_OPT_H -#define OMVLL_OPAQUE_CST_OPT_H -#include +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include -#include +#include #include -#include +#include "llvm/ADT/DenseSet.h" namespace omvll { -struct OpaqueConstantsSkip{}; +struct OpaqueConstantsSkip {}; struct OpaqueConstantsBool { - OpaqueConstantsBool(bool val) : value(val) {} - operator bool() const { return value; } - bool value = false; + OpaqueConstantsBool(bool Value) : Value(Value) {} + operator bool() const { return Value; } + bool Value = false; }; struct OpaqueConstantsLowerLimit { - OpaqueConstantsLowerLimit(uint64_t val) : value(val) {} - operator bool() const { return value > 0; } - uint64_t value = 0; + OpaqueConstantsLowerLimit(uint64_t Value) : Value(Value) {} + operator bool() const { return Value > 0; } + uint64_t Value = 0; }; struct OpaqueConstantsSet { - OpaqueConstantsSet(std::vector val) : - values(val.begin(), val.end()) - {} - inline bool contains(uint64_t value) const { return values.contains(value); } - inline bool empty() { return values.empty(); }; - inline operator bool() const { return !values.empty(); } - llvm::DenseSet values; + OpaqueConstantsSet(std::vector Value) + : Values(Value.begin(), Value.end()) {} + + inline bool contains(uint64_t Value) const { return Values.contains(Value); } + inline bool empty() { return Values.empty(); }; + inline operator bool() const { return !Values.empty(); } + llvm::DenseSet Values; }; using OpaqueConstantsOpt = std::variant< @@ -40,9 +44,4 @@ using OpaqueConstantsOpt = std::variant< OpaqueConstantsSet >; - - - -} - -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/opaque-field-access/OpaqueFieldAccess.hpp b/src/include/omvll/passes/opaque-field-access/OpaqueFieldAccess.hpp index 3166f8cc..d88e9522 100644 --- a/src/include/omvll/passes/opaque-field-access/OpaqueFieldAccess.hpp +++ b/src/include/omvll/passes/opaque-field-access/OpaqueFieldAccess.hpp @@ -1,7 +1,13 @@ -#ifndef OMVLL_OPAQUE_FIELD_ACCESS_H -#define OMVLL_OPAQUE_FIELD_ACCESS_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "llvm/IR/PassManager.h" +// Forward declarations namespace llvm { class UnaryInstruction; class StructType; @@ -10,40 +16,39 @@ class LoadInst; class StoreInst; class ConstantInt; class ConstantExpr; -} +} // end namespace llvm namespace omvll { -//! See: https://obfuscator.re/omvll/passes/opaque-fields-access for the details +// See https://obfuscator.re/omvll/passes/opaque-fields-access for details. struct OpaqueFieldAccess : llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &FAM); - bool runOnBasicBlock(llvm::BasicBlock &BB); - bool shouldRun(llvm::BasicBlock& BB); + bool shouldRun(llvm::BasicBlock &BB); - bool runOnLoad(llvm::LoadInst& Load); + bool runOnBasicBlock(llvm::BasicBlock &BB); - bool runOnStructRead(llvm::BasicBlock& BB, llvm::LoadInst& Load, - llvm::GetElementPtrInst& GEP, llvm::StructType& S); + bool runOnLoad(llvm::LoadInst &Load); - bool runOnBufferRead(llvm::BasicBlock& BB, llvm::LoadInst& Load, - llvm::GetElementPtrInst& GEP, llvm::ConstantInt& CI); + bool runOnStructRead(llvm::BasicBlock &BB, llvm::LoadInst &Load, + llvm::GetElementPtrInst &GEP, llvm::StructType &S); - bool runOnConstantExprRead(llvm::BasicBlock& BB, llvm::LoadInst& Load, - llvm::ConstantExpr& CE); + bool runOnBufferRead(llvm::BasicBlock &BB, llvm::LoadInst &Load, + llvm::GetElementPtrInst &GEP, llvm::ConstantInt &CI); - bool runOnStore(llvm::StoreInst& Store); + bool runOnConstantExprRead(llvm::BasicBlock &BB, llvm::LoadInst &Load, + llvm::ConstantExpr &CE); - bool runOnStructWrite(llvm::BasicBlock& BB, llvm::StoreInst& Store, - llvm::GetElementPtrInst& GEP, llvm::StructType& S); + bool runOnStore(llvm::StoreInst &Store); - bool runOnBufferWrite(llvm::BasicBlock& BB, llvm::StoreInst& Store, - llvm::GetElementPtrInst& GEP, llvm::ConstantInt& CI); + bool runOnStructWrite(llvm::BasicBlock &BB, llvm::StoreInst &Store, + llvm::GetElementPtrInst &GEP, llvm::StructType &S); - bool runOnConstantExprWrite(llvm::BasicBlock& BB, llvm::StoreInst& Store, - llvm::ConstantExpr& CE); + bool runOnBufferWrite(llvm::BasicBlock &BB, llvm::StoreInst &Store, + llvm::GetElementPtrInst &GEP, llvm::ConstantInt &CI); + bool runOnConstantExprWrite(llvm::BasicBlock &BB, llvm::StoreInst &Store, + llvm::ConstantExpr &CE); }; -} -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/opaque-field-access/OpaqueFieldAccessOpt.hpp b/src/include/omvll/passes/opaque-field-access/OpaqueFieldAccessOpt.hpp index 6b899c79..9b9dffa3 100644 --- a/src/include/omvll/passes/opaque-field-access/OpaqueFieldAccessOpt.hpp +++ b/src/include/omvll/passes/opaque-field-access/OpaqueFieldAccessOpt.hpp @@ -1,21 +1,22 @@ -#ifndef OMVLL_OPAQUE_FIELD_OPT_H -#define OMVLL_OPAQUE_FIELD_OPT_H -#include +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// namespace omvll { struct StructAccessOpt { - StructAccessOpt(bool val) : value(val) {} - operator bool() const { return value; } - bool value = false; + StructAccessOpt(bool Value) : Value(Value) {} + operator bool() const { return Value; } + bool Value = false; }; struct VarAccessOpt { - VarAccessOpt(bool val) : value(val) {} - operator bool() const { return value; } - bool value = false; + VarAccessOpt(bool Value) : Value(Value) {} + operator bool() const { return Value; } + bool Value = false; }; -} - -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/string-encoding/StringEncoding.hpp b/src/include/omvll/passes/string-encoding/StringEncoding.hpp index 1576fb3c..0caf8391 100644 --- a/src/include/omvll/passes/string-encoding/StringEncoding.hpp +++ b/src/include/omvll/passes/string-encoding/StringEncoding.hpp @@ -1,111 +1,115 @@ -#ifndef OMVLL_STRING_ENCODING_H -#define OMVLL_STRING_ENCODING_H -#include "omvll/passes/string-encoding/StringEncodingOpt.hpp" +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// -#include -#include -#include -#include -#include #include +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/Triple.h" +#include "llvm/IR/PassManager.h" +#include "llvm/Support/RandomNumberGenerator.h" + +#include "omvll/passes/string-encoding/StringEncodingOpt.hpp" + +// Forward declarations namespace llvm { class ConstantDataSequential; class GlobalVariable; class CallInst; -} - +} // end namespace llvm namespace omvll { + struct ObfuscationConfig; class Jitter; -//! See: https://obfuscator.re/omvll/passes/strings-encoding for the details +// See https://obfuscator.re/omvll/passes/strings-encoding for details. struct StringEncoding : llvm::PassInfoMixin { - using enc_routine_t = void(*)(uint8_t* out, const char* in, uint64_t key, int len); + using EncRoutineFn = void (*)(uint8_t *Out, const char *In, uint64_t Key, + int Len); + using KeyBufferTy = std::vector; + using KeyIntTy = uint64_t; + using KeyTy = std::variant; + enum EncodingTy { - NONE = 0, - STACK, - STACK_LOOP, - GLOBAL, - REPLACE, + None = 0, + Stack, + StackLoop, + Global, + Replace, }; - using key_buffer_t = std::vector; - using key_int_t = uint64_t; - using KeyTy = std::variant; + struct EncodingInfo { EncodingInfo() = delete; - EncodingInfo(EncodingTy Ty) : type(Ty) {}; + EncodingInfo(EncodingTy Ty) : Type(Ty) {}; - EncodingTy type = EncodingTy::NONE; - KeyTy key; + EncodingTy Type = EncodingTy::None; + KeyTy Key; std::unique_ptr TM; std::unique_ptr HM; }; llvm::PreservedAnalyses run(llvm::Module &M, - llvm::ModuleAnalysisManager&); + llvm::ModuleAnalysisManager &MAM); static bool isRequired() { return true; } - - bool runOnBasicBlock(llvm::Module& M, llvm::Function& F, llvm::BasicBlock& BB, - ObfuscationConfig& userConfig); - - bool injectDecoding(llvm::BasicBlock& BB, llvm::Instruction& I, llvm::Use& OP, - llvm::GlobalVariable& G, llvm::ConstantDataSequential& data, - const EncodingInfo& info); - - bool injectOnStack(llvm::BasicBlock& BB, llvm::Instruction& I, llvm::Use& OP, - llvm::GlobalVariable& G, llvm::ConstantDataSequential& data, - const EncodingInfo& info); - - bool injectOnStackLoop(llvm::BasicBlock& BB, llvm::Instruction& I, llvm::Use& OP, - llvm::GlobalVariable& G, llvm::ConstantDataSequential& data, - const EncodingInfo& info); - - bool process(llvm::BasicBlock& BB, llvm::Instruction& I, llvm::Use& OP, - llvm::GlobalVariable& G, llvm::ConstantDataSequential& data, - StringEncodingOpt& opt); - - bool processReplace(llvm::BasicBlock& BB, llvm::Instruction& I, llvm::Use& OP, - llvm::GlobalVariable& G, - llvm::ConstantDataSequential& data, StringEncOptReplace& rep); - - bool processGlobal(llvm::BasicBlock& BB, llvm::Instruction& I, llvm::Use& OP, - llvm::GlobalVariable& G, - llvm::ConstantDataSequential& data, StringEncOptGlobal& global); - - bool processOnStack(llvm::BasicBlock& BB, llvm::Instruction& I, llvm::Use& OP, - llvm::GlobalVariable& G, - llvm::ConstantDataSequential& data, const StringEncOptStack& stack); - - bool processOnStackLoop(llvm::BasicBlock& BB, llvm::Instruction& I, llvm::Use& OP, - llvm::GlobalVariable& G, llvm::ConstantDataSequential& data); - - inline EncodingInfo* getEncoding(const llvm::GlobalVariable& GV) { - if (auto it = gve_info_.find(&GV); it != gve_info_.end()) { - return &it->second; - } + bool runOnBasicBlock(llvm::Module &M, llvm::Function &F, llvm::BasicBlock &BB, + ObfuscationConfig &UserConfig); + + bool injectDecoding(llvm::BasicBlock &BB, llvm::Instruction &I, llvm::Use &Op, + llvm::GlobalVariable &G, + llvm::ConstantDataSequential &Data, + const EncodingInfo &Info); + bool injectOnStack(llvm::BasicBlock &BB, llvm::Instruction &I, llvm::Use &Op, + llvm::GlobalVariable &G, + llvm::ConstantDataSequential &Data, + const EncodingInfo &Info); + bool injectOnStackLoop(llvm::BasicBlock &BB, llvm::Instruction &I, + llvm::Use &Op, llvm::GlobalVariable &G, + llvm::ConstantDataSequential &Data, + const EncodingInfo &Info); + bool process(llvm::BasicBlock &BB, llvm::Instruction &I, llvm::Use &Op, + llvm::GlobalVariable &G, llvm::ConstantDataSequential &Data, + StringEncodingOpt &Opt); + bool processReplace(llvm::BasicBlock &BB, llvm::Instruction &I, llvm::Use &Op, + llvm::GlobalVariable &G, + llvm::ConstantDataSequential &Data, + StringEncOptReplace &Rep); + bool processGlobal(llvm::BasicBlock &BB, llvm::Instruction &I, llvm::Use &Op, + llvm::GlobalVariable &G, + llvm::ConstantDataSequential &Data, + StringEncOptGlobal &Global); + bool processOnStack(llvm::BasicBlock &BB, llvm::Instruction &I, llvm::Use &Op, + llvm::GlobalVariable &G, + llvm::ConstantDataSequential &Data, + const StringEncOptStack &Stack); + bool processOnStackLoop(llvm::BasicBlock &BB, llvm::Instruction &I, + llvm::Use &Op, llvm::GlobalVariable &G, + llvm::ConstantDataSequential &Data); + + inline EncodingInfo *getEncoding(const llvm::GlobalVariable &GV) { + if (auto It = GVarEncInfo.find(&GV); It != GVarEncInfo.end()) + return &It->second; return nullptr; } private: - inline static Jitter *HOSTJIT = nullptr; + static inline Jitter *HostJIT = nullptr; void genRoutines(const llvm::Triple &Triple, EncodingInfo &EI, llvm::LLVMContext &Ctx); - void annotateRoutine(llvm::Module& M); - - std::vector inline_wlist_; - std::vector ctor_; - std::unique_ptr RNG_; - llvm::SmallSet obf_; - llvm::DenseMap> key_map_; - - llvm::DenseMap gve_info_; + void annotateRoutine(llvm::Module &M); + + std::vector ToInline; + std::vector Ctors; + std::unique_ptr RNG; + llvm::SmallSet Obf; + llvm::DenseMap> KeyMap; + llvm::DenseMap GVarEncInfo; }; - -} - -#endif +} // end namespace omvll diff --git a/src/include/omvll/passes/string-encoding/StringEncodingOpt.hpp b/src/include/omvll/passes/string-encoding/StringEncodingOpt.hpp index 201cae6c..d9b8c00a 100644 --- a/src/include/omvll/passes/string-encoding/StringEncodingOpt.hpp +++ b/src/include/omvll/passes/string-encoding/StringEncodingOpt.hpp @@ -1,5 +1,10 @@ -#ifndef OMVLL_STRING_ENCODING_OPT_H -#define OMVLL_STRING_ENCODING_OPT_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include #include #include @@ -7,20 +12,19 @@ namespace omvll { struct StringEncOptStack { - size_t loopThreshold = 10; + size_t LoopThreshold = 10; }; -struct StringEncOptSkip{}; -struct StringEncOptGlobal{}; -struct StringEncOptDefault{}; +struct StringEncOptSkip {}; +struct StringEncOptGlobal {}; +struct StringEncOptDefault {}; struct StringEncOptReplace { StringEncOptReplace() = default; - StringEncOptReplace(std::string str) : newString(std::move(str)) {}; - std::string newString; + StringEncOptReplace(std::string Str) : NewString(std::move(Str)) {}; + std::string NewString; }; - using StringEncodingOpt = std::variant< StringEncOptSkip, StringEncOptStack, @@ -29,6 +33,4 @@ using StringEncodingOpt = std::variant< StringEncOptDefault >; -} - -#endif +} // end namespace omvll diff --git a/src/include/omvll/utils.hpp b/src/include/omvll/utils.hpp index f82ac451..22363e5b 100644 --- a/src/include/omvll/utils.hpp +++ b/src/include/omvll/utils.hpp @@ -1,13 +1,19 @@ -#ifndef OMVLL_UTILS_H -#define OMVLL_UTILS_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/PassManager.h" #include "llvm/Support/Error.h" -#include +// Forward declarations namespace llvm { class Instruction; class BasicBlock; @@ -17,31 +23,32 @@ class Type; class Value; class MDNode; class MemoryBuffer; -} +} // end namespace llvm + namespace omvll { -std::string ToString(const llvm::Module& M); -std::string ToString(const llvm::BasicBlock& BB); -std::string ToString(const llvm::Instruction& I); -std::string ToString(const llvm::Type& Ty); -std::string ToString(const llvm::Value& V); -std::string ToString(const llvm::MDNode& N); +std::string ToString(const llvm::Module &M); +std::string ToString(const llvm::BasicBlock &BB); +std::string ToString(const llvm::Instruction &I); +std::string ToString(const llvm::Type &Ty); +std::string ToString(const llvm::Value &V); +std::string ToString(const llvm::MDNode &N); -std::string TypeIDStr(const llvm::Type& Ty); -std::string ValueIDStr(const llvm::Value& V); +std::string TypeIDStr(const llvm::Type &Ty); +std::string ValueIDStr(const llvm::Value &V); -void dump(llvm::Module& M, const std::string& file); -void dump(llvm::Function& F, const std::string& file); -void dump(const llvm::MemoryBuffer& MB, const std::string& file); +void dump(llvm::Module &M, const std::string &File); +void dump(llvm::Function &F, const std::string &File); +void dump(const llvm::MemoryBuffer &MB, const std::string &File); -size_t demotePHINode(llvm::Function& F); -size_t demoteRegs(llvm::Function& F); -size_t reg2mem(llvm::Function& F); +size_t demotePHINode(llvm::Function &F); +size_t demoteRegs(llvm::Function &F); +size_t reg2mem(llvm::Function &F); -void shuffleFunctions(llvm::Module& M); +void shuffleFunctions(llvm::Module &M); -[[noreturn]] void fatalError(const char* msg); -[[noreturn]] void fatalError(const std::string& msg); +[[noreturn]] void fatalError(const char *Msg); +[[noreturn]] void fatalError(const std::string &Msg); llvm::Expected> generateModule(llvm::StringRef Routine, const llvm::Triple &Triple, @@ -69,11 +76,11 @@ class IRChangesMonitor { IRChangesMonitor &operator=(const IRChangesMonitor &) = delete; private: - const llvm::Module &Mod; + const llvm::Module &M; ObfuscationConfig *UserConfig; std::string PassName; std::string OriginalIR; bool ChangeReported; }; -} -#endif + +} // end namespace omvll diff --git a/src/include/omvll/versioning.hpp b/src/include/omvll/versioning.hpp index 1fb9c50d..05389a90 100644 --- a/src/include/omvll/versioning.hpp +++ b/src/include/omvll/versioning.hpp @@ -1,7 +1,13 @@ -#ifndef OMVLL_VERIONING_H -#define OMVLL_VERIONING_H -#include -#include +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/VCSRevision.h" + #include "omvll/config.hpp" #ifdef LLVM_REVISION @@ -15,5 +21,3 @@ #define OMVLL_LLVM_MINOR LLVM_VERSION_MINOR #define OMVLL_LLVM_PATCH LLVM_VERSION_PATCH #define OMVLL_LLVM_VERSION_STRING LLVM_VERSION_STRING - -#endif diff --git a/src/include/omvll/visitvariant.hpp b/src/include/omvll/visitvariant.hpp index b0358804..7e68436e 100644 --- a/src/include/omvll/visitvariant.hpp +++ b/src/include/omvll/visitvariant.hpp @@ -1,7 +1,13 @@ -#ifndef OMVLL_VARIANT_VISIT_H -#define OMVLL_VARIANT_VISIT_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include -template struct overloaded : Ts... { using Ts::operator()...; }; -template overloaded(Ts...) -> overloaded; -#endif +template struct overloaded : Ts... { + using Ts::operator()...; +}; +template overloaded(Ts...) -> overloaded; diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt index a8a3088e..aa6ce97b 100644 --- a/src/passes/CMakeLists.txt +++ b/src/passes/CMakeLists.txt @@ -2,10 +2,10 @@ target_sources(OMVLL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Metadata.cpp ) -add_subdirectory("objcleaner") -add_subdirectory("strings-encoding") +add_subdirectory("objc-cleaner") +add_subdirectory("string-encoding") add_subdirectory("arithmetic") -add_subdirectory("flattening") +add_subdirectory("cfg-flattening") add_subdirectory("opaque-field-access") add_subdirectory("opaque-constants") add_subdirectory("break-cfg") diff --git a/src/passes/Metadata.cpp b/src/passes/Metadata.cpp index f597b2c1..62ca09be 100644 --- a/src/passes/Metadata.cpp +++ b/src/passes/Metadata.cpp @@ -1,120 +1,112 @@ -#include "omvll/passes/Metadata.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + #include "llvm/IR/Constants.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "omvll/passes/Metadata.hpp" #include "omvll/visitvariant.hpp" using namespace llvm; -namespace omvll { +static constexpr auto ObfKey = "obf"; -static constexpr const char OBF_KEY[] = "obf"; +namespace omvll { -void addMetadata(llvm::Instruction& I, MetaObf M) { - return addMetadata(I, llvm::ArrayRef(M)); +void addMetadata(Instruction &I, MetaObf M) { + addMetadata(I, ArrayRef(M)); } -Metadata* serialize(LLVMContext& C, const MetaObf& MObf) { - ConstantAsMetadata* Ty = ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(C), MObf.type, - /* signed ? */false)); - Metadata* value = std::visit(overloaded { - [] (std::monostate) -> Metadata* { return nullptr; }, - [&C] (uint64_t value) -> Metadata* { - return ConstantAsMetadata::get( - ConstantInt::get(Type::getInt64Ty(C), value, /* signed ? */false)); - }, - }, MObf.value); - - if (value) { - return MDTuple::get(C, {Ty, value}); - } - +Metadata *serialize(LLVMContext &C, const MetaObf &MObf) { + ConstantAsMetadata *Ty = ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(C), MObf.Type, /* signed ? */ false)); + + Metadata *Value = + std::visit(overloaded{ + [](std::monostate) -> Metadata * { return nullptr; }, + [&C](uint64_t value) -> Metadata * { + return ConstantAsMetadata::get(ConstantInt::get( + Type::getInt64Ty(C), value, /* signed ? */ false)); + }, + }, + MObf.Value); + + if (Value) + return MDTuple::get(C, {Ty, Value}); return MDTuple::get(C, {Ty}); } -MetaObf deserialize(LLVMContext& C, const Metadata& Meta) { - auto* Root = dyn_cast(&Meta); - if (Root == nullptr) { - return MetaObf::None; - } +MetaObf deserialize(LLVMContext &C, const Metadata &Meta) { + auto *Root = dyn_cast(&Meta); + if (!Root) + return MetaObfTy::None; - if (Root->getNumOperands() < 1) { - return MetaObf::None; - } + if (Root->getNumOperands() < 1) + return MetaObfTy::None; - const MDOperand& opType = Root->getOperand(0); - auto* CM = dyn_cast(opType.get()); - - if (CM == nullptr) { - return MetaObf::None; - } + const MDOperand &OpType = Root->getOperand(0); + auto *CM = dyn_cast(OpType.get()); + if (!CM) + return MetaObfTy::None; - auto* CI = dyn_cast(CM->getValue()); + auto *CI = dyn_cast(CM->getValue()); + if (!CI) + return MetaObfTy::None; - if (CI == nullptr) { - return MetaObf::None; - } - - auto T = MObfTy(CI->getLimitedValue()); + auto T = MetaObfTy(CI->getLimitedValue()); MetaObf MO(T); if (Root->getNumOperands() > 1) { - const MDOperand& Val = Root->getOperand(1); - if (auto* CM = dyn_cast(Val.get())) { - if (auto* CI = dyn_cast(CM->getValue())) { - MO.value = CI->getLimitedValue(); - } - } + const MDOperand &Val = Root->getOperand(1); + if (auto *CM = dyn_cast(Val.get())) + if (auto *CI = dyn_cast(CM->getValue())) + MO.Value = CI->getLimitedValue(); } return MO; } -void addMetadata(llvm::Instruction& I, llvm::ArrayRef M) { - auto& C = I.getContext(); - llvm::SmallVector metadata; - for (auto MObf : M) { - metadata.push_back(serialize(C, MObf)); - } - MDTuple* node = MDTuple::get(C, metadata); - I.setMetadata(OBF_KEY, node); -} - +void addMetadata(Instruction &I, ArrayRef M) { + LLVMContext &Ctx = I.getContext(); + SmallVector Metadata; + for (auto MObf : M) + Metadata.push_back(serialize(Ctx, MObf)); + MDTuple *Node = MDTuple::get(Ctx, Metadata); + I.setMetadata(ObfKey, Node); +} -llvm::SmallVector getObfMetadata(llvm::Instruction& I) { - LLVMContext& C = I.getContext(); - llvm::SmallVector result; - if (auto* node = I.getMetadata(OBF_KEY)) { - for (const MDOperand& op : node->operands()) { - if (auto MO = deserialize(C, *op); !MO.isNone()) { - result.push_back(std::move(MO)); - } - } - } - return result; +SmallVector getObfMetadata(Instruction &I) { + LLVMContext &Ctx = I.getContext(); + SmallVector Result; + if (auto *Node = I.getMetadata(ObfKey)) + for (const MDOperand &Op : Node->operands()) + if (auto MO = deserialize(Ctx, *Op); !MO.isNone()) + Result.push_back(std::move(MO)); + return Result; } -std::optional getObf(llvm::Instruction &I, MObfTy M) { - auto* node = I.getMetadata(OBF_KEY); - if (node == nullptr) { +std::optional getObf(Instruction &I, MetaObfTy M) { + auto *Node = I.getMetadata(ObfKey); + if (!Node) return std::nullopt; - } - for (const MDOperand& op : node->operands()) { - MetaObf MO = deserialize(I.getContext(), *op); - if (MO.type == M) { + + for (const MDOperand &Op : Node->operands()) { + MetaObf MO = deserialize(I.getContext(), *Op); + if (MO.Type == M) return MO; - } } return std::nullopt; } -bool hasObf(llvm::Instruction& I, MObfTy M) { +bool hasObf(Instruction &I, MetaObfTy M) { std::optional Obf = getObf(I, M); if (Obf.has_value()) return Obf->hasValue() && !(Obf->isNone()); return false; } -} +} // end namespace omvll diff --git a/src/passes/anti-hook/AntiHook.cpp b/src/passes/anti-hook/AntiHook.cpp index 734f1e3c..903d26da 100644 --- a/src/passes/anti-hook/AntiHook.cpp +++ b/src/passes/anti-hook/AntiHook.cpp @@ -1,32 +1,34 @@ -#include "omvll/passes/anti-hook/AntiHook.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include "llvm/Demangle/Demangle.h" +#include "llvm/IR/Constants.h" +#include "llvm/Support/MemoryBuffer.h" + +#include "omvll/ObfuscationConfig.hpp" +#include "omvll/PyConfig.hpp" +#include "omvll/jitter.hpp" #include "omvll/log.hpp" +#include "omvll/passes/anti-hook/AntiHook.hpp" #include "omvll/utils.hpp" -#include "omvll/PyConfig.hpp" -#include "omvll/passes/Metadata.hpp" -#include "omvll/ObfuscationConfig.hpp" -#include "omvll/Jitter.hpp" - -#include -#include -#include using namespace llvm; namespace omvll { -/* - * The current versions of Frida (as of 16.0.2) fail to hook functions - * that begin with instructions using x16/x17 registers. - * - * These stubs are using these registers and are injected in the prologue of the - * function to protect - */ +// Versions of Frida (as of 16.0.2) fail to hook functions that begin with +// instructions using x16/x17 registers. These stubs are using these registers +// and are injected in the prologue of the function to protect. + struct PrologueInfoTy { std::string Asm; - size_t size; + size_t Size; }; -static const std::vector ANTI_FRIDA_PROLOGUES = { +// clang-format off +static const std::vector AntiFridaPrologues = { {R"delim( mov x17, x17; mov x16, x16; @@ -37,58 +39,52 @@ static const std::vector ANTI_FRIDA_PROLOGUES = { mov x17, x17; )delim", 2} }; +// clang-format on -bool AntiHook::runOnFunction(llvm::Function &F) { - if (F.getInstructionCount() == 0) { +bool AntiHook::runOnFunction(Function &F) { + if (F.getInstructionCount() == 0) return false; - } - if (F.hasPrologueData()) { - fatalError("Can't inject a hooking prologue in the function '" + demangle(F.getName().str()) + "' " - "since there is one."); - } + if (F.hasPrologueData()) + fatalError("Cannot inject a hooking prologue in the function " + + demangle(F.getName().str()) + " since there is one."); SDEBUG("[{}] Injecting Anti-Frida prologue in {}", name(), F.getName()); - std::uniform_int_distribution Dist(0, ANTI_FRIDA_PROLOGUES.size() - 1); - size_t idx = Dist(*RNG_); - const PrologueInfoTy& P = ANTI_FRIDA_PROLOGUES[idx]; + std::uniform_int_distribution Dist(0, AntiFridaPrologues.size() - 1); + size_t Idx = Dist(*RNG); + const PrologueInfoTy &P = AntiFridaPrologues[Idx]; - std::unique_ptr insts = jitter_->jitAsm(P.Asm, P.size); + std::unique_ptr Insts = JIT->jitAsm(P.Asm, P.Size); + if (!Insts) + fatalError("Cannot JIT Anti-Frida prologue: \n" + P.Asm); - if (insts == nullptr) { - fatalError("Can't JIT Anti-Frida prologue: \n" + P.Asm); - } - - auto* Int8Ty = Type::getInt8Ty(F.getContext()); - auto* Prologue = ConstantDataVector::getRaw(insts->getBuffer(), insts->getBufferSize(), Int8Ty); + auto *Int8Ty = Type::getInt8Ty(F.getContext()); + auto *Prologue = ConstantDataVector::getRaw(Insts->getBuffer(), + Insts->getBufferSize(), Int8Ty); F.setPrologueData(Prologue); return true; } -PreservedAnalyses AntiHook::run(Module &M, - ModuleAnalysisManager &FAM) { - PyConfig &config = PyConfig::instance(); - SINFO("[{}] Executing on module {}", name(), M.getName()); +PreservedAnalyses AntiHook::run(Module &M, ModuleAnalysisManager &FAM) { bool Changed = false; - jitter_ = std::make_unique(M.getTargetTriple()); - - RNG_ = M.createRNG(name()); + PyConfig &Config = PyConfig::instance(); + SINFO("[{}] Executing on module {}", name(), M.getName()); + JIT = std::make_unique(M.getTargetTriple()); + RNG = M.createRNG(name()); - for (Function& F : M) { - if (!config.getUserConfig()->anti_hooking(F.getParent(), &F)) + for (Function &F : M) { + if (!Config.getUserConfig()->antiHooking(F.getParent(), &F)) continue; Changed |= runOnFunction(F); } - if (Changed) - SINFO("[{}] Changes applied on module {}", name(), M.getName()); + SINFO("[{}] Changes {} applied on module {}", name(), Changed ? "" : "not", + M.getName()); - return Changed ? PreservedAnalyses::none() : - PreservedAnalyses::all(); - -} + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); } +} // end namespace omvll diff --git a/src/passes/anti-hook/CMakeLists.txt b/src/passes/anti-hook/CMakeLists.txt index e9d03a60..c9dcc8de 100644 --- a/src/passes/anti-hook/CMakeLists.txt +++ b/src/passes/anti-hook/CMakeLists.txt @@ -1,4 +1,3 @@ - target_sources(OMVLL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/AntiHook.cpp ) diff --git a/src/passes/arithmetic/Arithmetic.cpp b/src/passes/arithmetic/Arithmetic.cpp index 0524a7ab..6b46b0a6 100644 --- a/src/passes/arithmetic/Arithmetic.cpp +++ b/src/passes/arithmetic/Arithmetic.cpp @@ -1,201 +1,181 @@ -#include "omvll/passes/arithmetic/Arithmetic.hpp" -#include "omvll/log.hpp" -#include "omvll/utils.hpp" -#include "omvll/PyConfig.hpp" -#include "omvll/ObfuscationConfig.hpp" -#include "omvll/passes/Metadata.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// -#include "llvm/Demangle/Demangle.h" #include "llvm/IR/Constants.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/NoFolder.h" #include "llvm/IR/PatternMatch.h" -#include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include + +#include "omvll/ObfuscationConfig.hpp" +#include "omvll/PyConfig.hpp" +#include "omvll/log.hpp" +#include "omvll/passes/Metadata.hpp" +#include "omvll/passes/arithmetic/Arithmetic.hpp" +#include "omvll/utils.hpp" using namespace llvm; using namespace PatternMatch; -namespace omvll { +static constexpr auto MBAFunctionName = "__omvll_mba"; + +// This flag can be used for disabling the inlining of the MBA function wrappers +// (mostly for debugging). +static constexpr bool ShouldInline = true; -/* This flag can be used for disabling the inlining of the MBA function wrappers - * (mostly for debugging) - */ -static constexpr bool INLINE_WRAPPER = true; +namespace omvll { -// LLVM's InstVisitor to pattern match and replace arithmetic operations with MBA -// The current MBA are take from sspam: https://github.com/quarkslab/sspam/blob/master/sspam/simplifier.py#L30-L53 -struct ArithmeticVisitor : public InstVisitor { +// LLVM's InstVisitor to pattern match and replace arithmetic operations with +// MBA The current MBA are take from sspam: +// https://github.com/quarkslab/sspam/blob/master/sspam/simplifier.py#L30-L53 +struct ArithmeticVisitor + : public InstVisitor { using BuilderTy = IRBuilder; BuilderTy &Builder; - ArithmeticVisitor(BuilderTy& B): - Builder(B) {} + ArithmeticVisitor(BuilderTy &B) : Builder(B) {} - Instruction* visitXor(BinaryOperator &I) { + Instruction *visitXor(BinaryOperator &I) { Value *X, *Y; // Match X ^ Y - if (!match(&I, m_Xor(m_Value(X), m_Value(Y)))) { + if (!match(&I, m_Xor(m_Value(X), m_Value(Y)))) return nullptr; - } // (X | Y) - (X & Y) - return BinaryOperator::CreateSub( - Builder.CreateOr(X, Y), - Builder.CreateAnd(X, Y), - "mba_xor" - ); + return BinaryOperator::CreateSub(Builder.CreateOr(X, Y), + Builder.CreateAnd(X, Y), "mba_xor"); } - Instruction* visitAdd(BinaryOperator &I) { + Instruction *visitAdd(BinaryOperator &I) { Value *X, *Y; // Match X + Y - if (!match(&I, m_Add(m_Value(X), m_Value(Y)))) { + if (!match(&I, m_Add(m_Value(X), m_Value(Y)))) return nullptr; - } // (A & B) + (A | B) - return BinaryOperator::CreateAdd( - Builder.CreateAnd(X, Y), - Builder.CreateOr(X, Y), - "mba_add" - ); + return BinaryOperator::CreateAdd(Builder.CreateAnd(X, Y), + Builder.CreateOr(X, Y), "mba_add"); } - Instruction* visitAnd(BinaryOperator &I) { + Instruction *visitAnd(BinaryOperator &I) { Value *X, *Y; // Match X & Y - if (!match(&I, m_And(m_Value(X), m_Value(Y)))) { + if (!match(&I, m_And(m_Value(X), m_Value(Y)))) return nullptr; - } // (X + Y) - (X | Y) - return BinaryOperator::CreateSub( - Builder.CreateAdd(X, Y), - Builder.CreateOr(X, Y), - "mba_and" - ); + return BinaryOperator::CreateSub(Builder.CreateAdd(X, Y), + Builder.CreateOr(X, Y), "mba_and"); } - Instruction* visitOr(BinaryOperator &I) { + Instruction *visitOr(BinaryOperator &I) { Value *X, *Y; // Match X | Y - if (!match(&I, m_Or(m_Value(X), m_Value(Y)))) { + if (!match(&I, m_Or(m_Value(X), m_Value(Y)))) return nullptr; - } // X + Y + 1 + (~X | ~Y) return BinaryOperator::CreateAdd( - Builder.CreateAdd( - Builder.CreateAdd(X, Y), - ConstantInt::get(X->getType(), 1) - ), - Builder.CreateOr( - Builder.CreateNot(X), - Builder.CreateNot(Y) - ), "mba_or" - ); + Builder.CreateAdd(Builder.CreateAdd(X, Y), + ConstantInt::get(X->getType(), 1)), + Builder.CreateOr(Builder.CreateNot(X), Builder.CreateNot(Y)), "mba_or"); } - Instruction* visitSub(BinaryOperator &I) { + Instruction *visitSub(BinaryOperator &I) { Value *X, *Y; // Match X - Y - if (!match(&I, m_Sub(m_Value(X), m_Value(Y)))) { + if (!match(&I, m_Sub(m_Value(X), m_Value(Y)))) return nullptr; - } // (X ^ -Y) + 2*(X & -Y) return BinaryOperator::CreateAdd( Builder.CreateXor(X, Builder.CreateNeg(Y)), Builder.CreateMul(ConstantInt::get(X->getType(), 2), - Builder.CreateAnd(X, Builder.CreateNeg(Y)) - ), "mba_sub" - ); + Builder.CreateAnd(X, Builder.CreateNeg(Y))), + "mba_sub"); } - Instruction* visitInstruction(Instruction&) { return nullptr; } + Instruction *visitInstruction(Instruction &) { return nullptr; } }; -bool Arithmetic::isSupported(const BinaryOperator& Op) { - const auto opcode = Op.getOpcode(); - return opcode == Instruction::Add || - opcode == Instruction::Sub || - opcode == Instruction::And || - opcode == Instruction::Xor || - opcode == Instruction::Or; +bool Arithmetic::isSupported(const BinaryOperator &Op) { + const auto Opcode = Op.getOpcode(); + return Opcode == Instruction::Add || Opcode == Instruction::Sub || + Opcode == Instruction::And || Opcode == Instruction::Xor || + Opcode == Instruction::Or; } -Function* Arithmetic::injectFunWrapper(Module& M, BinaryOperator& Op, Value& Lhs, Value& Rhs) { - auto* FTy = FunctionType::get(Op.getType(), {Lhs.getType(), Rhs.getType()}, /*vargs=*/false); - auto* F = Function::Create(FTy, llvm::GlobalValue::PrivateLinkage, - "__omvll_mba", M); - if constexpr (INLINE_WRAPPER) { +Function *Arithmetic::injectFunWrapper(Module &M, BinaryOperator &Op, + Value &Lhs, Value &Rhs) { + auto *FTy = FunctionType::get(Op.getType(), {Lhs.getType(), Rhs.getType()}, + /*vargs=*/false); + auto *F = Function::Create(FTy, llvm::GlobalValue::PrivateLinkage, + MBAFunctionName, M); + + if constexpr (ShouldInline) F->addFnAttr(Attribute::AlwaysInline); - } else { + else F->addFnAttr(Attribute::NoInline); - } F->addFnAttr(Attribute::OptimizeNone); F->setCallingConv(CallingConv::C); - BasicBlock* EntryBB = BasicBlock::Create(F->getContext(), "entry", F); - Value* p1 = F->getArg(0); - Value* p2 = F->getArg(1); + BasicBlock *EntryBB = BasicBlock::Create(F->getContext(), "entry", F); + Value *P1 = F->getArg(0); + Value *P2 = F->getArg(1); IRBuilder<> EBuild(EntryBB); EBuild.SetInsertPoint(EntryBB); - EBuild.CreateRet(EBuild.CreateBinOp(Op.getOpcode(), p1, p2)); + EBuild.CreateRet(EBuild.CreateBinOp(Op.getOpcode(), P1, P2)); return F; } bool Arithmetic::runOnBasicBlock(BasicBlock &BB) { - size_t counter = 0; + size_t Counter = 0; SmallVector ToErase; - DenseMap ToObfuscate; - for (Instruction& I : BB) { - // First, check if there is O-MVLL metadata associated with the current instruction. - // If it is the case, access the number of iterations - size_t nbRounds = 0; - if (auto MO = getObf(I, MObfTy::OPAQUE_OP)) { - if (auto* Val = MO->get()) { - nbRounds = *Val; + DenseMap ToObfuscate; + for (Instruction &I : BB) { + // First, check if there is O-MVLL metadata associated with the current + // instruction. If it is the case, access the number of iterations. + size_t Rounds = 0; + if (auto MO = getObf(I, MetaObfTy::OpaqueOp)) { + if (auto *Val = MO->get()) { + Rounds = *Val; } - } - else if (auto it = opts_.find(BB.getParent()); it != opts_.end()) { - nbRounds = it->second.iterations; + } else if (auto It = Opts.find(BB.getParent()); It != Opts.end()) { + Rounds = It->second.Iterations; } - // No need to continue if there is a 0-round - if (nbRounds == 0) { + // No need to continue if there is a 0-round. + if (Rounds == 0) continue; - } - // Now, check that we are on a BinOp instruction - auto* BinOp = dyn_cast(&I); - if (BinOp == nullptr) { + // Now check that we are in a BinOp instruction. + auto *BinOp = dyn_cast(&I); + if (!BinOp) continue; - } - if (!isSupported(*BinOp)) { + if (!isSupported(*BinOp)) continue; - } - Value* lhs = BinOp->getOperand(0); - Value* rhs = BinOp->getOperand(1); + Value *Lhs = BinOp->getOperand(0); + Value *Rhs = BinOp->getOperand(1); - Function* FWrap = injectFunWrapper(*BB.getModule(), *BinOp, *lhs, *rhs); - ToObfuscate[FWrap] = nbRounds; + Function *FWrap = injectFunWrapper(*BB.getModule(), *BinOp, *Lhs, *Rhs); + ToObfuscate[FWrap] = Rounds; IRBuilder<> IRB(&I); - Instruction* Result = IRB.CreateCall(FWrap->getFunctionType(), FWrap, {lhs, rhs}); + Instruction *Result = + IRB.CreateCall(FWrap->getFunctionType(), FWrap, {Lhs, Rhs}); - ++counter; + ++Counter; I.replaceAllUsesWith(Result); ToErase.emplace_back(&I); @@ -207,16 +187,15 @@ bool Arithmetic::runOnBasicBlock(BasicBlock &BB) { ArithmeticVisitor::BuilderTy Builder(&BB); ArithmeticVisitor Visitor(Builder); - for (auto& [F, round] : ToObfuscate) { - for (BasicBlock& BB : *F) { - for (size_t i = 0; i < round; ++i) { - for (Instruction& I : BB) { + for (const auto &[F, Round] : ToObfuscate) { + for (BasicBlock &BB : *F) { + for (size_t Idx = 0; Idx < Round; ++Idx) { + for (Instruction &I : BB) { Builder.SetInsertPoint(&I); Instruction *Result = Visitor.visit(I); - if (Result == nullptr || Result == &I) { + if (!Result || Result == &I) continue; - } SINFO("[{}][{}] Replacing {} with {}", name(), F->getName(), I.getName(), Result->getName()); @@ -232,50 +211,47 @@ bool Arithmetic::runOnBasicBlock(BasicBlock &BB) { InstParent->getInstList().insert(InsertPos, Result); #endif I.replaceAllUsesWith(Result); - //ToErase.emplace_back(&I); } } } } - //for_each(ToErase, [](Instruction *I) { I->eraseFromParent(); }); - bool Changed = counter > 0; + bool Changed = Counter > 0; return Changed; } PreservedAnalyses Arithmetic::run(Module &M, ModuleAnalysisManager &FAM) { - PyConfig &config = PyConfig::instance(); - SINFO("[{}] Executing on module {}", name(), M.getName()); bool Changed = false; - RNG_ = M.createRNG(name()); + PyConfig &Config = PyConfig::instance(); + SINFO("[{}] Executing on module {}", name(), M.getName()); + RNG = M.createRNG(name()); IRChangesMonitor ModuleChanges(M, name()); - auto& Fs = M.getFunctionList(); - - // "Backup" all the functions since the pass adds new functions and thus, - // break the iterator - std::vector LFs; - LFs.reserve(Fs.size()); - std::transform(Fs.begin(), Fs.end(), std::back_inserter(LFs), - [] (Function& F) { return &F; }); - - for (Function* F : LFs) { - ArithmeticOpt opt = config.getUserConfig()->obfuscate_arithmetic(&M, F); - if (!opt) + // Backup all the functions since the pass adds new functions and thus, break + // the iterator. + std::vector ToVisit; + auto &Functions = M.getFunctionList(); + ToVisit.reserve(Functions.size()); + std::transform(Functions.begin(), Functions.end(), + std::back_inserter(ToVisit), [](auto &F) { return &F; }); + + for (Function *F : ToVisit) { + ArithmeticOpt Opt = Config.getUserConfig()->obfuscateArithmetics(&M, F); + if (!Opt) continue; SINFO("[{}] Visiting function {}", name(), F->getName()); - opts_.insert({F, std::move(opt)}); + Opts.insert({F, std::move(Opt)}); for (BasicBlock &BB : *F) Changed |= runOnBasicBlock(BB); } - SINFO("[{}] Changes{}applied on module {}", name(), Changed ? " " : " not ", + SINFO("[{}] Changes {} applied on module {}", name(), Changed ? "" : "not", M.getName()); ModuleChanges.notify(Changed); return ModuleChanges.report(); } -} +} // end namespace omvll diff --git a/src/passes/arithmetic/CMakeLists.txt b/src/passes/arithmetic/CMakeLists.txt index f5f7593a..d015098d 100644 --- a/src/passes/arithmetic/CMakeLists.txt +++ b/src/passes/arithmetic/CMakeLists.txt @@ -1,4 +1,3 @@ - target_sources(OMVLL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Arithmetic.cpp ) diff --git a/src/passes/break-cfg/BreakControlFlow.cpp b/src/passes/break-cfg/BreakControlFlow.cpp index a4caab3b..274cad18 100644 --- a/src/passes/break-cfg/BreakControlFlow.cpp +++ b/src/passes/break-cfg/BreakControlFlow.cpp @@ -1,63 +1,57 @@ -#include +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include "llvm/Demangle/Demangle.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/NoFolder.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" -#include "omvll/Jitter.hpp" #include "omvll/ObfuscationConfig.hpp" #include "omvll/PyConfig.hpp" +#include "omvll/jitter.hpp" #include "omvll/log.hpp" #include "omvll/passes/Metadata.hpp" #include "omvll/passes/break-cfg/BreakControlFlow.hpp" #include "omvll/utils.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - using namespace llvm; - namespace omvll { -static constexpr size_t INST_SIZE = 4; -static constexpr size_t FUNCTION_ALIGNMENT = 0x20; // Maximum value according to AArch64Subtarget - -static constexpr std::array, 4> NOP_INSTS = { - { - {0x1f, 0x20, 0x03, 0xd5}, // NOP - {0xe0, 0x03, 0x00, 0xaa}, // mov x0, x0 - {0x42, 0x00, 0xc2, 0x93}, // ror x2, x2, #0 - {0xf4, 0x03, 0x14, 0xaa}, // mov x20, x20 - } -}; +static constexpr size_t InstSize = 4; +static constexpr size_t FunctionAlignment = + 0x20; // Maximum value according to AArch64Subtarget. +static constexpr std::array, 4> NopInsts = {{ + {0x1F, 0x20, 0x03, 0xD5}, // nop + {0xE0, 0x03, 0x00, 0xAA}, // mov x0, x0 + {0x42, 0x00, 0xC2, 0x93}, // ror x2, x2, #0 + {0xF4, 0x03, 0x14, 0xAA}, // mov x20, x20 +}}; bool BreakControlFlow::runOnFunction(Function &F) { - - if (F.getInstructionCount() == 0) { + if (F.getInstructionCount() == 0) return false; - } SINFO("[{}] Visiting function {}", name(), F.getName()); ValueToValueMapTy VMap; - ClonedCodeInfo info; - Function* FCopied = CloneFunction(&F, VMap, &info); + ClonedCodeInfo CCI; + Function *ClonedF = CloneFunction(&F, VMap, &CCI); F.deleteBody(); - Function& Trampoline = F; + Function &Trampoline = F; const auto AsmBreakingStub = R"delim( - // !! This block must be aligned on 32 bytes !! + // This block must be aligned on 32 bytes adr x1, #0x10; ldr x0, [x1, #61]; ldr x1, #16; @@ -68,139 +62,134 @@ bool BreakControlFlow::runOnFunction(Function &F) { .byte 0xF8, 0xFF, 0xE2, 0xC2; )delim"; - std::unique_ptr insts = Jitter_->jitAsm(AsmBreakingStub, 8); - if (insts->getBufferSize() % FUNCTION_ALIGNMENT) { - fatalError(fmt::format("Bad alignment for the ASM block ({})", insts->getBufferSize())); - } + std::unique_ptr Insts = JIT->jitAsm(AsmBreakingStub, 8); + if (Insts->getBufferSize() % FunctionAlignment) + fatalError(fmt::format("Bad alignment for the assembly block ({})", + Insts->getBufferSize())); - Constant* Prologue = nullptr; - auto* Int8Ty = Type::getInt8Ty(F.getContext()); + Constant *Prologue = nullptr; + auto *Int8Ty = Type::getInt8Ty(F.getContext()); size_t PrologueSize = 0; if (F.hasPrologueData()) { - if (auto* Data = dyn_cast(F.getPrologueData())) { - StringRef raw = Data->getRawDataValues(); - StringRef breakingBuffer = insts->getBuffer(); + if (auto *Data = dyn_cast(F.getPrologueData())) { + StringRef Raw = Data->getRawDataValues(); + StringRef BreakingBuffer = Insts->getBuffer(); SmallVector NewPrologue; - NewPrologue.reserve(raw.size() + insts->getBufferSize()); - NewPrologue.append(breakingBuffer.begin(), breakingBuffer.end()); - NewPrologue.append(raw.begin(), raw.end()); - if (NewPrologue.size() % FUNCTION_ALIGNMENT) { - const size_t align = alignTo(NewPrologue.size(), FUNCTION_ALIGNMENT); - const size_t delta = align - NewPrologue.size(); - if (delta % 4) fatalError("Bad alignment"); + NewPrologue.reserve(Raw.size() + Insts->getBufferSize()); + NewPrologue.append(BreakingBuffer.begin(), BreakingBuffer.end()); + NewPrologue.append(Raw.begin(), Raw.end()); - const size_t nb = delta / INST_SIZE; - std::uniform_int_distribution Dist(0, NOP_INSTS.size() - 1); + if (NewPrologue.size() % FunctionAlignment) { + const size_t Align = alignTo(NewPrologue.size(), FunctionAlignment); + const size_t Delta = Align - NewPrologue.size(); + if (Delta % 4) + fatalError("Bad alignment"); - for (size_t i = 0; i < nb; ++i) { - auto& NOP = NOP_INSTS[Dist(*RNG_)]; - NewPrologue.append(NOP.begin(), NOP.end()); + const size_t Num = Delta / InstSize; + std::uniform_int_distribution Dist(0, NopInsts.size() - 1); + + for (size_t Idx = 0; Idx < Num; ++Idx) { + const auto &NopInst = NopInsts[Dist(*RNG)]; + NewPrologue.append(NopInst.begin(), NopInst.end()); } } + PrologueSize = NewPrologue.size(); Prologue = ConstantDataVector::get(F.getContext(), NewPrologue); F.setPrologueData(nullptr); } } else { - PrologueSize = insts->getBufferSize(); - Prologue = ConstantDataVector::getRaw(insts->getBuffer(), insts->getBufferSize(), Int8Ty); + PrologueSize = Insts->getBufferSize(); + Prologue = ConstantDataVector::getRaw(Insts->getBuffer(), + Insts->getBufferSize(), Int8Ty); } - if (Prologue == nullptr) { - fatalError("Can't inject BreakControlFlow prologue in the function " - "'" + demangle(F.getName().str()) + "'"); - } + if (!Prologue) + fatalError("Missing prologue in function " + demangle(F.getName().str())); SDEBUG("[{}][{}] Injecting breaking stub", name(), F.getName()); - FCopied->setPrologueData(Prologue); - FCopied->setLinkage(GlobalValue::InternalLinkage); + ClonedF->setPrologueData(Prologue); + ClonedF->setLinkage(GlobalValue::InternalLinkage); // "Demote" StructRet arguments as it can introduces - // a conflict in CodeGen (observed in CPython - _PyEval_InitGIL) - for (Argument& arg : FCopied->args()) { - if (arg.hasStructRetAttr()) { - unsigned ArgNo = arg.getArgNo(); - FCopied->removeParamAttr(ArgNo, Attribute::StructRet); - FCopied->addParamAttr(ArgNo, Attribute::NoAlias); + // a conflict in CodeGen (observed in CPython - _PyEval_InitGIL). + for (const auto &Arg : ClonedF->args()) { + if (Arg.hasStructRetAttr()) { + unsigned ArgNo = Arg.getArgNo(); + ClonedF->removeParamAttr(ArgNo, Attribute::StructRet); + ClonedF->addParamAttr(ArgNo, Attribute::NoAlias); } } - BasicBlock* Entry = BasicBlock::Create(Trampoline.getContext(), "Entry", &Trampoline); + BasicBlock *Entry = + BasicBlock::Create(Trampoline.getContext(), "Entry", &Trampoline); IRBuilder IRB(Entry); - SmallVector args; - for (Argument& A : F.args()) { - args.push_back(&A); - } - AllocaInst* VarDst = IRB.CreateAlloca(IRB.getInt64Ty()); - AllocaInst* VarFAddr = IRB.CreateAlloca(IRB.getInt64Ty()); + SmallVector Args; + for (auto &Arg : F.args()) + Args.push_back(&Arg); + + AllocaInst *VarDst = IRB.CreateAlloca(IRB.getInt64Ty()); + AllocaInst *VarFAddr = IRB.CreateAlloca(IRB.getInt64Ty()); - IRB.CreateStore(IRB.CreatePtrToInt(FCopied, IRB.getInt64Ty()), VarFAddr, + IRB.CreateStore(IRB.CreatePtrToInt(ClonedF, IRB.getInt64Ty()), VarFAddr, /* volatile */ true); - LoadInst* LoadFAddr = IRB.CreateLoad(IRB.getInt64Ty(), VarFAddr); + LoadInst *LoadFAddr = IRB.CreateLoad(IRB.getInt64Ty(), VarFAddr); - Value* OpaqueFAddr = IRB.CreateAdd(LoadFAddr, - ConstantInt::get(IRB.getInt64Ty(), 0)); - if (auto* Op = dyn_cast(OpaqueFAddr)) { - addMetadata(*Op, {MetaObf(OPAQUE_OP, 2llu), - MetaObf(OPAQUE_CST)}); - } + Value *OpaqueFAddr = + IRB.CreateAdd(LoadFAddr, ConstantInt::get(IRB.getInt64Ty(), 0)); + if (auto *Op = dyn_cast(OpaqueFAddr)) + addMetadata(*Op, {MetaObf(OpaqueOp, 2LLU), MetaObf(OpaqueCst)}); - Value* DstAddr = IRB.CreateAdd(OpaqueFAddr, - ConstantInt::get(IRB.getInt64Ty(), PrologueSize)); + Value *DstAddr = IRB.CreateAdd( + OpaqueFAddr, ConstantInt::get(IRB.getInt64Ty(), PrologueSize)); - if (auto* Op = dyn_cast(DstAddr)) { - addMetadata(*Op, {MetaObf(OPAQUE_OP, 2llu), - MetaObf(OPAQUE_CST)}); - } + if (auto *Op = dyn_cast(DstAddr)) + addMetadata(*Op, {MetaObf(OpaqueOp, 2LLU), MetaObf(OpaqueCst)}); + + StoreInst *StoreFunc = IRB.CreateStore(DstAddr, VarDst, /* volatile */ true); + addMetadata(*StoreFunc, {MetaObf(ProtectFieldAccess), MetaObf(OpaqueCst)}); - StoreInst* StoreFunc = IRB.CreateStore(DstAddr, VarDst, /* volatile */true); - addMetadata(*StoreFunc, {MetaObf(PROTECT_FIELD_ACCESS), - MetaObf(OPAQUE_CST),}); - LoadInst* FAddr = IRB.CreateLoad(IRB.getInt64Ty(), VarDst, /* volatile */true); - Value* FPtr = IRB.CreatePointerCast(FAddr, FCopied->getFunctionType()); - if (FCopied->getFunctionType()->getReturnType()->isVoidTy()) { - IRB.CreateCall(FCopied->getFunctionType(), FPtr, args); + LoadInst *FAddr = + IRB.CreateLoad(IRB.getInt64Ty(), VarDst, /* volatile */ true); + Value *FPtr = IRB.CreatePointerCast(FAddr, ClonedF->getFunctionType()); + + if (ClonedF->getFunctionType()->getReturnType()->isVoidTy()) { + IRB.CreateCall(ClonedF->getFunctionType(), FPtr, Args); IRB.CreateRetVoid(); } else { - IRB.CreateRet(IRB.CreateCall(FCopied->getFunctionType(), FPtr, args)); + IRB.CreateRet(IRB.CreateCall(ClonedF->getFunctionType(), FPtr, Args)); } + Trampoline.addFnAttr(Attribute::OptimizeForSize); Trampoline.addFnAttr(Attribute::NoInline); return true; } - - -PreservedAnalyses BreakControlFlow::run(Module &M, - ModuleAnalysisManager &FAM) { - PyConfig &config = PyConfig::instance(); - SINFO("[{}] Executing on module {}", name(), M.getName()); - RNG_ = M.createRNG(name()); - Jitter_ = std::make_unique(M.getTargetTriple()); +PreservedAnalyses BreakControlFlow::run(Module &M, ModuleAnalysisManager &FAM) { bool Changed = false; - std::vector Fs; - for (Function& F : M) { + PyConfig &Config = PyConfig::instance(); + SINFO("[{}] Executing on module {}", name(), M.getName()); + RNG = M.createRNG(name()); + JIT = std::make_unique(M.getTargetTriple()); + + for (Function &F : M) { if (F.isDeclaration() || F.isIntrinsic()) continue; - Fs.push_back(&F); - } - for (Function *F : Fs) - if (config.getUserConfig()->break_control_flow(&M, F)) - Changed |= runOnFunction(*F); + if (Config.getUserConfig()->breakControlFlow(&M, &F)) + Changed |= runOnFunction(F); + } - SINFO("[{}] Changes{}applied on module {}", name(), Changed ? " " : " not ", + SINFO("[{}] Changes {} applied on module {}", name(), Changed ? "" : "not", M.getName()); - return Changed ? PreservedAnalyses::none() : - PreservedAnalyses::all(); - -} + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); } +} // end namespace omvll diff --git a/src/passes/break-cfg/CMakeLists.txt b/src/passes/break-cfg/CMakeLists.txt index a2e2dfbc..255d193f 100644 --- a/src/passes/break-cfg/CMakeLists.txt +++ b/src/passes/break-cfg/CMakeLists.txt @@ -1,4 +1,3 @@ - target_sources(OMVLL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/BreakControlFlow.cpp ) diff --git a/src/passes/flattening/CMakeLists.txt b/src/passes/cfg-flattening/CMakeLists.txt similarity index 100% rename from src/passes/flattening/CMakeLists.txt rename to src/passes/cfg-flattening/CMakeLists.txt diff --git a/src/passes/cfg-flattening/ControlFlowFlattening.cpp b/src/passes/cfg-flattening/ControlFlowFlattening.cpp new file mode 100644 index 00000000..15381f82 --- /dev/null +++ b/src/passes/cfg-flattening/ControlFlowFlattening.cpp @@ -0,0 +1,474 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Local.h" + +#include "omvll/ObfuscationConfig.hpp" +#include "omvll/PyConfig.hpp" +#include "omvll/log.hpp" +#include "omvll/passes/cfg-flattening/ControlFlowFlattening.hpp" +#include "omvll/utils.hpp" + +using namespace llvm; + +namespace omvll { + +constexpr uint32_t Encode(uint32_t Id, uint32_t X, uint32_t Y) { + return (Id ^ X) + Y; +} + +template +void EmitTransition(IRBTy &IRB, AllocaInst *SV, BasicBlock *Dispatch, + uint32_t encId, uint32_t X, uint32_t Y) { + IRB.CreateStore(ConstantInt::get(IRB.getInt32Ty(), encId), SV); + LoadInst *Val = IRB.CreateLoad(IRB.getInt32Ty(), SV, true); + + IRB.CreateStore( + IRB.CreateAdd(IRB.CreateXor(Val, ConstantInt::get(IRB.getInt32Ty(), X)), + ConstantInt::get(IRB.getInt32Ty(), Y)), + SV, true); + + IRB.CreateBr(Dispatch); +} + +template +void EmitTransition(IRBTy &IRB, AllocaInst *SV, BasicBlock *Dispatch, + AllocaInst *TT, AllocaInst *TF, Value *Cond, + uint32_t TrueId, uint32_t FalseId, uint32_t X, uint32_t Y) { + IRB.CreateStore(ConstantInt::get(IRB.getInt32Ty(), TrueId), TT); + IRB.CreateStore(ConstantInt::get(IRB.getInt32Ty(), FalseId), TF); + + LoadInst *True = IRB.CreateLoad(IRB.getInt32Ty(), TT, true); + LoadInst *False = IRB.CreateLoad(IRB.getInt32Ty(), TF, true); + + auto *EncTrue = + IRB.CreateAdd(IRB.CreateXor(True, ConstantInt::get(IRB.getInt32Ty(), X)), + ConstantInt::get(IRB.getInt32Ty(), Y)); + + auto *EncFalse = + IRB.CreateAdd(IRB.CreateXor(False, ConstantInt::get(IRB.getInt32Ty(), X)), + ConstantInt::get(IRB.getInt32Ty(), Y)); + + IRB.CreateStore(IRB.CreateSelect(Cond, EncTrue, EncFalse), SV, true); + IRB.CreateBr(Dispatch); +} + +template void EmitDefaultCaseAssembly(IRBTy &IRB, Triple TT) { + // clang-format off + auto *FType = FunctionType::get(IRB.getVoidTy(), false); + if (TT.isAArch64()) { + IRB.CreateCall(FType, InlineAsm::get( + FType, + R"delim( + ldr x1, #-8; + blr x1; + mov x0, x1; + .byte 0xF1, 0xFF; + .byte 0xF2, 0xA2; + )delim", + "", + /* hasSideEffects */ true, + /* isStackAligned */ true + )); + } else if (TT.isX86()) { + // FIXME: This assembly may not confuse a decompiler. + IRB.CreateCall(FType, InlineAsm::get( + FType, + R"delim( + nop; + .byte 0xF1, 0xFF; + .byte 0xF2, 0xA2; + )delim", + "", + /* hasSideEffects */ true, + /* isStackAligned */ true + )); + } else { + fatalError("Unsupported target for Control-Flow Flattening obfuscation: " + + TT.str()); + } + // clang-format on +} + +bool ControlFlowFlattening::runOnFunction(Function &F, + RandomNumberGenerator &RNG) { + if (F.getInstructionCount() == 0) + return false; + + bool Changed = false; + std::string DemangledName = demangle(F.getName().str()); + std::uniform_int_distribution Dist(10); + std::uniform_int_distribution Dist8(10, 254); + const uint8_t X = Dist8(RNG); + const uint8_t Y = Dist8(RNG); + + SINFO("[{}] Visiting function {}", ControlFlowFlattening::name(), + DemangledName); + + SmallVector FlattedBBs; + demotePHINode(F); + + BasicBlock *EntryBlock = &F.getEntryBlock(); + DenseSet NormalDest2Split; + + auto IsFromInvoke = [](const auto &BB) { + return any_of(predecessors(&BB), [](const auto *Pred) { + return isa(Pred->getTerminator()); + }); + }; + + for (BasicBlock &BB : F) { + if (BB.isLandingPad()) + continue; + + if (IsFromInvoke(BB)) + NormalDest2Split.insert(&BB); + } + + /* + * +------------------+ +------------------+ + * | | | | + * +--------+---------+ +---------+--------+ + * | Normal | Unwind | | | | + * +---+----+---------+ +----+----+--------+ + * | | + * +---+----+ +----+----+ + * | | | +--+ Intermediate Block + * | | +---------+ | + * | | | + * +--------+ +---------+<-+ Original Block + * Normal Dst | | + * | | + * | | + * +---------+ + */ + + for (BasicBlock *BB : NormalDest2Split) { + auto *Trampoline = BasicBlock::Create(BB->getContext(), ".normal_split", + BB->getParent(), BB); + for (BasicBlock *Pred : predecessors(BB)) { + // Handle Invoke + if (auto *Invoke = dyn_cast(Pred->getTerminator())) { + Invoke->setNormalDest(Trampoline); + continue; + } + + // Handle Branch + if (auto *Branch = dyn_cast(Pred->getTerminator())) { + for (size_t Idx = 0; Idx < Branch->getNumSuccessors(); ++Idx) { + if (Branch->getSuccessor(Idx) == BB) + Branch->setSuccessor(Idx, Trampoline); + } + continue; + } + + // Handle Switch + if (auto *Switch = dyn_cast(Pred->getTerminator())) { + SwitchInst::CaseIt Begin = Switch->case_begin(); + SwitchInst::CaseIt End = Switch->case_end(); + for (auto It = Begin; It != End; ++It) { + if (It->getCaseSuccessor() == BB) + It->setSuccessor(Trampoline); + } + continue; + } + } + + // Branch the Trampoline to the original BasicBlock. + BranchInst::Create(BB, Trampoline); + } + + for (BasicBlock &BB : F) { + if (EntryBlock == &BB) + continue; + + FlattedBBs.push_back(&BB); + } + + const size_t BlockSize = + count_if(FlattedBBs, [](const auto *BB) { return !BB->isLandingPad(); }); + if (BlockSize <= 1) { + SWARN("[{}] Block too small (#{}) to be flattened", + ControlFlowFlattening::name(), FlattedBBs.size()); + return false; + } + + if (auto *Branch = dyn_cast(EntryBlock->getTerminator())) { + if (Branch->isConditional()) { + Value *Cond = Branch->getCondition(); + if (auto *Inst = dyn_cast(Cond)) { + BasicBlock *EntrySplit = + EntryBlock->splitBasicBlockBefore(Inst, "EntrySplit"); + FlattedBBs.insert(FlattedBBs.begin(), EntryBlock); + +#ifdef OMVLL_DEBUG + for (Instruction &I : *EntrySplit) { + SDEBUG("[{}][EntrySplit] {}", ControlFlowFlattening::name(), + ToString(I)); + } + + for (Instruction &I : *EntryBlock) { + SDEBUG("[{}][EntryBlock] {}", ControlFlowFlattening::name(), + ToString(I)); + } +#endif + + EntryBlock = EntrySplit; + } else { + SWARN("[{}] Found condition that is not an instruction", + ControlFlowFlattening::name()); + } + } + } else if (auto *Switch = dyn_cast(EntryBlock->getTerminator())) { + BasicBlock *EntrySplit = + EntryBlock->splitBasicBlockBefore(Switch, "EntrySplit"); + FlattedBBs.insert(FlattedBBs.begin(), EntryBlock); + EntryBlock = EntrySplit; + } else if (auto *Invoke = dyn_cast(EntryBlock->getTerminator())) { + BasicBlock *EntrySplit = + EntryBlock->splitBasicBlockBefore(Invoke, "EntrySplit"); + FlattedBBs.insert(FlattedBBs.begin(), EntryBlock); + EntryBlock = EntrySplit; + } + + SDEBUG("[{}] Erasing {}", ControlFlowFlattening::name(), + ToString(*EntryBlock->getTerminator())); + EntryBlock->getTerminator()->eraseFromParent(); + + // Create a state encoding for the BB to flatten. + DenseMap SwitchEnc; + SmallSet SwitchRnd; + for (BasicBlock *ToFlat : FlattedBBs) { + if (ToFlat->isLandingPad()) + // Landing pads are not embedded in the switch. + continue; + + uint32_t Rnd = 0; + do { + Rnd = Dist(RNG); + uint32_t Enc = Encode(Rnd, X, Y); + if (!SwitchRnd.contains(Rnd) && !SwitchRnd.contains(Enc)) { + SwitchRnd.insert(Rnd); + SwitchRnd.insert(Enc); + break; + } + } while (true); + SwitchEnc[ToFlat] = Rnd; + } + + IRBuilder<> EntryIR(EntryBlock); + AllocaInst *SwitchVar = + EntryIR.CreateAlloca(EntryIR.getInt32Ty(), 0, "SwitchVar"); + AllocaInst *TmpTrue = + EntryIR.CreateAlloca(EntryIR.getInt32Ty(), 0, "TmpTrue"); + AllocaInst *TmpFalse = + EntryIR.CreateAlloca(EntryIR.getInt32Ty(), 0, "TmpFalse"); + + EntryIR.CreateStore(EntryIR.getInt32(Encode(SwitchEnc[FlattedBBs[0]], X, Y)), + SwitchVar); + + auto &Ctx = F.getContext(); + auto *FlatLoopEntry = + BasicBlock::Create(Ctx, "FlatLoopEntry", &F, EntryBlock); + auto *FlatLoopEnd = BasicBlock::Create(Ctx, "FlatLoopEnd", &F, EntryBlock); + auto *DefaultCase = BasicBlock::Create(Ctx, "DefaultCase", &F, FlatLoopEnd); + + IRBuilder<> FlatLoopEntryIR(FlatLoopEntry), FlatLoopEndIR(FlatLoopEnd), + DefaultCaseIR(DefaultCase); + + LoadInst *LoadSwitchVar = FlatLoopEntryIR.CreateLoad( + FlatLoopEntryIR.getInt32Ty(), SwitchVar, "SwitchVar"); + EntryBlock->moveBefore(FlatLoopEntry); + EntryIR.CreateBr(FlatLoopEntry); + FlatLoopEndIR.CreateBr(FlatLoopEntry); + + EmitDefaultCaseAssembly(DefaultCaseIR, + Triple(F.getParent()->getTargetTriple())); + DefaultCaseIR.CreateBr(FlatLoopEnd); + + SwitchInst *Switch = FlatLoopEntryIR.CreateSwitch(LoadSwitchVar, DefaultCase); + + for (BasicBlock *ToFlat : FlattedBBs) { + if (ToFlat->isLandingPad()) + // Landing pads should not be present in the switch case since they are + // not directly called by flattened blocks. + continue; + + auto ItEncId = SwitchEnc.find(ToFlat); + if (ItEncId == SwitchEnc.end()) + fatalError( + fmt::format("Cannot find the encoded index for the basic block: {}", + ToString(*ToFlat))); + + uint32_t SwitchId = Encode(ItEncId->second, X, Y); + ToFlat->moveBefore(FlatLoopEnd); + auto *Id = dyn_cast( + ConstantInt::get(Switch->getCondition()->getType(), SwitchId)); + Switch->addCase(Id, ToFlat); + } + + // Update the basic block with the switch var. + for (BasicBlock *ToFlat : FlattedBBs) { + Instruction *Term = ToFlat->getTerminator(); + SDEBUG("[{}] Flattening {} ({})", ControlFlowFlattening::name(), + ToString(*ToFlat), ToString(*Term)); + + if (isa(Term) || isa(Term)) { + /* Typically a ret instruction + * { + * if (...) { + * return X; + * } + * } + * + */ + continue; + } + + if (isa(Term)) + // Nothing to do as it will 'resume' from information already known. + continue; + + if (isa(Term)) + // Already processed with the early 'split'. + continue; + + if (isa(Term)) { + auto *SwitchTerm = dyn_cast(Term); + + std::vector Cases; + std::transform( + SwitchTerm->case_begin(), SwitchTerm->case_end(), + std::back_inserter(Cases), + [](const SwitchInst::CaseHandle &Handle) { return &Handle; }); + + for (const SwitchInst::CaseHandle &Handle : SwitchTerm->cases()) { + BasicBlock *Target = Handle.getCaseSuccessor(); + auto ItEncId = SwitchEnc.find(Target); + if (ItEncId == SwitchEnc.end()) + fatalError("Unable to find the encoded id for the basic block: " + + ToString(*Target)); + + ConstantInt *DestId = Switch->findCaseDest(Target); + if (!DestId) + fatalError(fmt::format("Unable to find {} in the switch case", + ToString(*Target))); + + const uint32_t EncId = ItEncId->second; + auto *DispatchBlock = BasicBlock::Create(Ctx, "", &F, FlatLoopEnd); + IRBuilder IRB(DispatchBlock); + EmitTransition(IRB, SwitchVar, FlatLoopEnd, EncId, X, Y); + SwitchTerm->setSuccessor(Handle.getSuccessorIndex(), DispatchBlock); + } + + continue; + } + + auto *Branch = dyn_cast(Term); + if (!Branch) + fatalError(fmt::format("[{}] Weird '{}' is not a branch", + ControlFlowFlattening::name().str(), + ToString(*Term))); + + if (Branch->isUnconditional()) { + BasicBlock *Target = Branch->getSuccessor(0); + auto ItEncId = SwitchEnc.find(Target); + if (ItEncId == SwitchEnc.end()) + fatalError(fmt::format( + "Unable to find the encoded id for the basic block: '{}'", + ToString(*Target))); + + ConstantInt *DestId = Switch->findCaseDest(Target); + if (!DestId) + fatalError(fmt::format("Unable to find {} in the switch case", + ToString(*Target))); + + const uint32_t EncId = ItEncId->second; + IRBuilder IRB(Branch); + EmitTransition(IRB, SwitchVar, FlatLoopEnd, EncId, X, Y); + Branch->eraseFromParent(); + continue; + } + + if (Branch->isConditional()) { + BasicBlock *TrueCase = Branch->getSuccessor(0); + BasicBlock *FalseCase = Branch->getSuccessor(1); + + auto ItTrue = SwitchEnc.find(TrueCase); + auto ItFalse = SwitchEnc.find(FalseCase); + + if (ItTrue == SwitchEnc.end()) + fatalError(fmt::format( + "Unable to find the encoded id for the (true) basic block: '{}'", + ToString(*TrueCase))); + + if (ItFalse == SwitchEnc.end()) + fatalError(fmt::format( + "Unable to find the encoded id for the (false) basic block: '{}'", + ToString(*FalseCase))); + + ConstantInt *TrueId = Switch->findCaseDest(TrueCase); + ConstantInt *FalseId = Switch->findCaseDest(FalseCase); + + if (!TrueId) + fatalError( + fmt::format("Unable to find {} (true case) in the switch case", + ToString(*TrueCase))); + + if (!FalseId) + fatalError( + fmt::format("Unable to find {} (false case) in the switch case", + ToString(*FalseCase))); + + const uint32_t TrueEncId = ItTrue->second; + const uint32_t FalseEncId = ItFalse->second; + + IRBuilder IRB(Branch); + EmitTransition(IRB, SwitchVar, FlatLoopEnd, TmpTrue, TmpFalse, + Branch->getCondition(), TrueEncId, FalseEncId, X, Y); + Branch->eraseFromParent(); + continue; + } + } + + Changed = true; + return Changed; +} + +PreservedAnalyses ControlFlowFlattening::run(Module &M, + ModuleAnalysisManager &MAM) { + bool Changed = false; + PyConfig &Config = PyConfig::instance(); + SINFO("[{}] Executing on module {}", name(), M.getName()); + std::unique_ptr RNG = M.createRNG(name()); + + for (Function &F : M) { + if (!Config.getUserConfig()->controlFlowGraphFlattening(&M, &F)) + continue; + + bool MadeChange = runOnFunction(F, *RNG); + if (MadeChange) + reg2mem(F); + + Changed |= MadeChange; + } + + SINFO("[{}] Changes {} applied on module {}", name(), Changed ? "" : "not", + M.getName()); + + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); +} + +} // end namespace omvll diff --git a/src/passes/cleaning/CMakeLists.txt b/src/passes/cleaning/CMakeLists.txt index c91d6630..dee4ce97 100644 --- a/src/passes/cleaning/CMakeLists.txt +++ b/src/passes/cleaning/CMakeLists.txt @@ -1,4 +1,3 @@ - target_sources(OMVLL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Cleaning.cpp ) diff --git a/src/passes/cleaning/Cleaning.cpp b/src/passes/cleaning/Cleaning.cpp index 1f063e23..01ff3655 100644 --- a/src/passes/cleaning/Cleaning.cpp +++ b/src/passes/cleaning/Cleaning.cpp @@ -1,41 +1,45 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include "llvm/Demangle/Demangle.h" + #include "omvll/log.hpp" #include "omvll/utils.hpp" #include "omvll/passes/cleaning/Cleaning.hpp" #include "omvll/omvll_config.hpp" -#include - using namespace llvm; namespace omvll { PreservedAnalyses Cleaning::run(Module &M, ModuleAnalysisManager &FAM) { - if (!config.cleaning) + if (!Config.Cleaning) return PreservedAnalyses::all(); - SINFO("[{}] Executing on module {}", name(), M.getName()); bool Changed = false; - for (Function& F : M) { + SINFO("[{}] Executing on module {}", name(), M.getName()); + + for (Function &F : M) { std::string Name = demangle(F.getName().str()); StringRef NRef = Name; - if (NRef.startswith("_JNIEnv::") && config.inline_jni_wrappers) { + if (NRef.startswith("_JNIEnv::") && Config.InlineJniWrappers) { SDEBUG("[{}] Inlining {}", Name); F.addFnAttr(Attribute::AlwaysInline); Changed = true; } } - if (config.shuffle_functions) { + if (Config.ShuffleFunctions) { shuffleFunctions(M); Changed = true; } - SINFO("[{}] Changes{}applied on module {}", name(), Changed ? " " : " not ", + SINFO("[{}] Changes {} applied on module {}", name(), Changed ? "" : "not", M.getName()); - return Changed ? PreservedAnalyses::none() : - PreservedAnalyses::all(); - -} + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); } +} // end namespace omvll diff --git a/src/passes/flattening/ControlFlowFlattening.cpp b/src/passes/flattening/ControlFlowFlattening.cpp deleted file mode 100644 index 6345182b..00000000 --- a/src/passes/flattening/ControlFlowFlattening.cpp +++ /dev/null @@ -1,483 +0,0 @@ -#include "omvll/passes/flattening/ControlFlowFlattening.hpp" -#include "omvll/ObfuscationConfig.hpp" -#include "omvll/log.hpp" -#include "omvll/utils.hpp" -#include "omvll/PyConfig.hpp" - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/Demangle/Demangle.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Dominators.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/InlineAsm.h" -#include "llvm/IR/NoFolder.h" -#include "llvm/Passes/PassBuilder.h" -#include "llvm/Support/RandomNumberGenerator.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/Transforms/Utils/Local.h" - -using namespace llvm; - -namespace omvll { - -constexpr uint32_t Encode(uint32_t Id, uint32_t X, uint32_t Y) { - return (Id ^ X) + Y; -} - -template -void EmitTransition(IRBTy& IRB, AllocaInst* SV, BasicBlock* Dispatch, - uint32_t encId, uint32_t X, uint32_t Y) { - IRB.CreateStore(ConstantInt::get(IRB.getInt32Ty(), encId), SV); - LoadInst* val = IRB.CreateLoad(IRB.getInt32Ty(), SV, true); - - IRB.CreateStore( - IRB.CreateAdd( - IRB.CreateXor(val, ConstantInt::get(IRB.getInt32Ty(), X)), - ConstantInt::get(IRB.getInt32Ty(), Y) - ), SV, true); - - IRB.CreateBr(Dispatch); -} - -template -void EmitTransition(IRBTy& IRB, AllocaInst* SV, BasicBlock* Dispatch, - AllocaInst* TT, AllocaInst* TF, Value* Cond, - uint32_t TrueId, uint32_t FalseId, uint32_t X, uint32_t Y) { - - IRB.CreateStore(ConstantInt::get(IRB.getInt32Ty(), TrueId), TT); - IRB.CreateStore(ConstantInt::get(IRB.getInt32Ty(), FalseId), TF); - - LoadInst* valTrue = IRB.CreateLoad(IRB.getInt32Ty(), TT, true); - LoadInst* valFalse = IRB.CreateLoad(IRB.getInt32Ty(), TF, true); - - auto* encTrue = - IRB.CreateAdd( - IRB.CreateXor(valTrue, ConstantInt::get(IRB.getInt32Ty(), X)), - ConstantInt::get(IRB.getInt32Ty(), Y)); - - auto* encFalse = - IRB.CreateAdd( - IRB.CreateXor(valFalse, ConstantInt::get(IRB.getInt32Ty(), X)), - ConstantInt::get(IRB.getInt32Ty(), Y)); - - IRB.CreateStore( - IRB.CreateSelect(Cond, encTrue, encFalse), - SV, true); - IRB.CreateBr(Dispatch); -} - - -template -void EmitTransition(IRBTy& IRB, AllocaInst* V, BasicBlock* Dispatch, - uint32_t TrueId, uint32_t FalseId, uint32_t X, uint32_t Y) { - -} - -template -void EmitDefaultCaseAssembly(IRBTy& IRB, Triple TT) { - auto* FType = FunctionType::get(IRB.getVoidTy(), false); - if (TT.isAArch64()) { - IRB.CreateCall(FType, InlineAsm::get( - FType, - R"delim( - ldr x1, #-8; - blr x1; - mov x0, x1; - .byte 0xF1, 0xFF; - .byte 0xF2, 0xA2; - )delim", - "", - /* hasSideEffects */ true, - /* isStackAligned */ true - )); - } else if (TT.isX86()) { - // FIXME: This assembly may not confuse a decompiler - IRB.CreateCall(FType, InlineAsm::get( - FType, - R"delim( - nop; - .byte 0xF1, 0xFF; - .byte 0xF2, 0xA2; - )delim", - "", - /* hasSideEffects */ true, - /* isStackAligned */ true - )); - } else { - fatalError("Unsupported target for Control-Flow Flattening obfuscation: " + TT.str()); - } -} - -bool ControlFlowFlattening::runOnFunction(Function& F, RandomNumberGenerator& RNG) { - std::uniform_int_distribution Dist(10); - std::uniform_int_distribution Dist8(10, 254); - const uint8_t X = Dist8(RNG); - const uint8_t Y = Dist8(RNG); - - StringRef name = F.getName(); - std::string demangledName = demangle(name.str()); - StringRef demangled = demangledName; - - bool Changed = false; - - if (F.getInstructionCount() == 0) { - return false; - } - - SINFO("[{}] Visiting function {}", ControlFlowFlattening::name(), demangled); - - SmallVector flattedBB; - demotePHINode(F); - - BasicBlock* EntryBlock = &F.getEntryBlock(); - DenseSet NormalDest2Split; - - auto IsFromInvoke = [] (const BasicBlock& BB) { - return any_of(predecessors(&BB), [] (const BasicBlock* Pred) { - return isa(Pred->getTerminator()); - }); - }; - - for (BasicBlock& BB : F) { - if (BB.isLandingPad()) { - continue; - } - if (IsFromInvoke(BB)) { - NormalDest2Split.insert(&BB); - } - } - - /* - * +------------------+ +------------------+ - * | | | | - * +--------+---------+ +---------+--------+ - * | Normal | Unwind | | | | - * +---+----+---------+ +----+----+--------+ - * | | - * +---+----+ +----+----+ - * | | | +--+ Intermediate Block - * | | +---------+ | - * | | | - * +--------+ +---------+<-+ Original Block - * Normal Dst | | - * | | - * | | - * +---------+ - */ - - - for (BasicBlock* BB : NormalDest2Split) { - auto* Trampoline = BasicBlock::Create(BB->getContext(), ".normal_split", - BB->getParent(), BB); - for (BasicBlock* Pred : predecessors(BB)) { - // [Inst]: Invoke - if (auto* Invoke = dyn_cast(Pred->getTerminator())) { - Invoke->setNormalDest(Trampoline); - continue; - } - // [Inst]: Br - if (auto* Branch = dyn_cast(Pred->getTerminator())) { - for (size_t i = 0; i < Branch->getNumSuccessors(); ++i) { - if (Branch->getSuccessor(i) == BB) { - Branch->setSuccessor(i, Trampoline); - } - } - continue; - } - // [Inst]: Switch - if (auto* SW = dyn_cast(Pred->getTerminator())) { - SwitchInst::CaseIt begin = SW->case_begin(); - SwitchInst::CaseIt end = SW->case_end(); - for (auto it = begin; it != end; ++it) { - if (it->getCaseSuccessor() == BB) { - it->setSuccessor(Trampoline); - } - } - continue; - } - } - // Branch the Trampoline to the original BasicBlock - BranchInst::Create(BB, Trampoline); - } - - for (BasicBlock& BB : F) { - if (&BB == EntryBlock) { - continue; - } - flattedBB.push_back(&BB); - } - - const size_t nbBlock = count_if(flattedBB, [] (BasicBlock* BB) {return !BB->isLandingPad();}); - if (nbBlock <= 1) { - SWARN("[{}] Is too small (#{}) to be flattened", - ControlFlowFlattening::name(), flattedBB.size()); - return false; - } - - if (auto* br = dyn_cast(EntryBlock->getTerminator())) { - if (br->isConditional()) { - Value* cond = br->getCondition(); - if (auto* instCond = dyn_cast(cond)) { - BasicBlock *EntrySplit = - EntryBlock->splitBasicBlockBefore(instCond, "EntrySplit"); - flattedBB.insert(flattedBB.begin(), EntryBlock); - -#ifdef OMVLL_DEBUG - for (Instruction &I : *EntrySplit) { - SDEBUG("[{}][EntrySplit] {}", ControlFlowFlattening::name(), - ToString(I)); - } - - for (Instruction& I : *EntryBlock) { - SDEBUG("[{}][EntryBlock] {}", ControlFlowFlattening::name(), - ToString(I)); - } -#endif // OMVLL_DEBUG - - EntryBlock = EntrySplit; - } else { - SWARN("[{}] Found condition is not an instruction", - ControlFlowFlattening::name()); - } - } - } - else if (auto* swInst = dyn_cast(EntryBlock->getTerminator())) { - BasicBlock *EntrySplit = - EntryBlock->splitBasicBlockBefore(swInst, "EntrySplit"); - flattedBB.insert(flattedBB.begin(), EntryBlock); - EntryBlock = EntrySplit; - } - - else if (auto* Invoke = dyn_cast(EntryBlock->getTerminator())) { - BasicBlock *EntrySplit = - EntryBlock->splitBasicBlockBefore(Invoke, "EntrySplit"); - flattedBB.insert(flattedBB.begin(), EntryBlock); - EntryBlock = EntrySplit; - } - - SDEBUG("[{}] Erasing {}", ControlFlowFlattening::name(), - ToString(*EntryBlock->getTerminator())); - EntryBlock->getTerminator()->eraseFromParent(); - - /* Create a state encoding for the BB to flatten */ - DenseMap SwitchEnc; - SmallSet SwitchRnd; - for (BasicBlock* toFlat : flattedBB) { - if (toFlat->isLandingPad()) { - /* LandingPad are not embedded in the switch */ - continue; - } - uint32_t rnd = 0; - do { - rnd = Dist(RNG); - uint32_t enc = Encode(rnd, X, Y); - if (!SwitchRnd.contains(rnd) && !SwitchRnd.contains(enc)) { - SwitchRnd.insert(rnd); - SwitchRnd.insert(enc); - break; - } - } while (true); - SwitchEnc[toFlat] = rnd; - } - - IRBuilder<> EntryIR(EntryBlock); - AllocaInst* switchVar = EntryIR.CreateAlloca(EntryIR.getInt32Ty(), 0, "SwitchVar"); - AllocaInst* tmpTrue = EntryIR.CreateAlloca(EntryIR.getInt32Ty(), 0, "TmpTrue"); - AllocaInst* tmpFalse = EntryIR.CreateAlloca(EntryIR.getInt32Ty(), 0, "TmpFalse"); - - EntryIR.CreateStore(EntryIR.getInt32(Encode(SwitchEnc[flattedBB[0]], X, Y)), switchVar); - - auto* flatLoopEntry = BasicBlock::Create(F.getContext(), "FlatLoopEntry", &F, EntryBlock); - auto* flatLoopEnd = BasicBlock::Create(F.getContext(), "FlatLoopEnd", &F, EntryBlock); - auto* defaultCase = BasicBlock::Create(F.getContext(), "DefaultCase", &F, flatLoopEnd); - - IRBuilder<> FlatLoopEntryIR(flatLoopEntry), - FlatLoopEndIR(flatLoopEnd), - DefaultCaseIR(defaultCase); - - LoadInst* loadSwitchVar = FlatLoopEntryIR.CreateLoad(FlatLoopEntryIR.getInt32Ty(), switchVar, "SwitchVar"); - EntryBlock->moveBefore(flatLoopEntry); - EntryIR.CreateBr(flatLoopEntry); - FlatLoopEndIR.CreateBr(flatLoopEntry); - - EmitDefaultCaseAssembly(DefaultCaseIR, Triple(F.getParent()->getTargetTriple())); - DefaultCaseIR.CreateBr(flatLoopEnd); - - SwitchInst* switchInst = FlatLoopEntryIR.CreateSwitch(loadSwitchVar, defaultCase); - - for (BasicBlock* toFlat : flattedBB) { - if (toFlat->isLandingPad()) { - /* LandingPad should not be present in the switch case - * since they are not directly called by flattened blocks - */ - continue; - } - auto itEncId = SwitchEnc.find(toFlat); - if (itEncId == SwitchEnc.end()) { - fatalError(fmt::format("Can't find the encoded index for the basic block: {}", ToString(*toFlat))); - } - uint32_t switchId = Encode(itEncId->second, X, Y); - toFlat->moveBefore(flatLoopEnd); - auto* id = dyn_cast(ConstantInt::get(switchInst->getCondition()->getType(), switchId)); - switchInst->addCase(id, toFlat); - } - - /* Update the basic block with the switch var */ - for (BasicBlock* toFlat : flattedBB) { - Instruction* terminator = toFlat->getTerminator(); - SDEBUG("[{}] Flattening {} ({})", ControlFlowFlattening::name(), - ToString(*toFlat), ToString(*terminator)); - - if (isa(terminator) || isa(terminator)) { - /* Typically a ret instruction - * { - * if (...) { - * return X; - * } - * } - * - */ - continue; - } - - if (isa(terminator)) { - /* Nothing to do as it will 'resume' from information - * already known. - */ - continue; - } - - if (isa(terminator)) { - /* Already processed with the early 'split' */ - continue; - } - - if (isa(terminator)) { - auto* switchTerm = dyn_cast(terminator); - - std::vector cases; - std::transform(switchTerm->case_begin(), switchTerm->case_end(), std::back_inserter(cases), - [] (const SwitchInst::CaseHandle& handle) { return &handle; }); - - for (const SwitchInst::CaseHandle& handle : switchTerm->cases()) { - BasicBlock* target = handle.getCaseSuccessor(); - auto itEncId = SwitchEnc.find(target); - - if (itEncId == SwitchEnc.end()) { - fatalError("Unable to find the encoded id for the basic block: " + ToString(*target)); - } - - ConstantInt *destId = switchInst->findCaseDest(target); - if (destId == nullptr) { - fatalError(fmt::format("Unable to find {} in the switch case", ToString(*target))); - } - - const uint32_t encId = itEncId->second; - auto* dispatchBlock = BasicBlock::Create(F.getContext(), "", &F, flatLoopEnd); - - IRBuilder IRB(dispatchBlock); - EmitTransition(IRB, switchVar, flatLoopEnd, encId, X, Y); - switchTerm->setSuccessor(handle.getSuccessorIndex(), dispatchBlock); - } - continue; - } - - auto* branch = dyn_cast(terminator); - if (branch == nullptr) { - fatalError(fmt::format("[{}] Weird '{}' is not a branch", - ControlFlowFlattening::name().str(), ToString(*terminator))); - } - - if (branch->isUnconditional()) { - BasicBlock* target = branch->getSuccessor(0); - auto itEncId = SwitchEnc.find(target); - - if (itEncId == SwitchEnc.end()) { - fatalError(fmt::format("Unable to find the encoded id for the basic block: '{}'", ToString(*target))); - } - - ConstantInt *destId = switchInst->findCaseDest(target); - if (destId == nullptr) { - fatalError(fmt::format("Unable to find {} in the switch case", ToString(*target))); - } - - const uint32_t encId = itEncId->second; - - IRBuilder IRB(branch); - EmitTransition(IRB, switchVar, flatLoopEnd, encId, X, Y); - branch->eraseFromParent(); - continue; - } - - if (branch->isConditional()) { - BasicBlock* trueCase = branch->getSuccessor(0); - BasicBlock* falseCase = branch->getSuccessor(1); - - auto itTrue = SwitchEnc.find(trueCase); - auto itFalse = SwitchEnc.find(falseCase); - - if (itTrue == SwitchEnc.end()) { - fatalError(fmt::format("Unable to find the encoded id for the (true) basic block: '{}'", ToString(*trueCase))); - } - - if (itFalse == SwitchEnc.end()) { - fatalError(fmt::format("Unable to find the encoded id for the (false) basic block: '{}'", ToString(*falseCase))); - } - - ConstantInt *trueId = switchInst->findCaseDest(trueCase); - ConstantInt *falseId = switchInst->findCaseDest(falseCase); - - if (trueId == nullptr ) { - fatalError(fmt::format("Unable to find {} (true case) in the switch case", ToString(*trueCase))); - } - if (falseId == nullptr ) { - fatalError(fmt::format("Unable to find {} (false case) in the switch case", ToString(*falseCase))); - } - - const uint32_t trueEncId = itTrue->second; - const uint32_t falseEncId = itFalse->second; - - IRBuilder IRB(branch); - EmitTransition(IRB, switchVar, flatLoopEnd, - tmpTrue, tmpFalse, branch->getCondition(), - trueEncId, falseEncId, X, Y); - branch->eraseFromParent(); - continue; - } - } - - Changed = true; - return Changed; - -} - -PreservedAnalyses ControlFlowFlattening::run(Module &M, - ModuleAnalysisManager &MAM) { - PyConfig &config = PyConfig::instance(); - SINFO("[{}] Executing on module {}", name(), M.getName()); - std::unique_ptr RNG = M.createRNG(name()); - - bool Changed = false; - for (Function& F : M) { - if (!config.getUserConfig()->flatten_cfg(&M, &F)) - continue; - - bool fChanged = runOnFunction(F, *RNG); - - if (fChanged) { - reg2mem(F); - } - - Changed |= fChanged; - } - - SINFO("[{}] Changes{}applied on module {}", name(), Changed ? " " : " not ", - M.getName()); - - return Changed ? PreservedAnalyses::none() : - PreservedAnalyses::all(); - -} -} - diff --git a/src/passes/objcleaner/CMakeLists.txt b/src/passes/objc-cleaner/CMakeLists.txt similarity index 100% rename from src/passes/objcleaner/CMakeLists.txt rename to src/passes/objc-cleaner/CMakeLists.txt diff --git a/src/passes/objc-cleaner/ObjCleaner.cpp b/src/passes/objc-cleaner/ObjCleaner.cpp new file mode 100644 index 00000000..8faabc5e --- /dev/null +++ b/src/passes/objc-cleaner/ObjCleaner.cpp @@ -0,0 +1,78 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include "llvm/IR/Constants.h" +#include "llvm/Support/Regex.h" + +#include "omvll/ObfuscationConfig.hpp" +#include "omvll/PyConfig.hpp" +#include "omvll/log.hpp" +#include "omvll/passes/objc-cleaner/ObjCleaner.hpp" +#include "omvll/utils.hpp" + +using namespace llvm; + +namespace omvll { + +inline bool isObjCVar(const GlobalVariable &G) { + if (!G.hasName()) + return false; + + StringRef Name = G.getName(); + return Name.startswith("OBJC_") || Name.startswith("_OBJC_"); +} + +PreservedAnalyses ObjCleaner::run(Module &M, ModuleAnalysisManager &FAM) { + bool Changed = false; + SINFO("[{}] Executing on module {}", name(), M.getName()); + + for (GlobalVariable &G : M.getGlobalList()) { + if (!isObjCVar(G)) + continue; + + if (!G.hasInitializer()) + continue; + + SDEBUG("[{}] ObjC -> {} {}", name(), G.getName().str(), + ToString(*G.getValueType())); + + if (auto *CSTy = dyn_cast(G.getInitializer())) { + if (CSTy->getType()->getName().contains("_objc_method")) { + // TODO: Should these be handled? + ; + } + } + + if (G.isNullValue() || G.isZeroValue()) + continue; + + if (!(G.isConstant() && G.hasInitializer())) + continue; + + ConstantDataSequential *Data = + dyn_cast(G.getInitializer()); + + if (!Data || !Data->isCString()) + continue; + + SDEBUG("[{}] ObjC Var: {}: {}", name(), G.getName(), + Data->getAsCString().str()); + + std::string Str = Data->getAsCString().str(); + SINFO("String: {}", Str); + Regex R("SampleClass"); + std::string Rep = R.sub("NewName", Str); + Constant *NewVal = ConstantDataArray::getString(M.getContext(), Rep); + + G.setInitializer(NewVal); + } + + SINFO("[{}] Changes {} applied on module {}", name(), Changed ? "" : "not", + M.getName()); + + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); +} + +} // end namespace omvll diff --git a/src/passes/objcleaner/ObjCleaner.cpp b/src/passes/objcleaner/ObjCleaner.cpp deleted file mode 100644 index b0529546..00000000 --- a/src/passes/objcleaner/ObjCleaner.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "omvll/passes/objcleaner/ObjCleaner.hpp" -#include "omvll/log.hpp" -#include "omvll/utils.hpp" -#include "omvll/PyConfig.hpp" -#include "omvll/passes/Metadata.hpp" -#include "omvll/ObfuscationConfig.hpp" - -#include -#include - -using namespace llvm; - -namespace omvll { - -inline bool isObjCVar(const GlobalVariable& G) { - if (!G.hasName()) { - return false; - } - - StringRef GName = G.getName(); - return GName.startswith("OBJC_") || GName.startswith("_OBJC_"); -} - -PreservedAnalyses ObjCleaner::run(Module &M, - ModuleAnalysisManager &FAM) { - SINFO("[{}] Executing on module {}", name(), M.getName()); - bool Changed = false; - - for (GlobalVariable& G : M.getGlobalList()) { - if (!isObjCVar(G)) { - continue; - } - - if (!G.hasInitializer()) { - continue; - } - - SDEBUG("[{}] ObjC -> {} {}", name(), G.getName().str(), - ToString(*G.getValueType())); - - if (auto *CSTy = dyn_cast(G.getInitializer())) { - if (CSTy->getType()->getName().contains("_objc_method")) { - } - } - - if (G.isNullValue() || G.isZeroValue()) { - continue; - } - - if (!(G.isConstant() && G.hasInitializer())) { - continue; - } - - ConstantDataSequential* data = dyn_cast(G.getInitializer()); - - if (data == nullptr || !data->isCString()) { - continue; - } - SDEBUG("[{}] ObjC Var: {}: {}", name(), G.getName(), - data->getAsCString().str()); - //std::string value = data->getAsCString().str(); - //SINFO("String: {}", value); - //Regex R("SampleClass"); - //std::string rep = R.sub("NewName", value); - //Constant* NewVal = ConstantDataArray::getString(M.getContext(), rep); - //G.setInitializer(NewVal); - } - - //for (Function& F : M) { - // Changed |= runOnFunction(F); - //} - - SINFO("[{}] Changes{}applied on module {}", name(), Changed ? " " : " not ", - M.getName()); - - return Changed ? PreservedAnalyses::none() : - PreservedAnalyses::all(); - -} -} - diff --git a/src/passes/opaque-constants/CMakeLists.txt b/src/passes/opaque-constants/CMakeLists.txt index 6a1d583c..cbc93b52 100644 --- a/src/passes/opaque-constants/CMakeLists.txt +++ b/src/passes/opaque-constants/CMakeLists.txt @@ -1,4 +1,3 @@ - target_sources(OMVLL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/OpaqueConstants.cpp ${CMAKE_CURRENT_SOURCE_DIR}/GenOpaque.cpp diff --git a/src/passes/opaque-constants/GenOpaque.cpp b/src/passes/opaque-constants/GenOpaque.cpp index 82894c79..1e76f237 100644 --- a/src/passes/opaque-constants/GenOpaque.cpp +++ b/src/passes/opaque-constants/GenOpaque.cpp @@ -1,136 +1,135 @@ -#include "omvll/passes/opaque-constants/OpaqueConstants.hpp" -#include "omvll/passes/Metadata.hpp" -#include "omvll/utils.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// #include "llvm/IR/IRBuilder.h" #include "llvm/IR/NoFolder.h" - #include "llvm/Support/RandomNumberGenerator.h" +#include "omvll/passes/Metadata.hpp" +#include "omvll/passes/opaque-constants/OpaqueConstants.hpp" +#include "omvll/utils.hpp" + +#include "GenOpaque.hpp" + using namespace llvm; namespace omvll { -static constexpr uint8_t STACK_ALIGNEMENT = 0x04; +static constexpr uint8_t StackAlignment = 0x04; -/* Zero Value Gen - * ============================================ - */ - -Value* GetOpaqueZero_1(Instruction& I, OpaqueContext& C, Type* Ty, RandomNumberGenerator& RNG) { +/* ========= Zero Value Gen ========= */ +Value *getOpaqueZero1(Instruction &I, OpaqueContext &C, Type *Ty, + RandomNumberGenerator &RNG) { IRBuilder IRB(&I); - auto* T1Ptr = IRB.CreatePointerCast(C.T1, Ty->getPointerTo()); - auto* T2Ptr = IRB.CreatePointerCast(C.T2, Ty->getPointerTo()); - - auto* tmp1 = IRB.CreateLoad(Ty, T1Ptr, true); - auto* tmp2 = IRB.CreateLoad(Ty, T2Ptr, true); - - Value* MBAXor = IRB.CreateXor(tmp2, tmp1); - if (auto* Inst = dyn_cast(MBAXor)) { - addMetadata(*Inst, MetaObf(MObfTy::OPAQUE_OP, 3llu)); - } - Value* NewZero = - IRB.CreateIntCast( - IRB.CreateSub( - // MBA(x ^ y) - (x ^ y) - MBAXor, IRB.CreateXor(tmp2, tmp1)), - Ty, false); + auto *T1Ptr = IRB.CreatePointerCast(C.T1, Ty->getPointerTo()); + auto *T2Ptr = IRB.CreatePointerCast(C.T2, Ty->getPointerTo()); + + auto *Tmp1 = IRB.CreateLoad(Ty, T1Ptr, true); + auto *Tmp2 = IRB.CreateLoad(Ty, T2Ptr, true); + + Value *MBAXor = IRB.CreateXor(Tmp2, Tmp1); + if (auto *Inst = dyn_cast(MBAXor)) + addMetadata(*Inst, MetaObf(MetaObfTy::OpaqueOp, 3LLU)); + + // MBA(x ^ y) - (x ^ y) + Value *NewZero = IRB.CreateIntCast( + IRB.CreateSub(MBAXor, IRB.CreateXor(Tmp2, Tmp1)), Ty, false); return NewZero; } -Value* GetOpaqueZero_2(Instruction& I, OpaqueContext& C, Type* Ty, RandomNumberGenerator& RNG) { - return GetOpaqueZero_1(I, C, Ty, RNG); +Value *getOpaqueZero2(Instruction &I, OpaqueContext &C, Type *Ty, + RandomNumberGenerator &RNG) { + return getOpaqueZero1(I, C, Ty, RNG); } -Value* GetOpaqueZero_3(Instruction& I, OpaqueContext& C, Type* Ty, RandomNumberGenerator& RNG) { - return GetOpaqueZero_1(I, C, Ty, RNG); +Value *getOpaqueZero3(Instruction &I, OpaqueContext &C, Type *Ty, + RandomNumberGenerator &RNG) { + return getOpaqueZero1(I, C, Ty, RNG); } - -/* One Value Gen - * ============================================ - */ -Value* GetOpaqueOne_1(Instruction& I, OpaqueContext& C, Type* Ty, RandomNumberGenerator& RNG) { +/* ========= One Value Gen ========= */ +Value *getOpaqueOne1(Instruction &I, OpaqueContext &C, Type *Ty, + RandomNumberGenerator &RNG) { std::uniform_int_distribution Dist(1, 50); uint8_t Odd = Dist(RNG); - if (Odd % 2 == 0) { Odd += 1; } + if (Odd % 2 == 0) + Odd += 1; IRBuilder IRB(&I); - auto* T2Addr = IRB.CreatePtrToInt(C.T2, IRB.getInt64Ty()); - auto* OddAddr = IRB.CreateAdd(T2Addr, ConstantInt::get(IRB.getInt64Ty(), Odd, false)); + auto *T2Addr = IRB.CreatePtrToInt(C.T2, IRB.getInt64Ty()); + auto *OddAddr = + IRB.CreateAdd(T2Addr, ConstantInt::get(IRB.getInt64Ty(), Odd, false)); + + auto *LSB = + IRB.CreateAnd(OddAddr, ConstantInt::get(IRB.getInt64Ty(), 1, false)); + auto *OpaqueOne = IRB.CreateIntCast(LSB, Ty, false); - auto* LSB = IRB.CreateAnd(OddAddr, ConstantInt::get(IRB.getInt64Ty(), 1, false)); - auto* OpaqueOne = IRB.CreateIntCast(LSB, Ty, false); return OpaqueOne; } -Value* GetOpaqueOne_2(Instruction& I, OpaqueContext& C, Type* Ty, RandomNumberGenerator& RNG) { - return GetOpaqueOne_1(I, C, Ty, RNG); +Value *getOpaqueOne2(Instruction &I, OpaqueContext &C, Type *Ty, + RandomNumberGenerator &RNG) { + return getOpaqueOne1(I, C, Ty, RNG); } -Value* GetOpaqueOne_3(Instruction& I, OpaqueContext& C, Type* Ty, RandomNumberGenerator& RNG) { - return GetOpaqueOne_1(I, C, Ty, RNG); +Value *getOpaqueOne3(Instruction &I, OpaqueContext &C, Type *Ty, + RandomNumberGenerator &RNG) { + return getOpaqueOne1(I, C, Ty, RNG); } - -/* Value != {0, 1} Gen - * ============================================ - */ - -Value* GetOpaqueConst_1(Instruction& I, OpaqueContext& C, const ConstantInt& CI, RandomNumberGenerator& RNG) { +/* ========= Value != {0, 1} Gen ========= */ +Value *getOpaqueConst1(Instruction &I, OpaqueContext &C, const ConstantInt &CI, + RandomNumberGenerator &RNG) { uint64_t Val = CI.getLimitedValue(); - - if (Val <= 1) { + if (Val <= 1) return nullptr; - } std::uniform_int_distribution Dist(1, Val); uint8_t Split = Dist(RNG); - Module* M = I.getModule(); - GlobalVariable* GV = M->getGlobalVariable("__omvll_opaque_gv", true); - - if (GV == nullptr) { - fatalError("Can't find __omvll_opaque_gv"); - } + Module *M = I.getModule(); + GlobalVariable *GV = M->getGlobalVariable(OpaqueGVName, true); + if (!GV) + fatalError("Cannot find __omvll_opaque_gv"); uint64_t LHS = Val - Split; uint64_t RHS = Split; - auto* Ty = CI.getType(); - + auto *Ty = CI.getType(); IRBuilder IRB(&I); - auto* GVPtr = IRB.CreatePointerCast(GV, Ty->getPointerTo()); - auto* T1Ptr = IRB.CreatePointerCast(C.T1, Ty->getPointerTo()); + auto *GVPtr = IRB.CreatePointerCast(GV, Ty->getPointerTo()); + auto *T1Ptr = IRB.CreatePointerCast(C.T1, Ty->getPointerTo()); - auto* T2Addr = IRB.CreatePtrToInt(C.T2, IRB.getInt64Ty()); - auto* LSB = IRB.CreateAnd(T2Addr, ConstantInt::get(IRB.getInt64Ty(), STACK_ALIGNEMENT, false)); - auto* OpaqueZero = IRB.CreateIntCast(LSB, Ty, false); + auto *T2Addr = IRB.CreatePtrToInt(C.T2, IRB.getInt64Ty()); + auto *LSB = IRB.CreateAnd( + T2Addr, ConstantInt::get(IRB.getInt64Ty(), StackAlignment, false)); + auto *OpaqueZero = IRB.CreateIntCast(LSB, Ty, false); - //auto* - auto* OpaqueLHS = IRB.CreateAdd(OpaqueZero, ConstantInt::get(Ty, LHS, false)); - auto* OpaqueRHS = IRB.CreateAdd(OpaqueZero, ConstantInt::get(Ty, RHS, false)); + auto *OpaqueLHS = IRB.CreateAdd(OpaqueZero, ConstantInt::get(Ty, LHS, false)); + auto *OpaqueRHS = IRB.CreateAdd(OpaqueZero, ConstantInt::get(Ty, RHS, false)); IRB.CreateStore(OpaqueLHS, GVPtr, /*volatile=*/true); IRB.CreateStore(OpaqueRHS, T1Ptr, /*volatile=*/true); - auto* Add = IRB.CreateAdd( - IRB.CreateLoad(Ty, GVPtr, true), - IRB.CreateLoad(Ty, T1Ptr, true)); - if (auto* InstAdd = dyn_cast(Add)) { - addMetadata(*InstAdd, MetaObf(MObfTy::OPAQUE_OP, 3llu)); - } - return Add; -} + auto *Add = IRB.CreateAdd(IRB.CreateLoad(Ty, GVPtr, true), + IRB.CreateLoad(Ty, T1Ptr, true)); + if (auto *InstAdd = dyn_cast(Add)) + addMetadata(*InstAdd, MetaObf(MetaObfTy::OpaqueOp, 3LLU)); -Value* GetOpaqueConst_2(Instruction& I, OpaqueContext& C, const ConstantInt& CI, RandomNumberGenerator& RNG) { - return GetOpaqueConst_1(I, C, CI, RNG); + return Add; } -Value* GetOpaqueConst_3(Instruction& I, OpaqueContext& C, const ConstantInt& CI, RandomNumberGenerator& RNG) { - return GetOpaqueConst_1(I, C, CI, RNG); +Value *getOpaqueConst2(Instruction &I, OpaqueContext &C, const ConstantInt &CI, + RandomNumberGenerator &RNG) { + return getOpaqueConst1(I, C, CI, RNG); } +Value *getOpaqueConst3(Instruction &I, OpaqueContext &C, const ConstantInt &CI, + RandomNumberGenerator &RNG) { + return getOpaqueConst1(I, C, CI, RNG); } +} // end namespace omvll diff --git a/src/passes/opaque-constants/GenOpaque.hpp b/src/passes/opaque-constants/GenOpaque.hpp index 983f4da1..45db9a6e 100644 --- a/src/passes/opaque-constants/GenOpaque.hpp +++ b/src/passes/opaque-constants/GenOpaque.hpp @@ -1,5 +1,11 @@ -#ifndef OMVLL_GEN_OPAQUE_H -#define OMVLL_GEN_OPAQUE_H +#pragma once + +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +// Forward declarations namespace llvm { class Value; class Instruction; @@ -7,23 +13,39 @@ class OpaqueContext; class ConstantInt; class RandomNumberGenerator; class Type; -} +} // end namespace llvm + namespace omvll { + +static constexpr auto OpaqueGVName = "__omvll_opaque_gv"; + struct OpaqueContext; -// Opaque ZERO generator -llvm::Value* GetOpaqueZero_1(llvm::Instruction& I, OpaqueContext& C, llvm::Type* Ty, llvm::RandomNumberGenerator& RNG); -llvm::Value* GetOpaqueZero_2(llvm::Instruction& I, OpaqueContext& C, llvm::Type* Ty, llvm::RandomNumberGenerator& RNG); -llvm::Value* GetOpaqueZero_3(llvm::Instruction& I, OpaqueContext& C, llvm::Type* Ty, llvm::RandomNumberGenerator& RNG); - -// Opaque ONE generator -llvm::Value* GetOpaqueOne_1(llvm::Instruction& I, OpaqueContext& C, llvm::Type* Ty, llvm::RandomNumberGenerator& RNG); -llvm::Value* GetOpaqueOne_2(llvm::Instruction& I, OpaqueContext& C, llvm::Type* Ty, llvm::RandomNumberGenerator& RNG); -llvm::Value* GetOpaqueOne_3(llvm::Instruction& I, OpaqueContext& C, llvm::Type* Ty, llvm::RandomNumberGenerator& RNG); - -// Opaque VALUE != {0, 1} generator -llvm::Value* GetOpaqueConst_1(llvm::Instruction& I, OpaqueContext& C, const llvm::ConstantInt& Val, llvm::RandomNumberGenerator& RNG); -llvm::Value* GetOpaqueConst_2(llvm::Instruction& I, OpaqueContext& C, const llvm::ConstantInt& Val, llvm::RandomNumberGenerator& RNG); -llvm::Value* GetOpaqueConst_3(llvm::Instruction& I, OpaqueContext& C, const llvm::ConstantInt& Val, llvm::RandomNumberGenerator& RNG); -} -#endif +// Opaque Zero generator. +llvm::Value *getOpaqueZero1(llvm::Instruction &I, OpaqueContext &C, + llvm::Type *Ty, llvm::RandomNumberGenerator &RNG); +llvm::Value *getOpaqueZero2(llvm::Instruction &I, OpaqueContext &C, + llvm::Type *Ty, llvm::RandomNumberGenerator &RNG); +llvm::Value *getOpaqueZero3(llvm::Instruction &I, OpaqueContext &C, + llvm::Type *Ty, llvm::RandomNumberGenerator &RNG); + +// Opaque One generator. +llvm::Value *getOpaqueOne1(llvm::Instruction &I, OpaqueContext &C, + llvm::Type *Ty, llvm::RandomNumberGenerator &RNG); +llvm::Value *getOpaqueOne2(llvm::Instruction &I, OpaqueContext &C, + llvm::Type *Ty, llvm::RandomNumberGenerator &RNG); +llvm::Value *getOpaqueOne3(llvm::Instruction &I, OpaqueContext &C, + llvm::Type *Ty, llvm::RandomNumberGenerator &RNG); + +// Opaque Value != {0, 1} generator. +llvm::Value *getOpaqueConst1(llvm::Instruction &I, OpaqueContext &C, + const llvm::ConstantInt &Val, + llvm::RandomNumberGenerator &RNG); +llvm::Value *getOpaqueConst2(llvm::Instruction &I, OpaqueContext &C, + const llvm::ConstantInt &Val, + llvm::RandomNumberGenerator &RNG); +llvm::Value *getOpaqueConst3(llvm::Instruction &I, OpaqueContext &C, + const llvm::ConstantInt &Val, + llvm::RandomNumberGenerator &RNG); + +} // end namespace omvll diff --git a/src/passes/opaque-constants/OpaqueConstants.cpp b/src/passes/opaque-constants/OpaqueConstants.cpp index bb7a7eed..21118f9e 100644 --- a/src/passes/opaque-constants/OpaqueConstants.cpp +++ b/src/passes/opaque-constants/OpaqueConstants.cpp @@ -1,13 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// -#include "omvll/ObfuscationConfig.hpp" -#include "omvll/PyConfig.hpp" -#include "omvll/log.hpp" -#include "omvll/passes/Metadata.hpp" -#include "omvll/passes/opaque-constants/OpaqueConstants.hpp" -#include "omvll/utils.hpp" -#include "omvll/visitvariant.hpp" - -#include "llvm/Demangle/Demangle.h" #include "llvm/IR/Constants.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstVisitor.h" @@ -16,272 +11,275 @@ #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "omvll/ObfuscationConfig.hpp" +#include "omvll/PyConfig.hpp" +#include "omvll/log.hpp" +#include "omvll/passes/Metadata.hpp" +#include "omvll/passes/opaque-constants/OpaqueConstants.hpp" +#include "omvll/utils.hpp" +#include "omvll/visitvariant.hpp" + #include "GenOpaque.hpp" using namespace llvm; namespace omvll { -inline bool isEligible(const Instruction& I) { - return !isa(I) && - !isa(I) && - !isa(I); +inline bool isEligible(const Instruction &I) { + return !isa(I) && !isa(I) && !isa(I); } -inline bool isSkip(const OpaqueConstantsOpt& opt) { - return std::get_if(&opt) != nullptr; +inline bool isSkip(const OpaqueConstantsOpt &Opt) { + return std::get_if(&Opt) != nullptr; } -bool OpaqueConstants::Process(Instruction& I, Use& Op, ConstantInt& CI, OpaqueConstantsOpt* opt) { - if (!isEligible(I)) { +bool OpaqueConstants::process(Instruction &I, Use &Op, ConstantInt &CI, + OpaqueConstantsOpt *Opt) { + if (!isEligible(I)) return false; - } - BasicBlock& BB = *I.getParent(); - OpaqueContext* ctx = getOrCreateContext(BB); - if (ctx == nullptr) { - SWARN("[{}] Can't opaque {}", name(), ToString(BB)); + BasicBlock &BB = *I.getParent(); + OpaqueContext *Ctx = getOrCreateContext(BB); + if (!Ctx) { + SWARN("[{}] Cannot opaque {}", name(), ToString(BB)); return false; } - /* - * Special processing for 0 values - * =============================== - */ + + // Special processing for 0 values. if (CI.isZero()) { - if (opt != nullptr) { - bool shouldProtect = std::visit(overloaded { - [] (OpaqueConstantsSkip&) { return false; }, - [] (OpaqueConstantsBool& v) { return v.value; }, - [] (OpaqueConstantsLowerLimit& v) { return false; }, - [] (OpaqueConstantsSet& v) { return v.contains(0); }, - }, *opt); - - if (!shouldProtect) { + if (Opt) { + bool ShouldProtect = + std::visit(overloaded{ + [](OpaqueConstantsSkip &) { return false; }, + [](OpaqueConstantsBool &V) { return V.Value; }, + [](OpaqueConstantsLowerLimit &V) { return false; }, + [](OpaqueConstantsSet &V) { return V.contains(0); }, + }, + *Opt); + + if (!ShouldProtect) return false; - } } - Value* NewZero = GetOpaqueZero(I, *ctx, CI.getType()); - if (NewZero == nullptr) { - SWARN("[{}] Can't opaque {}", name(), ToString(CI)); + Value *NewZero = getOpaqueZero(I, *Ctx, CI.getType()); + if (!NewZero) { + SWARN("[{}] Cannot opaque {}", name(), ToString(CI)); return false; } Op.set(NewZero); return true; } - /* - * Special processing for 1 values - * =============================== - */ + // Special processing for 1 values. if (CI.isOne()) { - if (opt != nullptr) { - bool shouldProtect = std::visit(overloaded { - [] (OpaqueConstantsSkip&) { return false; }, - [] (OpaqueConstantsBool& v) { return v.value; }, - [] (OpaqueConstantsLowerLimit& v) { return v.value >= 1; }, - [] (OpaqueConstantsSet& v) { return v.contains(1); }, - }, *opt); - - if (!shouldProtect) { + if (Opt) { + bool ShouldProtect = std::visit( + overloaded{ + [](OpaqueConstantsSkip &) { return false; }, + [](OpaqueConstantsBool &V) { return V.Value; }, + [](OpaqueConstantsLowerLimit &V) { return V.Value >= 1; }, + [](OpaqueConstantsSet &V) { return V.contains(1); }, + }, + *Opt); + + if (!ShouldProtect) return false; - } } - Value* NewOne = GetOpaqueOne(I, *ctx, CI.getType()); - if (NewOne == nullptr) { - SWARN("[{}] Can't opaque {}", name(), ToString(CI)); + Value *NewOne = getOpaqueOne(I, *Ctx, CI.getType()); + if (!NewOne) { + SWARN("[{}] Cannot opaque {}", name(), ToString(CI)); return false; } Op.set(NewOne); return true; } - bool shouldProtect = std::visit(overloaded { - [] (OpaqueConstantsSkip&) { return false; }, - [] (OpaqueConstantsBool& v) { return v.value; }, - [&CI] (OpaqueConstantsLowerLimit& v) { return CI.getLimitedValue() > v.value; }, - [&CI] (OpaqueConstantsSet& v) { - static constexpr uint64_t MAGIC = 0x4208d8df2c6415bc; - const uint64_t LV = CI.getLimitedValue(MAGIC); - if (LV == MAGIC) { - return true; - } - return !v.empty() && v.contains(LV); - }, - }, *opt); - - if (!shouldProtect) { + bool ShouldProtect = + std::visit(overloaded{ + [](OpaqueConstantsSkip &) { return false; }, + [](OpaqueConstantsBool &V) { return V.Value; }, + [&CI](OpaqueConstantsLowerLimit &V) { + return CI.getLimitedValue() > V.Value; + }, + [&CI](OpaqueConstantsSet &V) { + static constexpr uint64_t Magic = 0x4208D8DF2C6415BC; + const uint64_t LV = CI.getLimitedValue(Magic); + if (LV == Magic) + return true; + return !V.empty() && V.contains(LV); + }, + }, + *Opt); + + if (!ShouldProtect) return false; - } - - Value* NewCst = GetOpaqueCst(I, *ctx, CI); - if (NewCst == nullptr) { - SWARN("[{}] Can't opaque {}", name(), ToString(CI)); + Value *NewCst = getOpaqueCst(I, *Ctx, CI); + if (!NewCst) { + SWARN("[{}] Cannot opaque {}", name(), ToString(CI)); return false; } Op.set(NewCst); return true; } -Value* OpaqueConstants::GetOpaqueZero(Instruction& I, OpaqueContext& C, Type* Ty) { - static constexpr auto MAX_CASES = 3; - static_assert(RandomNumberGenerator::max() >= MAX_CASES); - std::uniform_int_distribution Dist(1, MAX_CASES); +Value *OpaqueConstants::getOpaqueZero(Instruction &I, OpaqueContext &C, + Type *Ty) { + static constexpr auto MaxCases = 3; + static_assert(RandomNumberGenerator::max() >= MaxCases); + std::uniform_int_distribution Dist(1, MaxCases); - uint8_t Sel = Dist(*RNG_); + uint8_t Sel = Dist(*RNG); switch (Sel) { - case 1: return GetOpaqueZero_1(I, C, Ty, *RNG_); - case 2: return GetOpaqueZero_2(I, C, Ty, *RNG_); - case 3: return GetOpaqueZero_3(I, C, Ty, *RNG_); - default: - { - SWARN("[{}] RNG number ({}) out of range for generating opaque zero", - name(), Sel); - return nullptr; - } + case 1: + return getOpaqueZero1(I, C, Ty, *RNG); + case 2: + return getOpaqueZero2(I, C, Ty, *RNG); + case 3: + return getOpaqueZero3(I, C, Ty, *RNG); + default: { + SWARN("[{}] RNG number ({}) out of range for generating opaque zero", + name(), Sel); + return nullptr; + } } } -Value* OpaqueConstants::GetOpaqueOne(Instruction& I, OpaqueContext& C, Type* Ty) { - static constexpr auto MAX_CASES = 3; - static_assert(RandomNumberGenerator::max() >= MAX_CASES); - std::uniform_int_distribution Dist(1, MAX_CASES); +Value *OpaqueConstants::getOpaqueOne(Instruction &I, OpaqueContext &C, + Type *Ty) { + static constexpr auto MaxCases = 3; + static_assert(RandomNumberGenerator::max() >= MaxCases); + std::uniform_int_distribution Dist(1, MaxCases); - uint8_t Sel = Dist(*RNG_); + uint8_t Sel = Dist(*RNG); switch (Sel) { - case 1: return GetOpaqueOne_1(I, C, Ty, *RNG_); - case 2: return GetOpaqueOne_2(I, C, Ty, *RNG_); - case 3: return GetOpaqueOne_3(I, C, Ty, *RNG_); - default: - { - SWARN("[{}] RNG number ({}) out of range for generating opaque one", - name(), Sel); - return nullptr; - } + case 1: + return getOpaqueOne1(I, C, Ty, *RNG); + case 2: + return getOpaqueOne2(I, C, Ty, *RNG); + case 3: + return getOpaqueOne3(I, C, Ty, *RNG); + default: { + SWARN("[{}] RNG number ({}) out of range for generating opaque one", name(), + Sel); + return nullptr; + } } } +Value *OpaqueConstants::getOpaqueCst(Instruction &I, OpaqueContext &C, + const ConstantInt &CI) { + static constexpr auto MaxCases = 3; + static_assert(RandomNumberGenerator::max() >= MaxCases); + std::uniform_int_distribution Dist(1, MaxCases); -Value* OpaqueConstants::GetOpaqueCst(Instruction& I, OpaqueContext& C, const ConstantInt& CI) { - static constexpr auto MAX_CASES = 3; - static_assert(RandomNumberGenerator::max() >= MAX_CASES); - std::uniform_int_distribution Dist(1, MAX_CASES); - - uint8_t Sel = Dist(*RNG_); + uint8_t Sel = Dist(*RNG); switch (Sel) { - case 1: return GetOpaqueConst_1(I, C, CI, *RNG_); - case 2: return GetOpaqueConst_2(I, C, CI, *RNG_); - case 3: return GetOpaqueConst_3(I, C, CI, *RNG_); - default: - { - SWARN("[{}] RNG number ({}) out of range for generating opaque value", - name(), Sel); - return nullptr; - } + case 1: + return getOpaqueConst1(I, C, CI, *RNG); + case 2: + return getOpaqueConst2(I, C, CI, *RNG); + case 3: + return getOpaqueConst3(I, C, CI, *RNG); + default: { + SWARN("[{}] RNG number ({}) out of range for generating opaque value", + name(), Sel); + return nullptr; + } } } -bool OpaqueConstants::Process(Instruction& I, OpaqueConstantsOpt* opt) { +bool OpaqueConstants::process(Instruction &I, OpaqueConstantsOpt *Opt) { bool Changed = false; - #ifdef OMVLL_DEBUG - std::string InstStr = ToString(I); - #endif - for (Use& Op : I.operands()) { - if (auto* CI = dyn_cast(Op)) { - Changed |= Process(I, Op, *CI, opt); - } - } - #ifdef OMVLL_DEBUG - if (Changed) { +#ifdef OMVLL_DEBUG + std::string InstStr = ToString(I); +#endif + for (Use &Op : I.operands()) + if (auto *CI = dyn_cast(Op)) + Changed |= process(I, Op, *CI, Opt); + +#ifdef OMVLL_DEBUG + if (Changed) SDEBUG("[{}] Opaquize constant in instruction {}", name(), InstStr); - } - #endif +#endif + return Changed; } +OpaqueContext *OpaqueConstants::getOrCreateContext(BasicBlock &BB) { + if (auto It = Ctx.find(&BB); It != Ctx.end()) + return &It->second; -OpaqueContext* OpaqueConstants::getOrCreateContext(BasicBlock& BB) { - if (auto it = ctx_.find(&BB); it != ctx_.end()) { - return &it->second; - } - - auto IP = BB.getFirstInsertionPt(); - if (IP == BB.end()) { + auto InsertPt = BB.getFirstInsertionPt(); + if (InsertPt == BB.end()) return nullptr; - } - OpaqueContext& ctx = ctx_[&BB]; - IRBuilder IRB(&*IP); - ctx.T1 = IRB.CreateAlloca(IRB.getInt64Ty()); - ctx.T2 = IRB.CreateAlloca(IRB.getInt64Ty()); - IRB.CreateAlignedStore( - IRB.CreatePtrToInt(ctx.T2, IRB.getInt64Ty()), ctx.T1, Align(8)); + OpaqueContext &OpaqueCtx = Ctx[&BB]; + IRBuilder IRB(&*InsertPt); + OpaqueCtx.T1 = IRB.CreateAlloca(IRB.getInt64Ty()); + OpaqueCtx.T2 = IRB.CreateAlloca(IRB.getInt64Ty()); - IRB.CreateAlignedStore( - IRB.CreatePtrToInt(ctx.T1, IRB.getInt64Ty()), ctx.T2, Align(8)); + IRB.CreateAlignedStore(IRB.CreatePtrToInt(OpaqueCtx.T2, IRB.getInt64Ty()), + OpaqueCtx.T1, Align(8)); + IRB.CreateAlignedStore(IRB.CreatePtrToInt(OpaqueCtx.T1, IRB.getInt64Ty()), + OpaqueCtx.T2, Align(8)); - return &ctx; + return &OpaqueCtx; } -bool OpaqueConstants::runOnBasicBlock(llvm::BasicBlock &BB, OpaqueConstantsOpt* opt) { +bool OpaqueConstants::runOnBasicBlock(llvm::BasicBlock &BB, + OpaqueConstantsOpt *Opt) { bool Changed = false; - for (Instruction& I : BB) { - if (hasObf(I, MObfTy::OPAQUE_CST)) { - OpaqueConstantsOpt force = OpaqueConstantsBool(true); - Changed |= Process(I, &force); - } - else if (opt != nullptr) { - Changed |= Process(I, opt); + for (Instruction &I : BB) { + if (hasObf(I, MetaObfTy::OpaqueCst)) { + OpaqueConstantsOpt Force = OpaqueConstantsBool(true); + Changed |= process(I, &Force); + } else if (Opt) { + Changed |= process(I, Opt); } } + return Changed; } -PreservedAnalyses OpaqueConstants::run(Module &M, - ModuleAnalysisManager &FAM) { - PyConfig &config = PyConfig::instance(); - SINFO("[{}] Executing on module {}", name(), M.getName()); - RNG_ = M.createRNG(name()); - +PreservedAnalyses OpaqueConstants::run(Module &M, ModuleAnalysisManager &FAM) { bool Changed = false; - auto* Int64Ty = Type::getInt64Ty(M.getContext()); - M.getOrInsertGlobal("__omvll_opaque_gv", Int64Ty, - [&] () { - return new GlobalVariable(M, Int64Ty, /*isConstant=*/false, - GlobalValue::PrivateLinkage, - ConstantInt::get(Int64Ty, 0), - "__omvll_opaque_gv"); - }); - - for (Function& F : M) { - OpaqueConstantsOpt opt = config.getUserConfig()->obfuscate_constants(&M, &F); - OpaqueConstantsOpt* inserted = nullptr; - if (isSkip(opt)) + PyConfig &Config = PyConfig::instance(); + SINFO("[{}] Executing on module {}", name(), M.getName()); + RNG = M.createRNG(name()); + + auto *Int64Ty = Type::getInt64Ty(M.getContext()); + M.getOrInsertGlobal(OpaqueGVName, Int64Ty, [&]() { + return new GlobalVariable(M, Int64Ty, /*isConstant=*/false, + GlobalValue::PrivateLinkage, + ConstantInt::get(Int64Ty, 0), OpaqueGVName); + }); + + for (Function &F : M) { + OpaqueConstantsOpt Opt = Config.getUserConfig()->obfuscateConstants(&M, &F); + OpaqueConstantsOpt *Inserted = nullptr; + if (isSkip(Opt)) continue; - auto ret = opts_.insert({&F, std::move(opt)}); - if (ret.second) { - inserted = &ret.first->second; - } + auto Ret = Opts.insert({&F, std::move(Opt)}); + if (Ret.second) + Inserted = &Ret.first->second; - for (BasicBlock& BB : F) { + for (BasicBlock &BB : F) { // Don't try opaque constants when potentially handling infinite loops. if (is_contained(successors(&BB), &BB)) continue; - Changed |= runOnBasicBlock(BB, inserted); + Changed |= runOnBasicBlock(BB, Inserted); } } - SINFO("[{}] Changes{}applied on module {}", name(), Changed ? " " : " not ", + SINFO("[{}] Changes {} applied on module {}", name(), Changed ? "" : "not", M.getName()); - return Changed ? PreservedAnalyses::none() : - PreservedAnalyses::all(); - -} + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); } +} // end namespace omvll diff --git a/src/passes/opaque-field-access/CMakeLists.txt b/src/passes/opaque-field-access/CMakeLists.txt index cacf4327..0f83e552 100644 --- a/src/passes/opaque-field-access/CMakeLists.txt +++ b/src/passes/opaque-field-access/CMakeLists.txt @@ -1,4 +1,3 @@ - target_sources(OMVLL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/OpaqueFieldAccess.cpp ) diff --git a/src/passes/opaque-field-access/OpaqueFieldAccess.cpp b/src/passes/opaque-field-access/OpaqueFieldAccess.cpp index 4c35092e..d7753bd8 100644 --- a/src/passes/opaque-field-access/OpaqueFieldAccess.cpp +++ b/src/passes/opaque-field-access/OpaqueFieldAccess.cpp @@ -1,201 +1,181 @@ -#include "omvll/passes/opaque-field-access/OpaqueFieldAccess.hpp" -#include "omvll/log.hpp" -#include "omvll/utils.hpp" -#include "omvll/PyConfig.hpp" -#include "omvll/ObfuscationConfig.hpp" -#include "omvll/passes/Metadata.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// -#include "llvm/Demangle/Demangle.h" #include "llvm/IR/Constants.h" -#include "llvm/IR/Metadata.h" #include "llvm/IR/IRBuilder.h" -#include "llvm/Transforms/Utils/BasicBlockUtils.h" -#include "llvm/IR/NoFolder.h" #include "llvm/IR/InstVisitor.h" -#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/NoFolder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "omvll/ObfuscationConfig.hpp" +#include "omvll/PyConfig.hpp" +#include "omvll/log.hpp" +#include "omvll/passes/Metadata.hpp" +#include "omvll/passes/opaque-constants/OpaqueConstants.hpp" +#include "omvll/passes/opaque-field-access/OpaqueFieldAccess.hpp" +#include "omvll/utils.hpp" using namespace llvm; namespace omvll { -bool OpaqueFieldAccess::runOnStructRead(BasicBlock& BB, LoadInst& Load, - GetElementPtrInst& GEP, StructType& S) -{ - PyConfig& conf = PyConfig::instance(); - if (!conf.getUserConfig()->obfuscate_struct_access(BB.getModule(), BB.getParent(), &S)) { +bool OpaqueFieldAccess::runOnStructRead(BasicBlock &BB, LoadInst &Load, + GetElementPtrInst &GEP, StructType &S) { + PyConfig &Config = PyConfig::instance(); + if (!Config.getUserConfig()->obfuscateStructAccess(BB.getModule(), + BB.getParent(), &S)) return false; - } SINFO("[{}] Executing on module {} over structure {}", name(), BB.getModule()->getName(), S.getName()); - SmallVector indices(GEP.idx_begin(), GEP.idx_end()); - - if (indices.size() != 2) { + SmallVector Indices(GEP.idx_begin(), GEP.idx_end()); + if (Indices.size() != 2) { SWARN("[{}] Load {} expects 2 indices for the structure {}", name(), ToString(Load), S.getName()); return false; } - Value* Idx0 = indices[0]; - Value* Idx1 = indices[1]; - - auto* ZeroVal = dyn_cast(Idx0); - auto* OffVal = dyn_cast(Idx1); - - if (ZeroVal == nullptr || OffVal == nullptr) { + auto *ZeroVal = dyn_cast(Indices[0]); + auto *OffVal = dyn_cast(Indices[1]); + if (!ZeroVal || !OffVal) return false; - } - if (ZeroVal->getLimitedValue() != 0) { + if (ZeroVal->getLimitedValue() != 0) SWARN("[{}] Expecting a zero value for the getelementptr: {}", name(), ToString(GEP)); - } - const DataLayout& DL = BB.getParent()->getParent()->getDataLayout(); + const DataLayout &DL = BB.getParent()->getParent()->getDataLayout(); uint64_t ComputedOffset = DL.getStructLayout(&S)->getElementOffset(OffVal->getLimitedValue()); SDEBUG("[{}] Obfuscating field READ access on {}->#{} (offset: {})", name(), S.getName(), OffVal->getLimitedValue(), ComputedOffset); IRBuilder IRB(&Load); - - Value* opaqueOffset = - IRB.CreateAdd( - ConstantInt::get(IRB.getInt32Ty(), 0), - ConstantInt::get(IRB.getInt32Ty(), ComputedOffset)); - - if (auto* OpAdd = dyn_cast(opaqueOffset)) { - addMetadata(*OpAdd, {MetaObf(OPAQUE_CST), MetaObf(OPAQUE_OP, 2llu)}); - } - - Value* newGEP = - IRB.CreateBitCast( - IRB.CreateInBoundsGEP(IRB.getInt8Ty(), - IRB.CreateBitCast(GEP.getPointerOperand(), IRB.getInt8PtrTy()), - opaqueOffset), + Value *OpaqueOffset = + IRB.CreateAdd(ConstantInt::get(IRB.getInt32Ty(), 0), + ConstantInt::get(IRB.getInt32Ty(), ComputedOffset)); + + if (auto *OpAdd = dyn_cast(OpaqueOffset)) + addMetadata(*OpAdd, {MetaObf(OpaqueCst), MetaObf(OpaqueOp, 2LLU)}); + + Value *NewGEP = IRB.CreateBitCast( + IRB.CreateInBoundsGEP( + IRB.getInt8Ty(), + IRB.CreateBitCast(GEP.getPointerOperand(), IRB.getInt8PtrTy()), + OpaqueOffset), GEP.getResultElementType()->getPointerTo()); - GEP.replaceAllUsesWith(newGEP); + GEP.replaceAllUsesWith(NewGEP); return true; } - -bool OpaqueFieldAccess::runOnBufferRead(BasicBlock& BB, LoadInst& Load, - GetElementPtrInst& GEP, ConstantInt& CI) -{ - if (!hasObf(Load, MObfTy::PROTECT_FIELD_ACCESS)) { +bool OpaqueFieldAccess::runOnBufferRead(BasicBlock &BB, LoadInst &Load, + GetElementPtrInst &GEP, + ConstantInt &CI) { + if (!hasObf(Load, MetaObfTy::ProtectFieldAccess)) return false; - } + SDEBUG("[{}] Obfuscating buffer load access", name()); IRBuilder IRB(&Load); const uint64_t Val = CI.getLimitedValue(); - uint32_t lhs, rhs = 0; + uint32_t Lhs, Rhs = 0; + if (Val % 2 == 0) { - lhs = Val / 2; - rhs = Val / 2; + Lhs = Val / 2; + Rhs = Val / 2; } else { - lhs = (Val - 1) / 2; - rhs = (Val + 1) / 2; + Lhs = (Val - 1) / 2; + Rhs = (Val + 1) / 2; } - Value* opaqueOffset = - IRB.CreateAdd( - ConstantInt::get(IRB.getInt32Ty(), lhs), - ConstantInt::get(IRB.getInt32Ty(), rhs)); + Value *OpaqueOffset = IRB.CreateAdd(ConstantInt::get(IRB.getInt32Ty(), Lhs), + ConstantInt::get(IRB.getInt32Ty(), Rhs)); - if (auto* Op = dyn_cast(opaqueOffset)) { - addMetadata(*Op, {MetaObf(OPAQUE_CST), MetaObf(OPAQUE_OP, 2llu)}); - } + if (auto *Op = dyn_cast(OpaqueOffset)) + addMetadata(*Op, {MetaObf(OpaqueCst), MetaObf(OpaqueOp, 2LLU)}); - Value* newGEP = - IRB.CreateBitCast( - IRB.CreateInBoundsGEP(IRB.getInt8Ty(), - IRB.CreateBitCast(GEP.getPointerOperand(), IRB.getInt8PtrTy()), - opaqueOffset), + Value *NewGEP = IRB.CreateBitCast( + IRB.CreateInBoundsGEP( + IRB.getInt8Ty(), + IRB.CreateBitCast(GEP.getPointerOperand(), IRB.getInt8PtrTy()), + OpaqueOffset), GEP.getResultElementType()->getPointerTo()); - GEP.replaceAllUsesWith(newGEP); + GEP.replaceAllUsesWith(NewGEP); return true; } -bool OpaqueFieldAccess::runOnConstantExprRead(BasicBlock& BB, LoadInst& Load, ConstantExpr& CE) -{ - if (CE.getNumOperands() < 3) { +bool OpaqueFieldAccess::runOnConstantExprRead(BasicBlock &BB, LoadInst &Load, + ConstantExpr &CE) { + if (CE.getNumOperands() < 3) return false; - } - auto* GV = dyn_cast(CE.getOperand(0)); - auto* OpOffset = dyn_cast(CE.getOperand(2)); - if (GV == nullptr || OpOffset == nullptr) { + auto *GV = dyn_cast(CE.getOperand(0)); + auto *OpOffset = dyn_cast(CE.getOperand(2)); + if (!GV || !OpOffset) return false; - } - Type *GVTy = GV->getValueType(); + Type *GVTy = GV->getValueType(); if (!GVTy->isArrayTy()) { - SDEBUG("[{}] Global variable {} won't be processed: non array type", name(), - GV->getName()); + SDEBUG("[{}] Global variable {} will not be processed: non array type", + name(), GV->getName()); return false; } - Type* ElTy = GVTy->getArrayElementType(); - - PyConfig& conf = PyConfig::instance(); - if (!conf.getUserConfig()->obfuscate_variable_access(BB.getModule(), BB.getParent(), GV)) { + Type *ElTy = GVTy->getArrayElementType(); + PyConfig &Config = PyConfig::instance(); + if (!Config.getUserConfig()->obfuscateVariableAccess(BB.getModule(), + BB.getParent(), GV)) return false; - } SINFO("[{}] Executing on module {} over variable {}", name(), BB.getModule()->getName(), GV->getName()); IRBuilder IRB(&Load); - Value* opaqueOffset = - IRB.CreateSub( + Value *OpaqueOffset = IRB.CreateSub( ConstantInt::get(IRB.getInt32Ty(), OpOffset->getLimitedValue()), ConstantInt::get(IRB.getInt32Ty(), 0)); + if (auto *Op = dyn_cast(OpaqueOffset)) + addMetadata(*Op, {MetaObf(OpaqueCst), MetaObf(OpaqueOp, 2LLU)}); - if (auto* Op = dyn_cast(opaqueOffset)) { - addMetadata(*Op, {MetaObf(OPAQUE_CST), MetaObf(OPAQUE_OP, 2llu)}); - } - - - Value* newGEP = IRB.CreateInBoundsGEP(ElTy, - IRB.CreateBitCast(GV, ElTy->getPointerTo()), - opaqueOffset); - Load.setOperand(0, newGEP); + Value *NewGEP = IRB.CreateInBoundsGEP( + ElTy, IRB.CreateBitCast(GV, ElTy->getPointerTo()), OpaqueOffset); + Load.setOperand(0, NewGEP); return true; } +bool OpaqueFieldAccess::runOnLoad(LoadInst &Load) { + Value *LoadOp = Load.getPointerOperand(); -bool OpaqueFieldAccess::runOnLoad(LoadInst& Load) { - Value* LoadOp = Load.getPointerOperand(); - - if (auto* GEP = dyn_cast(LoadOp)) { - Value* Target = GEP->getPointerOperand(); - Type* TargetTy = Target->getType(); + if (auto *GEP = dyn_cast(LoadOp)) { + Value *Target = GEP->getPointerOperand(); + Type *TargetTy = Target->getType(); - if (TargetTy->isPointerTy() && !TargetTy->isOpaquePointerTy()) { + // TODO: Update this properly after opaque ptr migration. + if (TargetTy->isPointerTy() && !TargetTy->isOpaquePointerTy()) TargetTy = TargetTy->getNonOpaquePointerElementType(); - } if (TargetTy->isStructTy()) { - auto* Struct = dyn_cast(TargetTy); + auto *Struct = dyn_cast(TargetTy); return runOnStructRead(*Load.getParent(), Load, *GEP, *Struct); } - /* Catch this kind of instruction: - * `getelementptr i8, i8* %26, i32 4, !dbg !2963` - */ + + // Catch this instruction: `getelementptr i8, i8* %26, i32 4, !dbg !2963` if (Target->getType()->isPointerTy() && TargetTy->isIntegerTy()) { - SmallVector indices(GEP->idx_begin(), GEP->idx_end()); - if (indices.size() == 1 && isa(indices[0])) { + SmallVector Indices(GEP->idx_begin(), GEP->idx_end()); + if (Indices.size() == 1 && isa(Indices[0])) { if (TargetTy->getPrimitiveSizeInBits() == 8) { - return runOnBufferRead(*Load.getParent(), Load, *GEP, *dyn_cast(indices[0])); + return runOnBufferRead(*Load.getParent(), Load, *GEP, + *cast(Indices[0])); } else { - if (hasObf(Load, MObfTy::PROTECT_FIELD_ACCESS)) { - // TODO(romain): To implement - SWARN("[{}] Load instruction {} won't be protected", name(), + if (hasObf(Load, MetaObfTy::ProtectFieldAccess)) { + // TODO(romain): To implement. + SWARN("[{}] Load instruction {} will not be protected", name(), ToString(Load)); } } @@ -203,101 +183,86 @@ bool OpaqueFieldAccess::runOnLoad(LoadInst& Load) { } if (TargetTy->isArrayTy()) { - // TODO(romain): To implement + // TODO(romain): To implement. SDEBUG("[{}] ArrayTy is not supported yet: {}", name(), ToString(Load)); return false; } } - if (auto* CE = dyn_cast(LoadOp)) { + if (auto *CE = dyn_cast(LoadOp)) return runOnConstantExprRead(*Load.getParent(), Load, *CE); - } return false; } -bool OpaqueFieldAccess::runOnConstantExprWrite(llvm::BasicBlock& BB, llvm::StoreInst& Store, - llvm::ConstantExpr& CE) -{ - - if (CE.getNumOperands() < 3) { +bool OpaqueFieldAccess::runOnConstantExprWrite(BasicBlock &BB, StoreInst &Store, + ConstantExpr &CE) { + if (CE.getNumOperands() < 3) return false; - } - auto* GV = dyn_cast(CE.getOperand(0)); - auto* OpOffset = dyn_cast(CE.getOperand(2)); - if (GV == nullptr || OpOffset == nullptr) { + auto *GV = dyn_cast(CE.getOperand(0)); + auto *OpOffset = dyn_cast(CE.getOperand(2)); + if (!GV || !OpOffset) return false; - } + Type *GVTy = GV->getValueType(); if (!GVTy->isArrayTy()) { - SDEBUG("[{}] Global variable {} won't be processed: non Array type", name(), - GV->getName()); + SDEBUG("[{}] Global variable {} will not be processed: non Array type", + name(), GV->getName()); + return false; } - Type* ElTy = GVTy->getArrayElementType(); - - PyConfig& conf = PyConfig::instance(); - if (!conf.getUserConfig()->obfuscate_variable_access(BB.getModule(), BB.getParent(), GV)) { + Type *ElTy = GVTy->getArrayElementType(); + PyConfig &Config = PyConfig::instance(); + if (!Config.getUserConfig()->obfuscateVariableAccess(BB.getModule(), + BB.getParent(), GV)) return false; - } SINFO("[{}] Executing on module {} over variable {}", name(), BB.getModule()->getName(), GV->getName()); IRBuilder IRB(&Store); - Value* opaqueOffset = - IRB.CreateOr( + Value *OpaqueOffset = IRB.CreateOr( ConstantInt::get(IRB.getInt32Ty(), OpOffset->getLimitedValue()), ConstantInt::get(IRB.getInt32Ty(), 0)); + if (auto *Op = dyn_cast(OpaqueOffset)) + addMetadata(*Op, {MetaObf(OpaqueCst), MetaObf(OpaqueOp, 2LLU)}); - if (auto* Op = dyn_cast(opaqueOffset)) { - addMetadata(*Op, {MetaObf(OPAQUE_CST), MetaObf(OPAQUE_OP, 2llu)}); - } - - Value* newGEP = IRB.CreateInBoundsGEP(ElTy, - IRB.CreateBitCast(GV, ElTy->getPointerTo()), - opaqueOffset); - Store.setOperand(1, newGEP); + Value *NewGEP = IRB.CreateInBoundsGEP( + ElTy, IRB.CreateBitCast(GV, ElTy->getPointerTo()), OpaqueOffset); + Store.setOperand(1, NewGEP); return true; } -bool OpaqueFieldAccess::runOnStructWrite(BasicBlock& BB, StoreInst& Store, - GetElementPtrInst& GEP, StructType& S) -{ - PyConfig& conf = PyConfig::instance(); - if (!conf.getUserConfig()->obfuscate_struct_access(BB.getModule(), BB.getParent(), &S)) { +bool OpaqueFieldAccess::runOnStructWrite(BasicBlock &BB, StoreInst &Store, + GetElementPtrInst &GEP, + StructType &S) { + PyConfig &Config = PyConfig::instance(); + if (!Config.getUserConfig()->obfuscateStructAccess(BB.getModule(), + BB.getParent(), &S)) return false; - } SINFO("[{}] Executing on module {} over structure {}", name(), BB.getModule()->getName(), S.getName()); - SmallVector indices(GEP.idx_begin(), GEP.idx_end()); - - if (indices.size() != 2) { + SmallVector Indices(GEP.idx_begin(), GEP.idx_end()); + if (Indices.size() != 2) { SWARN("[{}] Store {} expects 2 indices for the structure {}", name(), ToString(Store), S.getName()); return false; } - Value* Idx0 = indices[0]; - Value* Idx1 = indices[1]; - - auto* ZeroVal = dyn_cast(Idx0); - auto* OffVal = dyn_cast(Idx1); - - if (ZeroVal == nullptr || OffVal == nullptr) { + auto *ZeroVal = dyn_cast(Indices[0]); + auto *OffVal = dyn_cast(Indices[1]); + if (!ZeroVal || !OffVal) return false; - } - if (ZeroVal->getLimitedValue() != 0) { + if (ZeroVal->getLimitedValue() != 0) SWARN("[{}] Expecting a zero value for the getelementptr {}", name(), ToString(GEP)); - } - const DataLayout& DL = BB.getParent()->getParent()->getDataLayout(); + const DataLayout &DL = BB.getParent()->getParent()->getDataLayout(); uint64_t ComputedOffset = DL.getStructLayout(&S)->getElementOffset(OffVal->getLimitedValue()); SDEBUG("[{}] Obfuscating field WRITE access on {}->#{} (offset: {})", name(), @@ -305,92 +270,88 @@ bool OpaqueFieldAccess::runOnStructWrite(BasicBlock& BB, StoreInst& Store, IRBuilder IRB(&Store); - Value* opaqueOffset = - IRB.CreateAdd( - ConstantInt::get(IRB.getInt32Ty(), 0), - ConstantInt::get(IRB.getInt32Ty(), ComputedOffset)); + Value *OpaqueOffset = + IRB.CreateAdd(ConstantInt::get(IRB.getInt32Ty(), 0), + ConstantInt::get(IRB.getInt32Ty(), ComputedOffset)); - if (auto* OpAdd = dyn_cast(opaqueOffset)) { - addMetadata(*OpAdd, {MetaObf(OPAQUE_CST), MetaObf(OPAQUE_OP, 2llu)}); - } + if (auto *OpAdd = dyn_cast(OpaqueOffset)) + addMetadata(*OpAdd, {MetaObf(OpaqueCst), MetaObf(OpaqueOp, 2LLU)}); - Value* newGEP = - IRB.CreateBitCast( - IRB.CreateInBoundsGEP(IRB.getInt8Ty(), - IRB.CreateBitCast(GEP.getPointerOperand(), IRB.getInt8PtrTy()), - opaqueOffset), + Value *NewGEP = IRB.CreateBitCast( + IRB.CreateInBoundsGEP( + IRB.getInt8Ty(), + IRB.CreateBitCast(GEP.getPointerOperand(), IRB.getInt8PtrTy()), + OpaqueOffset), GEP.getResultElementType()->getPointerTo()); - GEP.replaceAllUsesWith(newGEP); + GEP.replaceAllUsesWith(NewGEP); return true; } -bool OpaqueFieldAccess::runOnBufferWrite(BasicBlock& BB, StoreInst& Store, - GetElementPtrInst& GEP, ConstantInt& CI) -{ - if (!hasObf(Store, MObfTy::PROTECT_FIELD_ACCESS)) { +bool OpaqueFieldAccess::runOnBufferWrite(BasicBlock &BB, StoreInst &Store, + GetElementPtrInst &GEP, + ConstantInt &CI) { + if (!hasObf(Store, MetaObfTy::ProtectFieldAccess)) return false; - } + SDEBUG("[{}] Obfuscating buffer write access", name()); + IRBuilder IRB(&Store); uint64_t Val = CI.getLimitedValue(); - uint32_t lhs, rhs = 0; + uint32_t Lhs, Rhs = 0; + if (Val % 2 == 0) { - lhs = Val / 2; - rhs = Val / 2; + Lhs = Val / 2; + Rhs = Val / 2; } else { - lhs = (Val - 1) / 2; - rhs = (Val + 1) / 2; + Lhs = (Val - 1) / 2; + Rhs = (Val + 1) / 2; } - Value* opaqueOffset = - IRB.CreateAdd( - ConstantInt::get(IRB.getInt32Ty(), lhs), - ConstantInt::get(IRB.getInt32Ty(), rhs)); + Value *OpaqueOffset = IRB.CreateAdd(ConstantInt::get(IRB.getInt32Ty(), Lhs), + ConstantInt::get(IRB.getInt32Ty(), Rhs)); - if (auto* Op = dyn_cast(opaqueOffset)) { - addMetadata(*Op, {MetaObf(OPAQUE_CST), MetaObf(OPAQUE_OP, 2llu)}); - } + if (auto *Op = dyn_cast(OpaqueOffset)) + addMetadata(*Op, {MetaObf(OpaqueCst), MetaObf(OpaqueOp, 2LLU)}); - Value* newGEP = - IRB.CreateBitCast( - IRB.CreateInBoundsGEP(IRB.getInt8Ty(), - IRB.CreateBitCast(GEP.getPointerOperand(), IRB.getInt8PtrTy()), - opaqueOffset), + Value *NewGEP = IRB.CreateBitCast( + IRB.CreateInBoundsGEP( + IRB.getInt8Ty(), + IRB.CreateBitCast(GEP.getPointerOperand(), IRB.getInt8PtrTy()), + OpaqueOffset), GEP.getResultElementType()->getPointerTo()); - GEP.replaceAllUsesWith(newGEP); + GEP.replaceAllUsesWith(NewGEP); return true; } -bool OpaqueFieldAccess::runOnStore(StoreInst& Store) { - Value* StoreOp = Store.getPointerOperand(); +bool OpaqueFieldAccess::runOnStore(StoreInst &Store) { + Value *StoreOp = Store.getPointerOperand(); - if (auto* GEP = dyn_cast(StoreOp)) { - Value* Target = GEP->getPointerOperand(); - Type* TargetTy = Target->getType(); + if (auto *GEP = dyn_cast(StoreOp)) { + Value *Target = GEP->getPointerOperand(); + Type *TargetTy = Target->getType(); - if (TargetTy->isPointerTy() && !TargetTy->isOpaquePointerTy()) { + // TODO: Update this properly after opaque ptr migration. + if (TargetTy->isPointerTy() && !TargetTy->isOpaquePointerTy()) TargetTy = TargetTy->getNonOpaquePointerElementType(); - } if (TargetTy->isStructTy()) { - auto* Struct = dyn_cast(TargetTy); + auto *Struct = dyn_cast(TargetTy); return runOnStructWrite(*Store.getParent(), Store, *GEP, *Struct); } - /* Catch this kind of instruction: - * `getelementptr i8, i8* %26, i32 4, !dbg !2963` - */ + // Catch this instruction: `getelementptr i8, i8* %26, i32 4, !dbg !2963` if (Target->getType()->isPointerTy() && TargetTy->isIntegerTy()) { - SmallVector indices(GEP->idx_begin(), GEP->idx_end()); - if (indices.size() == 1 && isa(indices[0])) { + SmallVector Indices(GEP->idx_begin(), GEP->idx_end()); + if (Indices.size() == 1 && isa(Indices[0])) { if (TargetTy->getPrimitiveSizeInBits() == 8) { - return runOnBufferWrite(*Store.getParent(), Store, *GEP, *dyn_cast(indices[0])); + return runOnBufferWrite(*Store.getParent(), Store, *GEP, + *cast(Indices[0])); } else { - if (hasObf(Store, MObfTy::PROTECT_FIELD_ACCESS)) { - // TODO(romain): To implement - SWARN("[{}] Store instruction {} won't be protected", name(), + if (hasObf(Store, MetaObfTy::ProtectFieldAccess)) { + // TODO(romain): To implement. + SWARN("[{}] Store instruction {} will not be protected", name(), ToString(Store)); } } @@ -399,7 +360,7 @@ bool OpaqueFieldAccess::runOnStore(StoreInst& Store) { } if (TargetTy->isArrayTy()) { - // TODO(romain): To implement + // TODO(romain): To implement. SDEBUG("[{}] ArrayTy is not supported yet for store {}", name(), ToString(Store)); return false; @@ -408,21 +369,18 @@ bool OpaqueFieldAccess::runOnStore(StoreInst& Store) { return false; } - if (auto* CE = dyn_cast(StoreOp)) { + if (auto *CE = dyn_cast(StoreOp)) return runOnConstantExprWrite(*Store.getParent(), Store, *CE); - } return false; } bool OpaqueFieldAccess::runOnBasicBlock(BasicBlock &BB) { bool Changed = false; - for (Instruction& Inst : BB) { - if (auto* Load = dyn_cast(&Inst)) { + for (Instruction &Inst : BB) { + if (auto *Load = dyn_cast(&Inst)) Changed |= runOnLoad(*Load); - } - else if (auto* Store = dyn_cast(&Inst)) { + else if (auto *Store = dyn_cast(&Inst)) Changed |= runOnStore(*Store); - } } return Changed; } @@ -430,19 +388,14 @@ bool OpaqueFieldAccess::runOnBasicBlock(BasicBlock &BB) { PreservedAnalyses OpaqueFieldAccess::run(Module &M, ModuleAnalysisManager &FAM) { bool Changed = false; - //dump(M, "/tmp/a.ll"); - for (Function& F : M) { - for (BasicBlock& BB : F) { + for (Function &F : M) + for (BasicBlock &BB : F) Changed |= runOnBasicBlock(BB); - } - } - SINFO("[{}] Changes{}applied on module {}", name(), Changed ? " " : " not ", + SINFO("[{}] Changes {} applied on module {}", name(), Changed ? "" : "not", M.getName()); - return Changed ? PreservedAnalyses::none() : - PreservedAnalyses::all(); - -} + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); } +} // end namespace omvll diff --git a/src/passes/strings-encoding/CMakeLists.txt b/src/passes/string-encoding/CMakeLists.txt similarity index 99% rename from src/passes/strings-encoding/CMakeLists.txt rename to src/passes/string-encoding/CMakeLists.txt index 6cbb2fe4..46d1ae0f 100644 --- a/src/passes/strings-encoding/CMakeLists.txt +++ b/src/passes/string-encoding/CMakeLists.txt @@ -1,4 +1,3 @@ - target_sources(OMVLL PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/StringEncoding.cpp ) diff --git a/src/passes/strings-encoding/StringEncoding.cpp b/src/passes/string-encoding/StringEncoding.cpp similarity index 55% rename from src/passes/strings-encoding/StringEncoding.cpp rename to src/passes/string-encoding/StringEncoding.cpp index 189f1778..790bb5d4 100644 --- a/src/passes/strings-encoding/StringEncoding.cpp +++ b/src/passes/string-encoding/StringEncoding.cpp @@ -1,66 +1,71 @@ -#include "omvll/passes/string-encoding/StringEncoding.hpp" +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + +#include + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/CodeGen/StableHashing.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/NoFolder.h" +#include "llvm/Support/Host.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" -#include "omvll/log.hpp" -#include "omvll/utils.hpp" -#include "omvll/PyConfig.hpp" -#include "omvll/visitvariant.hpp" #include "omvll/ObfuscationConfig.hpp" +#include "omvll/PyConfig.hpp" +#include "omvll/jitter.hpp" +#include "omvll/log.hpp" #include "omvll/passes/Metadata.hpp" -#include "omvll/Jitter.hpp" #include "omvll/passes/arithmetic/Arithmetic.hpp" +#include "omvll/passes/string-encoding/StringEncoding.hpp" +#include "omvll/utils.hpp" +#include "omvll/visitvariant.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +using namespace llvm; -static llvm::ExitOnError ExitOnErr; +static ExitOnError ExitOnErr; -using namespace llvm; +static constexpr auto DecodeFunctionName = "__omvll_decode"; +static constexpr auto CtorPrefixName = "__omvll_ctor_"; namespace omvll { -static const std::vector ROUTINES = { - R"delim( - void encode(char* out, char* in, unsigned long long key, int size) { - unsigned char* raw_key = (unsigned char*)(&key); +static const std::vector Routines = { + R"delim( + void encode(char *out, char *in, unsigned long long key, int size) { + unsigned char *raw_key = (unsigned char*)(&key); for (int i = 0; i < size; ++i) { out[i] = in[i] ^ raw_key[i % sizeof(key)]; } - return; } - void decode(char* out, char* in, unsigned long long key, int size) { - unsigned char* raw_key = (unsigned char*)(&key); + void decode(char *out, char *in, unsigned long long key, int size) { + unsigned char *raw_key = (unsigned char*)(&key); for (int i = 0; i < size; ++i) { out[i] = in[i] ^ raw_key[i % sizeof(key)]; } } )delim", - R"delim( - void encode(char* out, char* in, unsigned long long key, int size) { - unsigned char* raw_key = (unsigned char*)(&key); + R"delim( + void encode(char *out, char *in, unsigned long long key, int size) { + unsigned char *raw_key = (unsigned char*)(&key); for (int i = 0; i < size; ++i) { out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i; } - return; } - void decode(char* out, char* in, unsigned long long key, int size) { - unsigned char* raw_key = (unsigned char*)(&key); + void decode(char *out, char *in, unsigned long long key, int size) { + unsigned char *raw_key = (unsigned char*)(&key); for (int i = 0; i < size; ++i) { out[i] = in[i] ^ raw_key[i % sizeof(key)] ^ i; } @@ -68,7 +73,7 @@ static const std::vector ROUTINES = { )delim", }; -inline bool isEligible(const GlobalVariable& G) { +inline bool isEligible(const GlobalVariable &G) { if (G.isNullValue() || G.isZeroValue()) return false; @@ -78,21 +83,21 @@ inline bool isEligible(const GlobalVariable& G) { return true; } -inline bool isEligible(const ConstantDataSequential& CDS) { +inline bool isEligible(const ConstantDataSequential &CDS) { return CDS.isCString() && CDS.getAsCString().size() > 1; } -inline bool isSkip(const StringEncodingOpt& encInfo) { - return std::get_if(&encInfo) != nullptr; +inline bool isSkip(const StringEncodingOpt &EncInfo) { + return std::get_if(&EncInfo) != nullptr; } GlobalVariable *extractGlobalVariable(ConstantExpr *Expr) { while (Expr) { - if (Expr->getOpcode() == llvm::Instruction::IntToPtr || - llvm::Instruction::isBinaryOp(Expr->getOpcode())) { + if (Expr->getOpcode() == Instruction::IntToPtr || + Instruction::isBinaryOp(Expr->getOpcode())) { Expr = dyn_cast(Expr->getOperand(0)); - } else if (Expr->getOpcode() == llvm::Instruction::PtrToInt || - Expr->getOpcode() == llvm::Instruction::GetElementPtr) { + } else if (Expr->getOpcode() == Instruction::PtrToInt || + Expr->getOpcode() == Instruction::GetElementPtr) { return dyn_cast(Expr->getOperand(0)); } else { break; @@ -139,23 +144,23 @@ createDecodingTrampoline(GlobalVariable &G, Use &EncPtr, Instruction *NewPt, IRB.SetInsertPoint(NewPt); auto *StoreKey = IRB.CreateStore(IRB.getInt64(KeyValI64), Key); - addMetadata(*StoreKey, MetaObf(OPAQUE_CST)); + addMetadata(*StoreKey, MetaObf(OpaqueCst)); IRB.CreateStore(IRB.getInt32(Size), StrSize); LoadInst *LoadKey = IRB.CreateLoad(IRB.getInt64Ty(), Key); - addMetadata(*LoadKey, MetaObf(OPAQUE_CST)); + addMetadata(*LoadKey, MetaObf(OpaqueCst)); auto *KeyVal = IRB.CreateBitCast(LoadKey, IRB.getInt64Ty()); LoadInst *LStrSize = IRB.CreateLoad(IRB.getInt32Ty(), StrSize); - addMetadata(*LStrSize, MetaObf(OPAQUE_CST)); + addMetadata(*LStrSize, MetaObf(OpaqueCst)); auto *VStrSize = IRB.CreateBitCast(LStrSize, IRB.getInt32Ty()); Function *FDecode = EI.TM->getFunction("decode"); - if (FDecode == nullptr) - fatalError("Can't find the 'decode' routine"); + if (!FDecode) + fatalError("Cannot find decode routine"); // TODO: support ObjC strings as well if (isa(EncPtr)) @@ -171,7 +176,7 @@ createDecodingTrampoline(GlobalVariable &G, Use &EncPtr, Instruction *NewPt, auto *NewF = Function::Create(FDecode->getFunctionType(), GlobalValue::PrivateLinkage, - "__omvll_decode", NewPt->getModule()); + DecodeFunctionName, NewPt->getModule()); ValueToValueMapTy VMap; auto NewFArgsIt = NewF->arg_begin(); @@ -194,11 +199,9 @@ createDecodingTrampoline(GlobalVariable &G, Use &EncPtr, Instruction *NewPt, for (size_t Idx = 0; Idx < NewF->arg_size(); ++Idx) { auto *E = NewF->getFunctionType()->getParamType(Idx); auto *V = Args[Idx]->getType(); - if (E != V) { - std::string err = fmt::format("Args #{}: Expecting {} while it is {}", - Idx, ToString(*E), ToString(*V)); - fatalError(err.c_str()); - } + if (E != V) + fatalError(fmt::format("Args #{}: Expecting {} while it is {}", Idx, + ToString(*E), ToString(*V))); } if (IsPartOfStackVariable) { @@ -227,17 +230,18 @@ createDecodingTrampoline(GlobalVariable &G, Use &EncPtr, Instruction *NewPt, } bool StringEncoding::runOnBasicBlock(Module &M, Function &F, BasicBlock &BB, - ObfuscationConfig &userConfig) { + ObfuscationConfig &UserConfig) { bool Changed = false; for (Instruction &I : BB) { if (isa(I)) { - SWARN("{} contains Phi node which could raise issues!", demangle(F.getName().str())); + SWARN("{} contains Phi node which could raise issues!", + demangle(F.getName().str())); continue; } - for (Use& Op : I.operands()) { - GlobalVariable *G = dyn_cast(Op->stripPointerCasts()); + for (Use &Op : I.operands()) { + auto *G = dyn_cast(Op->stripPointerCasts()); // Is the operand a constant expression? if (!G) @@ -275,13 +279,13 @@ bool StringEncoding::runOnBasicBlock(Module &M, Function &F, BasicBlock &BB, continue; auto *Data = dyn_cast(G->getInitializer()); - if (Data == nullptr) + if (!Data) continue; // Create a default option which skips the encoding. auto EncInfoOpt = std::make_unique(StringEncOptSkip()); - if (EncodingInfo* EI = getEncoding(*G)) { + if (EncodingInfo *EI = getEncoding(*G)) { // The global variable is already encoded. // Let's check if we should insert a decoding stub. Changed |= injectDecoding(BB, I, Op, *G, *Data, *EI); @@ -291,7 +295,7 @@ bool StringEncoding::runOnBasicBlock(Module &M, Function &F, BasicBlock &BB, if (isEligible(*Data)) { // Get the encoding type from the user. EncInfoOpt = std::make_unique( - userConfig.obfuscate_string(&M, &F, Data->getAsCString().str())); + UserConfig.obfuscateString(&M, &F, Data->getAsCString().str())); } if (isSkip(*EncInfoOpt) || @@ -308,18 +312,17 @@ bool StringEncoding::runOnBasicBlock(Module &M, Function &F, BasicBlock &BB, } PreservedAnalyses StringEncoding::run(Module &M, ModuleAnalysisManager &MAM) { - SINFO("[{}] Executing on module {}", name(), M.getName()); bool Changed = false; + SINFO("[{}] Executing on module {}", name(), M.getName()); auto &Config = PyConfig::instance(); ObfuscationConfig *UserConfig = Config.getUserConfig(); + RNG = M.createRNG(name()); - RNG_ = M.createRNG(name()); - - std::vector FuncsToVisit; + std::vector ToVisit; for (Function &F : M) - FuncsToVisit.emplace_back(&F); + ToVisit.emplace_back(&F); - for (auto *F : FuncsToVisit) { + for (Function *F : ToVisit) { demotePHINode(*F); std::string DemangledFName = demangle(F->getName().str()); SDEBUG("[{}] Visiting function {}", name(), DemangledFName); @@ -328,43 +331,37 @@ PreservedAnalyses StringEncoding::run(Module &M, ModuleAnalysisManager &MAM) { } } - // Inline function - for (CallInst *Callee : inline_wlist_) { + // Inline functions. + for (CallInst *Callee : ToInline) { InlineFunctionInfo IFI; InlineResult Res = InlineFunction(*Callee, IFI); - if (!Res.isSuccess()) { + if (!Res.isSuccess()) fatalError(Res.getFailureReason()); - } } - inline_wlist_.clear(); - // Create CTOR functions - for (Function *F : ctor_) + // Create constructors functions. + for (Function *F : Ctors) appendToGlobalCtors(M, F, 0); - ctor_.clear(); - SINFO("[{}] Changes{}applied on module {}", name(), Changed ? " " : " not ", + SINFO("[{}] Changes {} applied on module {}", name(), Changed ? "" : "not", M.getName()); - return Changed ? PreservedAnalyses::none() : - PreservedAnalyses::all(); + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); } bool StringEncoding::injectDecoding(BasicBlock &BB, Instruction &I, Use &Op, GlobalVariable &G, - ConstantDataSequential &data, - const StringEncoding::EncodingInfo &info) { - switch (info.type) { - case EncodingTy::NONE: - case EncodingTy::REPLACE: - case EncodingTy::GLOBAL: - return false; - - case EncodingTy::STACK: - return injectOnStack(BB, I, Op, G, data, info); - - case EncodingTy::STACK_LOOP: - return injectOnStackLoop(BB, I, Op, G, data, info); + ConstantDataSequential &Data, + const StringEncoding::EncodingInfo &Info) { + switch (Info.Type) { + case EncodingTy::None: + case EncodingTy::Replace: + case EncodingTy::Global: + return false; + case EncodingTy::Stack: + return injectOnStack(BB, I, Op, G, Data, Info); + case EncodingTy::StackLoop: + return injectOnStackLoop(BB, I, Op, G, Data, Info); } return false; @@ -372,64 +369,63 @@ bool StringEncoding::injectDecoding(BasicBlock &BB, Instruction &I, Use &Op, bool StringEncoding::injectOnStack(BasicBlock &BB, Instruction &I, Use &Op, GlobalVariable &G, - ConstantDataSequential &data, - const StringEncoding::EncodingInfo &info) { - auto *Key = std::get_if(&info.key); - if (Key == nullptr) + ConstantDataSequential &Data, + const StringEncoding::EncodingInfo &Info) { + auto *Key = std::get_if(&Info.Key); + if (!Key) fatalError("String stack decoding is expecting a buffer as a key"); - StringRef Str = data.getRawDataValues(); + StringRef Str = Data.getRawDataValues(); uint64_t StrSz = Str.size(); - Use& EncPtr = Op; + Use &EncPtr = Op; IRBuilder IRB(&BB); IRB.SetInsertPoint(&I); - // Allocate a buffer on the stack that will contain the decoded string + // Allocate a buffer on the stack that will contain the decoded string. AllocaInst *ClearBuffer = IRB.CreateAlloca(IRB.getInt8Ty(), IRB.getInt32(StrSz)); - llvm::SmallVector Indexes(StrSz); + SmallVector Indexes(StrSz); for (size_t I = 0; I < Indexes.size(); ++I) Indexes[I] = I; - shuffle(Indexes.begin(), Indexes.end(), *RNG_); + shuffle(Indexes.begin(), Indexes.end(), *RNG); for (size_t I = 0; I < StrSz; ++I) { size_t J = Indexes[I]; - // Access the char at EncPtr[i] + // Access the char at EncPtr[I]. Value *EncGEP = IRB.CreateGEP( IRB.getInt8Ty(), IRB.CreatePointerCast(EncPtr, IRB.getInt8PtrTy()), IRB.getInt32(J)); - // Load the encoded char + // Load the encoded char. LoadInst *EncVal = IRB.CreateLoad(IRB.getInt8Ty(), EncGEP); - addMetadata(*EncVal, MetaObf(PROTECT_FIELD_ACCESS)); + addMetadata(*EncVal, MetaObf(ProtectFieldAccess)); Value *DecodedGEP = IRB.CreateGEP(IRB.getInt8Ty(), ClearBuffer, IRB.getInt32(J)); StoreInst *StoreKey = IRB.CreateStore(ConstantInt::get(IRB.getInt8Ty(), (*Key)[J]), DecodedGEP, /* volatile */ true); - addMetadata(*StoreKey, { - MetaObf(PROTECT_FIELD_ACCESS), - MetaObf(OPAQUE_CST), + MetaObf(ProtectFieldAccess), + MetaObf(OpaqueCst), }); LoadInst *KeyVal = IRB.CreateLoad(IRB.getInt8Ty(), DecodedGEP); - addMetadata(*KeyVal, MetaObf(PROTECT_FIELD_ACCESS)); + addMetadata(*KeyVal, MetaObf(ProtectFieldAccess)); - // Decode the value with a xor + // Decode the value with a xor. Value *DecVal = IRB.CreateXor(KeyVal, EncVal); if (auto *Op = dyn_cast(DecVal)) - addMetadata(*Op, MetaObf(OPAQUE_OP, 2llu)); + addMetadata(*Op, MetaObf(OpaqueOp, 2LLU)); - // Store the value + // Store the value. StoreInst *StoreClear = IRB.CreateStore(DecVal, DecodedGEP, /* volatile */ true); - addMetadata(*StoreClear, MetaObf(PROTECT_FIELD_ACCESS)); + addMetadata(*StoreClear, MetaObf(ProtectFieldAccess)); } I.setOperand(Op.getOperandNo(), ClearBuffer); @@ -438,136 +434,143 @@ bool StringEncoding::injectOnStack(BasicBlock &BB, Instruction &I, Use &Op, bool StringEncoding::injectOnStackLoop( BasicBlock &BB, Instruction &I, Use &Op, GlobalVariable &G, - ConstantDataSequential &data, const StringEncoding::EncodingInfo &info) { - auto *Key = std::get_if(&info.key); - if (Key == nullptr) + ConstantDataSequential &Data, const StringEncoding::EncodingInfo &Info) { + auto *Key = std::get_if(&Info.Key); + if (!Key) fatalError("String stack decoding loop is expecting a buffer as a key! "); - uint64_t StrSz = data.getRawDataValues().size(); - + uint64_t StrSz = Data.getRawDataValues().size(); SDEBUG("Key for 0x{:010x}", *Key); - CallInst *Callee = - createDecodingTrampoline(G, Op, &I, *Key, StrSz, info, true); - inline_wlist_.push_back(Callee); - + auto *Callee = createDecodingTrampoline(G, Op, &I, *Key, StrSz, Info, true); + ToInline.push_back(Callee); return true; } bool StringEncoding::process(BasicBlock &BB, Instruction &I, Use &Op, - GlobalVariable &G, ConstantDataSequential &data, - StringEncodingOpt &opt) { - bool Changed = std::visit(overloaded { - [&] (StringEncOptSkip& ) { return false; }, - [&] (StringEncOptStack& stack) { return processOnStack(BB, I, Op, G, data, stack); }, - [&] (StringEncOptGlobal& global) { return processGlobal(BB, I, Op, G, data, global); }, - [&] (StringEncOptReplace& rep) { return processReplace(BB, I, Op, G, data, rep); }, - [&] (StringEncOptDefault&) { - if (data.getElementByteSize() < 20) { - StringEncOptStack stack{6}; - return processOnStack(BB, I, Op, G, data, stack); - } - StringEncOptGlobal global; - return processGlobal(BB, I, Op, G, data, global); - }, - }, opt); + GlobalVariable &G, ConstantDataSequential &Data, + StringEncodingOpt &Opt) { + bool Changed = + std::visit(overloaded{ + [&](StringEncOptSkip &) { return false; }, + [&](StringEncOptStack &Stack) { + return processOnStack(BB, I, Op, G, Data, Stack); + }, + [&](StringEncOptGlobal &Global) { + return processGlobal(BB, I, Op, G, Data, Global); + }, + [&](StringEncOptReplace &Rep) { + return processReplace(BB, I, Op, G, Data, Rep); + }, + [&](StringEncOptDefault &) { + if (Data.getElementByteSize() < 20) { + StringEncOptStack Stack{6}; + return processOnStack(BB, I, Op, G, Data, Stack); + } + StringEncOptGlobal Global; + return processGlobal(BB, I, Op, G, Data, Global); + }, + }, + Opt); return Changed; } -bool StringEncoding::processReplace(BasicBlock &BB, Instruction &, Use &, +bool StringEncoding::processReplace(BasicBlock &BB, Instruction &I, Use &Op, GlobalVariable &G, - ConstantDataSequential &data, - StringEncOptReplace &rep) { - SDEBUG("Replacing {} with {}", data.getAsString().str(), rep.newString); - const size_t Inlen = rep.newString.size(); - - if (rep.newString.size() < data.getNumElements()) { - SWARN("'{}' is smaller than the original string. It will be padded with 0", rep.newString); - rep.newString = - rep.newString.append(data.getNumElements() - Inlen - 1, '\0'); + ConstantDataSequential &Data, + StringEncOptReplace &Rep) { + auto &New = Rep.NewString; + const size_t InLen = New.size(); + SDEBUG("Replacing {} with {}", Data.getAsString().str(), New); + + if (New.size() < Data.getNumElements()) { + SWARN("'{}' is smaller than the original string. It will be padded with 0", + New); + New = New.append(Data.getNumElements() - InLen - 1, '\0'); } else { - SWARN("'{}' is lager than the original string. It will be shrinked to fit the original data", rep.newString); - rep.newString = rep.newString.substr(0, data.getNumElements() - 1); + SWARN("'{}' is larger than the original string. It will be shrinked to fit " + "the original data", + New); + New = New.substr(0, Data.getNumElements() - 1); } - Constant *NewVal = - ConstantDataArray::getString(BB.getContext(), rep.newString); + Constant *NewVal = ConstantDataArray::getString(BB.getContext(), New); G.setInitializer(NewVal); - gve_info_.insert({&G, EncodingInfo(EncodingTy::REPLACE)}); + GVarEncInfo.insert({&G, EncodingInfo(EncodingTy::Replace)}); return true; } bool StringEncoding::processGlobal(BasicBlock &BB, Instruction &I, Use &Op, GlobalVariable &G, - ConstantDataSequential &data, - StringEncOptGlobal &global) { + ConstantDataSequential &Data, + StringEncOptGlobal &Global) { Module *M = BB.getModule(); - StringRef Str = data.getRawDataValues(); + LLVMContext &Ctx = BB.getContext(); + StringRef Str = Data.getRawDataValues(); uint64_t StrSz = Str.size(); - std::uniform_int_distribution Dist(1, std::numeric_limits::max()); - uint64_t Key = Dist(*RNG_); + size_t Max = std::numeric_limits::max(); + std::uniform_int_distribution Dist(1, Max); + uint64_t Key = Dist(*RNG); SDEBUG("Key for {}: 0x{:010x}", Str.str(), Key); std::vector Encoded(StrSz); - EncodingInfo EI(EncodingTy::GLOBAL); - EI.key = Key; - genRoutines(Triple(BB.getModule()->getTargetTriple()), EI, BB.getContext()); - - auto JIT = StringEncoding::HOSTJIT->compile(*EI.HM); - if (auto E = JIT->lookup("encode")) { - auto Enc = reinterpret_cast(E->getValue()); - Enc(Encoded.data(), Str.data(), Key, StrSz); + EncodingInfo EI(EncodingTy::Global); + EI.Key = Key; + genRoutines(Triple(M->getTargetTriple()), EI, Ctx); + + auto JIT = StringEncoding::HostJIT->compile(*EI.HM); + if (auto EncodeSym = JIT->lookup("encode")) { + auto EncodeFn = reinterpret_cast(EncodeSym->getValue()); + EncodeFn(Encoded.data(), Str.data(), Key, StrSz); } else { - fatalError("Can't find 'encode' in the routine"); + fatalError("Cannot find encode routine"); } - Constant *StrEnc = ConstantDataArray::get(BB.getContext(), Encoded); + Constant *StrEnc = ConstantDataArray::get(Ctx, Encoded); G.setConstant(false); G.setInitializer(StrEnc); - // Now create a module constructor for the encoded string - // FType: void decode(); - LLVMContext& Ctx = BB.getContext(); - FunctionType* FTy = FunctionType::get(Type::getVoidTy(BB.getContext()), - /* no args */{}, /* no var args */ false); + + // Now create a module constructor for the encoded string. + FunctionType *FTy = FunctionType::get(Type::getVoidTy(Ctx), /* no args */ {}, + /* no var args */ false); + unsigned GlobalIDHashVal = - llvm::stable_hash_combine_string(G.getGlobalIdentifier()); - unsigned HashCombinedVal = - llvm::stable_hash_combine(GlobalIDHashVal, StrSz, Key); - std::string Name = "__omvll_ctor_" + utostr(HashCombinedVal); + stable_hash_combine_string(G.getGlobalIdentifier()); + unsigned HashCombinedVal = stable_hash_combine(GlobalIDHashVal, StrSz, Key); + std::string Name = CtorPrefixName + utostr(HashCombinedVal); FunctionCallee FCallee = M->getOrInsertFunction(Name, FTy); - auto* F = cast(FCallee.getCallee()); - F->setLinkage(llvm::GlobalValue::PrivateLinkage); + auto *F = cast(FCallee.getCallee()); + F->setLinkage(GlobalValue::PrivateLinkage); auto *EntryBB = BasicBlock::Create(Ctx, "entry", F); ReturnInst::Create(Ctx, EntryBB); - CallInst *Callee = + auto *Callee = createDecodingTrampoline(G, Op, &*EntryBB->begin(), Key, StrSz, EI); - ctor_.push_back(F); - inline_wlist_.push_back(Callee); + Ctors.push_back(F); + ToInline.push_back(Callee); + GVarEncInfo.insert({&G, std::move(EI)}); - gve_info_.insert({&G, std::move(EI)}); return true; } void StringEncoding::genRoutines(const Triple &TargetTriple, EncodingInfo &EI, LLVMContext &Ctx) { Triple HostTriple(sys::getProcessTriple()); + if (!HostJIT) + HostJIT = new Jitter(HostTriple.getTriple()); - if (HOSTJIT == nullptr) - HOSTJIT = new Jitter(HostTriple.getTriple()); - - std::uniform_int_distribution Dist(0, ROUTINES.size() - 1); - size_t idx = Dist(*RNG_); - StringRef R = ROUTINES[idx]; + std::uniform_int_distribution Dist(0, Routines.size() - 1); + size_t Idx = Dist(*RNG); ExitOnError ExitOnErr("Nested clang invocation failed: "); - std::string Routine = (Twine("extern \"C\" {\n") + R + "}\n").str(); + std::string Routine = + (Twine("extern \"C\" {\n") + Routines[Idx] + "}\n").str(); { EI.HM = ExitOnErr( - generateModule(Routine, HostTriple, "cpp", HOSTJIT->getContext(), + generateModule(Routine, HostTriple, "cpp", HostJIT->getContext(), {"-std=c++17", "-mllvm", "--opaque-pointers"})); } @@ -581,76 +584,74 @@ void StringEncoding::genRoutines(const Triple &TargetTriple, EncodingInfo &EI, } } -void StringEncoding::annotateRoutine(llvm::Module& M) { - for (Function& F : M) { - for (BasicBlock& BB : F) { - for (Instruction& I : BB) { - if (auto* Load = dyn_cast(&I)) { - addMetadata(*Load, MetaObf(PROTECT_FIELD_ACCESS)); - } - else if (auto* Store = dyn_cast(&I)) { - addMetadata(*Store, MetaObf(PROTECT_FIELD_ACCESS)); - } - else if (auto* BinOp = dyn_cast(&I)) { - if (Arithmetic::isSupported(*BinOp)) { - addMetadata(*BinOp, MetaObf(OPAQUE_OP, 2llu)); - } - } +void StringEncoding::annotateRoutine(Module &M) { + for (Function &F : M) { + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + if (auto *Load = dyn_cast(&I)) + addMetadata(*Load, MetaObf(ProtectFieldAccess)); + else if (auto *Store = dyn_cast(&I)) + addMetadata(*Store, MetaObf(ProtectFieldAccess)); + else if (auto *BinOp = dyn_cast(&I); + BinOp && Arithmetic::isSupported(*BinOp)) + addMetadata(*BinOp, MetaObf(OpaqueOp, 2LLU)); } } } } -bool StringEncoding::processOnStackLoop(BasicBlock& BB, Instruction& I, Use& Op, GlobalVariable& G, - ConstantDataSequential& data) -{ - StringRef Str = data.getRawDataValues(); +bool StringEncoding::processOnStackLoop(BasicBlock &BB, Instruction &I, Use &Op, + GlobalVariable &G, + ConstantDataSequential &Data) { + LLVMContext &Ctx = BB.getContext(); + StringRef Str = Data.getRawDataValues(); uint64_t StrSz = Str.size(); - std::uniform_int_distribution Dist(1, std::numeric_limits::max()); - uint64_t key = Dist(*RNG_); + size_t Max = std::numeric_limits::max(); + std::uniform_int_distribution Dist(1, Max); + uint64_t Key = Dist(*RNG); - SDEBUG("Key for {}: 0x{:010x}", Str.str(), key); + SDEBUG("Key for {}: 0x{:010x}", Str.str(), Key); std::vector Encoded(StrSz); - EncodingInfo EI(EncodingTy::STACK_LOOP); - EI.key = key; + EncodingInfo EI(EncodingTy::StackLoop); + EI.Key = Key; - genRoutines(Triple(BB.getModule()->getTargetTriple()), EI, BB.getContext()); + genRoutines(Triple(BB.getModule()->getTargetTriple()), EI, Ctx); - auto JIT = StringEncoding::HOSTJIT->compile(*EI.HM); - if (auto E = JIT->lookup("encode")) { - auto Enc = reinterpret_cast(E->getValue()); - Enc(Encoded.data(), Str.data(), key, StrSz); + auto JIT = StringEncoding::HostJIT->compile(*EI.HM); + if (auto EncodeSym = JIT->lookup("encode")) { + auto EncodeFn = reinterpret_cast(EncodeSym->getValue()); + EncodeFn(Encoded.data(), Str.data(), Key, StrSz); } else { - fatalError("Can't find 'encode' in the routine"); + fatalError("Cannot find encode routine"); } - Constant *StrEnc = ConstantDataArray::get(BB.getContext(), Encoded); + Constant *StrEnc = ConstantDataArray::get(Ctx, Encoded); G.setInitializer(StrEnc); - auto It = gve_info_.insert({&G, std::move(EI)}).first; - return injectOnStackLoop( - BB, I, Op, G, *dyn_cast(StrEnc), It->getSecond()); + auto It = GVarEncInfo.insert({&G, std::move(EI)}).first; + return injectOnStackLoop(BB, I, Op, G, *cast(StrEnc), + It->getSecond()); } bool StringEncoding::processOnStack(BasicBlock &BB, Instruction &I, Use &Op, GlobalVariable &G, - ConstantDataSequential &data, - const StringEncOptStack &stack) { - std::uniform_int_distribution Dist(1, 254); - StringRef Str = data.getRawDataValues(); + ConstantDataSequential &Data, + const StringEncOptStack &Stack) { + StringRef Str = Data.getRawDataValues(); uint64_t StrSz = Str.size(); + std::uniform_int_distribution Dist(1, 254); SDEBUG("[{}] {}: {}", name(), BB.getParent()->getName(), - data.isCString() ? data.getAsCString() : ""); + Data.isCString() ? Data.getAsCString() : ""); - if (StrSz >= stack.loopThreshold) - return processOnStackLoop(BB, I, Op, G, data); + if (StrSz >= Stack.LoopThreshold) + return processOnStackLoop(BB, I, Op, G, Data); std::vector Encoded(StrSz); std::vector Key(StrSz); std::generate(std::begin(Key), std::end(Key), - [&Dist, this]() { return Dist(*RNG_); }); + [&Dist, this]() { return Dist(*RNG); }); for (size_t I = 0; I < StrSz; ++I) Encoded[I] = static_cast(Str[I]) ^ static_cast(Key[I]); @@ -658,11 +659,11 @@ bool StringEncoding::processOnStack(BasicBlock &BB, Instruction &I, Use &Op, Constant *StrEnc = ConstantDataArray::get(BB.getContext(), Encoded); G.setInitializer(StrEnc); - EncodingInfo EI(EncodingTy::STACK); - EI.key = std::move(Key); - auto It = gve_info_.insert({&G, std::move(EI)}).first; - return injectOnStack(BB, I, Op, G, *dyn_cast(StrEnc), + EncodingInfo EI(EncodingTy::Stack); + EI.Key = std::move(Key); + auto It = GVarEncInfo.insert({&G, std::move(EI)}).first; + return injectOnStack(BB, I, Op, G, *cast(StrEnc), It->getSecond()); } -} // namespace omvll +} // end namespace omvll diff --git a/src/test/core/plugin/Inputs/config_empty.py b/src/test/core/plugin/Inputs/config_empty.py index fb51ddd5..4284cf1a 100644 --- a/src/test/core/plugin/Inputs/config_empty.py +++ b/src/test/core/plugin/Inputs/config_empty.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from functools import lru_cache diff --git a/src/test/core/plugin/find-omvll-config-cwd.c b/src/test/core/plugin/find-omvll-config-cwd.c index edbf3a1c..536b6905 100644 --- a/src/test/core/plugin/find-omvll-config-cwd.c +++ b/src/test/core/plugin/find-omvll-config-cwd.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // Prepare test output dir with yml-config, py-config and fake python-libs dir // RUN: rm -rf %T_cwd // RUN: mkdir -p %T_cwd diff --git a/src/test/core/plugin/find-omvll-config-plugindir.c b/src/test/core/plugin/find-omvll-config-plugindir.c index 4d498788..b4e0adf3 100644 --- a/src/test/core/plugin/find-omvll-config-plugindir.c +++ b/src/test/core/plugin/find-omvll-config-plugindir.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // Prepare test output dir with plugin, yml-config, py-config and fake python-libs dir // RUN: rm -rf %T_plugindir // RUN: mkdir -p %T_plugindir diff --git a/src/test/lit.cfg.py b/src/test/lit.cfg.py index 5c50086e..881732ef 100644 --- a/src/test/lit.cfg.py +++ b/src/test/lit.cfg.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import os import lit import sys diff --git a/src/test/lit.site.cfg.py.in b/src/test/lit.site.cfg.py.in index 079d8096..c107dfd4 100644 --- a/src/test/lit.site.cfg.py.in +++ b/src/test/lit.site.cfg.py.in @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import lit.llvm config.omvll_plugin_dir = "@PROJECT_BINARY_DIR@" diff --git a/src/test/passes/arithmetic/config_rounds_0.py b/src/test/passes/arithmetic/config_rounds_0.py index 8cc7379c..aa82ff64 100644 --- a/src/test/passes/arithmetic/config_rounds_0.py +++ b/src/test/passes/arithmetic/config_rounds_0.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from functools import lru_cache diff --git a/src/test/passes/arithmetic/config_rounds_1.py b/src/test/passes/arithmetic/config_rounds_1.py index 26eafcfe..d2a0f23e 100644 --- a/src/test/passes/arithmetic/config_rounds_1.py +++ b/src/test/passes/arithmetic/config_rounds_1.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from functools import lru_cache diff --git a/src/test/passes/arithmetic/config_rounds_2.py b/src/test/passes/arithmetic/config_rounds_2.py index 5a1798f3..34ef8a60 100644 --- a/src/test/passes/arithmetic/config_rounds_2.py +++ b/src/test/passes/arithmetic/config_rounds_2.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from functools import lru_cache diff --git a/src/test/passes/arithmetic/config_rounds_3.py b/src/test/passes/arithmetic/config_rounds_3.py index c6fb6e32..c96bc297 100644 --- a/src/test/passes/arithmetic/config_rounds_3.py +++ b/src/test/passes/arithmetic/config_rounds_3.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from functools import lru_cache diff --git a/src/test/passes/arithmetic/report_diff-x86_64-linux.c b/src/test/passes/arithmetic/report_diff-x86_64-linux.c index 3f03e0b8..20970a92 100644 --- a/src/test/passes/arithmetic/report_diff-x86_64-linux.c +++ b/src/test/passes/arithmetic/report_diff-x86_64-linux.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // REQUIRES: x86-registered-target // RUN: env OMVLL_CONFIG=%S/report_diff.py clang -target x86_64-pc-linux-gnu -fpass-plugin=%libOMVLL -O1 -S %s -o /dev/null -DNEEDS_OBFUSCATION | FileCheck %s diff --git a/src/test/passes/arithmetic/report_diff.py b/src/test/passes/arithmetic/report_diff.py index aca7c792..6367cf19 100644 --- a/src/test/passes/arithmetic/report_diff.py +++ b/src/test/passes/arithmetic/report_diff.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from difflib import unified_diff from functools import lru_cache diff --git a/src/test/passes/arithmetic/xor-aarch64-android.c b/src/test/passes/arithmetic/xor-aarch64-android.c index 29132ee6..b949fe98 100644 --- a/src/test/passes/arithmetic/xor-aarch64-android.c +++ b/src/test/passes/arithmetic/xor-aarch64-android.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // REQUIRES: aarch64-registered-target // XFAIL: host-platform-macOS diff --git a/src/test/passes/arithmetic/xor-aarch64-ios.c b/src/test/passes/arithmetic/xor-aarch64-ios.c index 58823ca1..09ebfc7c 100644 --- a/src/test/passes/arithmetic/xor-aarch64-ios.c +++ b/src/test/passes/arithmetic/xor-aarch64-ios.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // REQUIRES: aarch64-registered-target // RUN: clang -target arm64-apple-ios -O1 -fno-verbose-asm -S %s -o - | FileCheck --check-prefix=R0 %s diff --git a/src/test/passes/arithmetic/xor-x86_64-darwin.c b/src/test/passes/arithmetic/xor-x86_64-darwin.c index 75fa82a8..31c0ed4a 100644 --- a/src/test/passes/arithmetic/xor-x86_64-darwin.c +++ b/src/test/passes/arithmetic/xor-x86_64-darwin.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // REQUIRES: x86-registered-target // RUN: clang -target x86_64-apple-darwin -O1 -S %s -o - | FileCheck --check-prefix=R0 %s diff --git a/src/test/passes/arithmetic/xor-x86_64-linux.c b/src/test/passes/arithmetic/xor-x86_64-linux.c index c0f92b41..08542d67 100644 --- a/src/test/passes/arithmetic/xor-x86_64-linux.c +++ b/src/test/passes/arithmetic/xor-x86_64-linux.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // REQUIRES: x86-registered-target // RUN: clang -target x86_64-pc-linux-gnu -O1 -S %s -o - | FileCheck --check-prefix=R0 %s diff --git a/src/test/passes/break-cfg/basic-aarch64-ios.c b/src/test/passes/break-cfg/basic-aarch64-ios.c index 96d27a71..af9ad46b 100644 --- a/src/test/passes/break-cfg/basic-aarch64-ios.c +++ b/src/test/passes/break-cfg/basic-aarch64-ios.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // REQUIRES: aarch64-registered-target // UNSUPPORTED: system-{{.*}} diff --git a/src/test/passes/break-cfg/config_all.py b/src/test/passes/break-cfg/config_all.py index 149959e7..dbd9008e 100644 --- a/src/test/passes/break-cfg/config_all.py +++ b/src/test/passes/break-cfg/config_all.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from functools import lru_cache diff --git a/src/test/passes/flattening/basic-aarch64.c b/src/test/passes/cfg-flattening/basic-aarch64.c similarity index 85% rename from src/test/passes/flattening/basic-aarch64.c rename to src/test/passes/cfg-flattening/basic-aarch64.c index 54c5d3fa..de9d2472 100644 --- a/src/test/passes/flattening/basic-aarch64.c +++ b/src/test/passes/cfg-flattening/basic-aarch64.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // REQUIRES: aarch64-registered-target // TODO: Add CHECK lines @@ -7,7 +12,7 @@ // RUN: clang -target arm64-apple-ios -O1 -fno-verbose-asm -S %s -o - // RUN: env OMVLL_CONFIG=%S/config_all.py clang -target arm64-apple-ios -fpass-plugin=%libOMVLL -O1 -fno-verbose-asm -S %s -o - -int check_password(const char* passwd, unsigned len) { +int check_password(const char *passwd, unsigned len) { if (len != 5) { return 0; } diff --git a/src/test/passes/flattening/basic-native.c b/src/test/passes/cfg-flattening/basic-native.c similarity index 88% rename from src/test/passes/flattening/basic-native.c rename to src/test/passes/cfg-flattening/basic-native.c index 8dcd8876..69f68c4e 100644 --- a/src/test/passes/flattening/basic-native.c +++ b/src/test/passes/cfg-flattening/basic-native.c @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // TODO: Require that the native target is registered. // TODO: Make sure Clang finds a linker on our host machine. @@ -13,7 +18,7 @@ // RUN: %T/basic-native-obf right // RUN: not %T/basic-native-obf wrong -int check_password(const char* passwd) { +int check_password(const char *passwd) { if (passwd[0] == 'r') { if (passwd[1] == 'i') { if (passwd[2] == 'g') { diff --git a/src/test/passes/flattening/config_all.py b/src/test/passes/cfg-flattening/config_all.py similarity index 78% rename from src/test/passes/flattening/config_all.py rename to src/test/passes/cfg-flattening/config_all.py index 5fee4fa7..8df88c07 100644 --- a/src/test/passes/flattening/config_all.py +++ b/src/test/passes/cfg-flattening/config_all.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from functools import lru_cache diff --git a/src/test/passes/opaque-constants/config_all.py b/src/test/passes/opaque-constants/config_all.py index 67fa7982..f3f58b3b 100644 --- a/src/test/passes/opaque-constants/config_all.py +++ b/src/test/passes/opaque-constants/config_all.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from functools import lru_cache diff --git a/src/test/passes/opaque-constants/opaque-constants-infinite-loop.ll b/src/test/passes/opaque-constants/opaque-constants-infinite-loop.ll index da249528..05137d4b 100644 --- a/src/test/passes/opaque-constants/opaque-constants-infinite-loop.ll +++ b/src/test/passes/opaque-constants/opaque-constants-infinite-loop.ll @@ -1,3 +1,7 @@ +; +; This file is distributed under the Apache License v2.0. See LICENSE for details. +; + ; RUN: env OMVLL_CONFIG=%S/config_all.py clang -target aarch64-linux-android -fpass-plugin=%libOMVLL -O0 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=O0 ; RUN: env OMVLL_CONFIG=%S/config_all.py clang -target aarch64-linux-android -fpass-plugin=%libOMVLL -O1 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=O1 ; RUN: env OMVLL_CONFIG=%S/config_all.py clang -target arm64-apple-ios -fpass-plugin=%libOMVLL -O0 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=O0 diff --git a/src/test/passes/strings-encoding/basic-aarch64-ce-init.ll b/src/test/passes/string-encoding/basic-aarch64-ce-init.ll similarity index 88% rename from src/test/passes/strings-encoding/basic-aarch64-ce-init.ll rename to src/test/passes/string-encoding/basic-aarch64-ce-init.ll index 67310292..ae888ef9 100644 --- a/src/test/passes/strings-encoding/basic-aarch64-ce-init.ll +++ b/src/test/passes/string-encoding/basic-aarch64-ce-init.ll @@ -1,3 +1,7 @@ +; +; This file is distributed under the Apache License v2.0. See LICENSE for details. +; + ; REQUIRES: aarch64-registered-target ; RUN: env OMVLL_CONFIG=%S/config_replace.py clang++ -fpass-plugin=%libOMVLL \ diff --git a/src/test/passes/strings-encoding/basic-aarch64.cpp b/src/test/passes/string-encoding/basic-aarch64.cpp similarity index 86% rename from src/test/passes/strings-encoding/basic-aarch64.cpp rename to src/test/passes/string-encoding/basic-aarch64.cpp index 98c678bb..924f5c8e 100644 --- a/src/test/passes/strings-encoding/basic-aarch64.cpp +++ b/src/test/passes/string-encoding/basic-aarch64.cpp @@ -1,3 +1,8 @@ +// +// This file is distributed under the Apache License v2.0. See LICENSE for +// details. +// + // REQUIRES: aarch64-registered-target // The default object contains the file-name string: @@ -28,9 +33,10 @@ // CHECK-REPLACED-NOT: [[FILE_NAME]] extern void *stderr; -extern int fprintf(void * __stream, const char *__format, ...); +extern int fprintf(void *__stream, const char *__format, ...); -#define LOG_ERROR(MSG) fprintf(stderr, "Error: %s (%s:%d)\n", MSG, __FILE__, __LINE__) +#define LOG_ERROR(MSG) \ + fprintf(stderr, "Error: %s (%s:%d)\n", MSG, __FILE__, __LINE__) bool check_code(int code) { if (code != 2) { diff --git a/src/test/passes/strings-encoding/basic-ios-swift.ll b/src/test/passes/string-encoding/basic-ios-swift.ll similarity index 90% rename from src/test/passes/strings-encoding/basic-ios-swift.ll rename to src/test/passes/string-encoding/basic-ios-swift.ll index 85c5db7d..82465a7c 100644 --- a/src/test/passes/strings-encoding/basic-ios-swift.ll +++ b/src/test/passes/string-encoding/basic-ios-swift.ll @@ -1,3 +1,7 @@ +; +; This file is distributed under the Apache License v2.0. See LICENSE for details. +; + ; REQUIRES: aarch64-registered-target ; The 'replace' configuration encodes the string and adds logic that decodes it at load-time: diff --git a/src/test/passes/strings-encoding/config_remove.py b/src/test/passes/string-encoding/config_remove.py similarity index 100% rename from src/test/passes/strings-encoding/config_remove.py rename to src/test/passes/string-encoding/config_remove.py diff --git a/src/test/passes/strings-encoding/config_replace.py b/src/test/passes/string-encoding/config_replace.py similarity index 85% rename from src/test/passes/strings-encoding/config_replace.py rename to src/test/passes/string-encoding/config_replace.py index 561a173c..a3dc1272 100644 --- a/src/test/passes/strings-encoding/config_replace.py +++ b/src/test/passes/string-encoding/config_replace.py @@ -1,3 +1,7 @@ +# +# This file is distributed under the Apache License v2.0. See LICENSE for details. +# + import omvll from functools import lru_cache