最近了解了下android中的hook技术,一段代码黑掉了activity的启动过程,Demo中涉及到framework层activity的启动机制,JAVA的反射+代理机制,不得不说掌握这个技术需要深厚的技术功底才行。文章地址:在这里,写这篇博客是记录下自己对其中用到的技术的理解。
调用startActivity会进入framework,然后通过IActivityManager对象的startActivity方法启动activity,Demo中的做法总结下就是以下几步:
- 通过反射,拿到IActivityManager对象;
- 自定义一个代理类的调度类,它要实现InvocationHandler的invoke方法;
- 以IActivityManager为参数,创建调度类实例;
- 以IActivityManager和调度类实例为参数,生成一个代理类的实例;
用代理类的实例,替换第一步拿到的IActivityManager对象。
下面通过代码,一步一步解析其中的原理。
拿到IActivityManager对象
startActivity会调用到Instrumentation类中的execStartActivity方法,并执行其中的下面的代码:
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
ActivityManagerNative.getDefault()返回的是一个IActivityManager对象:
gDefault是什么?
它是一个Singleton类型的对象,通过get方法,可以拿到其中的实例:
这个实例,就是IActivityManager,它包含了一系列的启动activity的方法:
hook的核心思想,是通过反射,将系统对象替换成我们创建的代理类对象,那么我们先要获得这个IActivityManager对象,也是通过反射。
关于反射,参考这里
通过反射,我们可以拿到一个类的private成员或private方法,具体步骤如下:
class Point{
private int x;
public int y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
public class FiledReflect {
public static void main(String[] args) throws Exception {
Point p1=new Point(5, 39);
Point p2=new Point(3, 9);
</span>
//p1.getClass()是拿到p1对应的类,getField("y")是拿到这个类对应的某个field
Field fieldY = p1.getClass().getField("y");
//为了拿到field的具体值,需要某个具体的instance
int y = (int) fieldY.get(p1);
System.out.println(y);
//对私有成员变量,可以进行暴力反射
Field fieldX = p1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);//暴力反射
int x = (int) fieldX.get(p1);//取对应p1对象的x字段的值
System.out.println(x);
}
}
**field是属于某个类的,fieldvalue是属于某个对象的**
回到Demo中,通过下面的代码获得IActivityManager实例,注释很清楚了:
//通过完整的类名拿到class
Class<?> ActivityManagerNativeClss = Class.forName("android.app.ActivityManagerNative");
//拿到这个类的某个field
Field gDefaultFiled = ActivityManagerNativeClss.getDeclaredField("gDefault");
//field为private,设置为可访问的
gDefaultFiled.setAccessible(true);
//拿到ActivityManagerNative的gDefault的Field的实例
//gDefault为static类型,不需要传入具体的对象
Object gDefaultFiledValue = gDefaultFiled.get(null);
//拿到Singleton类
Class<?> SingletonClass = Class.forName("android.util.Singleton");
//拿到类对应的field
Field mInstanceField = SingletonClass.getDeclaredField("mInstance");
//field是private
mInstanceField.setAccessible(true);
//gDefaultFiledValue是Singleton的实例对象
//拿到IActivityManager实例
Object iActivityManagerObject = mInstanceField.get(gDefaultFiledValue);
到这里,终于拿到了系统startActivity用到的IActivityManager,下面我们要创建一个代理类替换它。
自定义一个代理类的调度类
java有个代理类:
代理类实现了一些申明的接口,和调用InvocationHandler的方法。不是很好理解,我们再看InvocationHandler:
简言之,就是代理类的方法,都会通过InvocationHandler的invoke方法来处理。注释中也给出了用法,通过override它的invoke方法实现代理功能(就是做一些其它事)。
下面就来实现这个自定义InvocationHandler:
private class AmsInvocationHandler implements InvocationHandler {
private Object iActivityManagerObject;
private AmsInvocationHandler(Object iActivityManagerObject) {
this.iActivityManagerObject = iActivityManagerObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.i("HookUtil", method.getName());
//我要在这里搞点事情
if ("startActivity".contains(method.getName())) {
Log.e("HookUtil","Activity已经开始启动");
Log.e("HookUtil","小弟到此一游!!!");
}
return method.invoke(iActivityManagerObject, args);
}
}
就按照注释中的做法,override invoke方法,处理一些其它业务。
代理类实例,传入对应的参数就行了:
AmsInvocationHandler handler = new AmsInvocationHandler(iActivityManagerObject);
为啥要自定义一个InvocationHandler?因为创建一个代理类实例,需要对应的InvocationHandler。
生成一个代理类的实例
AmsInvocationHandler handler = new AmsInvocationHandler(iActivityManagerObject);
Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[]{IActivityManagerIntercept}, handler);
传入当前线程的classloader,代理对象需要实现的接口,和InvocationHandler实例,就创建了一个代理类实例。接口就是系统的IActivityManager类。
替换第一步拿到的IActivityManager对象
依然是通过反射:
mInstanceField.set(gDefaultFiledValue, proxy);
将gDefaultFiledValue这个Singleton类的mInstance field的value改为创建的proxy对象。
完整代码和在Demo中的调用
public class HookUtil {
private Class<?> proxyActivity;
private Context context;
public HookUtil(Class<?> proxyActivity, Context context) {
this.proxyActivity = proxyActivity;
this.context = context;
}
public void hookAms() {
try {
//通过完整的类名拿到class
Class<?> ActivityManagerNativeClss = Class.forName("android.app.ActivityManagerNative");
//拿到这个类的某个field
Field gDefaultFiled = ActivityManagerNativeClss.getDeclaredField("gDefault");
//field为private,设置为可访问的
gDefaultFiled.setAccessible(true);
//拿到ActivityManagerNative的gDefault的Field的实例
//gDefault为static类型,不需要传入具体的对象
Object gDefaultFiledValue = gDefaultFiled.get(null);
//拿到Singleton类
Class<?> SingletonClass = Class.forName("android.util.Singleton");
//拿到类对应的field
Field mInstanceField = SingletonClass.getDeclaredField("mInstance");
//field是private
mInstanceField.setAccessible(true);
//gDefaultFiledValue是Singleton的实例对象
//拿到IActivityManager
Object iActivityManagerObject = mInstanceField.get(gDefaultFiledValue);
AmsInvocationHandler handler = new AmsInvocationHandler(iActivityManagerObject);
Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[]{IActivityManagerIntercept}, handler);
mInstanceField.set(gDefaultFiledValue, proxy);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private class AmsInvocationHandler implements InvocationHandler {
private Object iActivityManagerObject;
private AmsInvocationHandler(Object iActivityManagerObject) {
this.iActivityManagerObject = iActivityManagerObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.i("HookUtil", method.getName());
//我要在这里搞点事情
if ("startActivity".contains(method.getName())) {
Log.e("HookUtil","Activity已经开始启动");
Log.e("HookUtil","小弟到此一游!!!");
}
return method.invoke(iActivityManagerObject, args);
}
}
}
在Application类oncreate中,调用:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HookUtil hookUtil=new HookUtil(SecondActivity.class, this);
hookUtil.hookAms();
}
}
之后在app的任意地方调用startActivity都能看到代理对象在invoke中添加的log了。
Demo始终是最好的学习方法。