Better

业精于勤荒于嬉

Windows obfuscator llvm6.0

Better's Avatar 2020-07-18 llvm

  1. 1. 准备环境
    1. 1.1. 确保 gcc g++ 环境
    2. 1.2. 添加 cmake 环境变量
    3. 1.3. 检查 ndk 版本
    4. 1.4. 检查 clang 版本
  2. 2. 安装 obfuscator llvm
    1. 2.1. 第一步编译 obfuscator
    2. 2.2. 第二步集成到 Android NDK
    3. 2.3. 第三步如何使用
  3. 3. 是否生效
  4. 4. 参数解释
  5. 5. 遇到的错误
    1. 5.1. Cannot open /dev/random
  6. 6. Mac 下搭建
  7. 7. 参考

下面是 Windows 10 系统中搭建 obfuscator llvm 6.0 步骤,适配的是 Android NDK 17.2.4988734

大致步骤是先准备环境,下载源码,编译源码,集成到 Android NDK 中,最后在项目中使用。

最后在 Mac10.15.5 系统中也搭建成功了。

准备环境

确保 gcc g++ 环境

由于是在windows上安装的(系统是Windows 10),因为没有gcc g++环境,需要安装mingw。

下载地址:mingw-w64

离线包安装: x86_64-posix-seh

如果使用在线安装会很慢,所以下载了离线安装包,这样耗费了些时间
online

下载离线安装包,解压就行。
director

添加路径 D:\mingw64\bin 以及 D:\mingw64\include 到系统环境变量中

这个是使用在线安装参考:在Windows中安装MinGW-w64
离线安装参考:MinGW-w64离线安装,测试可用
官方下载地址:mingw-w64

添加 cmake 环境变量

这里使用的是Android SDK自带的 3.10.2 版本
添加系统环境变量D:\Software\Android\Android\cmake\3.10.2.4988404\bin 方便直接使用cmake命令

检查 ndk 版本

这里是准备直接使用 Android SDK 目录下的ndk-bundle,这个也是ndk,本地一直有这个目录,点进去查看source.properties 文件:

1
2
Pkg.Desc = Android NDK
Pkg.Revision = 17.2.4988734

也就是说本地的SDK 是17.2.4988734

检查 clang 版本

检查本地的clang 版本,这个是非常重要的,决定了接下来编译哪个版本的 obfuscator

