Android中Hook Instrumentation 的实现

来源:互联网 发布:php域名授权函数 编辑:程序博客网 时间:2024/04/30 22:43

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);    }}

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

代码下载

5、总结

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

0 1
原创粉丝点击