Skip to content

Rust obfuscation guide

eskarn edited this page May 13, 2022 · 1 revision

Welcome to the obfuscator-llvm wiki!

Rust obfuscation guide

As rustc is based on LLVM and our obfuscator is a LLVM Plugin you can leverage rustc and cargo to obfuscate your Rust code. Starting with Rust 1.59.0 (stable), we've contributed[^1] to the compiler a way to register any modern LLVM plugin, and insert it in the optimization pipeline.

To compile the LLVM plugin we need compatible LLVM headers, and librairies with the rustc version used to compile Rust code. This can be done with two approaches:

  1. Either use a nightly toolchain, and get the LLVM submodule revision used for the toolchain revision:
    • then clone and build only this LLVM revision
    • use of the nightly toolchain is mandatary to have the "-Z" option
  2. Or clone and build a custom Rust toolchain matching any revision you need (min stable version 1.59.0):
    • usable with stable or unstable
    • it will build the required LLVM version automatically with the same configuration as an official toolchain
    • the built toolchain can be used exclusively for obfuscated projects

For simplicity, the steps below follow the second approach.

[^1]: Add another codegen option pass-plugins 1b3a5f29dd. This commit is necessary to compile Rust code using our obufscation plugin (Rust stable >= 1.59.0 includes these changes)

Prerequisites

You need some packages to compile LLVM and the Rust toolchain:

  • python 3
  • git
  • rustup
  • a C/C++ compiler: gcc or llvm
  • cmake
  • ninja
  • pkg-config
  • libssl-dev / openssl-devel

On debian/ubuntu:

sudo apt install python3 git build-essential curl cmake ninja-build pkg-config libssl-dev

Then install rustup as you prefer (steps are described at rust-lang.org)

Install a nightly Rust toolchain

Install any official nightly Rust toolchain (which doesn't need to be set as default). This is necessary because we need Cargo nightly to invoke a custom toolchain (which we will build in the next step) with the "-Z" option.

Install the toolchain using rustup:

rustup toolchain install nightly
rustup toolchain list -v
# stable-x86_64-unknown-linux-gnu (default)       /home/madeline/.rustup/toolchains/stable-x86_64-unknown-linux-gnu
# nightly-x86_64-unknown-linux-gnu                /home/madeline/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu

Refer to this page for detailed cargo behavior description with a custom toolchain: https://rust-lang.github.io/rustup/concepts/toolchains.html.

1. Build Rust and LLVM

Select any Rust version (in our case 1.60.0), clone and rebuild a custom toolchain:

mkdir ~/tmp && cd ~/tmp
git clone --depth 1 --branch 1.60.0 https://github.com/rust-lang/rust
cd rust
python3 x.py setup user # (this will setup the build env)

(when asked about installing git hooks, just answer no)

Edit config.toml to enable building clang:

[llvm]
clang = true
link-shared = true

Build the compiler and LLVM:

python3 x.py build
# Build completed successfully in 0:24:40

The build result is located at build and we are mostly interested at thoses directories:

  • build/x86_64-unknown-linux-gnu/stage1 The stage1 rust toolchain rustc is going to use. (managed by rustup)
  • build/x86_64-unknown-linux-gnu/llvm/lib/cmake The cmake files we need to use LLVM headers and librairies to compile our plugin.

2. Link the custom toolchain

If you have rustup installed, the freshly built toolchain is already linked to rustup:

rustup toolchain list -v
# stable-x86_64-unknown-linux-gnu (default)       /home/madeline/.rustup/toolchains/stable-x86_64-unknown-linux-gnu
# nightly-x86_64-unknown-linux-gnu                /home/madeline/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu
# stage1                                          /home/madeline/tmp/rust/build/x86_64-unknown-linux-gnu/stage1

If it's not the case you can add it yourself:

rustup toolchain link my-rust $(readlink -f build/x86_64-unknown-linux-gnu/stage1)

3. Build the obufscation plugin

Now use the built Rust LLVM to build the obfuscation plugin:

cd ~/tmp
git clone git@github.com:eshard/obfuscator-llvm.git
cd obfuscator-llvm
mkdir build && cd build
cmake -DLLVM_DIR=~/tmp/rust/build/x86_64-unknown-linux-gnu/llvm/lib/cmake ..
make -j8 && cp libLLVMObfuscator.so /tmp

We have now built the obfuscation plugin libLLVMObfuscator.so.

4. Configure your project to use the obfuscation plugin

Within you project, configure cargo by creating a .cargo/config file. We are going to use -Z (unstable options) and -C (codegen) to respectively use our obufscation plugin and specify which passes[^2] to use for obfuscation.

[target.x86_64-unknown-linux-gnu]
rustflags = [
    "-Z", "llvm-plugins=/tmp/libLLVMObfuscator.so",
    "-C", "passes=flattening bogus",
]

Note: the passes codegen argument can only be specified for module passes, othewise for all cases you can omit it, and specify the pass(es) with an environment variable as such:

export LLVM_OBF_SCALAROPTIMIZERLATE_PASSES="flattening, bogus, substitution, split-basic-blocks"

See the README for a list of all available environment variables.

5. Build an obfuscated Rust project:

Sample command for a fully obfuscated build with all dependencies (including Rust standard library):

cargo +my-rust build -Z build-std=panic_abort,std,core,alloc,proc_macro --target x86_64-unknown-linux-gnu --release

Options explanation:

  • +my-rust allows us to use our custom built toolchain (use +stage1 if you didn't performed rustup toolchain link).
  • -Z build-std option will force rustc to re-build its standard library (instead of using a prebuilt version sometimes available with some toolchains).
  • --target could be any target installed through rustup (such as aarch64-linux-android or armv7-linux-androideabi).