Android中Hook Instrumentation 的实现

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 的反射机制 可以然我们逃脱限制,无所不能。

你可能感兴趣的:(Android)