一、首先我们要找到Hook的点
1. 分析
-
我们先大概看下activity的启动流程(图片来自Android 插件化开发指南)
- 当我们调用startActivity的时候,AMS对我们要启动的Activity进行检查,是否在AndroidManifest中声明过,如果没有就报没有在AndroidManifest的错误。这个时候需要欺骗AMS,我们需要hook,要它去检查一个我们预配置的Activity,通过AMS的检查。
- 为什么不能直接hook掉AMS,AMS是系统的进程,管理者所有app的启动,不仅仅只是我们自己的app。
2. 看看源码怎么启动的(主要就是拦截这个方法startActivity)
//通过ActivityManagerNative.getDefault()获取一个对象,开始启动新的Activity
int result = ActivityManagerNative.getDefault().startActivity(whoThread,
who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
3. 看看代码的实现
private Context mContext;
private Class> mProxyClass;
private String TAG = HookUtil.class.getName();
private final String EXTRA_ORIGIN_INTENT = "EXTRA_ORIGIN_INTENT";
public HookUtil(Context context, Class> proxyClass) {
this.mContext = context.getApplicationContext();
this.mProxyClass = proxyClass;
}
public void hookStartActivity() throws Exception {
//1. 通过反射,拿到IActivityManager对象;
Class amnClass = Class.forName("android.app.ActivityManagerNative");
//2. 获得指定的私有属性
Field gDefaultField = amnClass.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
// 获取字段上面的值传递null 证明是属性是static的,此处返回的是
// new Singleton()
Object gDefault = gDefaultField.get(null);
Class singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//不是static的方法 ,需要传入当前使用的对象 此处返回的是IActivityManager
Object iamInstance = mInstanceField.get(gDefault);
Class iamClass = Class.forName("android.app.IActivityManager");
Object proxyInstance = Proxy.newProxyInstance(HookUtil.class.getClassLoader(),
new Class[]{iamClass},
// InvocationHandler 必须执行者,谁去执行
new StartActivityInvocationHandler(iamInstance));
//f.set(obj, "刘德华");
mInstanceField.set(gDefault, proxyInstance);
}
private class StartActivityInvocationHandler implements InvocationHandler {
// 方法执行者
private Object mObject;
public StartActivityInvocationHandler(Object object) {
this.mObject = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 替换Intent,过AndroidManifest.xml检测
if (method.getName().equals("startActivity")) {
Log.e(TAG,"Activity已经开始启动");
Log.e(TAG,"小弟到此一游!!!");
// 1.首先获取原来的Intent
Intent originIntent = (Intent) args[2];
// 2.创建一个安全的
Intent safeIntent = new Intent(mContext, mProxyClass);
args[2] = safeIntent;
// 3.绑定原来的Intent
safeIntent.putExtra(EXTRA_ORIGIN_INTENT, originIntent);
}
return method.invoke(mObject, args);
}
}
4. 初始化插件
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mButton.setText("test");
//初始化插件
HookUtil hookUtil =
new HookUtil(this, ProxyActivity.class);
try {
hookUtil.hookStartActivity();
} catch (Exception e) {
e.printStackTrace();
}
}
@OnClick(R.id.btn)
public void btnOnclick() {
Intent intent = new Intent(MainActivity.this, Main3Activity.class
);
startActivity(intent);
}
5. 打印结果(没有报错,打印了当前activity的stop方法,证明通过AMS的检查了)
com.dhcc.net.plug.HookUtil: Activity已经开始启动
com.dhcc.net.plug.HookUtil: 小弟到此一游!!!
二、启动时替换成我们自己的Actvity
1. 当AMS加载activty完成以后,就要启动activity了,这个时候他是通过ActivityThread类中的Handler去处理的。我们首先看看Handler是怎么分发消息的(我们处理msg.callback,将优先级最大化)
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
2. 替换成为自己的Activity
public void hookLaunchActivity() throws Exception{
// 3.4.1 获取ActivityThread实例
Class> atClass = Class.forName("android.app.ActivityThread");
Field scatField = atClass.getDeclaredField("sCurrentActivityThread");
scatField.setAccessible(true);
Object sCurrentActivityThread = scatField.get(null);
// 3.4.2 获取ActivityThread中的mH
Field mhField = atClass.getDeclaredField("mH");
mhField.setAccessible(true);
Object mHandler = mhField.get(sCurrentActivityThread);
// 3.4 设置当前对象(也就是ActivityThread)的mH的成员变量
Class> handlerClass = Class.forName("android.os.Handler");
Field mCallbackField = handlerClass.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
mCallbackField.set(mHandler,new HandlerCallBack());
}
private class HandlerCallBack implements Handler.Callback{
@Override
public boolean handleMessage(Message msg) {
Log.e(TAG,"handleMessage");
// 每发一个消息都会走一次这个CallBack发放
if(msg.what == 100){
handleLaunchActivity(msg);
}
return false;
}
/**
* 开始启动创建Activity拦截
* @param msg
*/
private void handleLaunchActivity(Message msg) {
try {
Object record = msg.obj;
// 1.从record 获取过安检的Intent
Field intentField = record.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
Intent proxyInent = (Intent) intentField.get(record);
// 2.从safeIntent中获取原来的originIntent
Intent realIntent = proxyInent.getParcelableExtra(EXTRA_ORIGIN_INTENT);
// 3.重新设置回去
if(realIntent != null){
Log.e(TAG,"启动我们自己的activity了");
intentField.set(record,realIntent);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
3. 修改一下OnCreate方法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mButton.setText("test");
//初始化插件
HookUtil hookUtil =new HookUtil(this, ProxyActivity.class);
try {
hookUtil.hookStartActivity();
hookUtil.hookLaunchActivity();
} catch (Exception e) {
e.printStackTrace();
}
}
4. 看看打印结果
com.dhcc.net.plug.HookUtil: Activity已经开始启动
com.dhcc.net.plug.HookUtil: 小弟到此一游!!!
com.dhcc.net.plug.HookUtil: 启动我们自己的activity了
com.dhcc.net E/Main3Activity: 我是没有注册的Activity