初识ollvm - aeskkkey/reverse GitHub Wiki

个人笔记,转载请注明出处。


一、简介

llvm

LLVM(Low Level Virtual Machine)是一个开源的编译器基础架构,它包含了一组模块化、可重用的编译器和工具,支持多种编程语言和目标架构,包括 x86、ARM 和 MIPS 等。

LLVM 的核心思想是将编译器分为前端和后端两个部分,前端负责将源代码转换为中间表示(IR),后端负责将中间表示转换为目标机器的汇编代码。这种设计使得 LLVM 可以支持多种编程语言,因为只需要为每种语言编写一个前端,就可以利用后端的通用性支持多种目标架构。

img

ollvm

OLLVM是一套开源的基于LLVM框架的代码混淆工具。ollvm整个项目框架包含了三个相对独立的LLVM pass,每个pass实现了一种混淆方式,通过这些混淆手段,可以模糊原程序或者某一部分的算法,给逆向分析带来一些困难。

OLLVM默认支持 -fla -sub -bcf 三个混淆参数:

  • -sub instruction substitution(指令替换),将一些简单的运算复杂化
  • -fla control flow flattening(控制流平坦化),通过多个case-swich结构将程序的控制流变成扁平形状,打破原有的逻辑结构
  • -bcf bogus control flow(控制流伪造),在源程序的控制流中添加一些基本块,这些基本块仅仅起了连接作用,并不影响实际的执行逻辑。
  • Functions annotations, 对单个函数进行混淆

ollvm开源项目在github/obfuscator-llvm/obfuscator,但是只更新到了4.0;更高版本的ollvm,由其他大神fork修改而来。

基于ollvm的进一步改进

这些项目,一方面是对三个pass流程做了改进,另一方面也加入了独特的字符串混淆方式。混淆效果对比可以参考对目前开源OLLVM的字符串加密混淆学习笔记

名称 github 支持llvm版本
Armarirs https://github.com/GoSSIP-SJTU/Armariris
yag00 https://github.com/yag00/obfuscator 3.3, 3.4, 3.5
Hikari https://github.com/HikariObfuscator/Hikari 6.0, 7.0, 8.0
Goron https://github.com/amimo/goron 7.1.0, 8.0.1, 9.0.0, 10.x
Arkari https://github.com/KomiMoe/Arkari 14.x, 15.x, 16.x, 17.x

还有其他等等

二、mac环境ndk集成llvm

参考文章:clang-9: https://github.com/o2e/OLLVM-9.0.1

1) 选择ollvm版本

github/obfuscator-llvm/obfuscator里的llvm版本只更新到4.0;需要下载和ndk版本匹配的llvm。

找到要使用的ndk路径,AndroidStudio里面可能使用不同版本的ndk,cmd + ;查看项目配置,查看Modules里面的NDK版本;或者build.gradle配置里面查看ndkVersion

例如版本为21.4.7075529,则路径为

$SDK_DIR/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/AndroidVersion.txt,内容为

9.0.9
based on r365631c3

那么就需要下载ollvm 9x的版本。到github/heroims/obfuscator的branches里面,找到llvm-9.0.1。

有看到文章说如下的,高版本的可能会有其他问题

在 NDK r21 版本的时候,还可以直接编译 OLLVM 9.0.1 然后用 clang 的几个可执行文件替换 NDK 下的同名文件,不过 NDK r21 不支持 API 30 之后的版本,需要升级到 NDK r23。这时候以前直接替换的方法无法正常编译 Native 项目,因此需要换一种方法。

后续步骤请用llvm-9.0.1和ndk r21,经过测试llvm-12和ndk r23是有问题的。

2) 下载并编译ollvm

参考文档

$ git clone -b llvm-9.0.1 https://github.com/heroims/obfuscator.git
$ cd obfuscator
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF -DLLVM_CREATE_XCODE_TOOLCHAIN=ON ../

-DCMAKE_BUILD_TYPE=Release: 构建 release 版本,比 debug 版本编译快很多 -DLLVM_INCLUDE_TESTS=OFF: 关闭 llvm 的头文件测试,也是为了加快编译速度

3) 替换ndk里的clang

找到路径$SDK_DIR/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/bin

将编译输出目录build/bin下的clang clang++ clang-9 clang-cl拷贝到上述路径下,并覆盖同名文件,覆盖前先备份原文件。然后查看version信息确认替换成功。

$SDK_DIR/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang -v
Obfuscator-LLVM clang version 9.0.1 (https://github.com/heroims/obfuscator.git db285197d7462d31aa1488679f9c7bd27db4bfe0) (based on Obfuscator-LLVM 9.0.1)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: $SDK_DIR/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/bin

补全头文件到ndk。进入obfuscator/build/lib/clang/9.0.1/include/,然后复制如下文件stdarg.hstddef.h__stddef_max_align_t.hfloat.h stdbool.h$SDK_DIR/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include;这个编译时候看报错缺什么头文件就补就行。

4) 配置CMakeList开启混淆

