Fetching...

-

Just a minute...

关键词:代码混淆,代码反混淆及其原理,代码反混淆软件测试与性能对比。

前言

我们的大创项目其实是分两方面进行的,一方面,我们从代码混淆的角度比较各种软件对安卓程序的加固能力;另一方面,我们着重针对OLLVM进行反混淆测试。OLLVM集成了代码混淆的几种最基本的方法:控制流平坦化,虚假控制流,指令替代等三种方式;其中控制流平坦化和虚假控制流可以混淆各个基本块之间的执行顺序,使得程序的执行流程更难读懂,但是它们有一个致命的攻破方法——符号执行。这不仅成为了每一位资深逆向人员必备的技能,而且也逐渐被开发成了自动化反混淆工具。目前市面上开源的自动化反混淆工具很少,有一款是针对安卓应用的simplify ,另外我在github上面搜寻了OLLVM的反混淆软件,排除大部分无用的项目,最终只发现了这两个有实际价值的项目。

针对控制流平坦化的反混淆工具,不足之处就是只适用于带thumb2指令的ios macho格式文件,因此它的平台普适性不高,应用范围极其窄。 该项目于两年前便停止更新。

deobf主要也是针对控制流平坦化这种主流的代码混淆方式。相比ollvm_de_fla,deobf的优势多很多。首先,这是一个新兴项目,创建于大约四个月前,在四天前还曾发布了重大更新。另外,这个项目适用于linux和windows等大众平台,可以不限于thumb指令的使用,有着广泛的应用前景。在项目的说明文档中,很清楚的说明了项目的拷贝方法,安装方法和运行参数。不仅可以反混淆一般的so文件,也可以针对性的处理抖音的libcms.so文件。

但是这个项目依然有着自己的不足,只适用于python3.7版本,更低级的版本不能兼容。

下载与安装

由于deobf需要python3.7环境支持,所以需要下载python3.7,在linux下默认的环境没有python3.7,升级步骤可以参考一篇不错的文章 。后来在windows下也安装了一个,按照项目链接下第二步的详细做法完成即可。但是这里建议把32位和64位的keystone都下下来(当然32位的比较重要)。如果运行报错很有可能是dll的原因,两个dll混搭着用应该就没有问题。

deobf反混淆原理初探

基于OLLVM的反混淆框架的实现

一般的反混淆软件都会有以下几个模块:

1)基本块识别模块 :识别出有用块和无用块。经 OLLVM 控制流平展化混 淆的程序中会增加很多无用的基本块以混淆程序逻辑,这就 需要设计有效的基本块识别算法,找出有用的基本块和无用 的基本块。

2)与程序执行流程相关模块 :确定有用块之间的前后关系,得到真实有效的程序执行路径,因为混淆程序中的很多基本块跳转逻辑并不是程序的实际执行流程。 一般采用符号执行技术。

3)指令修复模块 :修复二进制程序。在使用 NOP 指令填充无用基本块 后,为使程序正常运行,我们需要对跳转指令的跳转偏移量 进行修正;同时,还需要将 cmov 条件传送指令改写成相应 的条件跳转指令,并在其后添加一条 jmp 指令,使其跳向另 一分支。

参考文献

[1]肖顺陶,周安民,刘亮,贾鹏,刘露平.基于符号执行的OLLVM反混淆框架[J/OL].计算机应用:1-6[2020-03-22].

[2]肖顺陶,周安民,刘亮,贾鹏,刘露平.基于符号执行的底层虚拟机混淆器反混淆框架[J].计算机应用,2018,38(06):1745-1750.

通过IDA调试确定基本块的前后关系

这是deobf的一个突出特点,它是根据一个IDA-python脚本直接对软件执行调试,通过这里确定程序的执行流程。参考脚本

这也就意味着,和传统采用的符号执行不同,deobf不需要虚拟执行的结果,而是实打实的进行了调试。但是这样的缺点就是不能抗反动态调试。万一一个程序加入了很多的动态调试函数,那么讲给deobf的运行效果带来很大的挑战。

deobf反混淆效果

参考

由于deobf的软件的基本框架和上述论文中的反混淆框架在思路上完全一致,除了确定基本块以外其他方面的实现方案上大致相同,因此在这里先引用一下论文中有关混淆效果的数据,如下:

image-20200322231048178

image-20200322231036631

验证

为了验证OLLVM的反混淆效果是否真的和论文一致,我使用项目中提供给测试使用的so文件进行混淆,这个项目运用了

ida查看混淆后的控制流图,十分复杂庞大:

image-20200320000323204

