An LLVM module pass that performs identifier mangling at the IR level — randomizing function names, global variable names, and struct type names at compile time to hinder static analysis and reverse engineering.
The pass hooks into LLVM's new pass manager as a module pass — meaning it operates on the entire module at once rather than function by function.
For each module it processes:
- Functions — every non-declaration function except
maingets its name replaced with a 10-character random alphanumeric string - Global variables — all global variable names are replaced
- Struct types — all named struct types are renamed
The randomization uses std::mt19937 seeded from std::random_device, generating strings from a 62-character alphabet (a-z, A-Z, 0-9).
The pass operates at the LLVM IR level, not the source level — which means:
- It's language-agnostic (works on any language that compiles to LLVM IR)
- Symbol renaming is consistent within the module (all references are updated)
- Debug symbols and DWARF info still reference original names unless stripped separately
- LLVM (tested with LLVM 14+)
- CMake ≥ 3.13
- Clang (to compile input code to IR)
git clone https://github.com/Ily455/IM-LLVM-Pass.git
cd IM-LLVM-Pass
mkdir build && cd build
cmake ..
makeThis produces build/ManglePass.so.
clang -S -emit-llvm input.c -o input.llProduce readable IR:
opt -S -load-pass-plugin ./build/ManglePass.so -passes=manglepass input.ll -o output.llProduce bitcode:
opt -load-pass-plugin ./build/ManglePass.so -passes=manglepass input.ll -o output.bcclang output.ll -o outputSee the example/ directory for a full walkthrough.
Input C code with meaningful names:
struct structurino { int iks; int igrig; };
int varstandsforvideoassistantrefereee = 666;
void my_function(int a, int b) { ... }After the pass — all identifiers replaced with random strings at the IR level:
IR diff:
Assembly diff:
The binary remains functionally identical — only the symbol names change.
Identifier mangling is a weak standalone obfuscation. A few things worth knowing:
mainis intentionally preserved (required entry point for the linker)- External library calls (e.g.
printf) are declarations, not definitions — they are not renamed - Debug info (
-g) still embeds original names in DWARF sections — strip separately withllvm-strip --strip-debug - A determined analyst can recover intent through dataflow analysis regardless of symbol names
- This pass is designed as a building block, not a complete obfuscation solution
IM-LLVM-Pass/
├── ManglePass.cpp # Pass implementation
├── CMakeLists.txt # Build configuration
├── LICENSE
└── example/
├── test.c # Sample input
├── test.ll # Normal IR
├── mangled-test.ll # IR after pass
├── normal-assembly.asm
├── mangled-assembly.asm
├── IR-diff.png
└── assembly-diff.png
MIT — see LICENSE.