set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -mllvm -fla -mllvm -split -mllvm -split_num=3 -mllvm -sub -mllvm -sub_loop=3 -mllvm -bcf -mllvm -bcf_prob=40 -mllvm -sobf")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mllvm -fla -mllvm -split -mllvm -split_num=3 -mllvm -sub -mllvm -sub_loop=3 -mllvm -bcf -mllvm -bcf_prob=40 -mllvm -sobf")

set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -mllvm -fla -mllvm -split -mllvm -split_num=3 -mllvm -sub -mllvm -sub_loop=3 -mllvm -bcf -mllvm -bcf_prob=40 -mllvm -sobf")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -mllvm -fla -mllvm -split -mllvm -split_num=3 -mllvm -sub -mllvm -sub_loop=3 -mllvm -bcf -mllvm -bcf_prob=40 -mllvm -sobf")
  • 控制流扁平化;把一些if-else语句,嵌套成do-while语句
-mllvm -fla:激活控制流平坦化
-mllvm -split:激活基本块分割。在一起使用时改善展平。
-mllvm -split_num=3:如果激活了传递,则在每个基本块上应用3次。默认值:1
  • 指令替换
-mllvm -sub:激活指令替换
-mllvm -sub_loop=3:如果激活了传递,则在函数上应用3次。默认值:1
  • 虚假控制流程
-mllvm -bcf:激活虚假控制流程
-mllvm -bcf_loop=3:如果激活了传递,则在函数上应用3次。默认值:1
-mllvm -bcf_prob=40:如果激活了传递,基本块将以40%的概率进行模糊处理。默认值:30
  • 其他设置
-mllvm -sobf 开启字符串混淆
-mllvm -seed=0xdeadbeaf 指定随机数种子生成器

三、mac环境ndk集成goron

1) 下载和编译

goron支持llvm9,因此直接在上述mac步骤基础上,

git clone -b llvm-9.0.0 https://github.com/amimo/goron.git
cd goron
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_ENABLE_PROJECTS=clang -G "Unix Makefiles" ../llvm
make -j12

2) 覆盖ndk

cp clang clang++ clang-9 clang-cl $SDK_DIR/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/bin/

$SDK_DIR/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang -v
clang version 9.0.0 (https://github.com/amimo/goron.git e943a8a78325632df64988e05d66ad5fa0e0c6f6)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: $SDK_DIR/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64/bin

3) 配置CMakeList开启混淆

  • 混淆过程间相关

  • 间接跳转,并加密跳转目标(-mllvm -irobf-indbr)

  • 间接函数调用,并加密目标函数地址(-mllvm -irobf-icall)

  • 间接全局变量引用,并加密变量地址(-mllvm -irobf-indgv)

  • 字符串(c string)加密功能(-mllvm -irobf-cse)

  • 过程相关控制流平坦混淆(-mllvm -irobf-cff)

set(irobf_indbr "-mllvm -irobf-indbr") # 间接跳转
set(irobf_icall "-mllvm -irobf-icall") # 间接函数调用
set(irobf_indgv "-mllvm -irobf-indgv") # 间接全局变量引用
set(irobf_cse "-mllvm -irobf-cse") # 字符串加密
set(irobf_cff "-mllvm -irobf-cff") # 过程相关控制流平坦化
set(ollvm_cfg "-mllvm -irobf ${irobf_indbr} ${irobf_icall} ${irobf_indgv} ${irobf_cse} ${irobf_cff}") # 全部启用

set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${ollvm_cfg}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${ollvm_cfg}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${ollvm_cfg}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${ollvm_cfg}")

四、windows环境ndk集成goron

1) windows GCC开发环境

下载MinGW-w64,关于MinGW和MinGw-64w的关系,可以看MinGW-w64安装教程。教程里的install已经无法使用了。直接到SourceForge,找到MinGW-W64 GCC-8.1.0列表下的x86_64-posix-seh,下载这个。

  • gcc-8.1.0是gcc版本号。
  • x86_64是我windows主机的cpu架构;
  • posix系统接口协议,开发 Windows 程序,需要选择 win32 ;而开发 Linux、Unix、Mac OS 等其他操作系统下的程序,则需要选择 posix 。这里编译llvm,一定要选posix;否则编译过程中会报找不到std::threadstd::mutex等错误。

下载完成后,解压缩包得到mingw64文件夹,然后将mingw64/bin目录添加到系统环境变量。添加完成之后,bash里面应当看到gcc版本信息。

$ gcc --version
gcc.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

2) 下载编译goron

环境配好后,基本同mac,注意-G指定为MinGW即可

git clone -b llvm-9.0.0 https://github.com/amimo/goron.git
cd goron
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_ENABLE_PROJECTS=clang -G "MinGW  Makefiles" ../llvm
make -j16

然后参考前面步骤替换ndk里的clang。

配置CMakelist步骤也是一样的。