1
2
3
4
5
D:\Software\Android\Android\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++.exe --version
Android (4691093 based on r316199) clang version 6.0.2 (https://android.googlesource.com/toolchain/clang 183abd29fc496f55536e7d904e0abae47888fc7f) (https://android.googlesource.com/toolchain/llvm 34361f192e41ed6e4e8f9aca80a4ea7e9856f327) (based on LLVM 6.0.2svn)
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: D:\Software\Android\Android\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64\bin

结果是 6.0.2的,之前一直使用4.0版本,各种出问题。

安装 obfuscator llvm

官方文档是4.0的:wiki-Installation

1
2
3
4
5
$ git clone -b llvm-4.0 https://github.com/obfuscator-llvm/obfuscator.git
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release ../obfuscator/
$ make -j7

6.0.0版本:LLVM 6.0 + Ollvm + Armariris

1
2
3
4
5
6
7
8
9
10
11
12
13
git clone https://github.com/yazhiwang/ollvm-tll.git
mkdir build
cd build
// 编译
cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release ../ollvm-tll/
make -j7
// 编译完成后,连接NDK
$(NDK_FOLDER)/toolchains/
cp -rf llvm orig_llvm
cd llvm/prebuilt/darwin-x86_64 # it depends on your OS, darwin-x86_64 can be others
rm -rf *
cp -rf ($BUILD_OLLVM)/bin .
cp -rf ($BUILD_OLLVM)/lib .

文档上已经写的很清楚应该怎么做了,很好

Armariris: 由上海交通大学密码与计算机安全实验室维护的LLVM混淆框架,主要是由字符串混淆。这里也有一个坑,从后面看到,就是设置种子的长度。

编译过程中如果测试用例跑不过,可以在cmake 后面跟参数 -DLLVM_INCLUDE_TESTS=OFF 关闭测试用例检查,不过我没有出这个问题。

第一步编译 obfuscator

工程 clone下来后,编译、链接生成可执行文件

在 cmd 中执行:

1
2
3
4
5
6
7
8
git clone https://github.com/yazhiwang/ollvm-tll.git obfuscator-6.0
cd obfuscator-6.0
mkdir build
cd build
// 生成 obfuscator
cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release ../ |tee b_build.txt
// 生成可执行程序
mingw32-make -j7 |tee b_make.txt

|tee b_build.txt主要是将命令行日志打印到本地文件中

有两个注意的地方:

  1. -G "MinGW Makefiles" 表示使用MinGW 来构建
  2. 因为前面是mingw构建的,所有使用了mingw32-make而不是make指令,在这里耗费了些时间

make 过程确实比较耗时,起码30分钟吧。

  1. 生成obfuscator步骤后,就会在 build目录下生成binlib目录
  2. make 之后会在 build目录下生成binlib会有具体的执行文件和依赖库

这里并没有将生产文件放到外部目录,如在外部目录需要修改参数

1
cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release ../obfuscator/  |tee b_build.txt

4.0版本能编译完成,提示有错误:

1
2
3
4
[100%] Built target llvm-lto2
[100%] Linking CXX shared library ..\..\..\..\bin\libclang.dll
[100%] Built target libclang
mingw32-make: *** [Makefile:151: all] Error 2

下载6.0版本,过程中也有一些错误

1
2
3
4
5
6
7
8
9
[ 98%] Built target arcmt-test
[ 98%] Built target clang-check
[ 98%] Built target clang-func-mapping
[ 98%] Built target libclang
[ 98%] Built target llvm-ranlib
[ 98%] Built target llvm-lib
[100%] Built target llvm-dlltool
[100%] Built target c-index-test
[100%] Built target c-arcmt-test

make过程中也会有错误,先不管,能编译完成就行。

编译完成后可以简单检查下生产的clang是否可用,版本号能正常打印就ok

1
2
cd build/lib
clang++.exe --version

第二步集成到 Android NDK

一共看到3种方式,最后只有一种方式成功了。因为项目使用的cmake方式,但是网上大部分都是ndk-build的方式。
修改目录就行:D:\Software\Android\Android\ndk-bundle\toolchains\llvm\prebuilt\windows-x86_64

  1. 先备份下修改的目录
  2. 打开修改的目录,将libbin目录删除
  3. 然后将将刚刚生成的build目录下的binlib拷贝过去

就这样就使用了新的ollvm了。因为Cmake默认是要llvm的clang编译,这里相当于直接替换成了新的clang了。

第三步如何使用

项目使用的是Cmake,所以再工程的CMakeLists.txt中增加

1
2
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mllvm -fla")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -fla")

打包APK,或者执行gradle任务 externalNativeBuildRelease 生成的so文件以及被混淆。

是否生效

对比前后so的函数,查看语句是否变复杂,是否添加了一些额外的干扰阅读的变量。

参数解释

控制流扁平化
这个模式主要是把一些if-else语句,嵌套成do-while语句

  • -mllvm -fla:激活控制流扁平化
  • -mllvm -split:激活基本块分割。在一起使用时改善展平。
  • -mllvm -split_num=3:如果激活了传递,则在每个基本块上应用3次。默认值:1

指令替换
这个模式主要用功能上等效但更复杂的指令序列替换标准二元运算符(+ , – , & , | 和 ^)

  • -mllvm -sub:激活指令替换
  • -mllvm -sub_loop=3:如果激活了传递,则在函数上应用3次。默认值:1

