本项目旨在通过 实际动手写代码 的方式,系统性学习 LLVM,包括:
- 通过编写一门简单语言来学习 LLVM 前端与 IR
- 编写 LLVM Pass(含 OLLVM 混淆)来掌握 LLVM 中端优化技术
- 通过各种小 demo 熟悉 LLVM API 与 IR 构造方式
这是一个分模块的学习型仓库,适合用于长期积累与完整掌握 LLVM 技术栈。
learn_llvm/
├── CMakeLists.txt # 顶层 CMake,统一管理三大模块
├── build/ # 构建输出目录(生成的 bin/lib 都在这里)
│
├── learn-demo/ # 零散的小实验、API 演示、IR 构造练习
│ ├── CMakeLists.txt
│ └── src/
│ └── demo_xxx.cpp
│
├── learn-lang/ # 通过“写一门语言”来学习 LLVM
│ ├── CMakeLists.txt
│ └── src/
│ └── xxx.cpp
│
└── learn-pass/ # 编写 LLVM Pass(含 OLLVM 混淆 Pass)
├── CMakeLists.txt
└── src/
└── pass_xxx.cpp
这里用于快速测试 LLVM API,包含:
- 构造 IR(IRBuilder)
- 输出
.ll文件 - 使用模块、函数、基本块、指令
- IR 变量、常量、函数调用等示例
- 各种零碎功能验证(如 C++17 + LLVM API 的组合)
适合作为 LLVM 世界的实验田、沙盒区。
从最小语言开始,逐步实现:
- 词法分析器(Lexer)
- 语法分析器(Parser)
- AST 结构
- 生成 LLVM IR(IRBuilder)
- 控制流(if / for)
- 函数与外部调用
- JIT 执行(ORC JIT)
- 简单优化 Pass(常量折叠等)
最终目标: 实现一个可运行的小型语言解释器,支持表达式、函数、控制流。
这是学习 LLVM 前端与 IR 的最佳实践。
本模块实现 LLVM Pass(基于 new Pass Manager):
-
Hello Pass
-
遍历函数/基本块/指令
-
修改 IR
-
常见 OLLVM 混淆 pass(集中在
learn-pass/src/Kotoamatsukami):- 控制流平坦化(CFF)
- 虚假控制流(Bogus Control Flow)
- 指令替换(Instruction Substitution)
- 字符串/常量混淆
- 其它辅助 Pass:
ForObs、SplitBasicBlock、AddJunkCode、AntiDebug、GVEncrypt、Loopen等
未来可加入:
- 反混淆(Deobfuscation)
- 静态分析(数据流 / CFG / 循环分析)
- SSA 相关操作
ndk_app/ 是一个完整的 Android NDK 示例工程,演示如何将 Kotoamatsukami 混淆
插件集成到 Android 原生库的构建流程中。
NDK 26 的 clang-17 是经 PGO+BOLT 优化的 Google 特供构建,与任何独立 LLVM 17
(Homebrew / apt)存在内部 ABI 差异,直接加载插件会在 PassBuilder 注册回调
时崩溃。
native-lib.cpp
│
▼ NDK clang (aarch64-linux-android24)
.bc ←── 保留完整的 Android ABI / sysroot 信息
│
▼ host opt + Kotoamatsukami.{dylib,so}
obf.bc ←── IR 层 pass,不依赖目标 ABI
│
▼ NDK clang (aarch64-linux-android24)
libndk_example.so ←── 最终 AArch64 Android 库
| Pass 组合 | 指令膨胀 |
|---|---|
bogus-control-flow |
4.4x |
bcf + substitution |
20.4x |
sbb + bcf + sub + flatten |
40.0x |
| 全量(默认) | 54.6x |
本仓库当前推荐使用 LLVM 17。Linux 上可以直接用系统包;macOS 上建议使用
Homebrew 的 llvm@17,不要直接装最新 llvm,否则很容易拿到 LLVM 22+。
./build.sh
./build.sh testbuild.sh 会自动探测 LLVM,并在 macOS 上优先选择 Homebrew llvm@17。
在 Linux / WSL 上会优先尝试系统包常见布局,例如
/usr/lib/llvm-17/bin/llvm-config,并保留 GNU 工具链优先级。
如果你需要显式覆盖,直接设置 LLVM_CONFIG、LLVM_DIR、CC、CXX 即可。
在项目根目录执行以下命令:
cd learn_llvm
cmake -S . -B build \
-DLLVM_DIR=$(llvm-config --cmakedir) \
-G Ninja
cmake --build build推荐直接使用仓库根目录的 CMakePresets.json。
在 CLion 里:
- 打开
Settings > Build, Execution, Deployment > CMake - 新建或选择一个 Profile
- 将
Load CMake Presets打开,或直接选择 preset - Apple Silicon 机器选择
macos-homebrew-llvm17-arm64 - Intel Mac 选择
macos-homebrew-llvm17-intel
如果你已经手动在 CLion 里填过错误的 LLVM_DIR,先执行一次
Tools > CMake > Reset Cache and Reload Project。
xcode-select --install
brew install cmake ninja llvm@17如果你希望手动指定 LLVM 17:
LLVM17_PREFIX=$(brew --prefix llvm@17)
cmake -S . -B build \
-DLLVM_DIR="$(${LLVM17_PREFIX}/bin/llvm-config --cmakedir)" \
-DCMAKE_C_COMPILER="${LLVM17_PREFIX}/bin/clang" \
-DCMAKE_CXX_COMPILER="${LLVM17_PREFIX}/bin/clang++" \
-G Ninja
cmake --build build构建完成后,所有可执行文件会统一放在:
learn_llvm/build/bin/
例如:
bin/demo_hello_ir
bin/lang_repl
bin/pass_hello
bin/test_pass_hello
bin/test_koto_loopen
bin/test_koto_passes
插件产物位于 build/lib/。Linux 下通常是 .so,macOS 下通常是 .dylib。
learn-demo/src/demo_new.cpp
CMake 会自动检测并生成新的可执行程序。
learn-lang/src/*.cpp
同样无需修改 CMake,会自动构建。
learn-pass/src/pass_xxx.cpp
以后加入插件式 Pass 时,我也可以帮你扩展支持 -fpass-plugin=xxx.so 的结构。
项目中已经集成了一批 GoogleTest 用例如下:
| 可执行文件 | 说明 |
|---|---|
bin/test_pass_hello |
覆盖 hello_pass_lib 中 Hello/Junk/SimpleObf/Flatten Pass 的基础行为 |
bin/test_koto_loopen |
验证 Kotoamatsukami::Loopen Pass 是否会注入 Kotoamatsukami_quick_pow 助手函数 |
bin/test_koto_passes |
批量测试 ForObs、BogusControlFlow、SplitBasicBlock、AddJunkCode 等混淆 Pass |
运行方式:
# 构建后直接运行某个测试
./build/bin/test_koto_passes
# 只运行 Loopen 测试
./build/bin/test_koto_loopen --gtest_filter='*Loopen*'这些测试会在运行时为 Kotoamatsukami 生成临时配置文件(位于 /tmp/koto_test_config_*.json),互不干扰。实际使用插件时,可手动编辑仓库根目录的 Kotoamatsukami.config 来启用/禁用指定的混淆 Pass。
- 基本 IR 构造(完成)
- 循环/分支 IR 例子
- 读取/打印
.ll/.bc - 使用 PassManager 对单个模块优化
- 表达式解析
- 变量与作用域
- 函数定义
- 控制流
- ORC JIT
- 简单优化
- REPL 调试工具
- Hello Pass(完成)
- 打印 CFG、Dominator Tree
- 控制流平坦化 Pass
- 字符串加密
- 指令替换
- 虚假控制流
- 反混淆
GitHub Actions(.github/workflows/ci.yml)会在 push 与 pull_request 触发,包含三个 job:
| Job | 说明 |
|---|---|
build-and-test |
标准构建 + ctest |
build-and-test-sanitized |
ASAN + UBSAN 构建 + ctest |
ndk-obfuscation-smoke |
安装 NDK 26、构建插件、跑两步 IR 混淆、验证 .so 导出 JNI 符号 |
本地若想与 CI 对齐:
# LLVM 构建与测试
./build.sh test
# NDK 混淆 smoke test(需要先 ./build.sh)
cmake -S ndk_app/app/src/main/cpp -B /tmp/ndk-ci-test \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-24 -G Ninja
cmake --build /tmp/ndk-ci-test
llvm-nm -D /tmp/ndk-ci-test/libndk_example.so | grep “T Java_”补充说明:当前仓库已经在 macOS + Homebrew llvm@17 环境下验证通过 ctest。