OLLVM环境搭建-Ubuntu20.04
注:这里没有使用配置NDK环境
什么是混淆和OLLVM?
关于OLLVM介绍这里极力推荐直接看这个Deobfuscation: recovering an OLLVM-protected program
早在1997年,学术界就开始了代码混淆的研究。目前使用最广泛的混淆分类方式是Collberg于1997年针对JAVA程序的安全问题提出的。根据Collbreg的定义,混淆可以分为以下四种类型:布局混淆、预防性混淆、数据混淆、控制混淆。布局混淆指删除或修改源代码中的对攻击者由于的信息,如变量名、调试信息等。显然,布局混淆在二进制代码混淆中可以忽略不计。预防性混淆指预先针对特定的反编译工具,解混淆方法进行防备。
OLLVM(Obfuscator-LLVM)是瑞士西北应用科技大学安全实验室于2010年6月份发起的一个项目,该项目旨在提供一套开源的针对LLVM的代码混淆工具,以增加逆向工程的难度。只不过Ollvm仅更新到llvm的4.0,2017年开始就没再更新。
Ollvm混淆主要分成三种模式,这三种模式主要是流程平坦化,指令替换,控制流伪造。
流程平坦化 :这个模式主要通过将if-else语句替换成do-while语句,然后通过switch语句来对流程的控制,这样就能模糊基本块之间的前后关系。
指令替换 :这个模式主要通过使用更复杂的指令序列来替换一些标准的二元运算符,从而增加逆向的难度。
控制流伪造 :这个模式主要是会在一个简单的运算中外包好几层if-else的判断,从而增加逆向的难度。
原理就是在不改变源代码的功能前提下,将C或C++代码中的if、while、for、do等控制语句转换成switch分支语句。这样做的好处是可以模糊switch中case代码块之间的关系,从而增加分析难度。具体做法是首先将要实现平坦化的方法分成多个基本块(就是case代码块)和一个入口块,为每个基本快编号,并让这些基本块都有共同的前驱模块和后继模块。前驱模块主要是进行基本块的分发,分发通过改变switch变量来实现。后继模块也可用于更新switch变量的值,并跳转到switch开始处。
环境搭建准备
Ubuntu20.04虚拟机、虚拟机中处理器数量为4个、运行内存4G,分配硬盘空间50g、CMake 3.16.6、g++/gcc为g+±9/gcc-9;顺手保存一个快照,感觉用Docker来编译OLLVM更方便!!!
更新一手
sudo apt update && sudo apt upgrade -y
1.换国内源
方法一:
软件和更新 -> ubuntu软件 -> 在下载自:框内选择 位于中国的服务器或者 手动选择国内源
方法二:
更改 source.list : 链接1
2.Ubuntu 扩容
Ubuntu虚拟机安装是默认为20G,如果未更改,则需要扩容,防止在交叉编译过程中出现空间不足(如果虚拟机关机后 扩展 选项为灰色 则需要删除快照)
方法见 链接2
3.(可选)查看 /dev/loop0至/dev/loopx(x=2、3、4…)占用率是否为100%
df -h
/dev/loopn这些设备在Linux下被称为回环设备。
如果为100%,则清理
清理方法:
sudo apt autoremove --purge snapd
再次查看磁盘使用情况:
df -h
4.(可选)清理
make clean #清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件
sudo apt autoclean #清理旧版本的软件缓存
sudo apt autoremove #删除系统不再使用的孤立软件
OLLVM环境搭建
主要参考下面两位大佬的来做的
- 跟着铁头干混淆2 ubuntu20.04编译ollvm
链接3 - OLLVM环境搭建-编译x86指令集可执行程序-Ubuntu 20.04
链接4
顺手更新一下
sudo apt update && sudo apt upgrade -y
1.gcc8 g++8 camke 安装
安装camke
sudo apt-get install cmake -y
安装 gcc8 g++8 降低版本
如果环境 gcc 和 g++的版本如果就是 8.x.x
这里就可以直接下一步了
gcc9 g++9 编译一定会失败,这时需要降低版本(这里真踩坑了)
安装gcc-8 g++8
sudo apt-get install gcc-8 g++-8 -y
安装完之后,配置一下优先级,方便调用gcc g++的时候,默认调用的是 gcc8 g++8
配置软件的优先级,可以根据需要去选择默认的版本
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 8
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 8
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 9
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 9
**gcc切换版本 默认gcc 8 **
sudo update-alternatives --config gcc
g++切换版本 默认g++ 8
sudo update-alternatives --config gcc
2.git LLVM源码
git源码时可能会出现超时或者网速很慢的情况,超时就多试几次,慢就耐心等待,直至完成
git clone -b llvm-4.0 https://github.com/obfuscator-llvm/obfuscator.git
如果没有git ,安装git
sudo apt install git
查看git 是否安装成功
git --version
需要对源码进行修改
ollvm目录/include/llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h
690行char修改为uint8_t
690 Expected<std::vector<uint8_t>> readMem(char *Dst, JITTargetAddress Src,uint64_t Size) {// Check for an 'out-of-band' error, e.g. from an MM destructor.if (ExistingError)return std::move(ExistingError);return callB<ReadMem>(Src, Size);}
2.编译安装
该过程比较长,需要耐心等待
这里着重说明一下/特别强调一下,不要急着复制粘贴命令,先把 编译安装 这个看完,因为有两种方式:make install和sudo make install,然后自己看情况选择
make install是普通用户安装软件的命令,它会将软件安装到当前用户的目录下,而sudo make install则是使用root用户权限安装软件,它会将软件安装到系统的全局目录下,以便所有用户都可以使用。
在源码同级目录
这里着重说明一下/特别强调一下,不要急着复制粘贴命令,先把
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF ../obfuscator/
make
sudo make install
如果 make 过慢,可以ctrl+C 停止 ,然后
make clean
make -j2 #亦可以make -j4 等加速编译
如果怕崩溃,建议直接make
如果直接 make install ,可能会出现
CMake Error at cmake_install.cmake:41 (file): file INSTALL cannot make direc
所以直接 sudo make install
sudo make install 之后就不用在bin目录下编译程序,如果怕冲突,可以在bin目录编译程序或者指定clang路径
在bin目录下应该这样编译程序
./clang -mllvm -fla test.c -o test1
将clang作为一个程序执行即可成功完成混淆
3.检验安装是否完成
如果安装完成了,赶紧建个快照吧,如果你还想回味一下过程,那就随意了
clang --version
4.检验ollvm是否可用
如果没有进行 sudo make install ,则需要在 bin 目录下编译源程序或者指定clang路径, 详见此链接
在桌面新建一个test文件夹,并新建文件test.cpp
mkdir test
cd test
touch test.cpp
打开test.cpp,键入或者粘贴测试代码:
#include <stdio.h>
#include <stdlib.h>int encryptFunc(int inputNum_1,int inputNum_2){int tmpNum_1 = 666, tmpNum_2 = 888, tmpNum_3 = 777;return tmpNum_1 ^ tmpNum_2 + tmpNum_3 * inputNum_1 - inputNum_2;
}int main(int argc,char *argv[]){int printNum = 55;if (argc > 1){printNum = encryptFunc(printNum, atoi(argv[1]));}else{printNum = encryptFunc(printNum, argc);}printf("Hello OLLVM %d\r\n", printNum);return 0;
}
然后进行混淆(控制流扁平化、指令替换、控制流伪造随便选一个)
(1)控制流扁平化
clang -mllvm -fla test.cpp -o test1
(2)指令替换
clang -mllvm -sub test.cpp -o test2
(3)控制流伪造
clang -mllvm -bcf test.cpp -o test3
这里选择控制流扁平化
混淆前:
混淆后:
OLLVM基本用法
参考 链接4
控制流平坦化(Control Flow Flattening)
可用选项:
-mllvm -fla : 激活控制流平坦化
-mllvm -split : 激活基本块分割
-mllvm -split_num=3 : 指定基本块分割的数目
clang -mllvm -fla -mllvm -split -mllvm -split_num=3 test.cpp -o test_fla
虚假控制流(Bogus Control Flow)
可用选项:
-mllvm -bcf : 激活虚假控制流
-mllvm -bcf_loop=3 : 混淆次数,这里一个函数会被混淆3次,默认为 1
-mllvm -bcf_prob=40 : 每个基本块被混淆的概率,这里每个基本块被混淆的概率为40%,默认为 30 %
clang -mllvm -bcf -mllvm -bcf_loop=3 -mllvm -bcf_prob=40 test.cpp -o test_bcf
指令替换(Instruction Substitution)
可用选项:
-mllvm -sub : 激活指令替代
-mllvm -sub_loop=3 : 混淆次数,这里一个函数会被混淆3次,默认为 1次
clang -mllvm -sub -mllvm -sub_loop=3 test.cpp -o test_sub
OLLVM去除
OLLVM的基本原理是添加分发器来控制流程的执行。针对这种混淆方式的还原也有了许多研究和工具,大致思路分为动态和静态两种:
- 动态:通过Unicorn模拟执行的方式获取各真实块的关系
- 静态:通过符号执行、中间语言分析等方式获取真实块之间的关系。
- AI:Chatgpt,据说还行
静态方式进行OLLVM反混淆的工具,许多是依赖于Binary Ninja, IDA Pro等商业软件提供的中间语言API接口。
这里贴出反OLLVM的几个链接,仅供参考
(1)利用符号执行去除控制流平坦化
tx的这个delfat是基于python2的
(2)cq674350529的deflat from github
这个是基于SnowGirls的deflat,利用angr框架实现去除控制流平坦化,这个deflat用的相对较多,但是在使用过程中发现处理for的逻辑不太好。
(3)2022祥云杯CTF babyparser 这里使用模拟执行unicorn来去除OLLVM,作者也给了文章中的unicorn deflat,可作参考
(4)ARM64 OLLVM反混淆 基本思路就是文中提到的采用模拟执行的方式来寻找两个真实块的关系
(5)使用unidbg去ollvm虚假分支反混淆
(6)OLLVM控制流平坦化的改进
(7)使用unicorn engin还原Armariris字符串混淆
(8)吾爱破解2016安全挑战赛cm7 Android CrackMe 分析详解
(9)使用IDA microcode去除ollvm混淆(上)
(10)AndroidNativeEmu和unidbg对抗ollvm的字符串混淆
(11)antiOllvm
…
部分参考资料
[1]参考1 OLLVM混淆环境搭建详细教程
[2]参考2 在虚拟机上的Ubuntu20.04进行LLVM的编译安装
[3]参考3 基于二进制代码的代码混淆研究
[4]参考4 简单介绍vmp,ollvm
[5]参考5 利用ollvm进行代码混淆
[6]参考6 OLLVM代码混淆移植与使用
[7]参考7 OLLVM混淆学习(0)——环境搭建及混淆初体验