虚假控制流程
这个模式主要嵌套几层判断逻辑,一个简单的运算都会在外面包几层if-else,所以这个模式加上编译速度会慢很多因为要做几层假的逻辑包裹真正有用的代码。
另外说一下这个模式编译的时候要浪费相当长时间

  • -mllvm -bcf:激活虚假控制流程
  • -mllvm -bcf_loop=3:如果激活了传递,则在函数上应用3次。默认值:1
  • -mllvm -bcf_prob=40:如果激活了传递,基本块将以40%的概率进行模糊处理。默认值:30

字符串混淆

  • -mllvm -sobf:编译时候添加选项开启字符串加密
  • -mllvm -seed=0xdeadbeaf:指定随机数生成器种子流程

在build文件中添加:

1
2
3
4
5
6
7
8
android {
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-mllvm -fla -mllvm -sub -mllvm -bcf -mllvm -sobf -mllvm -seed=0xdeadbeaf"
}
}
}

我是在CMakeLists.txt中修改的:

1
2
3
set(OLLVM "-mllvm -fla -mllvm -seed=32位长度大写 ")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OLLVM}")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OLLVM}")

遇到的错误

Cannot open /dev/random

配置了AS工程后rebuild编译成功,但是又警告

1
Cannot open /dev/random

搜索到ollvm 使用——“Cannot open /dev/random”错误的解决方法
文中说到修改\obfuscator-llvm-4.0\lib\Transforms\Obfuscation\CryptoUtils.cppbool CryptoUtils::prng_seed()方法。
我看了下这个文件是有这个方法prng_seed,按照他给的方法来做的话,完全行不通的样子,这个方法有返回值的,它提供的却没有。但是看到这个函数打印错误的地方,是seed的长度不匹配时,会这个报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
bool CryptoUtils::prng_seed(const std::string _seed) {
unsigned char s[16];
unsigned int i = 0;


/* We accept a prefix "0x" */
if (!(_seed.size() == 32 || _seed.size() == 34)) {
errs()<<"The AES-CTR PRNG seeding mechanism is expecting a 16-byte value "
"expressed in hexadecimal, like DEAD....BEEF\n";
return false;
}


seed = _seed;
...

然后又看到文章 Automated Obfuscation of Windows Malware and Exploits Using O-LLVM 说到产生这个错误的原因时没有设置aesSeed,马上设置,但是还是报错。然后再 Armariris 官方文档上看到是要设置 -mllvm -seed=0xdeadbeaf。也就是 seed 的长度必须时32或者时34。修改后搞定,没有报警告了。

另外这里也有一个修改方案,不过没有使用 WIN10 x64搭建OLLVM4.0 android NDK 编译环境跨坑指南

Mac 下搭建

mac 环境下搭建就方便点。因为不需要安装gcc、g++环境,系统因为自带都有。具有的步骤跟前面类似。不同的是:

将 cmake 添加到环境变量中

1
2
export CMAKE_ROOT=~/Documents/program/Android/SDK/cmake/3.10.2.4988404/bin
export PATH=$CMAKE_ROOT:$PATH

同时需要查看 Android SDK 目录下的ndk-bundle 版本是不是17.2.4988734,因为不同的ndk版本,可能使用的llvm不一样。如果没有这版本的SDK,使用SDK Manager 下载就行。

1
2
3
android{
ndkVersion '17.2.4988734'
}

下载源码,编译编码、生产可执行文件,这些都是一样的步骤,不需要使用 -G "MinGW Makefiles" 参数

1
2
3
4
5
makdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ../ |tee build_cmak.txt
cd build
make -j7 |tee build_make.txt

最后将生产的文件夹📂 libbin 替换 ndk 目录中的 llvm目录下的文件夹📂 libbin

我这里的路径是:~/SDK/ndk/17.2.4988734/toolchains/llvm/prebuilt/darwin-x86_64,里面并没有lib目录

参考

windows 下搭建:

【技术分享】为OLLVM添加字符串混淆功能

windows系统OLLVM + NDK 混淆编译环境搭建

参数解释:

WIN10 x64搭建OLLVM4.0 android NDK 编译环境跨坑指南

mac 下搭建:

在Mac&iOS App中使用ollvm

This article was last updated on days ago, and the information described in the article may have changed.