而反编译后的伪C代码如下,十分复杂,出现多个嵌套的while循环和if分支语句:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
int __fastcall JNI_OnLoad(int a1)
{
int v1; // r0
signed int v2; // r6
int v3; // r11
signed int v4; // r5
bool v5; // zf
signed int i; // r0
int result; // r0
int v8; // [sp+8h] [bp-30h]
int v9; // [sp+Ch] [bp-2Ch]
int v10; // [sp+10h] [bp-28h]
int v11; // [sp+14h] [bp-24h]
int v12; // [sp+18h] [bp-20h]

v10 = 0;
v1 = (*(int (**)(void))(*(_DWORD *)a1 + 24))();
v2 = 189500648;
v8 = v1;
while ( 1 )
{
LABEL_3:
while ( 1 )
{
v3 = v1;
v4 = v2;
if ( v2 > 941059666 )
break;
if ( v2 > -526232813 )
{
if ( v2 == -526232812 )
{
LABEL_2:
v2 = 1535406375;
v1 = -1;
continue;
}
if ( v2 == 189500648 )
{
v2 = 2058077166;
if ( v8 )
v2 = 1482189424;
}
}
else
{
v2 = 1535406375;
v1 = 65540;
if ( v4 == -1125020146 )
continue;
v5 = v4 == -724953770;
LABEL_10:
v1 = v3;
v2 = v4;
if ( v5 )
goto LABEL_2;
}
}
if ( v2 <= 1535406374 )
{
if ( v2 == 941059667 )
{
v11 = (*(int (__fastcall **)(int, const char *))(*(_DWORD *)v9 + 24))(v9, "com/douyu/lib/http/JniMakeUrl");
for ( i = 1448204801; ; i = 1422645221 )
{
v2 = -1125020146;
if ( i > 1398646745 )
goto LABEL_36;
LABEL_33:
while ( i == 193258894 )
{
v5 = (*(int (__fastcall **)(int, int, char **, signed int))(*(_DWORD *)v9 + 860))(v9, v11, off_45004, 7) == 0;
i = -686378191;
if ( !v5 )
i = -986576434;
if ( i > 1398646745 )
{
LABEL_36:
while ( 2 )
{
while ( i == 1448204801 )
{
i = 193258894;
if ( !v11 )
i = 1398646746;
if ( i <= 1398646745 )
goto LABEL_33;
}
if ( i == 1398646746 )
{
LABEL_32:
i = 1422645221;
v2 = -724953770;
continue;
}
break;
}
if ( i == 1422645221 )
{
v1 = v3;
goto LABEL_3;
}
while ( 1 )
LABEL_47:
;
}
}
if ( i == -986576434 )
goto LABEL_32;
if ( i != -686378191 )
goto LABEL_47;
}
}
v5 = v2 == 1482189424;
goto LABEL_10;
}
if ( v2 != 2058077166 )
break;
v2 = 941059667;
v9 = v10;
if ( !v10 )
v2 = -526232812;
}
if ( v2 != 1535406375 )
goto LABEL_3;
result = _stack_chk_guard - v12;
if ( _stack_chk_guard == v12 )
result = v3;
return result;
}

执行如下反混淆指令,反混淆其中的JNI_Onload函数:

python deobf.py libmakeurl2.4.9.so url.so ins-url.trc 0x0000342C 0x00003668 1

以下是对各个参数的注释:

待处理的混淆文件:libmakeurl2.4.9.so

反混淆后输出的文件:url.so

通过ida调试时产生的目标函数的跟踪文件(可以跟踪关键指令或函数) :ins-url.trc

混淆代码段起始部分偏移:0x0000342C(JNI_Onload函数开头)

混淆代码段结束部分偏移:0x00003668(JNI_Onload函数结尾)

目标函数是否是thumb:是

反混淆后的控制流图,程序执行流程变得十分清晰:

image-20200320000158559

反混淆后的函数也变得简短易懂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __fastcall JNI_OnLoad(int a1)
{
int v1; // r11
int v2; // ST0C_4
int result; // r0
int v4; // [sp+10h] [bp-28h]
int v5; // [sp+14h] [bp-24h]
int v6; // [sp+18h] [bp-20h]

v4 = 0;
(*(void (**)(void))(*(_DWORD *)a1 + 24))();
v2 = v4;
v5 = (*(int (__fastcall **)(int))(*(_DWORD *)v4 + 24))(v4);
(*(int (__fastcall **)(int, int, char **, signed int))(*(_DWORD *)v2 + 860))(v2, v5, off_45004, 7);
result = _stack_chk_guard - v6;
if ( _stack_chk_guard == v6 )
result = v1;
return result;
}

其他OLLVM反混淆软件常用技术

符号执行

deobf的反混淆主要借助与符号执行技术,符号执行技术的核心思想是使用符号值来表示程序的输入数据,并将程序的运算过程逐指令或逐语句地转换为数学表达式,在CFG的基础上生成符号执行树,并为每一条路径建立一系列以输入数据为变量的符号表达式。在符号执行过程中,每当遇到判断与跳转语句时,deobf便会将当前执行路径的路径约束收集到该路径的约束集合中。通过使用约束求解器对约束集合进行求解,可以得到该条路径的可达性:如果约束求解的结果有解,表示该条路径可达,否则表示该条路径不可达,在时间与计算资源足够的理想情况下,符号执行能够遍历目标程序的所有路径并判断其可达性。

