Android Gradle plugin:gradle-8.2
NDK:25.1.8937393
Python: 3.12.1
Cmake: 3.26.4
LLVM: 14.x
Android Gradle plugin 的 gradle-8.2
版本默认使用的 NDK 版本为25.1.8937393
,对应的 LLVM 为14.x
。
需要根据实际情况选择 NDK 对应的 LLVM 版本,编译 OLLVM,LLVM 版本号可以通过:
Sdk_DIR\ndk\$version\toolchains\llvm\prebuilt\windows-x86_64\AndroidVersion.txt
文件看到。如 SDK Manager 中的 25.1.8937393
版本为:
1
2
3
|
14.0.6
based on r450784d
for additional information on LLVM revision and cherry-picks, see clang_source_info.md
|
可以在 module 的 build.gradle
中指定 NDK 版本
1
2
3
|
android {
ndkVersion "25.1.8937393"
}
|
可以在 github/heroims/obfuscator 仓库的分支中找到对应的移植源码
OLLVM14 可以根据 README.md 找到下载方法:
然后进行以下操作:
1
2
3
4
5
|
git clone --depth=1 -b release/14.x [email protected]:llvm/llvm-project.git
cd llvm-project
wget https://heroims.github.io/obfuscator/NewPass/ollvm14.patch
git apply ollvm14.patch
git apply --reject --ignore-whitespace ollvm14.patch
|
当你运行 git apply ollvm14.patch
时,Git 将尝试将名为 ollvm14.patch
的补丁文件应用到当前的代码库中。
而当你运行 git apply --reject --ignore-whitespace ollvm14.patch
时,Git 将以不同的方式处理。在这种情况下:
--reject
: 这个选项告诉 Git,在遇到无法直接应用的更改时创建拒绝文件(.rej
文件),其中包含未能应用的部分内容。这样做可以让你手动查看和解决无法自动合并的部分。
--ignore-whitespace
: 这个选项告诉 Git 在比较和应用补丁时忽略空格变化。有时候由于空格差异导致补丁无法直接匹配,使用此选项可以跳过空格差异而继续尝试应用补丁。
在源码的目录执行以下命令构建 cmake 配置
1
|
cmake -S llvm -B build -G Ninja -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF -DLLVM_ENABLE_NEW_PASS_MANAGER=OFF
|
对应的参数:
-G Ninja
使用 ninja 进行编译源码
-DLLVM_ENABLE_PROJECTS="clang"
启用 clang,有多个选择 但我们只需要 clang,官方文档有说明
-DCMAKE_BUILD_TYPE=Release
构建 release 版本,比 debug 版本编译快很多
-DLLVM_INCLUDE_TESTS=OFF
关闭 llvm 的头文件测试,也是为了加快编译速度
-DLLVM_ENABLE_NEW_PASS_MANAGER=OFF
这个非常重要,llvm-12.x 开始默认使用 newPM 进行编译源码,导致 ollvm 不起作用!
因此,需要加上这个参数禁用掉 newPM (在每个编译时增加 flag -flegacy-pass-manager 让 llvm 不走 newPM 编译也可以,但没必要)
执行以上命令后若提示 Configuration done. 则配置成功。
接下来执行以下命令开始编译:
1
|
cmake --build build -j16
|
其中 -j16
为指定的线程数,需要根据 CPU 调整。然后等待编译完成。
编译成功后需要把 build/bin
目录中的以下文件文件复制出来,方便后面操作:
- clang.exe
- clang++.exe
- clang-format.exe
注意这里的 [VERSION] 是你的 LLVM 版本。
同理,接着把 build/lib/clang/[VERSION]/include
目录中的也复制出来:
- __stddef_max_align_t.h
- float.h
- stdarg.h
- stddef.h
- stdbool.h
这是需要复制的文件结构内容:
├── build
│ └── bin
│ ├── clang
│ ├── clang++
│ └── clang-format
│ └── clang-cl
└── lib
└── clang
└── 14.0.6
└── include
├── __stddef_max_align_t.h
├── float.h
├── stdarg.h
├── stdbool.h
└── stddef.h
编译后的文件大小为 137MB,对比 NDK 目录下的 clang.exe 仅有 88.6MB。
编译后的文件可以通过 strip clang.exe
命令剥离可执行文件减小到 113MB。
把前面 bin 目录的 4 个文件复制到:
# macOS
~/Library/Android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/bin
# Windows
C:\Users\xiaoqi\AppData\Local\Android\Sdk\ndk\25.1.8937393\toolchains\llvm\prebuilt\windows-x86_64\bin
覆盖之前文件(覆盖前请备份)根据自己电脑情况做修改。
此时编译会出现找不到 libunwind
等库的错误,错误信息显示目录文件不存在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
> Task :app:configureCMakeDebug[armeabi-v7a] FAILED
C/C++: CMake Error at C:/Users/xiaoqi/AppData/Local/Android/Sdk/cmake/3.22.1/share/cmake-3.22/Modules/CMakeTestCCompiler.cmake:69 (message):
C/C++: The C compiler
C/C++: "C:/Users/xiaoqi/AppData/Local/Android/Sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/windows-x86_64/bin/clang.exe"
C/C++: is not able to compile a simple test program.
C/C++: It fails with the following output:
C/C++: Change Dir: C:/Users/xiaoqi/AndroidStudioProjects/NDK/app/.cxx/Debug/6y611i46/armeabi-v7a/CMakeFiles/CMakeTmp
C/C++:
C/C++: Run Build Command(s):C:\Users\xiaoqi\AppData\Local\Android\Sdk\cmake\3.22.1\bin\ninja.exe cmTC_cd6c3 && [1/2] Building C object CMakeFiles/cmTC_cd6c3.dir/testCCompiler.c.o
C/C++: [2/2] Linking C executable cmTC_cd6c3
C/C++: FAILED: cmTC_cd6c3
C/C++: cmd.exe /C "cd . && C:\Users\xiaoqi\AppData\Local\Android\Sdk\ndk\25.1.8937393\toolchains\llvm\prebuilt\windows-x86_64\bin\clang.exe --target=armv7-none-linux-androideabi24 --sysroot=C:/Users/xiaoqi/AppData/Local/Android/Sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/windows-x86_64/sysroot -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a -mthumb -Wformat -Werror=format-security -static-libstdc++ -Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,--fatal-warnings -Wl,--gc-sections -Wl,--no-undefined -Qunused-arguments -Wl,--gc-sections CMakeFiles/cmTC_cd6c3.dir/testCCompiler.c.o -o cmTC_cd6c3 -latomic -lm && cd ."
C/C++: ld: error: unable to find library -latomic
C/C++: ld: error: cannot open C:/Users/xiaoqi/AppData/Local/Android/Sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/14.0.6/lib/linux/libclang_rt.builtins-arm-android.a: No such file or directory
C/C++: ld: error: unable to find library -l:libunwind.a
C/C++: ld: error: cannot open C:/Users/xiaoqi/AppData/Local/Android/Sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/14.0.6/lib/linux/libclang_rt.builtins-arm-android.a: No such file or directory
C/C++: ld: error: unable to find library -l:libunwind.a
C/C++: clang: error: linker command failed with exit code 1 (use -v to see invocation)
C/C++: ninja: build stopped: subcommand failed.
C/C++:
C/C++:
C/C++:
C/C++: CMake will not be able to correctly generate this project.
C/C++: Call Stack (most recent call first):
C/C++: CMakeLists.txt:12 (project)
|
解决方法就是将:
SDK_DIR/ndk/25.1.8937393/toolchains/llvm/prebuilt/windows-x86_64/lib64/
目录下的 calng
目录,复制到 /lib
目录中,
并把 clang/14.0.3
修改为 14.0.6
。
这里的 14.0.6
是根据错误日志中出现的路径提取出来的,在控制台中输入 clang -v
查看当前 clang 的版本,按照日志中出现或当前使用的 clang 版本调整。
接着把 include 目录下 5 个文件复制到:
# macOS
~/Library/Android/sdk/ndk/25.1.8937393/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include
# Windows
C:\Users\xiaoqi\AppData\Local\Android\Sdk\ndk\25.1.8937393\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include
因为编译使用了 property 系统的头文件读取系统属性,会遇到 stdbool.h 缺失,所以 H 文件也必须复制过去。
至此,NDK 中 集成 OLLVM 已经完成了。接下来是配置和使用 OLLVM。
这部分网上参考的文档很多,这里也只是简单介绍一下参数
参数 |
说明 |
-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
中进行配置,如:
1
2
3
4
5
6
7
8
9
|
android {
defaultConfig {
extrnalNativeBuild {
cmake {
cppFlags '-mllvm -fla'
}
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
android {
compileSdk 32
ndkVersion "21.4.7075529"
defaultConfig {
applicationId "com.cat.stalker"
externalNativeBuild {
cmake {
abiFilters 'arm64-v8a'
cppFlags "-std=c++14 -fexceptions -fvisibility=hidden"
cppFlags "-mllvm -fla -mllvm -split -mllvm -split_num=3 -mllvm -sub -mllvm -sub_loop=3 -mllvm -sobf"
}
}
ndk {
abiFilters "arm64-v8a"
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
android {
ndkVersion "25.1.8937393"
namespace 'com.wwxiaoqi.ndk'
compileSdk 34
defaultConfig {
externalNativeBuild {
cmake {
cppFlags '-std=c++17 -fexceptions -fvisibility=hidden'
cppFlags "-mllvm -fla -mllvm -split -mllvm -split_num=3 -mllvm -sub -mllvm -sub_loop=3 -mllvm -sobf"
}
}
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.22.1'
}
}
}
|
也可以在 CMakeLists
中进行配置,如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 设置 llvm 混淆编译
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mllvm -fla -mllvm -sub -mllvm -sobf")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -fla -mllvm -sub -mllvm -sobf")
# 设置 llvm debug 模式混淆编译
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -mllvm -fla -mllvm -sobf")
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -mllvm -fla -mllvm -sobf")
# 设置 llvm release 模式混淆编译
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -mllvm -fla -mllvm -sub -mllvm -bcf -mllvm -sobf")
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mllvm -fla -mllvm -sub -mllvm -bcf -mllvm -sobf")
# 深度混淆
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -mllvm -bcf -mllvm -bcf_loop=4 -mllvm -bcf_prob=100 -mllvm -sub -mllvm -sub_loop=2 -mllvm -fla -mllvm -sobf -mllvm -split")
|
如果需要单个函数混淆的情况:
1
2
3
4
5
6
7
8
|
__attribute((__annotate__("bcf")))
__attribute((__annotate__("fla")))
__attribute((__annotate__("sub")))
__attribute((__annotate__("split")))
__attribute((__annotate__("sobf")))
void func(){
xxx
}
|