参考文章:[[原创]Android APP漏洞之战(14)——Ollvm混淆与反混淆-Android安全-看雪-安全社区|安全招聘|kanxue.com](https://bbs.kanxue.com/thread-274532.htm)
本文是对上述文章的一些操作的复现,顺便说一下大佬好强(膜拜)
一,什么是Ollvm
OLLVM(Obfuscator-LLVM)是瑞士西北应用科技大学于2010年6月份发起的一个项目,该项目旨在提供一套开源的针对LLVM的代码混淆工具,以增加对逆向工程的难度。怎么对代码进行混淆
需要先理解什么是LLVM:
LLVM是一个编译器框架,采用经典三段式设计,即前端,中端,后端,三部分有不同的操作
前端主要是不同的编译工具对代码进行词法分析,语法分析,语义分析,生成中间代码IR等
中端主要是对IR进行一系列优化,优化用的是Pass
后端把IR转成平台对应的机器码
LLVM可以将不同的语言转换成相同的中间代码IR,然后经过一系列的Pass,可以将IR转成不同平台对应的机器码
通过Pass进行代码的优化,而OLLVM则是更复杂的Pass,使代码变的复杂
这里放上一个简单代码作为基础代码,一会用这个代码来进行加密测试一下
#include <stdio.h>
#include <string.h>
typedef unsigned longULONG;
void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len)
{
int i = 0, j = 0;
unsigned char k[256] = {0};
unsigned char tmp = 0;
for (i = 0; i < 256; i++)
{
s[i] = i;
k[i] = key[i % Len];
}
for (i = 0; i < 256; i++)
{
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}
void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Len)
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
}
int main()
{
unsigned char s[256] = {0}, s2[256] = {0};
unsigned char key[] = "yesiamkey";
unsigned char data[] = {
204, 187, 250, 98, 118, 79, 207, 168, 29, 249, 78, 170, 106, 106, 126, 21, 3, 0, 27, 187, 211, 127};
unsigned long len = strlen(data);
int i;
rc4_init(s, (unsigned char *)key, strlen(key));
for (i = 0; i < 256; i++)
{
s2[i] = s[i];
}
rc4_crypt(s, data, len);
for (int i = 0; i < 100; i++)
printf("%c", data[i]);
return 0;
}
OLLVM分类:
OLLVM有三种sub(指令替换),fla(控制流平坦化),bcf(虚假控制流)
sub:指令替换
instruction substitution:简单的运算替换,将简单的指令替换为等价的更复杂的指令,比如a=b+c
=> a = b - (-c)
或者r = rand (); a = b + r; a = a + c; a = a - r
等等
适用于加法,减法,与或非等等运算,如果一个指令有多种混淆方法选择,ollvm随机选择一种
下面是用测试程序加混淆的对比
混淆不明显硬看也可以
bcf:虚假控制流
虚假控制流主要加入包含不透明谓词的条件跳转和不可达的基本块,来影响IDA的分析,基本的过程就是构造虚假基本块,通过不透明谓词进行条件跳转到真实的基本块
不透明谓词的原理指的是:利用全局变量来构造条件表达式以控制虚假控制流,因为这个全局变量不会有写入的地方,所以这个表达式即一条结果恒定的条件表达式,因此始终只会走向一个真实分支,而另外一个虚假分支即一个
用不到达的分支,例如
y>10 || x (x+1)%2==0这个不等式中x(x+1)%2==0是恒成立的,所以整个条件式是永真式,但是IDA不确定全局变量x,y是否会改变,因此无法识别出来,这样就增加虚假控制流
其中混淆后的代码调试可以发现,全局变量y_4和x_3都始终为0,那么y_4>=10和((x_3-1)*x_3)&1)!=0永假,所以根本不会执行goto LABEL_12代码,执行的是v11[i+10]=s[i]这一行代码
fla:控制流平坦化
将正常程序中的基本块的条件跳转关系拆开,在程序中引入大量的条件跳转语句或者指令,用一个集中的主分发块来调度基本块的执行,同时设置标志或者变量来控制下一个基本块的执行
这是没有进行fla混淆的程序流程
这是经过fla混淆的程序流程
1.将原始的条件分支语句(如f语句)拆分为独立的基本块。
2.将这些基本块按照一定的顺序排列在一起,形成一个新的程序流程。
3.引入一个控制变量或标志,用于表示当前应该执行的基本块。
4.在程序中插入条件语句或跳转指令,根据控制变量或标志来选择执行哪个基本块。
5.在每个基本块末尾设置控制变量或标志的值,以确定下个要执行的基本块。
·函数的开始地址为序言的地址
·序言的后继为主分发器
·后继为主分发器的块为预处理器
·后继为预处理器的块为真实块
·无后继的块为retn块
·剩下的为无用块
字符串加密:
之前还没有了解过ollvm中都是字符串加密混淆
字符串加密的原理很简单,编写一个pass将其中的字符串信息使用一些加密算法进行加密,然后特定的时间进行还原。一般含有字符串混淆、函数名混淆、不在init_array解密等
OLLVM给程序加混淆:
之前在windows上搞的加混淆工具clang失败,最后只能在Linux上编译了一个工具,可以加混淆成功
具体编译过程可以去看这篇文章很详细:OLLVM混淆初始及环境搭建 - Qsons - 博客园 (cnblogs.com)
然后输入指令即可混淆
OLLVM反混淆:
后面补充