其中,路径约束是指程序分支指令中与输入符号相关的分支条件的取值,是一系列不具有量词的布尔型公式。而路径约束集合则被用来存储每一条程序路径上收集到的约束,用“与”操作进行连接。

借用软件安全课程的图片,对于下图示例程序1:

image-20200319232030997

首先画出控制流图,切割基本块。然后计算得函数外部的输入变量数为2,我们只需要设置两个输入变量X,Y。程序中的所有都用与X,Y相关的式子代替。

image-20200319233648011

然后进入函数顺序执行第2-4行,不出现分叉,在第五行代码时,由于是判断语句,符号执行树出现分支,满足分支条件的作为左子树,不满足的作为右子树,左右子树又继续根据路径约束进一步细化,出现更多分支。最终扩展出的符号执行树如图:

image-20200319233036906

最后,通过将X、Y变量代入,计算叶子节点的约束集合是否为真,则可以知道哪些路径是可达的。

Related post
Comment
Share
  • ACTF2020密码学部分writeup

    编写的项目文件请参考项目链接。同时欢迎大家访问ACTF2020的所有赛题。喜欢的话请多多资瓷一下,给我们实验室的项目加个Star或者Fork,谢谢。 为了保护服务器的同时不给选手带来更多困难,密码学部分的交互题开了pow算力检测,我也...

    ACTF2020密码学部分writeup
  • 通过python脚本自动插入汇编反调试代码

    研究背景在之前OLLVM项目的研究过程中,我们发现反调试技术对反混淆脚本有一定的干扰作用,如果可以在OLLVM的中间代码中自动化插入反调试代码,那么就可以给OLLVM的代码混淆增加一层保障。 方案分析探讨多种方案以后,我认为最适合在汇...

    通过python脚本自动插入汇编反调试代码
  • 基于门限方案的条形码保密及容错技术

    关键词:门限方案,条形码保密,条形码容错,条形码认证与防伪造。 经历过初期两个小项目的探索,我们项目团队积累了一定的项目研究经验,在老师和16级学长的帮助下,我们把研究方向转到了门限方案的实际应用上。结合市面上用9张合并的条形码提高条...

    基于门限方案的条形码保密及容错技术
  • 2020新年原创脚本-其中的小把戏你清楚吗

    关键词:随机数素数生成,新年祝福小程序。 脚本创作这是我在大年三十写的一个程序,当时我正准备去伯克利交流,但由于疫情的缘故,出国变数增大,所以我就打算通过随机数“未卜先知”。以下就是我的脚本: 12345678910111213141...

    2020新年原创脚本-其中的小把戏你清楚吗
  • 基于CRT的物流信息安全处理方案

    关键词:中国剩余定理,密钥分发技术,隐私保护。 引言在2018年11月份的时候,段老师在密码学课上讲到了密钥分发协议,我当时就觉得这个协议很有意思也很有应用前景。后来老师还很主动地分享了一下它的idea,其中一部分就是有关物流单上的信...

    基于CRT的物流信息安全处理方案
  • 基于CRT的新型群文件共享系统

    关键词:隐私保护,权限管理,身份认证,中国剩余定理,密钥分发,密钥更新。 这个项目的是在2019年寒假期间进行的,4月份在中南大学信息安全作品赛答辩,但是由于功能只实现了主体部分,加之我在台上比较胆怯紧张,所以只获得团队三等奖,但是当...

    基于CRT的新型群文件共享系统
  • 记一次安卓代码加固软件的测试过程

    关键词:代码加固,软件测试,原理分析,过程分析。 在大创项目的实践中,我们对市面上的一些安卓代码加固软件进行了采集,经过搜集,发现了几类代码加固方法并分组进行研究。我发现很多代码加固软件都是对java字节码进行混淆与加固,另外一些则选...

    记一次安卓代码加固软件的测试过程
  • 对OLLVM代码加固技术的改进

    1.反静态调试反静态调试可以通过花指令,代码加密,代码加壳等方式实现。 请看图1所示的一段反调试代码: ​ 图1 花指...

    对OLLVM代码加固技术的改进
  • OLLVM代码加固机制分析

    我们通过自己编写测试代码,再用OLLVM的不同指令进行加固,并逆向查看加固效果,加深对代码加固机理的了解。OLLVM目前提供的功能包括控制流平坦化(fla指令),指令替代(sub指令),代码虚拟化(bcf指令)以及虚假控制流(obf指...

    OLLVM代码加固机制分析
  • 对音频缓存加密的探讨

    关键词:缓存解密,批量自动执行脚本,版权保护相关建议。 前段时间,某音乐被爆其缓存文件只使用了简单的异或加密,且容易得到加密密钥为0xa3。原文链接点击这里。以下是我的延伸探讨。 1.对音频缓存的批量解密攻击抱着好奇的心理,我把手机...

    对音频缓存加密的探讨
Please check the parameter of comment in config.yml of hexo-theme-Annie!