Frida--Android逆向之动态加载dex Hook(上)
基础环境
Mac Os 10.15.5 |
文章使用的是DDCTF2018的android逆向第二题Hello Baby Dex
示例地址:下载
知识点
Robust热修复框架原理 |
APK安装

输入字符并验证,错误会Toast出一些信息,可能输入正确的值才能过获取flag直接进入分析
静态代码分析
导入jeb中查看AndroidManifest.xml 找到程序的入口MainActivity
<activity android:name="cn.chaitin.geektan.crackme.MainActivity"> |
在命令行中使用apktool d xx.apk
,将APK文件反编译出来

接下来定位到MainActivity的onCreate()方法,可以看到一些可以的变量和方法比如~~上PatchProxy
,changeQuickRedirectd
等
同时看到在else中调用了this.runRobust()方法,并且在每个类中都会存在一些changeQuickRedirect变量,以及isSupport(),accessDispatch()方法。
private void runRobust() { |
在runRobust()方法的else中实例化了两个对象跟进第一个PatchMainpulateImp中
发现在fetchPatchList()方法中 调用了arg17.getAssets().open(**"GeekTan.BMP"**);
从资源文件夹中,还在了一个BMP的的图片文件,并将BMP文件内容写入GeekTan.jar中
具体代码如下:
try { |
查看对应的BMP文件发现是一个压缩包,里面存在一个dex文件
╰─$ file GeekTan.BMP |
╰─$ binwalk GeekTan.BMP |
同时在fetchPatchList()方法中实例化一个Patch对象
Patch v13 = new Patch(); |
在方法的最后调用
v0_6 = "cn.chaitin.geektan.crackme.PatchesInfoImpl"; |
然后到runRobust(),接下来实例化了PatchExecutor类,可以看到第二个参数就是PatchManipulateImp类的实例
Context v1_1 = this.getApplicationContext(); |
这个PatchExecutor类是在com.meituan.robust
包下的,是一个第三方包,目前美团官方已经将其开源,在此暂停上面的分析,简单学习一下Robust。
Robust热修复框架原理
Robust是美团推出的一款热修复框架,可以在github上面下载它的最新的源码
Robust的基本原理,主要从下面4个步骤进行学习
1.将APK代码中每个函数都在编译打包阶段自动插入一段代码
例如,原函数:
public long getIndex(){ |
他会被处理成这样:
//在该类中声明一个接口变量changeQuickRedirect |
- Robust为每个class增加了个类型为ChangeQuickRedirect的静态成员
- 每个方法前都插入了使用changeQuickRedirect相关的逻辑
- 当changeQuickRedirect不为null时,会执行到accessDispatch方法从而替换掉之前老的逻辑,达到修复的目的。
2.生成需要修复的类及方法的类文件并打包成dex
接下来你可能已经将需要修复的类及方法写好了,这个时候调用Robust的autopatch文件夹中的类及方法会生成如下主要文件:PatchesInfoImpl.java,xxxPatchControl.java(其中xxx为原类的名字)。
PatchesInfoImpl.java的内是由PatchesInfoFactory类的createPatchesInfoClass生成的,这是它生成PatchesInfoImpl逻辑,可以看到,这个类其实是用拼接得到的。
具体代码如下
private CtClass creatxePatchesInfoClass() { |
生成的PatchesInfoImpl类型及类内容如下
public class PatchesInfoImpl implements PatchesInfo { |
另外还会生成一个xxxPatchControl类,通过PatchesControlFactory
的createControlClass()
方法生成,具体的逻辑和生成PatchesInfoImpl类类似,其中每个Control类中都存在以下静态成员变量和方法。
private CtClass createControlClass(CtClass modifiedClass) throws Exception { |
将含有PatchesInfoImpl.java和xxxPatchControl.java,以及xxxPatch.java(具体修复的类)打包成dex文件。
public class xxxPatchControl implements ChangeQuickRedirect |
3.加载动态dex文件,以反射的方式修改替换旧类
回到我们刚刚分析的地方,跟进PatchExecutor类看看。
public void run() { |
在run方法中,主要做了2件事。
1.获取补丁列表
ist<Patch> patches = fetchPatchList(); |
2.应用补丁
applyPatchList(patches); |
跟进patch()进行分析
protected boolean patch(Context context, Patch patch) { |
4.isSupport和accessDispatch
我们再来看看onCreate()中的代码,虽然混淆后代码看起来很冗长,但是通过刚刚对Robust原理的简单分析,现在已经可以清晰的知道,这其实就是isSupport()和accessDispatch()。
public void onCreate(Bundle arg13) { |
public static boolean isSupport(Object[] paramsArray, Object current, ChangeQuickRedirect changeQuickRedirect, boolean isStatic, int methodNumber, Class[] paramsClassTypes, Class returnType) { |
通过上面的分析,可以知道只有当存在补丁的类changeQuickRedirect.isSupport()才会返回值。这个时候我们把刚刚第二步打包的dex反编译看看,我们可以看到在xxxPatchControl类中存在isSupport,它返回的值其实就是methodNumber。
public boolean isSupport(String methodName, Object[] paramArrayOfObject) { |
accessDispatch()方法,替换原方法。
public static Object accessDispatch(Object[] paramsArray, Object current, ChangeQuickRedirect changeQuickRedirect, boolean isStatic, int methodNumber, Class[] paramsClassTypes, Class returnType) { |
具体看看PatchControl类中的accessDispatch。
public Object accessDispatch(String methodName, Object[] paramArrayOfObject) { |
Robust的基本原理就是这些了
hook点分析
我们对Robust进行分析,现在已经比较清晰的知道了我们需要攻克的难点,它是通过Robust热修复框架将一些方法热修复了,所以我们这里必须知道,它修复了哪些类及方法。
foremost提取assets文件夹下的GeekTan.BMP,得到dex文件直接扔到jadx中进行分析。
─$ foremost GeekTan.BMP |
在PatchesInfoImpl类中可以看到2个要被修复的类信息。
MainActivityPatchControl类,我们看到在accessDispatch(),onCreate()和Joseph()方法将会通过判断ethodNumber来选取。
继续查看MainActivity$1PatchControl类,同样发现onClick被修复了。
所以这个时候,我们必须知道onClick真正执行的逻辑是什么。查看MainActivity$1Patch类中的真正的onClick方法。
package cn.chaitin.geektan.crackme; |
分析onClick方法,可以发现很多invokeReflectStaticMethod
,getFieldValue
,invokeReflectMethod
方法,同样我们还能发现flag就在这里面。
//flag是通过append将字符串以及Joseph(int,int)的返回值拼接构成的。 |
通过上面的分析,可以发现hook有2个思路:
1.hook EnhancedRobustUtils
类下的方法获取方法执行的返回值。
2.hook 动态加载的类MainActivityPatch
的Joseph
方法,直接调用它获取返回值。(下篇)
代码构造
先来看看EnhancedRobustUtils类下的方法invokeReflectMethod
。
public static Object invokeReflectMethod(String methodName, Object targetObject, Object[] parameters, Class[] args, Class declaringClass) { |
invokeReflectConstruct
public static Object invokeReflectConstruct(String className, Object[] parameter, Class[] args) { |
很简单,通过反射得到类的实例及方法,最终通过invoke代入参数执行方法。这里很幸运,我们发现这个EnhancedRobustUtils 是Robust自带的类,并不是动态加载的。
那hook就非常简单了,我们只需要简单的hook invokeReflectMethod
获取Joseph的返回值,以及equals的参数即可。
Java.perform(function(){ |
完整代码:
# -*- coding: UTF-8 -*- |
运行脚本 点击Onclick