O-MVLL Documentation

repository·main·Indexed 21 days ago

https://github.com/open-obfuscator/o-mvll

An LLVM-based obfuscator that leverages Python and the LLVM pass manager for advanced code obfuscation. It supports AArch64 and ARM architectures via Android NDK and iOS toolchains. O-MVLL provides a Python API for fine-grained control over obfuscation passes, including Control Flow Flattening and arithmetic obfuscation, and can be integrated as a Clang plugin using the -fpass-plugin flag.

Tokens
9.6K
Snippets
29
Records
36
Agent score
72%

What's inside O-MVLL

  1. What is O-MVLL

    main
    O-MVLL is an LLVM-based obfuscator driven by Python and the LLVM pass manager. It is designed to provide obfuscation capabilities by integrating with the LLVM toolchain. It currently supports AArch64 and ARM architectures and can be used with the Android NDK and iOS toolchains.
  2. Drive obfuscation passes via source annotations

    main

    Instead of managing lists of function names in Python, you can opt functions into or out of specific obfuscation passes directly in C/C++ source code using __attribute__((annotate("..."))).

    To make this work, you must use omvll.ObfuscationConfig.default_config within your config class and provide a matching string via the annotation keyword.

    Precedence (Strongest to Weakest):

    1. module_excludes
    2. function_excludes
    3. Negated annotation !<annotation>
    4. function_includes
    5. Annotation <annotation>
    6. probability

    Note: A negated annotation (e.g., !<flattening>) always wins, even over explicit includes or 100% probability.

    // Opt into arithmetic obfuscation
    __attribute__((annotate("arithmetic"))) 
    // Opt into control-flow flattening
    __attribute__((annotate("flattening"))) 
    int verify_license(const char *token) { ... }
    
    // Opt out of flattening, even if included in a list
    __attribute__((annotate("!flattening"))) 
    int hot_path(void) { ... }
    # In your ObfuscationConfig implementation:
    def obfuscate_arithmetic(self, mod, func):
        if omvll.ObfuscationConfig.default_config(self, mod, func, annotation="arithmetic"):
            return omvll.ArithmeticOpt(rounds=2)
        return omvll.ArithmeticOpt(False)
  3. Understand LLVM IR objects in O-MVLL callbacks

    main

    When writing custom obfuscation logic via omvll.ObfuscationConfig callbacks, you can access LLVM IR objects such as Module, Function, Struct, and GlobalVariable.

    Important: These objects are read-only. O-MVLL does not allow mutating the Intermediate Representation (IR) directly through these Python bindings. They are provided primarily for inspection and metadata retrieval during the obfuscation process.

  4. Configure Pass Phases for obfuscation

    main

    The pass_phases attribute in omvll.config allows you to control the execution timing of specific obfuscation passes. It accepts a dictionary where:

    • Keys are members of the omvll.Pass enumeration (representing specific obfuscation transformations).
    • Values are sets of members from the omvll.Phase enumeration (representing the stages of the compilation/obfuscation pipeline, such as omvll.Phase.Early).

    This allows you to pin certain passes to specific lifecycle stages of the O-MVLL process.

    omvll.config.pass_phases = {
        omvll.Pass.Arithmetic: {omvll.Phase.Early},
    }
  5. How O-MVLL API documentation is generated

    main

    O-MVLL API documentation is generated using Sphinx with the autodoc extension. The documentation is not manually written in .rst files; instead, it is pulled directly from the C++ docstrings embedded in the pybind11 bindings.

    To update or add documentation for a class, method, or attribute, you must edit the corresponding C++ docstring in the source code rather than the .rst files in doc/src/.

    Key source files for API docstrings:

    • src/core/python/PyConfig.cpp: Contains omvll.OMVLLConfig, omvll.Phase / omvll.Pass enums, and omvll.ObfuscationConfig callbacks.
    • src/core/python/pyobf_opt.cpp: Contains the *Opt option classes.
    • src/core/python/pyllvm.cpp: Contains Module, Function, Struct, and GlobalVariable.

    Manual documentation in .rst files is reserved for items that are not class members, such as the omvll.config instance attribute, configuration Template (doc/src/config.rst, doc/src/obfuscation.rst), and version attributes (doc/src/versioning.rst).

  6. Configure global O-MVLL settings via omvll_get_config()

    main

    To apply global settings across the O-MVLL pipeline, modify the attributes of the omvll.config instance within the omvll_get_config() function. This function should return your specific configuration object (e.g., an instance of omvll.ObfuscationConfig). Using @lru_cache(maxsize=1) is recommended to ensure the global configuration is initialized once and reused.

    Available global configuration attributes in omvll.OMVLLConfig include:

    • shuffle_functions: Boolean to enable/disable function shuffling.
    • inline_jni_wrappers: Boolean to enable/disable inlining of JNI wrappers.
    • global_mod_exclude: List of modules to exclude from global obfuscation.
    • global_func_exclude: List of functions to exclude from global obfuscation.
    • probability_seed: Seed for probabilistic obfuscation passes.
    • output_folder: Destination directory for obfuscated outputs.
    • pass_phases: A mapping that defines which obfuscation passes run during specific execution phases.
    @lru_cache(maxsize=1)
    def omvll_get_config() -> omvll.ObfuscationConfig:
        omvll.config.shuffle_functions = True
        omvll.config.inline_jni_wrappers = True
        omvll.config.pass_phases = {
            omvll.Pass.Arithmetic: {omvll.Phase.Early},
        }
        return MyConfig()
  7. Build the O-MVLL documentation on Linux / Docker

    main

    In CI environments (Linux/Docker), you must build O-MVLL with the -DOMVLL_PY_STANDALONE=1 flag to produce an importable Python extension module (omvll.so). This allows Sphinx autodoc to introspect the module. Use the OMVLL_STANDALONE_DIR environment variable to point Sphinx to the location of the compiled .so file.

    cmake -GNinja -S src -B /standalone -DOMVLL_PY_STANDALONE=1 -DCMAKE_BUILD_TYPE=Release ...
    ninja -C /standalone                       # → /standalone/omvll.so
    OMVLL_STANDALONE_DIR=/standalone sphinx-build -b html doc doc/_build/html
  8. Install the omvll Python package for code completion

    main

    To enable code completion while working with O-MVLL, install the omvll package from PyPI using pip.

    Note: The omvll package only provides Python typing objects to facilitate IDE autocompletion and does not contain the actual obfuscation logic.

    $ python -m pip install [--user] omvll
  9. Build the O-MVLL documentation on local macOS

    main

    Building documentation on macOS requires Python 3.10 (due to pybind11 compatibility constraints) and a prebuilt dependency tree (LLVM 21.1.6, pybind11, spdlog).

    Follow these steps:

    1. Setup Sphinx environment: Create a virtual environment using Python 3.10 and install sphinx and furo.
    2. Build the standalone module: Use cmake with -DOMVLL_PY_STANDALONE=1. You must provide paths to the downloaded dependencies (LLVM, pybind11, spdlog) and the Python 3.10 framework. On macOS, this produces build/omvll.so.
    3. Generate HTML: Run sphinx-build while setting OMVLL_STANDALONE_DIR to your build directory.
    # 1. Sphinx environment (Python 3.10)
    python3.10 -m venv /tmp/omvll-doc-venv
    /tmp/omvll-doc-venv/bin/pip install sphinx furo
    
    # 2. Build the standalone module
    DEPS=~/Workspace/omvll-v1.9.1-macos-deps
    LLVM=$DEPS/LLVM-21.1.6-arm64-Darwin
    PY=/opt/homebrew/opt/python@3.10
    
    cmake -GNinja -S src -B build \
      -DOMVLL_PY_STANDALONE=1 \
      -DCMAKE_BUILD_TYPE=Release \
      -DPython3_EXECUTABLE=$PY/bin/python3.10 \
      -DPython3_INCLUDE_DIR=$PY/Frameworks/Python.framework/Versions/3.10/include/python3.10 \
      -DPython3_LIBRARY=$PY/Frameworks/Python.framework/Versions/3.10/lib/libpython3.10.dylib \
      -Dpybind11_DIR=$DEPS/share/cmake/pybind11 \
      -Dspdlog_DIR=$DEPS/lib/cmake/spdlog \
      -DLLVM_DIR=$LLVM/lib/cmake/llvm \
      -DClang_DIR=$LLVM/lib/cmake/clang
    ninja -C build OMVLL                            # → build/omvll.so
    
    # 3. Generate the HTML
    OMVLL_STANDALONE_DIR=$(pwd)/build \
      /tmp/omvll-doc-venv/bin/python -m sphinx -b html doc doc/_build/html
  10. Understand StringEncoding::EncodingInfo configuration

    main

    The EncodingInfo struct holds the configuration for a specific string encoding operation. It defines the encoding type, the key used for the transformation, and the associated decoding routines.

    Key fields:

    • Type: An EncodingTy value (e.g., Local, Global, Replace).
    • Key: A KeyTy variant which can be:
      • std::monostate (no key).
      • KeyBufferTy (std::vector<uint8_t>) for buffer-based keys.
      • KeyIntTy (uint64_t) for integer-based keys.
    • TM: A pointer to the llvm::Module.
    • EncodeFn: A pointer to the encoding routine function (EncRoutineFn *).
  11. Configure String Encoding types in O-MVLL

    main

    The StringEncoding pass supports four distinct encoding modes via the EncodingTy enum. These modes determine how strings are transformed and how their decoding logic is injected into the module:

    • None: No encoding is applied.
    • Local: String decoding logic is injected locally within the function using the string.
    • Global: String decoding logic is handled via global routines.
    • Replace: Replaces strings with encoded versions (specific behavior governed by StringEncOptReplace).
    enum EncodingTy {
        None = 0,
        Local,
        Global,
        Replace,
      };