学习笔记:插件化 Activity 之 Hook 点位
四大组件之 Activity:
名词解释
AMS:AMS Binder 对象 AMN:app 中获取 AMS 代理的对象 ATP:ApplicationThreadProxy 用于 AMS 和 APP 通信,AT 的代理对象 APT:ApplicationThread APP 中的 Binder 对象。APT 中会利用 mh 这个 Handler 发送消息做对应处理 AT:APT 会调用 AT 得方法,AT 中给 mh 发消息 mh:App 的 Handler,用于接受处理 AMS 等系统服务发送的消息处理
原始流程
startActivity 交互过程如下:
App:ContextTheme--->ContextWrapper--->Context--->ContextImpl:ContextImpl 内部调用 Instrumention.startActivity()传入启动 Activity 的类名(用于校验是否注册过没有注册抛异常)等信息,该方法其中通过 AMN 获取 AMS 代理对象和 AMS 进行通信。
AMS 远端:获取要启动的 activity 类名,观察是否在清单文件中注册过(PMS 在安装时会解析对应 APK 清单文件存储到本地)。接着给启动 activity 的进程发送一个 pause 的消息通过 ATP
然后判断该进程是否启动过(没有启动需要先和 zygote 用 socket 通信先创建一个进程 Process.start)1.没有启动过(没有启动需要先和 zygote 用 socket 通信先创建一个进程 Process.start,接着创建 ActivityThread,利用 AMN 把 ATP 传给 AMS(这样 AMS 就可以和 APP 通信了)接着 AMS 在给 ATP 发消息 AT 收到给 mh 发送消进行反射创建 Application 调用 onCreate,在接着就是下面 2 中所说的进程已经启动情况的步骤了)
2.启动过(AMS 会封装 Activity 变成一个 ActivityClientRecord,该对象会携带 cl 后面 mh 会根据这个 cl 去反射创建 Activity。)
APP 中:
ATP 中收到 AMS 发过来的消息取出 ActivityClientRecord,调用 AT 吧这个参数传进去。AT 中调用 mh 的 handlerPerformLaunchActivity。mh 其实是一个 handler,handleCallBack 中来决定调用哪个方法对应于本示例 startActivity 为下面的逻辑
回调中取出 acRecord,利用 acRecord 的 classloader 对象来反射创建对应 activity,并调用 onCreate 方法
通知最开始 pause 的那个 activity 恢复运行~(利用 ATP 也是)至此交互结束
Hook 点
我们可以 Hook 哪些点呢?提炼出精华我们在哪里可以把狸猫换成太子~
第一个点:
Instrumention 利用 AMN 获取到 AMS 代理,将信息传给 AMS 校验(我们在 Instrumention 中拦截这个代理,替换为我们自定义的对象。检测到 startActivity 时将 Activity 换成我们宿主中已经注册了的 StubActivity)好了,现在 AMS 校验过了,他要通知 APP 创建 Activity 对象了。但是这个时候是 StubService 的 Activity。我们需要把他换掉我们真实启动的 Activity。
第二个点:
我们怎么知道真实启动的 Activity 是谁呢?在第一步中其实整个流程传递的都是 Intent,包括我们接受到 ANS 消息时也是 Intent。既然是同一个 Intent 对象。我们在这个 Intent 中动手脚(APP 调用 AMS 把真实启动的 Activity 保存到 Intent 中,在 AMS 通知 APP 创建的时候在取出来)
好,第一个是在 Instrumention 中换成我们的代理接下来就会和 AMS 交互(这里是最后一步下一步就要和 AMS 通信因此选在这里进行 Hook)那么通知 ATP,AT,mh 中都可以进行还原取出 Intent 中真实启动的 activity。
我们在 mh 中最后一步反射创建的地方进行修改(和上面一样,在最接近现场的地方进行 Hook)。将 mh 替换为我们的 mh。这样我们收到 handlerPerformLaunchActivity 的消息时,就可以做我们自己的操作了
不同的 ClassLoader 会加载不同的类,classloader 中如果没有这个 class 是加载不了的。所以我们要用正确的 cl 去加载对象。这个 cl 其实就是插件的 cl,也就是 dexcl。我们在和 AMS 交互的时候可以传入对应的 cl,之后创建再取出正确的 cl。
注意点:
由于 Activity 默认是 Standard 模式,所以宿主中的一个 Activity 可以对应插件中的多个标准模式的 Activity。每次启动都会创建一个实例。但是 Android 中是有 LaunchMode 的,不同的启动模式对应的效果也不同。那么如何支持 LaunchMode 呢
版权声明: 本文为 InfoQ 作者【北洋】的原创文章。
原文链接:【http://xie.infoq.cn/article/fc73ca3e86bd52da450b49ad8】。文章转载请联系作者。
评论