Hi ,大家好 我是珍惜
给大家简单 介绍一下 如何用Xposed 实现 对某个类的 替换 覆盖
在不编译源码的情况 直接修改 原Class 内容
(之前的这篇帖子 也不错 xposed实现 Native层 Hook
当我们 在 findClas的时候 其实 我们最终都会去 dexElements数组里面 去寻找 我们需要的类
路径 为 :
ClassLoder.java->
BaseDexClassLoader.java->
DexPathList.java-> dexElements
这个寻找其实是 遍历 如下图
也就是说 我们只需要 我们想加载的 类 放在前面 在通过 反射 set 即可 做到 热修复
腾讯的 Tinker其实大概原理也是这样 阿里的是 动态修改 了 insns数组 直接修改 某个方法的字节码
这也是 热修复 分为冷插拔 和热插拔的 区分 (是否需要 二次启动 )
后面会详细 跟大家说 具体 原因
下面给大家 简单复现一下流程
很简单 当我点按钮的时候 会进行Testclass的 获取 会进行遍历 elements数组 生成这个类
调用方法 以后 肯定会崩掉 这种情况 xposed无论如何Hook都 很无力 (可以Try)
我们先首先生成一份正确的 dex 如下图
可以看到 在 Test函数 里面 我已经将 原来的异常 换成 Log 打印
xposed模块里面 写 先Hook attach拿到 对方进程的 classloader
在 attach 执行完毕 里面 进行 修复 因为这个时候 dex已经 加载完毕了 直接进行修复即可
(需要提前开启 被修复 app的 sd卡读写权限 因为 我是用对方的进程去读写 sd卡 内容的 )
在HookMain里面 进行 class的 初始化 拿到 每个 classloader里面的 element数组
然后进行合并 详细 功能 可以看一下 注释
将生成的dex放到 sd/dex/目录下面
先拿到 加载正确类的 classloader自己手动创建
然后 开始拿到 正确的 classloader
传入 对应的 classloader 通过反射的方式 拿到 element数组
拿到 两个 classloader里面的 elements数组 然后进行合并
当我们 把合并以后的 classloader 里面的 element数组 在 set原来的 回去
参数1 是 合并以后的 element数组
参数2 是 合并之前两个数组的大小 (用于判断是否合并成功 )
激活xposed模块 并运行
可以 看到 替换是成功的 壳子的不落地加载也是这个原理
如果 xposed取消 钩子 可以看得到 程序还是老样子进行崩溃 了
这块 有几个坑 跟大家说一下
问题1; 为什么 我不用 一个静态方法 作为 Test 如下图
如果 这个 这个方法是静态的 在MianActivity的时候 就会进行初始化 这个时候 这个类已经被初始化了
在此调用的时候他就不会 去findclass也就不会遍历 element 数组 所以会修复无效 因为 我现在
每一次 都是 new一个 新的对象 这个 对象 是未经过 初始化的 所以 需要 需要遍历 element数组 拿到 class 这个时候 可以完成热修复
问题2;热修复的 方式 为什么 分为冷插拔 和 热插拔
Tinker就属于 冷插拔 就是 在 错误 的类 element数组 前面 加入 我们自己需要的 正确的类 这种方式需要 App重启 才可以
阿里的 就属于热插拔 他是直接修改 错误方法的字节码 Insns数组 可以直接 修改 对应的 Bug 这种不需要重启
问题3 ; 这个xposed修复 的作用是什么 ?他有什么价值 ?
我可以在不编译 源码的情况 直接 修改源码里面的内容 随时可关,随时可开
只需要拿到 系统的 classloader 在加载自己修改 过以后的 内容 如上述操作 即可
具体项目 如下