[玩转WX之二]如何让插件稳定运行?一步一步分析Tinker热加载关键流程
本帖最后由 陈司机 于 2021-2-2 21:23 编辑系列文章:
上篇 [玩转WX之一]一行命令打印安卓WX数据库密码并取出数据库文件到电脑查看
有兴趣的请关注,系列文章保持高实用性,不说废话,点明过程中的重点,最后直接给出结果。
基础须知:
1.热加载是可以让安卓App不重新安装但是又能改机运行逻辑的技术,腾讯系软件使用自家的热加载系统Tinker。
2.应用程序会从清单的appliction name开始进行运行, 一把程序加壳,多dex,热加载,都在这里做工作,这是应用程序自己的代码能被最早执行的地方。
3.热加载分成总体分资源,so, dex热加载,业务逻辑代码绝大部分在dex, 所以一般关注dex热加载比较多。
4.系统会先执行Application里面的生命周期函数attachBaseContext,比我们熟悉的onCreate更早。
如果代码是热加载的,很有可能会导致插件运行出错。如下所示:
wx热加载前的classloader
dalvik.system.PathClassLoader,nativeLibraryDirectories=]]
wx热加载后classloader
dalvik.system.DelegateLastClassLoader,nativeLibraryDirectories=]]
为了兼容热加载,我们也要对类加载器进行修正。如何修正热加载器,我们要先对热加载流程做个分析,做到心中有数。
{:301_1001:}
以下代码基于WX7.0.19
我们开始从0开始跟进分析,先查看应用程序入口
来看类com.tencent.mm.app.Application
来看 com.tencent.mm.app.Application 父类 TinkerApplication
类初始化之后,就会由系统调用生命周期函数 attachBaseContext
loadTinker
反射调用真正的tryLoad
使用 tryLoadPatchFilesInternal 进行热加载
快速看下这个函数,发现比较重要的是loadTinkerJars
这个函数也对dex热加载文件进行验证以及读入
由loadTinkerJars进入installDexes
进入installDexes,根据不同的系统环境, 安装热更新
安卓热更新的关键是创建新的TinkerClassLoader,合并原有的classLoader
完成新的类加载器创建,并替换Application的类加载器
替换线程上下文和LoadedApk的classloader
替换classloader完毕,校验是否加载成功,如果成功了,就用tinker_xxx.dex等的类
经过上面分析,我们这样玩:
wx没替换classloader,我们就不替换xp的classloader;
wx替换了classloader,我们跟着替换xp的classloader。
```kotlin
class Hook : IXposedHookLoadPackage {
override fun handleLoadPackage(LoadPackageParam: XC_LoadPackage.LoadPackageParam) {
if (LoadPackageParam.packageName == "com.tencent.mm") {
Log.i("ACCESS", "%%% app_cl 1: ${AndroidAppHelper.currentApplication()?.classLoader}")
// 这个时候,wx未修改classloader,保持xp的不变,
XposedBridge.hookAllMethods("com.tencent.tinker.loader.app.TinkerApplication".getClass(LoadPackageParam.classLoader), "onBaseContextAttached", object :XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
LoadPackageParam.classLoader = XposedHelpers.callMethod(param.thisObject, "getClassLoader") as ClassLoader
LoggerUtil.i("ACCESS", "%%% app_cl 2: ${LoadPackageParam.classLoader}")
XposedBridge.hookAllMethods("com.tencent.mm.app.MMApplicationWrapper".getClass(LoadPackageParam.classLoader), "onCreate", object :XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
// 这个时候tinker有可能已经发生热加载了
val app = XposedHelpers.getObjectField(param.thisObject, "app")
val context = XposedHelpers.callMethod(app, "getApplicationContext") as Context
Log.i("ACCESS", "%% context:${context} cl:${context.classLoader}")
// 使用全局static对这两个关键变量进行保存。
Global.context = context
Global.classLoader = context.classLoader // 后面所有查找类加载类使用这个类加载器。不要再使用xp回调传过来的。或者使用TinkerClassLoader.findClass。
// 下面开始调用注入逻辑
}
})
}
})
}
}
}
````
学习学习 大神啊,学习了,非常感谢 谢谢大佬的分享{:1_886:}
页:
[1]