之前Anubis这个安卓恶意软件家族比较流行,而且通过官方的应用商店进行传播。
Android生态系统中的打包程序
下面说的打包程序就是我们常说的加固,但是呢,恶意软件也用,用于隐藏他们恶意的payloads。这包含反射,混淆,控制流平坦化和垃圾代码,当然Anubis 也是使用了这些来阻碍我们分析。
在运行时加载类
Android应用程序必须在AndroidManifest文件中定义其使用的服务,接收器和活动类才能使用它们。在Anubis示例中,很明显,清单文件中未定义的许多类仅存在于源代码中。
这意味着应在运行时将具有未定义类的文件加载到应用程序中。Android中有两种主要的运行时加载方式:
从文件:
1、API26之后,dalvik.system.DexFile.loadDex
2、dalvik.system.DexClassLoader
3、dalvik.system.PathClassLoader
从内存:
1、dalvik.system.InMemoryDexClassLoader(在恶意软件中不常见)
从文件加载需要在文件系统中存在一个dex / jar文件。Anubis解压缩加密的数据文件,然后删除解密的版本。后来,恶意软件继续将解密的dex加载到应用程序中。使用DexClassLoader加载后,恶意软件会删除解密的dex文件。跟踪dexClassLoader应该使加载例程清晰可见。由于dexClassLoader是dalvik.system的类,因此在代码中应包含“ dalvik.system.dexClassLoader”包,但找不到它。
反射
处理恶意软件时,另一个有用的方法是反射。反射是Java中的一个重要概念,它使您可以在不了解方法/类的情况下调用它们。有几种反映的类/方法。
- java.lang.Class.forName
- java.lang.ClassLoader.loadClass
- java.lang.reflect.Method
- java.lang.Class.getMethods
forName的用法示例
1 | cObj = Class.forName("dalvik.system.dexClassLoader"); |
cObj变量保存dexClassLoader的类对象。这使程序可以调用任何给定类的方法。问题是找到对反射方法进行函数调用的位置。
使用Frida抓取packers
frida是几乎每个操作系统都支持的动态检测工具包。Frida使得可以注入一段代码来操纵目标程序并跟踪程序调用。在这种情况下,它将用于跟踪进行了哪些反射调用,从而分析线程。进行前面提到的函数调用时,将另外调用console.log。但是在此之前,让我们快速回顾一下如何在Android模拟器上设置Frida。
从以下位置下载适合您的仿真器的frida-server:(
例如Genymotion使用x86架构。)
https://github.com/frida/frida/releases。
1 | adb push frida-server /data/local/tmp |
Frida工具在主机中安装
1 | pip install frida-tools |
设置完成后,我们可以编写一个脚本来挂钩目标方法。先定义一些类方法等
1 | var classDef = Java.use('java.lang.Class'); |
我们将使用此代码段更改方法的实现
1 | class.targetmethod.implementation = function(){ |
console.log(“[+] {x} function catched !”) 将使我们能够查看该函数是否被调用。如果函数采用任何参数(例如字符串),则在分析过程中记录这些参数可能会有所帮助。然后,我们可以获得有关所处线程的更多信息。Frida可以调用任何Android函数,包括getStackTrace()。但这需要引用当前线程对象。让我们从获取线程类的实例开始:
1 | var ThreadDef = Java.use('java.lang.Thread'); |
ThreadObj保存Thread类的实例,currentThread()可用于获取线程。调用getStackTrace(),我们可以遍历stackElements来打印调用堆栈。
1 | function stackTrace() { |
打印调用堆栈有助于识别反射和拆包机制的调用图。例如,dexClassLoader可能创建了反射。但是,当frida hook了dexClassLoader并打印调用堆栈时,我们可以在调用dexClassLoader之前看到这些函数。在应用程序的最开始就调用了解包例程。因此,应该尽快安装frida以赶上解包过程。幸运的是,frida中的-f选项使frida能够生成目标应用程序本身。frida接受带有-l参数的脚本。
frida -U -f appname -l dereflect.js
然后,frida等待用户输入继续。%resume将恢复该过程。完整脚本可在我的github存储库中找到。
https://github.com/eybisi/nwaystounpackmobilemalware/blob/master/dereflect.js
带stackTrace()的输出
您可以看到在write方法之前调用的函数。跟踪这些间隔函数后,您可以在它们之前看到RNlkfTEUX和lqfRafMrGew被调用。事实证明,它们是用于解密加密文件的非常重要的功能,稍后我们将再次介绍。
如何脱壳呢
- 动态
hooking:拦截file.delete (Java level),或者拦截unlink syscall (system level)
从内存里dump:用gameguardian来转存内存或者使用自定义工具转储内存
静态
手动干(作者应该是写脚本的意思吧)
动态的拦截是最简单的办法
通过挂钩:Java级别
当我第一次遇到Anubis并意识到它正在删除文件时,我的第一个解决方案是挂钩到file.delete函数。
1 | Java.perform(function() { |
这段代码始终将true返回给file.delete函数。截获后,我们就可以知道文件路径,获取到那个jar了
除此之外,我们还可以使用python调用frida使工作自动化,并浏览目标文件所在的文件夹。这些c&c服务器通常会生成数千个apk。由于它们每个都可以嵌入不同的IP地址,因此自动化工具可以使我们的生活更轻松。
具体参考这个:https://twitter.com/0xabc0/status/1072888987285630976
通过挂钩:系统级别
但是,如果恶意软件使用本机代码删除文件怎么办?我们不能总是钩在Java级别。我们需要更深入。
Unlink 函数有一个参数, 一个文件名的指针. 我们可以通过findExportByName来帮助我们hook. 代码来源于https://www.fortinet.com/blog/threat-research/defeating-an-android-packer-with-frida.html
但我稍微修改,会打印已删除的文件。
1 | var unlinkPtr = Module.findExportByName(null, 'unlink'); |
运行结果:
我们截获了unlink调用,因为我们的脚本只是用console.log替换了原始函数的代码,所以文件不会从文件系统中删除。
从内存dump
即使由于文件已加载到进程而从文件系统中删除文件,我们也可以从该进程的内存中获取已删除文件的痕迹。由于Android继承自Linux,因此我们可以使用/proc/pid文件夹为我们提供有关指定进程的内存区域的信息。让我们看一下cat /proc/pid/maps | grep dex过滤dex的目标。
我们发现了dex文件的踪迹。现在我们需要转储这些部分。
使用Gameguardian来转储内存:
这种方法是“作弊”,有一个称为GameGuardian的工具可用于游戏黑客。您可以使用GameGuardian做很多有趣的事情,但是我们现在仅使用转储机制。
让我们从安装和运行APK开始。然后启动GameGuardian,然后从左上方的按钮中选择应用程序名称。选择最右边的按钮及其下面的按钮。现在,您可以在菜单中看到转储内存选项。通过单击箭头按钮放置区域的十六进制代码或选择区域,然后按保存。
我们可以使用以下方法拉出转储的东西
1 | adb pull /storage/emulated/0/packer . |
然后您将在packer文件夹中看到2个文件
1 | com.eqrxhpdv.cbunlkwsqtz-dfb5a000-e0080000.bin com.eqrxhpdv.cbunlkwsqtz-maps.txt |
当使用file命令检查时,它会将我们的dex文件检测为数据文件,所以我们需要删除不属于dex文件的部分来修复。
使用自定义工具转储内存:
感谢@theempire_h,我们可以使用C程序转储目标应用程序的内存区域。
https://github.com/CyberSaxosTiGER/androidDump
1 | adb push androidDump /data/local/tmp |
它转储3个数据块
但是转储后,file命令仍然没有为我们提供正确的类型we事实证明,我们应该对文件进行一些修改。为了找到dex的魔术数字,我编写了此脚本。
https://github.com/eybisi/nwaystounpackmobilemalware/blob/master/deDex.py
1 | import binascii |
修复后我们就可以打开了
我们找到了失去了的class
静态方法:
这是一篇博客文章,从不同的角度解释了解包过程。
https://sysopfb.github.io/malware,/reverse-engineering/2018/08/30/Unpacking-Anubis-APK.html
我在stackTrace的帮助下找到了rc4密钥。但是显然,寻找^这个符号是从Anubis找到RC4例程的一种非常有效的方法。
在JADX中轻松找到rc4密钥,这里有个tips
- 搜索 “% length”
- 右键单击要使用的方法,然后点 find Usage
- 下面的bArr2用作rc4密钥进行解密
有了密钥,我们可以从APK的images文件夹中解密加密的文件。脚本带有2个参数,即bArr2和加密的文件
https://github.com/eybisi/nwaystounpackmobilemalware/blob/master/anubis_manual.py
解密并解压缩后,我们得到了dex
提取配置文件后,还有一个步骤来获取c&c服务器的地址。恶意软件从电报地址的页面获取,并用ASCII字母更改汉字。然后,它处理base64字符串。解码base64后,它用于service解密使用rc4方案加密的数据。这是一个将中文字符解密为c&c地址的代码段。(就是将中文一一对应数字字母,还原出数字字母是base64加密的,解密一下就好)
https://github.com/eybisi/nwaystounpackmobilemalware/blob/master/solve_chinese.py
我设法用Androguard在没有在模拟器中运行APK,去解密了Anubis payloads。转储dex文件后,我的脚本将找到打印c2和加密密钥的config类。Config Class位于a,b or c 或者较新版本的ooooooooooooo{0,2}o中
通过检查类源代码中“ this”关键字的计数,我设法解密了所有版本的anubis。这是我的脚本的输出,用于从Anubis示例中获取c2和密钥。
结论
有很多解压缩安卓恶意软件和跟踪pack机制的方法,我们会在未来看到恶意软件使用dalvik.system.InMemoryDexClassLoader,如果使用此选项,则删除挂钩将无法捕获已删除的文件,因为一切都将在内存中完成,但是转储内存将捕获这些方法。知道不同的方式总是有帮助的。