Releases: weaiken/ArmorOllvm
v1.0.0-Android-ArmorOllvm-toolschain
ArmorComp 中文文档
基于 LLVM 17 的 out-of-tree pass plugin 代码混淆框架,主要面向 Android NDK(arm64-v8a / armeabi-v7a)和 iOS。
无需修改 LLVM 源码 —— 构建后通过 -fpass-plugin 加载即可。
33 个混淆 Pass | 支持 arm64(完整 33 个) 和 arm32(IR 级 pass;6 个 AArch64 专属自动跳过)
目录
Pass 一览
| Pass | 标注 | 说明 |
|---|---|---|
| CFFPass | cff |
控制流平坦化 —— dispatch-switch 循环 |
| BCFPass | bcf |
虚假控制流 —— 不透明谓词死分支 |
| OpaquePredicatePass | op |
不透明谓词 —— 6 种公式(P0-P2 恒真 / P3-P5 恒假) |
| SubPass | sub |
指令替换 —— 13 种 ADD/SUB/AND/OR/XOR 等价模式 |
| MBAPass | mba |
混合布尔算术重写 —— 10 种公式 |
| SplitPass | split |
基本块拆分 —— 膨胀 CFG |
| StrEncPass | strenc |
字符串加密 —— XOR 密文 + 构造函数解密 |
| GlobalEncPass | genc |
全局整数变量加密 —— XOR 加密初始值 + ctor 解密 |
| IndirectCallPass | icall |
间接调用 —— 不透明指针隐藏调用目标 |
| IndirectBranchPass | ibr |
间接跳转 —— indirectbr 隐藏跳转目标 |
| IndirectGlobalVariablePass | igv |
间接全局变量 —— 代理指针隐藏全局变量引用 |
| SPOPass | spo |
栈指针混淆 —— TPIDR_EL0 双读 XOR 破坏 IDA sp_delta (AArch64) |
| ConstObfPass | co |
整数常量混淆 —— XOR-key 分裂隐藏所有数字字面量 |
| FuncWrapPass | fw |
函数包装混淆 —— 内部转发函数隐藏真实调用者 |
| RetAddrObfPass | rao |
返回地址混淆 —— TPIDR_EL0 双读 XOR sub/add SP (AArch64) |
| OutlinePass | outline |
基本块外提 —— 每个非入口 BB 提取为 __armorcomp_outline_N |
| FlattenDataFlowPass | df |
数据流平坦化 —— 所有 alloca 合并到单一 [N x i8] 池 |
| DataEncodingPass | denc |
局部变量内存编码 —— 每次 store/load 使用 XOR 编解码 |
| FuncSigObfPass | fsig |
函数签名混淆 —— 伪造参数读取 + 返回值写入 (AArch64) |
| DwarfPoisonPass | dpoison |
DWARF CFI 表投毒 —— 破坏 IDA .eh_frame 分析 (AArch64) |
| ConditionObfPass | cob |
条件比较混淆 —— ICmpInst 操作数加噪 |
| NeonTypeConfusionPass | ntc |
NEON/FP 类型混淆 —— fmov GPR↔SIMD 误导类型推断 (AArch64) |
| ReturnValueObfPass | rvo |
返回值混淆 —— eor x0, x0, volatile_zero(跨平台) |
| LRObfPass | lro |
链接寄存器混淆 —— eor x30, x30, volatile_zero (AArch64) |
| GEPObfPass | gepo |
GEP 索引混淆 —— 破坏 IDA 结构体 / 数组识别 |
| SwitchObfPass | sob |
Switch 混淆 —— 密集跳转表 + indirectbr |
| JunkCodePass | jci |
垃圾代码注入 —— 每个 BB 注入不可消除的算术链 |
| ArithmeticStatePass | asp |
CFF 状态变量 XOR 编码 —— 防止 IDA 解析状态机 |
| PointerXorPass | pxor |
指针 alloca XOR —— ptrtoint/xor/inttoptr 包装 |
| FakeAPICallPass | fapi |
伪 API 调用注入 —— getpid/getpagesize 噪声 |
| GlobalPointerObfPass | gpo |
全局函数指针加密 —— ctor 双重 XOR 解密 |
| LoopObfuscationPass | lob |
循环混淆 —— 循环入口注入垃圾计算链 |
| VMPPass | vmp |
虚拟机保护 —— 128 寄存器字节码 VM + XTEA 加密 + 完整性校验 + 多态 handler |
Android Studio 集成指南
前置要求
| 组件 | 版本要求 | 安装方式 |
|---|---|---|
| Android Studio | 任意版本(已测试 Hedgehog / Iguana / Jellyfish) | 官网下载 |
| Android NDK | r25+ (推荐 r26b) | Android Studio SDK Manager |
| LLVM 17 | Homebrew clang@17 (macOS) 或 clang-17 (Linux) | 见下文 |
| CMake | >= 3.22 | Android Studio SDK Manager |
| Ninja | 任意版本 | Android Studio 自带 |
安装 LLVM 17
macOS (Homebrew):
brew install llvm@17
# 验证安装
/opt/homebrew/opt/llvm@17/bin/clang --versionLinux (apt):
sudo apt install clang-17 libclang-17-dev为什么需要 brew clang@17?
NDK 自带的 clang 是静态链接 LLVM 的,无法
dlopen加载 pass plugin。
ArmorComp 需要动态链接的 clang@17 来加载libArmorComp.dylib。
toolchain 中的 launcher 脚本会自动拦截 NDK clang 调用,替换为 brew clang@17 + plugin,
同时保留 NDK 的--sysroot、--target、-resource-dir等交叉编译参数。
方案一:CMake 工具链文件(推荐)
这是最简单的集成方式,适用于使用 CMake 构建 native 代码的 Android 项目。
步骤 1:复制 toolchain 目录
将 ArmorComp 的 toolchain/ 目录复制到你的 Android 项目中:
your-app/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── cpp/
│ │ │ │ ├── CMakeLists.txt
│ │ │ │ └── native-lib.cpp
│ │ │ └── java/
│ │ └── ...
│ └── build.gradle
├── armorcomp/
│ └── toolchain/ ← 复制到这里
│ ├── bin/
│ ├── lib/
│ │ └── darwin-arm64/libArmorComp.dylib
│ ├── android.cmake ← 核心工具链文件
│ └── ...
└── build.gradle
步骤 2:修改 app/build.gradle
android {
// ...
defaultConfig {
// ...
externalNativeBuild {
cmake {
// 指向 ArmorComp 的 android.cmake 工具链文件
arguments "-DCMAKE_TOOLCHAIN_FILE=${rootDir}/armorcomp/toolchain/android.cmake"
// arm64-v8a: 完整支持全部 33 个 pass
// armeabi-v7a: 支持 IR 级 pass(6 个 AArch64 专属自动跳过)
abiFilters "arm64-v8a"
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.22.1"
}
}
}Kotlin DSL (build.gradle.kts):
android { defaultConfig { externalNativeBuild { cmake { arguments( "-DCMAKE_TOOLCHAIN_FILE=${rootDir}/armorcomp/toolchain/android.cmake" ) abiFilters("arm64-v8a") } } } externalNativeBuild { cmake { path = file("src/main/cpp/CMakeLists.txt") version = "3.22.1" } } }
步骤 3:在 C/C++ 源码中添加标注
// native-lib.c
#include <jni.h>
#include <string.h>
// 对核心算法启用 CFF + BCF + VMP 保护
__attribute__((annotate("cff")))
__attribute__((annotate("bcf")))
__attribute__((annotate("vmp")))
JNIEXPORT jint JNICALL
Java_com_example_myapp_MainActivity_verifyLicense(
JNIEnv *env, jobject thiz, jstring key) {
const char *nativeKey = (*env)->GetStringUTFChars(env, key, NULL);
int result = 0;
// 你的核心验证逻辑...
if (strcmp(nativeKey, "VALID_KEY") == 0) {
result = 1;
}
(*env)->ReleaseStringUTFChars(env, key, nativeKey);
return result;
}
// 对字符串加密
__attribute__((annotate("strenc")))
JNIEXPORT jstring JNICALL
Java_com_example_myapp_MainActivity_getApiKey(
JNIEnv *env, jobject thiz) {
const char *secret = "sk-AbCdEfGh123456"; // 编译后此字符串会被加密
return (*env)->NewStringUTF(env, secret);
}步骤 4:构建运行
在 Android Studio 中直接点击 Run 或 Build > Make Project。
构建日志(Build Output)中会看到 ArmorComp 的输出:
[ArmorComp] Plugin: .../armorcomp/toolchain/lib/darwin-arm64/libArmorComp.dylib
[ArmorComp] Launcher: .../armorcomp/toolchain/bin/armorcomp-launcher
[ArmorComp] NDK: .../Android/sdk/ndk/26.1.10909125
[ArmorComp][CFF] flattened: Java_com_example_myapp_MainActivity_verifyLicense
[ArmorComp][BCF] obfuscated: Java_com_example_myapp_MainActivity_verifyLicense
[ArmorComp][VMP] virtualized: Java_com_example_myapp_MainActivity_verifyLicense (N bytecode bytes, M virtual instrs)
[ArmorComp][StrEnc] encrypted 1 string(s) in module
工作原理
Android Studio
↓
Gradle → CMake (使用 android.cmake 工具链文件)
↓
CMake 发现 CMAKE_TOOLCHAIN_FILE → 加载 android.cmake
↓
android.cmake:
1. 定位 NDK toolchain → include(android.toolchain.cmake)
2. 定位 libArmorComp.dylib
3. 设置 CMAKE_C_COMPILER_LAUNCHER = armorcomp-launcher
↓
编译时 CMake 调用: armorcomp-launcher <NDK_CLANG> <FLAGS>
↓
armorcomp-launcher:
1. 从 NDK clang 路径推导 resource-dir
2. 丢弃 NDK clang,替换为 brew clang@17
3. 注入 -fpass-plugin=libArmorComp.dylib
4. 保留全部 NDK 编译参数 (--target, --sysroot, ...)
↓
brew clang@17 -fpass-plugin=libArmorComp.dylib --target=aarch64-linux-android21 ...
↓
LLVM 17 加载 ArmorComp → 运行各 pass → 输出混淆后的 .o
↓
NDK linker 链接 → libmynativelib.so (混淆后的 native library)
方案二:ndk-build 集成
适用于仍在使用 Android.mk / ndk-build 的旧项目。
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := mynativelib
LOCAL_SRC_FILES := native-lib.cpp secure_logic.c
# 引入 ArmorComp 工具链
include $(LOCAL_PATH)/../../armorcomp/toolchain/armorcomp.mk
include $(BUILD_SHARED_LIBRARY)Application.mk
APP_ABI := arm64-v8a
APP_PLATFORM := android-21armorcomp.mk 会自动将 TARGET_CC / TARGET_CXX 替换为 ArmorComp 的 clang wrapper。
方案三:手动 clang wrapper
适用于不使用 CMake/ndk-build 的自定义构建系统,或需要精确控制编译参数的场景。
# 直接使用 armorcomp-clang 编译单个文件
./armorcomp/toolchain/bin/armorcomp-clang \
--target=aarch64-linux-android21 \
--sysroot=$ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot \
-O0 \
-c native-lib.c -o native-lib.o
# 链接(使用 NDK linker)
$ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64/bin/ld.lld \
native-lib.o -o libmynativelib.so -shared \
--sysroot=$ANDROID_NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot函数标注方式
使用 __attribute__((annotate("pass_name"))) 标记需要保护的函数:
// 单个 pass
__attribute__((annotate("cff")))
int my_function(int x) { ... }
// 多个 pass —— 逐个叠加
__attribute__((annotate("cff")))
__attribute__((annotate("bcf")))
__attribute__((annotate("sub")))
__attribute__((annotate("mba")))
int highly_protected(int x) { ... }
// C++ 便捷宏
#define ARMORCOMP(...) __attribute__((annotate("cff"))) \
__attribute__((annotate("bcf"))) \
__attribute__((annotate("sub")))
ARMORCOMP()
int my_cpp_function(int x) { ... }可用标注值:
cff bcf op sub mba cob split strenc genc denc jci fapi icall ibr igv spo co gepo fw rao outline df fsig dpoison ntc rvo lro sob asp pxor gpo lob vmp
YAML 配置文件(无需改源码)
适用于保护第三方库或不方便修改源码的场景。
激活方式
# 方式 1: 环境变量
export ARMORCOMP_CONFIG=/path/to/armorcomp.yaml
# 方式 2: 自动发现(工作目录下的 armorcomp.yaml)
# 在项目根目录放置 armorcomp.yaml 即可在 Android Studio...