继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Hook Android 应用启动页

大利猫
关注TA
已关注
手记 15
粉丝 1万
获赞 872
问题

前两天看到联通沃商店的 SDK 能让接入方应用的入口 Activity 在联通的闪屏页面结束之后出现, 我想他是不是才用什么黑科技 hook 了人家应用的入口Activity, 反编译了它的源码看了下,并无什么黑科技, 而是要求接入方声明它的闪屏页面为入口页面,我在想能不能在接入方无知觉的情况下 hook 应用的入口 Activity,先 hold 住等我们自定义的 Activity 完成之后再进入?

问题再简化一下:如何 hook 一个应用的入口 Activity,插入自己的Activity?

思路

我们首先要知道一个应用的入口Activiy是怎么被创建,然后被现实出来的?如果看过插件化的相关实现文章就不难理解了,我们首先来看看用户点击启动图标之后是怎么进入第一个Activity的:

明白了Activity的创建和方法调用流程我们就可以决定在哪个环节 hook 了,不难发现,我们 hook 的最佳时间是 Activity 的 onCreate 方法被调用前, 也就是Instrumentation 的 callActivityOnCreate 方法,我们把它拦截掉, 怎么做?
你需要知道个知识点: 反射代理。我假定读我的文章的人都熟悉反射和代理了。

反射获取 Instrumentation

首先通过反射获取应用进程的 Instrumentation 对象,再来看看 Instrumentation 相关的引用关系:
ActivityThread -> Instrumentation
见 ActivityThread.java 中代码:

Instrumentation mInstrumentation;

现在我们想办法拿到 ActivityThread 的实例对象:

public static final ActivityThread currentActivityThread() {
 return sThreadLocal.get();
}

所以我们可以先反射 currentActivityThread() 方法拿到 ActivityThread 当前实例, 再反射 mInstrumentation 拿到 Instrumentation 对象。

      //获取当前的ActivityThread对象
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);

        //拿到在ActivityThread类里面的原始mInstrumentation对象
        Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
        mInstrumentationField.setAccessible(true);
        Instrumentation mInstrumentation = (Instrumentation) 
      mInstrumentationField.get(currentActivityThread);

获取了 Instrumentation 对象怎么拦截里面的方法?如果是接口实现类直接使用 JDK 的动态代理就能实现拦截, 这里只能使用静态代理, 我们自己创建一个 Instrumentation 的子类 ,就叫 InstrumentationProxy 吧,InstrumentationProxy 代替原来的 Instrumentation。

   //包装代理
        Instrumentation evilInstrumentation = new InstrumentationProxy(mInstrumentation);

        // 偷梁换柱

        mInstrumentationField.set(currentActivityThread, evilInstrumentation);

InstrumentationProxy 的实现:

public class InstrumentationProxy extends Instrumentation {

 public static final String TAG = "InstrumentationProxy";
 public Instrumentation target;

 //通过构造函数来传递对象
 public InstrumentationProxy(Instrumentation mInstrumentation) {
     target = mInstrumentation;
 }
 @Override
public void callActivityOnCreate(Activity activity, Bundle icicle) {
           // 就在这儿,干你想干的坏事

        target.callActivityOnCreate(activity, icicle);
 }

}

设计实现

以上算是完成了可行性技术预研, 接下来回到我们开头提的那个业务问题,就是针对问题的设计了, 首次启动时 hook 入口Acitvity。

首次启动注入广告

demo

https://github.com/liuguangli/HookLaunchActivity

打开App,阅读手记
7人推荐
发表评论
随时随地看视频慕课网APP