Android中Hook Instrumentation 的实现

之前一直听说有 Hook 这个技术,但是一直不知道有什么作用,今天通过 Hook Instrumentation 小试牛刀过把瘾。
在 Android 中 有个 Instrumentation 这个类,ActivityThread 拥有这个类的实例,并且通过它实现 Activity 很多操作,如 newActivity() ,startActivity(),callActivityOnCreate() 等。比如我们可以 Hook newActivity 这个方法,然后当某个状态还没有到达的时候,去 new 一个 Loading Activity,然后让系统去启动并显示它。

那么如何 Hook Instrumentation 来实现自己想要的行为呢?
通过 ActivityThread 的源码发现,在 main 方法有

ActivityThread thread = new ActivityThread();thread.attach(false);

这两行代码实例化了一个 ActivityThread 对象 并且调用的 attach 方法,把当前的对象赋值给了下面的这个变量 sCurrentActivityThread

private static volatile ActivityThread sCurrentActivityThread;

这个变量是 static 的,所以具体的步骤:
- 通过反射拿到 ActivityThread 的 sCurrentActivityThread 变量的值,然后再通该实例变量拿到mInstrumentation 变量。
- 再通过反射 将 mInstrumentation 变量设置为我们自定义的 CustomInstrumentation 对象。

这样当系统通过 ActivityThread 调用 它的的成员变量 mInstrumentation 调用 newActivity 等方法的时候,实际是调用我们 CustomInstrumentation 的 newActivity(),但前提是 我们的 CustomInstrumentation 继承 Instrumentation,并且重写 newActivity() 方法.最终我们可以里面干”坏事”了。

1、自定义 Instrumentation

public class CustomInstrumentation extends Instrumentation{    private Instrumentation base;    public CustomInstrumentation(Instrumentation base) {        this.base = base;    }    @Override    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {        Log.e("TAG", "invoked  CustomInstrumentation#newActivity, " + "class name =" + className + ", intent = " + intent);        return super.newActivity(cl, className, intent);    }}

CustomInstrumentation 类继承了 Instrumentation,并且重写了 newActivity()

2、将 ActivityThread 的 mInstrumentation 的值设置为 CustomInstrumentation

public class Hooker {    private static final String TAG = "Hooker";    public static void hookInstrumentation() throws Exception {        Class<?> activityThread = Class.forName("android.app.ActivityThread");        Method sCurrentActivityThread = activityThread.getDeclaredMethod("currentActivityThread");        sCurrentActivityThread.setAccessible(true);        //获取ActivityThread 对象        Object activityThreadObject = sCurrentActivityThread.invoke(activityThread);        //获取 Instrumentation 对象        Field mInstrumentation = activityThread.getDeclaredField("mInstrumentation");        mInstrumentation.setAccessible(true);        Instrumentation instrumentation = (Instrumentation) mInstrumentation.get(activityThreadObject);        CustomInstrumentation customInstrumentation = new CustomInstrumentation(instrumentation);        //将我们的 customInstrumentation 设置进去        mInstrumentation.set(activityThreadObject, customInstrumentation);    }}

3、测试

在 MyApplication 代理里调用 hookInstrumentation

public class MyApplication extends Application{    private static MyApplication INSTANCE ;    public static MyApplication getInstance() {        return INSTANCE;    }    @Override    public void onCreate() {        super.onCreate();        INSTANCE = this;        try {            Hooker.hookInstrumentation();        } catch (Exception e) {            e.printStackTrace();        }    }}

启动一个 Activity

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void onClick(View view) {        startActivity(new Intent(this, Main2Activity.class));    }}

最后打印出

05-05 09:26:37.959 32760-32760/com.meyhuan.hookinstrumention E/TAG: invoked  CustomInstrumentation#newActivity, class name =com.meyhuan.hookinstrumention.Main2Activity, intent = Intent { cmp=com.meyhuan.hookinstrumention/.Main2Activity }

4、搞怪一下

将 CustomInstrumentation 的 newActivity 改为 return super.newActivity(cl, Main2Activity.class.getName(), intent);

public class CustomInstrumentation extends Instrumentation{...    @Override    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {        Log.e("TAG", "invoked  CustomInstrumentation#newActivity, " + "class name =" + className + ", intent = " + intent);        return super.newActivity(cl, Main2Activity.class.getName(), intent);    }}

然后启动应用出现如下画面

代码下载

5、总结

虽然最后的例子没有很大的使用价值,但是可以提供一些解决问题的思路,比如当我们在 Application#onCreate() 启动不同的线程,初始化一些 SDK,但是当用户点击进入到某个需要用到这个 SDK 的 Activity 的时候,就可以通过在 CustomInstrumentation#newActivity() 去检测 SDK 是否初始化好了,如果没有就可以显示一个 Loading 的 Activity。Java 的反射机制 可以然我们逃脱限制,无所不能。

更多相关文章

  1. 1 mkfile
  2. Android(安卓)webkit 事件传递流程详解
  3. Android(安卓)Activity (一)
  4. Android中Parcelable接口用法
  5. android dexposed框架hook使用实例
  6. Android(安卓)全局变量 Application
  7. android之listView缓存机制
  8. Android(安卓)调用联系人列表,选择联系人
  9. Android(安卓)通过GPS进行定位

随机推荐

  1. 在Mac下编译Android源码
  2. startService与bindService的区别
  3. Android(安卓)NDK开发 环境搭建
  4. android之DEX文件格式
  5. Android通话默认打开扬声器的方法
  6. Android和Js交互及WebView优化
  7. android合理配置PRODUCT_LOCALES为你的系
  8. 第一篇 Android(安卓)驱动开发之简单概述
  9. 【Android(安卓)adb】 adb环境变量配置
  10. 告别 USB,用 wifi 进行 Android(安卓)真机