陈司机 发表于 2021-2-2 21:13

[玩转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。
                            // 下面开始调用注入逻辑
                        }
                  })
                }
            })
      }
    }
}

````

silianwuyi 发表于 2021-2-2 22:03

学习学习

lijs168 发表于 2021-2-2 22:44

大神啊,学习了,非常感谢

shiina0831 发表于 2021-2-3 09:37

谢谢大佬的分享{:1_886:}
页: [1]
查看完整版本: [玩转WX之二]如何让插件稳定运行?一步一步分析Tinker热加载关键流程