目录介绍
- 02.Hook基本流程
- 03.运行期Hook技术
- 04.编译器Hook技术
- 07.反编译apk看代码
02.Hook基本流程
基本流程 1、根据需求确定 要 hook 的对象
2、寻找要hook的对象的持有者,拿到要 hook 的对象
3、定义“要 hook 的对象”的代理类,并且创建该类的对象
4、使用上一步创建出来的对象,替换掉要 hook 的对象
使用示例
/**
* hook的核心代码
* 这个方法的唯一目的:用自己的点击事件,替换掉 View 原来的点击事件
*
* @param view hook的范围仅限于这个view
*/
@SuppressLint({"DiscouragedPrivateApi", "PrivateApi"})
public static void hook(Context context, final View view) {
try {
// 反射执行View类的getListenerInfo()方法,拿到v的mListenerInfo对象,这个对象就是点击事件的持有者
Method method = View.class.getDeclaredMethod("getListenerInfo");
method.setAccessible(true);//由于getListenerInfo()方法并不是public的,所以要加这个代码来保证访问权限
Object mListenerInfo = method.invoke(view);//这里拿到的就是mListenerInfo对象,也就是点击事件的持有者
// 要从这里面拿到当前的点击事件对象
Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");// 这是内部类的表示方法
Field field = listenerInfoClz.getDeclaredField("mOnClickListener");
final View.OnClickListener onClickListenerInstance = (View.OnClickListener) field.get(mListenerInfo);//取得真实的mOnClickListener对象
// 2. 创建我们自己的点击事件代理类
// 方式1:自己创建代理类
// ProxyOnClickListener proxyOnClickListener = new ProxyOnClickListener(onClickListenerInstance);
// 方式2:由于View.OnClickListener是一个接口,所以可以直接用动态代理模式
// Proxy.newProxyInstance的3个参数依次分别是:
// 本地的类加载器;
// 代理类的对象所继承的接口(用Class数组表示,支持多个接口)
// 代理类的实际逻辑,封装在new出来的InvocationHandler内
Object proxyOnClickListener = Proxy.newProxyInstance(context.getClass().getClassLoader(), new Class[]{View.OnClickListener.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d("HookSetOnClickListener", "点击事件被hook到了");//加入自己的逻辑
return method.invoke(onClickListenerInstance, args);//执行被代理的对象的逻辑
}
});
// 3. 用我们自己的点击事件代理类,设置到"持有者"中
field.set(mListenerInfo, proxyOnClickListener);
} catch (Exception e) {
e.printStackTrace();
}
}
// 自定义代理类
static class ProxyOnClickListener implements View.OnClickListener {
View.OnClickListener oriLis;
public ProxyOnClickListener(View.OnClickListener oriLis) {
this.oriLis = oriLis;
}
@Override
public void onClick(View v) {
Log.d("HookSetOnClickListener", "点击事件被hook到了");
if (oriLis != null) {
oriLis.onClick(v);
}
}
}
03.运行期Hook技术
3.1 Xposed
- 如果你对Xposed比较熟悉,并且手头有个root的设备安装了Xposed框架,那么直接开发一个Xposed模块来hook指定方法就可以了。
- 由于我的测试设备是有root权限的,Xposed方案对我来说难度不大,不过对于普通用户,有没有免root的方式呢?
3.2 VirtualXposed
- VirtualXposed 是基于VirtualApp 和 epic 在非ROOT环境下运行Xposed模块的实现(支持5.0~10.0)。
- https://github.com/android-hacker/VirtualXposed
- VirtualXposed其实就是一个支持Xposed的虚拟机,我们把开发好的Xposed模块和对应需要hook的App安装上去就能实现hook功能。
3.3 epic
- 在ART上重新实现了Dexposed,有着与Dexposed完全相同的能力和API,项目地址是epic。https://github.com/tiann/epic
- 所以如果不想折腾 Xposed 或者 VirtualXposed,只要在应用内接入epic,就可以实现应用内Xposed hook功能,满足运行hook需求。
- epic 存在兼容性问题,例如Android 11 只支持64位App,所以建议只在debug环境使用。
04.编译器Hook技术
- 手把手讲解 Android Hook入门Demo
- https://www.jianshu.com/p/74c12164ffca?tdsourcetag=s_pcqq_aiomsg
- ASM hook隐私方法调用,防止App被下架
- https://juejin.cn/post/7043399520486424612
- hook库
- https://github.com/eleme/lancet
07.反编译apk看代码
- 反编译apk文件查看源代码工具
- apktool
- dex2jar
- jd-gui
- 三个工具介绍
- apktool
- 作用:资源文件获取,可以提取出图片文件和布局文件进行使用查看
- 下载地址:https://ibotpeaches.github.io/Apktool/
- dex2jar
- 作用:将apk反编译成java源码(classes.dex转化成jar文件)
- 下载地址:https://github.com/pxb1988/dex2jar
- jd-gui
- 作用:查看APK中classes.dex转化成出的jar文件,即源码文件。
- 下载地址:https://mac.softpedia.com/get/Development/Java/JD-GUI.shtml
- 将生成的classes-dex2jar.jar直接拖动进入界面中,就可以看到反编译之后的源码结构了。
- 注:jd-gui 可视化反编译查看 .jar 文件。这里存在一个坑,jd-gui只能适配java8,否则无法正常打开运行
- apktool
- 反编译流程
- 第一步:apk反编译得到程序的源代码。直接执行:apktool d xxx.apk
- 执行命令apktool d apk文件会生成一个文件夹,这个文件夹下可以看到apk中的资源文件,如图片,xml文件等等
- 运行命令:apktool b 反编译生成的文件夹,会生成build文件夹。其中classes.dex文件就是我们所需要的了,不同的apk会生成不同数量的classes.dex文件
- 第二步:直接运行./d2j-dex2jar.sh apk路径
- 可以在dex2jar文件夹中看到classes-dex2jar.jar文件,这个就是最终的java文件
- 第三步:直接打开gui,把编译的jar直接拖到该工具中查看,即可完成操作。
- 第一步:apk反编译得到程序的源代码。直接执行:apktool d xxx.apk