Mac(ARM)下编译OLLVM并集成NDK25
Mac(ARM)下编译OLLVM并集成NDK25
本文介绍在Mac ARM架构(M1/M2/M3芯片)下,完成OLLVM混淆编译并集成至NDK25的步骤。
环境准备
Mac M2 |
编译OLLVM所需的版本需与LLVM保持一致,而LLVM版本由NDK决定。
Android Gradle plugin 的 gradle-8.7
版本默认使用的 NDK 版本为25.1.8937393
,对应的 LLVM 为14.0.6
。
可通过以下方式查询LLVM版本号:
Sdk_DIR\ndk\$version\toolchains\llvm\prebuilt\windows-x86_64\AndroidVersion.txt |
Android开发中,若不希望使用Gradle插件默认的NDK版本,可以在module的
build.gradle
文件中手动指定所需的NDK版本。
>android {
>ndkVersion "25.1.8937393"
>}
获取源码
可以在 github/heroims/obfuscator 仓库的分支中找到对应的移植源码
LLVM14源码
git clone -b release/14.x git@github.com:llvm/llvm-project.git |
OLLVM源码
cd llvm-project #进入LLVM14源码目录 |
编译OLLVM
1、因为 LLVM 使用 CMake 构建系统,必须先通过 CMake 配置生成构建文件,才能执行编译。
#进入LLVM14源码目录,执行以下命令 |
参数 | 含义 |
---|---|
-S llvm | 指定源码路径为当前目录下的llvm文件夹 |
-B build | 指定构建输出路径为当前目录下的build文件夹 |
-G Ninja | 使用 ninja 作为构建工具(更快、更轻量) |
-DLLVM_ENABLE_PROJECTS=”clang” | 只构建 clang 项目(OLLVM 混淆通常只需 Clang) |
-DCMAKE_BUILD_TYPE=Release | 编译为发布版,开启优化,关闭调试信息 |
-DLLVM_INCLUDE_TESTS=OFF | 不构建测试用例(节省时间) |
-DLLVM_ENABLE_NEW_PASS_MANAGER=OFF | 关闭NewPass管理器,启用LegacyPass(兼容OLLVM混淆) |
-G Ninja
如果不指定Ninja则会根据系统的默认环境自动选择一个构建工具。
可通过下列命令查看默认构建工具
>cmake --help查看输出中的Generators部分,带 * 的那一项就是当前默认,类似如下。
>The following generators are available on this platform (* marks default):
>* Unix Makefiles = Generates standard UNIX makefiles.
Ninja = Generates build.ninja files.-DLLVM_ENABLE_PROJECTS=”clang”
除此以外还支持 clang-tools-extra, lldb, lld, polly, or cross-project-tests
-DLLVM_ENABLE_NEW_PASS_MANAGER=OFF
这个非常重要,llvm-12.x 开始默认使用 newPM 进行编译源码,导致 ollvm 不起作用!
因此,需要加上这个参数禁用掉 newPM (在每个编译时增加 flag -flegacy-pass-manager 让 llvm 不走 newPM 编译也可以,但没必要)
执行以上命令后若提示 Configuration done. 则配置成功。
2、接下来执行以下命令开始编译
cmake --build build -j8 |
其中
-j8
为指定的线程数,需要根据 CPU 调整。然后等待编译完成。在 macOS 终端中通过以下命令查询 CPU 核心数:
sysctl -n hw.ncpu
执行完命令后进度条拉满及编译完成。
NDK集成OLLVM
1、编译成功后需要把 build/bin
目录中的以下文件文件复制出来,方便后面操作:
- clang
- clang++
- clang-[VERSION]
- clang-format
- clang-cl
注意这里的 [VERSION] 是你的 LLVM 主版本号,如LLVM14对应的则为clang-14。
同理,接着把 build/lib/clang/[VERSION]/include
目录中的也复制出来:
- __stddef_max_align_t.h
- float.h
- stdarg.h
- stddef.h
- stdbool.h
这是需要复制的文件结构内容:
├── build |
编译后可以使用
strip clang
命令来去除 Clang 可执行文件中的符号信息,从而减小体积,适用于部署阶段。
2、把前面 bin 目录的 4 个文件复制到:
# macOS |
覆盖之前文件(覆盖前请备份)根据自己电脑情况做修改。
接着把 include 目录下 5 个文件复制到:
# macOS |
因为编译使用了 property 系统的头文件读取系统属性,会遇到 stdbool.h 缺失,所以 H 文件也必须复制过去。
3、如果此时编译会出现找不到 libunwind
等库的错误,错误信息显示目录文件不存在
则将SDK_DIR/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/lib64/
目录下的calng
目录,复制到 SDK_DIR/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/lib
目录中
至此,NDK 中 集成 OLLVM 已经完成了。
SO编译配置
参数 | 说明 |
---|---|
-mllbm -sub | 激活指令替换 |
-mllvm -sub_loop=3 | 如果激活了传递,则在函数上应用3次。默认值:1 |
-mllvm -bcf | 激活虚假控制流程 |
-mllvm -bcf_loop=3 | 如果激活了传递,则在函数上应用3次。默认值:1 |
-mllvm -bcf_prob=40 | 如果激活了传递,基本块将以40%的概率进行模糊处理。默认值:30 |
-mllvm -fla | 激活控制流扁平化 |
-mllvm -split | 激活基本块分割。在一起使用时改善展平 |
-mllvm -split_num=3 | 如果激活了传递,则在每个基本块上应用3次。默认值:1 |
Heroims 在移植 OLLVM 时,集成了 Armariris 的字符串混淆功能。
参数 | 说明 |
---|---|
-mllvm -sobf | 编译时候添加选项开启字符串加密 |
-mllvm -seed= | 指定随机数生成器种子 |
在 build.gradle
中进行配置,如:
android { |
OLLVM验证
1、创建一个空的C文件
touch helloworld.c |
2、查看 LLVM 支持哪些 -mllvm 参数
./clang -mllvm -help helloworld.c |
如果输出中含有 -sub
、-fla
及-bcf
编译成功。
3、混淆效果验证(以armeabi-v7a为例 )
可以通过IDA逆向分析相关so库
未混淆Graph Overview
混淆后的Graph Overview
结语
通过以上流程,可在Mac ARM环境下完成OLLVM混淆编译并成功集成至NDK25,增强Android项目安全性。