Hook反射和代理具体实现?
举个例子吧,我们来hook常用的startActivity,在里面加一个打印,具体要怎么做呢?
我们查阅源码得知startActivity 会 调用到startActivityForResult
方法中,该方法中最终通过 mInstrumentation.execStartActivity方法
来执行的
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);
这个mInstrumentation
是Activity
的成员变量,我们就选择Instrumentation
为hook点,用代理Instrumentation
来替代原始的Instrumentation
来完成hook.
1.首先先写出代理Instrumentation
类,取名为InstrumentationProxy
,该类继承Instrumentation
,并包含Instrumentation
的引用.该类实现了execStartActivity方法
,其内部会通过反射找到并调用Instrumentation
的execStartActivity方法
.我们就可以在这个重写的execStartActivity
方法中加入我们的打印
2.接下来在MainActivity
中用InstrumentationProxy
来替换Instrumentation
,可以写一个方法,首先先利用反射找到 Activity
的 mInstrumentation
这个成员变量字段,然后创建我们自己的代理InstrumentationProxy
对象,在利用反射替换 Activity
的 mInstrumentation
的这个字段,然后把一开始找到的mInstrumentation
传入我们的代理InstrumentationProxy
中,这就基本完事儿了
3.我们正常startActivity 就会执行打印
public class InstrumentationProxy extends Instrumentation {
private final Instrumentation mInstrumentation;
public InstrumentationProxy(Instrumentation instrumentation) {
mInstrumentation = instrumentation;
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
System.out.println("---hook成功---"+who);
try {
//反射Instrumentation类的execStartActivity方法
Method method = Instrumentation.class.getDeclaredMethod("execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);
Object invoke = method.invoke(mInstrumentation, who, contextThread, token, target, intent, requestCode, options);
return (ActivityResult) invoke;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
replaceActivityInstrumentation(this);
}
/**
* 通过反射先找到Activity中的mInstrumentation字段
* 然后我们自己创建一个代理InstrumentationProxy,去替换这个字段,
* 然后我们就可以在这个代理InstrumentationProxy中的execStartActivity方法中插入我们的toast了,其实最终还是走的原来的那套逻辑,只不过
* Instrumentation对象被换成了代理InstrumentationProxy对象,然后里面的execStartActivity方法也被重新了
*/
public void replaceActivityInstrumentation(Activity activity) {
try {
Field field = Activity.class.getDeclaredField("mInstrumentation");
field.setAccessible(true);
Instrumentation instrumentation = (Instrumentation) field.get(activity);
InstrumentationProxy proxy = new InstrumentationProxy(instrumentation);
field.set(activity, proxy);
} catch (Exception e) {
e.printStackTrace();
}
}
public void click(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
}
}