首页
社区
课程
招聘
[原创]使用unicorn engin还原Armariris字符串混淆
发表于: 2019-6-26 14:29 11023

[原创]使用unicorn engin还原Armariris字符串混淆

2019-6-26 14:29
11023

孤挺花(Armariris) -- 由上海交通大学密码与计算机安全实验室维护的LLVM混淆框架 https://github.com/GoSSIP-SJTU/Armariris

编译

测试文件内容如下:

使用编译好的llvm编译这个测试的文件

使用isysroot指定sdk,然后使用-mllvm -sobf开启字符串混淆

我们直接看使用ida反汇编出来的代码

可以看到有两个printf函数打印了一些数据出来,我们点第一个打印的字符串,双击aRcur7777跳转到
字符串定义位置,这个字符串在data段

这个字符串我们本来输出的是test 1111这里显然不是,我们查看aRcur7777的交叉引用,发现两处,
其中一处是main函数中的printf,另一处应该就是还原这个字符串的位置了

所以__datadiv_decode14953400483976599729这个函数就是还原这个字符的函数,我们看他是如何做的还原
。跳转过去按F5反编译,得到的结果如下:

我们可以看到aRcur7777的还原是和6做了异或操作,那我们来验证一下是否是我们看到的这样。

aRcur7777的原始数据是[0x72, 0x63, 0x75, 0x72, 0x26, 0x37, 0x37, 0x37, 0x37]

每一位和6异或之后的结果是[0x74, 0x65, 0x73, 0x74, 0x20, 0x31, 0x31, 0x31, 0x31]

对于的ascii字符串就是test 1111

他这里做字符串混淆用的是一个很简单的原理,一个数字两次异或同一个值,得到的结果是本身的值。也就是
第一次异或就给字符串混淆了,再异或一次就把数据还原了。

字符串混淆的源文件在lib/Transforms/Obfuscation/StringObfuscation.cpp这个位置,
实现字符串混淆的是一个ModulePass,关于ModulePass可以参考http://llvm.org/doxygen/classllvm_1_1ModulePass.html#details
。在这个pass里面会遍历字符串,然后把字符串和生成的key异或,并替换原始的值,关键代码如下:

在替换了之后为了保证程序可以正常运行,还得加一个函数输还原字符串,还原字符串的
函数生成代码在addDecodeFunction中。在这里添加了.datadiv_decode开始的函数
加上一串随机字符串,里面进行了异或操作,将数据还原。然后将这个函数加入到了entry,这个在
elf文件的话,就会被加入到.init_array,在mach-o文件中就会被加入到__mod_init_func
代码也比较简单,可以参照源码看一下。

前面讲了原理其实很简单,那么怎么还原字符串呢,其实也有很多方式,第一种是内存dump,因为他会在
初始化程序的时候就把原始字符串还原回去。但是有时候我就行静态分析,不想执行之后去dump。如果只
静态分析,也可以去人工还原字符串。但是如果字符串很多,人工还原工作量很大。其实我们还可以使用
unicorn之类的工具,模拟去执行他的指令,把字符串进行还原。

因为不同操作系统可执行文件格式不一样。为了简单点,我们直接写一个ida插件。所以需要以下工具:

idautils.Functions()可以遍历函数,遍历匹配含有datadiv_decode的函数,保存他们
的起始地址,代码很简单,如下:

我这里分配内存的想法是直接用ida的api获取data段和text段的内容,以及起始地址,然后在
unicorn里面对于起始分配内存,将data段和text段写进去。

unicorn分配内存还是有些坑,不能直接在任意地址分配,必须得整除1024的才可以,所以需
要稍微计算一下分配的地址。这里对基地址减去对(1024 X 1024)求余的结果作为新的基地址,
然后分配内存的长度增加(1024 X 1024),实现的代码如下

算出起始地址之后使用unicorn的mem_map方法分配内存即可

模拟执行这里也比较简单,直接调用unicorn的emu_start方法,然后传入函数的起始地址即可开始
模拟执行,模拟执行完成之后将data段读出来,模拟执行下一个函数的时候使用这个data加载到内存中。
这样所有的.datadiv_decode函数执行之后data段就被还原了。将还原的data段用ida的patch去
修改掉原始的data,这个时候你看到的字符串就是原始的字符串了。

运行脚本前效果如下:

运行脚本之后效果如下:

这个时候可以选中字符串,然后按a就能得到下面的效果

代码以及二进制文件存放在https://github.com/smartdone/re_scripts/tree/master/ida
其中Armariris_string_obfuscation_bypass.py是ida用来还原Armariris混淆过的字符串的插件。sample里面的test_linux_x86_64和test_macos_x86_64是示例二进制文件。

如果本文说的有错误的地方,请及时指正,谢谢。

 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2020-1-24 17:23 被kanxue编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (12)
雪    币: 4831
活跃值: (479)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持支持
2019-6-26 14:49
0
雪    币: 209
活跃值: (38)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
2019-6-26 14:50
0
雪    币: 209
活跃值: (38)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
2019-6-26 14:51
0
雪    币: 29
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
好强!学习了!感谢楼主分享!
2019-6-26 14:56
0
雪    币: 1039
活跃值: (355)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
学习了。
2019-6-26 16:32
0
雪    币: 1935
活跃值: (4180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
unicorn 的妙用mark
2019-6-27 14:28
0
雪    币: 22
活跃值: (242)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
8
最近在试着用unicorn模拟执行来处理pop pc这种混淆变换,还原函数真正入口,效果还不错。
2019-6-28 15:56
0
雪    币: 3549
活跃值: (941)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
9
GeekCheng 最近在试着用unicorn模拟执行来处理pop pc这种混淆变换,还原函数真正入口,效果还不错。
我也准备开始研究这些
2019-6-28 16:10
0
雪    币: 208
活跃值: (484)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
10
GeekCheng 最近在试着用unicorn模拟执行来处理pop pc这种混淆变换,还原函数真正入口,效果还不错。
去汇编插花?是用模拟执行在动态获取跳转地址吗?
2019-7-1 09:22
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
请问一下楼主和网友们,为什么编译完孤挺花之后,我用clang编译那个test.c,它提示识别不了-sobf这个参数,是为什么呢,是不是我孤挺花没有集成到llvm编译环境里啊,求教。。
2020-4-9 21:20
0
雪    币: 3549
活跃值: (941)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
13
Sezangle丶 请问一下楼主和网友们,为什么编译完孤挺花之后,我用clang编译那个test.c,它提示识别不了-sobf这个参数,是为什么呢,是不是我孤挺花没有集成到llvm编译环境里啊,求教。。
你编译完之后,如果没有make install,那么就要使用你编译出来的那个clang来对test.c进行编译(用编译出来的绝对路径或者进入到编译出来的bin目录./clang都行),直接使用clang的话还是系统之前的编译器
2020-4-25 15:01
0
游客
登录 | 注册 方可回帖
返回
//