VirtualHook—基于 VirtualApp 的 Java 代码 hook 工具
本文大概
2165
字
读完共需
15
分钟
对于 Android 应用安全研究人员来说,Xposed 想必一定不陌生。作为一款流行的应用 hook 框架,Xposed 允许对应用进行无感知的 hook。许多实用工具,例如早期的脱壳工具 Zjdroid,关闭证书强校验的 JustTrustMe,Android 恶意应用分析沙盒 Cuckoo-Droid 等,均是通过 Xposed 完成。
然而,从 Android 5.0 时代起,DVM 模式被 ART 模式取代,代码执行的机制进行了大量的变动。在 ART 模式下安装 Xposed,不仅要求 root 权限,而且需要替换大量的系统库,因此其使用门槛大幅提高。
为此,我们研究并实现了一套 ART 模式下的 hook 工具:VirtualHook。通过这套工具,我们无需 root 权限,便可对应用进行无感知的 hook。本文便对这套工具进行基本的介绍。
基本原理
———————
VirtualHook 是基于以下两个框架实现的:
1. ,这是一套插件框架,允许应用以插件的方式运行在其构造的虚拟空间中,而无需实际安装应用
2. ,这是一套 hook 框架,实现了 ART 环境下的 Java 方法 hook
下面将对其分别进行介绍。
VirtualApp
————————
首先,hook 是什么?hook 就是在应用运行过程中,注入外部的代码,从而改变原有的执行流程。因此,应用中必须存在注入代码的窗口。
例如,使用了热修复技术的应用,就是在启动时加载并应用外部的补丁代码,这类应用便是本身自带了注入代码的窗口。但是,对于逆向分析或安全研究而言,所面对的通常是一个黑盒 App,我们并不知道其是否存在注入代码的窗口,或者存在但无法被利用。为此,我们必须要额外地为应用添加注入代码的窗口。Xposed 便是这样做的:它修改了系统库,对所有应用在启动时添加了注入窗口。但是,这就需要具有 root 权限。
那么,如何在不具有 root 权限的情况下实现呢?这就需要用到 VirtualApp 了。 VirtualApp 的工作原理介绍可见,简而言之,它通过代理常用系统服务的方式,在系统服务层与应用层之间添加了一层虚拟空间,从而允许在不安装应用的情况下运行应用。特别地,VirtualApp 本身并不需要 root 权限。
利用 VirtualApp 提供的虚拟空间,我们就可以实现很多事情了。应用启动时,会初始化 Application,此时会在应用所在的进程中调用 bindApplication()。而 VirtualApp 重写了相关代码,那么我们就可以在把注入代码的窗口放在这里,从而实现应用启动时,加载外部的 hook 代码。
其具体实现也非常简单,在类 VClientImpl 的方法 bindApplicationNoCheck() 中,完成了对应用 Application 的创建:
...
try {
mInstrumentation.callApplicationOnCreate(mInitialApplication);
PatchManager.getInstance().checkEnv(HCallbackHook.class);
if (conflict) {
PatchManager.getInstance().checkEnv(AppInstrumentation.class);
}
Application createdApp = ActivityThread.mInitialApplication.get(mainThread);
if (createdApp != null) {
mInitialApplication = createdApp;
}
} catch (Exception e) {
if (!mInstrumentation.onException(mInitialApplication, e)) {
throw new RuntimeException(
"Unable to create application " + mInitialApplication.getClass().getName()
+ ": " + e.toString(), e);
}
}
VActivityManager.get().appDoneExecuting();
}
我们只需要在这个方法的末尾处,添加注入窗口即可。从而,所有在 VirtualApp 中运行的应用,都会被附加上注入窗口。所以,本文所说的非 root 权限 hook 应用,更准确来说,hook 的是在 VirtualApp 中运行的应用。
YAHFA
————————
通过上述介绍,我们大致明白了如何通过 VirtualApp 实现非 root 环境下注入窗口的添加。那么接下来就需要考虑注入窗口的实现,以及如何完成目标方法的替换了,而这便是 hook 框架的工作。
我们调研了几款 hook 框架,包括 Xposed、AndFix 和 Legend,但发现如果在这里使用的话,多少都存在一定的问题:
1. Xposed 需要进行大量的代码变更,过于复杂,而且方法执行的额外开销会增多
2. AndFix 的代码量相对较小,但其进行了方法的全部替换,不支持原方法的调用;对于热修复来说也许影响不大,但对于逆向分析来说,很多时候我们是通过hook来插桩,所以原方法必须要备份以执行
3. Legend 与 AndFix 的实现方式基本一致,但试用后发现存在某些方法解析失败的问题。于是,我们重新设计实现了一套hook框架,即 YAHFA(Yet Another Hook Framework for ART),专门用于这里的代码hook。
至此,我们有了插件框架和 hook 框架,将其简单地集成,便得到了最终的 hook 工具,VirtualHook。其具有如下优点:
1. 支持 ART 模式,事实上也只支持 ART,因为这就是其设计的出发点,DVM模式下用 Xposed 就 OK 了。
2. 无需 root 权限。由于我们是通过 VirtualApp 为应用添加代码注入窗口,所以不需要修改系统本身的库,因此不需要 root 权限,安装难度降低。
3. 轻量。YAHFA 的工作目标,就是高效地运行 hook。hook方法执行所带来的额外开销仅有2、3条机器指令,执行原方法时甚至没有额外的开销。
4. 方便。由于是在 VirtualApp 中完成 hook,因此添加或删除 hook 插件后,不需要像 Xposed 那样重启设备,只需要退出目标应用再重新进入即可。
5. 精细。理论上来说,可以加载多个 hook 插件,甚至对不同应用使用不同 hook 插件。配合 VirtualApp 应用多开,能够更精细地控制 hook 的行为。
示 例
——————
在 VirtualHook 的 repo中,已经包含了一个示例用的 hook 插件demoHookPlugin,这个插件对以下方法进行了插桩:
AssetManager.open()
File()
URL.openConnection()
编译这个 hook 插件,会得到一个 apk,将其保存到设备 SD 卡的指定位置以待加载。随后启动 VirutalHook,添加并运行应用。在应用的日志中,便能看到插桩打印的日志,将上述方法调用时传入的参数打印出来。
由于 VirtualHook 使用的是 YAHFA 作为 hook 框架,因此 hook 插件的编写需要参考 YAHFA 的规则,具体可见 YAHFA 的。
其他说明
————————
由于 VirtualHook 是 VirtualApp 与 YAHFA 的集成,因此其适用范围就是两者的交集:目前支持的系统是 Android 5.1 和 6.0,支持的架构是 armeabi 和 x86。由于 VirtualHook 的出发点是便于安全研究,因此适配度不是其考虑的主要问题。
特别地,对于加固应用的 hook,目前使用上述的 demoHookPlugin 试验了360,腾讯,梆梆和爱加密。除了梆梆之外,其他加固的应用均可被 hook(爱加密把非错误的日志打印全部重定向了,所以必须通过 Log.e()打印日志)。梆梆加固的应用,在 VirtualApp 上无法运行,因此作为其下游的 VirtualHook,目前对梆梆加固的应用也无法使用。希望能够有人研究解决这一问题。
另一方面,目前 VirtualHook 只是对 Java 方法进行 hook。实际上,VirtualApp中自带了对 native 方法进行 hook 的功能,看雪上已经有利用该功能进行应用脱壳的实践了。后续可以将 native hook 一并整合起来,使 VirtualHook 的功能更强大。
最后放上。目前主要是作为思路验证和 PoC 的形式,所以其易用性和稳定度还有待提高。希望大家在试用中能够发现问题共同解决,以期将 VirtualHook 打造成为更加实用的工具。
本文由看雪论坛 nabla 原创
❤ 往期热门内容推荐
......
更多优秀文章,长按下方二维码,“关注看雪学院公众号”查看!
看雪论坛:http://bbs.pediy.com/
微信公众号 ID:ikanxue
微博:看雪安全
投稿、合作:www.kanxue.com