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 的反射机制 可以然我们逃脱限制,无所不能。
更多相关文章
- 1 mkfile
- Android(安卓)webkit 事件传递流程详解
- Android(安卓)Activity (一)
- Android中Parcelable接口用法
- android dexposed框架hook使用实例
- Android(安卓)全局变量 Application
- android之listView缓存机制
- Android(安卓)调用联系人列表,选择联系人
- Android(安卓)通过GPS进行定位