本工具仅用于安全研究和学习目的,请勿用于非法用途得益于Frida优秀的Java以及Native层Hook功能,现在基于Frida的脱壳脚本层出不穷,但是对Frida的检测也日益加强,现在都不必说企业壳,免费壳都配备了不少Frida检测。而Fart类基于修改Android源码进行主动调用脱壳的方案虽然非常完美,但是编译,刷机等操作却会对于刷机小白来说门槛较高,而且对源码的修改考虑不周就可能造成无法预知的错误,而且比较难Debug(而且因为需要Linux进行编译,对电脑配置不高使用虚拟机的人来说也会不大友好)这时Lsposed相对隐蔽的Hook手段和较低的开发成本就会让脱壳这门技术和过检测被划分开来,入门和实操难度也会低很多(但是也有部分厂商是上了比较强的针对lsposed的检测手段的,所以只能应对大多数企业抽取壳情况,而不是全部)Github仓库地址写在帖子末尾,本项目依赖于Xposed/Lsposed主动调用构造方法进行抽取壳的脱壳,但是并不是非常完善,很多功能还有待完善和优化。如果你对本项目感兴趣,非常欢迎参与讨论、魔改、二次开发、提交 Issue 或分享你的思路本项目的应用场景:应对多数未对Lsposed进行有效检测的企业抽取壳和免费壳的Dex加固工作流程:首先在Android上,Dex的动态加载大部分都是依赖于ClassLoader的,最常见的是通过DexClassLoader,PathClassLoader和InMemoryClassLoader进行加载。大多数的壳无非就是将Dex进行不同粒度的加密,但是实际交给ART虚拟机解释执行的时候都要进行动态解密。这就是我们脱壳的核心原理,在Dex解密的时候将其从内存中Dump下来。以下所有源码部分演示均以android-9.0.0_r61作为演示对于每一个App,都有一个非常核心的类:LoadedApk它被用于描述该应用在当前进程中的所有运行时信息,其中就有我们非常感兴趣的APP的真实ClassLoader,并且还为我们提供了getClassLoader方法,返回当前LoadedApk中的mClassLoader字段至于为什么大多数APPLoadedApk中的ClassLoader就是真实的ClassLoader:系统在启动 Activity 时,不会使用你自定义的 ClassLoader,而是使用 LoadedApk 中的 mClassLoader。你的 DexClassLoader 加载的类对系统框架层来说是"不可见"的,当App启动自定义的Activity,Service时就会找不到这些类。所以常见的壳会选择将解密后dex的ClassLoader插入到LoadedApk的mClassLoader中,或者在双亲委派链中加入自定义的ClassLoader。而大部分主流加固方案都采用替换 mClassLoader 的方式因为壳需要解密 DEX 并创建全新的 ClassLoader,这与"替换"方案天然契合。而"插入委派链"方案无法解决"原始 ClassLoader 不认识解密后 DEX"这一根本问题,且控制力,兼容性和安全性都不如直接替换获取LoadedApk的方案有很多种,我们这里选择这条反射链获取ClassLoader,因为偶然间在Android源码中看到了这种获取LoadedApk的方式,所以可能会更稳定一些因为Xposed对于反射获取字段的封装非常友好,所以我们不需要自己写大量反射,只需要借助Xposed的API:当然其实loadPackageParam.classLoader也是从LoadedApk中拿ClassLoader,虽然通过两种方式都可以获取,但是这里为了考虑到LoadedApk的ClassLoader可能被替换,而xposed获取的时机不一定是我们想要的时机,所以反射调用获取会更保险最初想过做一个Frida那样可以枚举ClassLoader的功能:从当前虚拟机的 Runtime 中获取唯一的 ClassLinker*,在一个 ART 线程中、并在线程被 JVM 暂停的状态下主动调用 ClassLinker::VisitClassLoaders,传入一个始终返回 true 的 visitor 回调。 该 visitor 是 Frida 构造的 native 回调函数指针,其签名为 bool visitor(mirror::ClassLoader* loader),(不包含 this 指针)。 ART 在遍历每一个已注册的 mirror::ClassLoader* 时,主动调用该 visitor,并将当前遍历到的 ClassLoader 作为参数传入。 Frida 在回调中将这些 loader 提升为 JNI 全局引用并保存,遍历结束后在 Java 世界中将它们包装为 java.lang.ClassLoader 对象数组返回但是Xposed实现该功能时间成本较高,而且很多壳并不需要这种方式去脱壳,LoadedApk已经够用,所以这里就暂时不做实现了首先众所周知的三大ClassLoader就是而它们都继承自BaseDexClassLoader,没有使用更上层的ClassLoader是因为在BaseDexClassLoader有很方便的DexPathList用于获取DexFile其中的dexElements属于Element这个内部类,其中我们就可以遍历所有dexElements的拿到该ClassLoader对应的所有DexFile可是我们都知道ART虚拟机中的DexFile和我们真正需要的Dex相差甚远,而从Android8系列开始Android源码中去除了DexFile类中的getBytes方法,想拿到我们需要的Dex最好的方法就是拿到Native层的Art::DexFile。而恰好DexFile中有两个比较有趣的字段,是mCookie和mInternalCookie,这两个字段虽然是Object类型,但是其实是long[],内部存储的数据就是指向Art::DexFile绝对地址的指针,所以我们就可以通过将其强转为long[]之后传到Native层去进行读写来Dump(强转之前先做验证)我们就可以封装这样两个函数来从BaseDexClassLoader中Dump我们需要的Dex文件64位下Art::DexFile的内存结构大致如下,我们可以使用偏移直接读,但是这里我选择使用dex\n头进行判断,避免Dump下来一大堆无效数据,当然有需求的话可以修改代码重新编译即可,直接通过对应偏移读取因为我们Native层的读取方式相对Android对开发者的期望来说,可以说是另辟蹊径,所以自然会导致很多问题,其中最重要的问题就是读取到不可读的地址,因为有些地址可能已经被释放了,但是还是被mCookie持有,因为Android本来就没指望有人这样去读取,所以在对一些APP的测试中直接读取就会导致崩溃所以我们需要在读取前借助pipe进行内核层读写,如果地址不可读,内核会返回 -EFAULT,不会让我们想要脱壳的进程崩溃此时在Xposed成功注入到对应APP进程后新建一个Thread,sleep一会之后进行Dump就是最初的JDex1了,
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-290669.htm