一、背景
如果有人问你,未在配置文件中注册的Activity可以启动吗。可能你一开始会回答不行,但是细细思考,你会发现,使用Android Hook等技术启动未注册的Activity也是可以的,这也是Android Hook 插件化技术原理的基础。
使用Android Hook 技术启动未注册的Activity,需要了解Java的反射机制 和Android App启动流程非常熟悉。
下面,我们从两点来讲解Android Hook 技术启动未注册的Activity:
- 通过对Instrumentation进行Hook
- 通过对AMN进行Hook
二、 对startActivity方法进行Hook
通过查阅startActivity的源码,我们可以看到startActivity最终都会走到startActivityFoResult()方法中,源码如下:
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
if(this.mParent == null) {
ActivityResult ar = this.mInstrumentation.execStartActivity(this, this.mMainThread.getApplicationThread(), this.mToken, this, intent, requestCode, options);
if(ar != null) {
this.mMainThread.sendActivityResult(this.mToken, this.mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
}
if(requestCode >= 0) {
this.mStartedActivity = true;
}
} else if(options != null) {
this.mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
this.mParent.startActivityFromChild(this, intent, requestCode);
}
}
接下来,我们再看一下mInstrumentation.execStartActivity()方法。
public Instrumentation.ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread)contextThread;
if(this.mActivityMonitors != null) {
Object e = this.mSync;
synchronized(this.mSync) {
int N = this.mActivityMonitors.size();
for(int i = 0; i < N; ++i) {
Instrumentation.ActivityMonitor am = (Instrumentation.ActivityMonitor)this.mActivityMonitors.get(i);
if(am.match(who, (Activity)null, intent)) {
++am.mHits;
if(am.isBlocking()) {
return requestCode >= 0?am.getResult():null;
}
break;
}
}
}
}
try {
intent.setAllowFds(false);
intent.migrateExtraStreamToClipData();
int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
checkStartActivityResult(var16, intent);
} catch (RemoteException var14) {
;
}
return null;
}
而execStartActivity()方法最终又会走到checkStartActivityResult()方法。所以,如果我们想要对startActivity方法进行Hook么,那么就需要在checkStartActivityResult()方法之前进行Hook。
对mInstrumentation进行Hook
接下来,我们使用一个简单的例子:打印日志来说明如果使用mInstrumentation进行Hook。
首先,打开Activity.class类,在里面我们可以Activity.class类中定义了私有变量Instrumentation。
private Instrumentation mInstrumentation;
我们要做的就是修改这个私有变量的值,在执行execStartActivity()方法前打印一行日志。首先,我们通过反射来获取这一私有变量。
Instrumentation instrumentation = (Instrumentation) Reflex.getFieldObject(Activity.class,MainActivity.this,"mInstrumentation");
然后,将这个Instrumentation替换成我们自己的Instrumentation,所以下面我们新建MyInstrumentation继承自Instrumentation,并且MyInstrumentation的execStartActivity方法不变。
public class MyInstrumentation extends Instrumentation {
private Instrumentation instrumentation;
public MyInstrumentation(Instrumentation instrumentation) {
this.instrumentation = instrumentation;
}
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
Log.d("MyInstrumentation","Instrumentation Hook11111");
Class[] classes = {Context.class,IBinder.class,IBinder.class,Activity.class,Intent.class,int.class, Bundle.class};
Object[] objects = {who,contextThread,token,target,intent,requestCode,options};
Log.d("MyInstrumentation","Instrumentation Hook22222");
return (ActivityResult) ReflexUtil.invokeInstanceMethod(instrumentation,"execStartActivity",classes,objects);
}
}
熟悉Java反射的同学都知道,我们可以使用Class.forName(name)
来获取类名,也可以使用getDeclaredMethod
来获取类的参数。为了方便使用,我们对这些常用的反射进行了封装。
public class ReflexUtil {
/**
* 获取无参构造函数
* @param className
* @return
*/
public static Object createObject(String className) {
Class[] pareTyples = new Class[]{};
Object[] pareVaules = new Object[]{};
try {
Class r = Class.forName(className);
return createObject(r, pareTyples, pareVaules);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取无参构造方法
* @param clazz
* @return
*/
public static Object createObject(Class clazz) {
Class[] pareTyple = new Class[]{};
Object[] pareVaules = new Object[]{};
return createObject(clazz, pareTyple, pareVaules);
}
/**
* 获取一个参数的构造函数 已知className
*
* @param className
* @param pareTyple
* @param pareVaule
* @return
*/
public static Object createObject(String className, Class pareTyple, Object pareVaule) {
Class[] pareTyples = new Class[]{pareTyple};
Object[] pareVaules = new Object[]{pareVaule};
try {
Class r = Class.forName(className);
return createObject(r, pareTyples, pareVaules);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取单个参数的构造方法 已知类
*
* @param clazz
* @param pareTyple
* @param pareVaule
* @return
*/
public static Object createObject(Class clazz, Class pareTyple, Object pareVaule) {
Class[] pareTyples = new Class[]{pareTyple};
Object[] pareVaules = new Object[]{pareVaule};
return createObject(clazz, pareTyples, pareVaules);
}
/**
* 获取多个参数的构造方法 已知className
* @param className
* @param pareTyples
* @param pareVaules
* @return
*/
public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) {
try {
Class r = Class.forName(className);
return createObject(r, pareTyples, pareVaules);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取构造方法
*
* @param clazz
* @param pareTyples
* @param pareVaules
* @return
*/
public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules) {
try {
Constructor ctor = clazz.getDeclaredConstructor(pareTyples);
ctor.setAccessible(true);
return ctor.newInstance(pareVaules);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取多个参数的方法
* @param obj
* @param methodName
* @param pareTyples
* @param pareVaules
* @return
*/
public static Object invokeInstanceMethod(Object obj, String methodName, Class[] pareTyples, Object[] pareVaules) {
if (obj == null) {
return null;
}
try {
//调用一个private方法 //在指定类中获取指定的方法
Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples);
method.setAccessible(true);
return method.invoke(obj, pareVaules);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取一个参数的方法
* @param obj
* @param methodName
* @param pareTyple
* @param pareVaule
* @return
*/
public static Object invokeInstanceMethod(Object obj, String methodName, Class pareTyple, Object pareVaule) {
Class[] pareTyples = {pareTyple};
Object[] pareVaules = {pareVaule};
return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
}
/**
* 获取无参方法
* @param obj
* @param methodName
* @return
*/
public static Object invokeInstanceMethod(Object obj, String methodName) {
Class[] pareTyples = new Class[]{};
Object[] pareVaules = new Object[]{};
return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
}
/**
* 无参静态方法
* @param className
* @param method_name
* @return
*/
public static Object invokeStaticMethod(String className, String method_name) {
Class[] pareTyples = new Class[]{};
Object[] pareVaules = new Object[]{};
return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
}
/**
* 获取一个参数的静态方法
* @param className
* @param method_name
* @param pareTyple
* @param pareVaule
* @return
*/
public static Object invokeStaticMethod(String className, String method_name, Class pareTyple, Object pareVaule) {
Class[] pareTyples = new Class[]{pareTyple};
Object[] pareVaules = new Object[]{pareVaule};
return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
}
/**
* 获取多个参数的静态方法
* @param className
* @param method_name
* @param pareTyples
* @param pareVaules
* @return
*/
public static Object invokeStaticMethod(String className, String method_name, Class[] pareTyples, Object[] pareVaules) {
try {
Class obj_class = Class.forName(className);
return invokeStaticMethod(obj_class, method_name, pareTyples, pareVaules);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 无参静态方法
* @param method_name
* @return
*/
public static Object invokeStaticMethod(Class clazz, String method_name) {
Class[] pareTyples = new Class[]{};
Object[] pareVaules = new Object[]{};
return invokeStaticMethod(clazz, method_name, pareTyples, pareVaules);
}
/**
* 一个参数静态方法
* @param clazz
* @param method_name
* @param classType
* @param pareVaule
* @return
*/
public static Object invokeStaticMethod(Class clazz, String method_name, Class classType, Object pareVaule) {
Class[] classTypes = new Class[]{classType};
Object[] pareVaules = new Object[]{pareVaule};
return invokeStaticMethod(clazz, method_name, classTypes, pareVaules);
}
/**
* 多个参数的静态方法
* @param clazz
* @param method_name
* @param pareTyples
* @param pareVaules
* @return
*/
public static Object invokeStaticMethod(Class clazz, String method_name, Class[] pareTyples, Object[] pareVaules) {
try {
Method method = clazz.getDeclaredMethod(method_name, pareTyples);
method.setAccessible(true);
return method.invoke(null, pareVaules);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static Object getFieldObject(String className, Object obj, String filedName) {
try {
Class obj_class = Class.forName(className);
return getFieldObject(obj_class, obj, filedName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public static Object getFieldObject(Class clazz, Object obj, String filedName) {
try {
Field field = clazz.getDeclaredField(filedName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void setFieldObject(Class clazz, Object obj, String filedName, Object filedVaule) {
try {
Field field = clazz.getDeclaredField(filedName);
field.setAccessible(true);
field.set(obj, filedVaule);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void setFieldObject(String className, Object obj, String filedName, Object filedVaule) {
try {
Class obj_class = Class.forName(className);
setFieldObject(obj_class, obj, filedName, filedVaule);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Object getStaticFieldObject(String className, String filedName) {
return getFieldObject(className, null, filedName);
}
public static Object getStaticFieldObject(Class clazz, String filedName) {
return getFieldObject(clazz, null, filedName);
}
public static void setStaticFieldObject(String classname, String filedName, Object filedVaule) {
setFieldObject(classname, null, filedName, filedVaule);
}
public static void setStaticFieldObject(Class clazz, String filedName, Object filedVaule) {
setFieldObject(clazz, null, filedName, filedVaule);
}
可以看到,在MyInstrumentation类中,我们直接反射execStartActivity方法来和默认的方法保持一致。
(ActivityResult) Reflex.invokeInstanceMethod(instrumentation,"execStartActivity",classes,objects)
然后,再使用我们自定义的MyInstrumentation替换原来的Instrumentation即可。完整代码如下:
Instrumentation instrumentation = (Instrumentation) ReflexUtil.getFieldObject(Activity.class,this,"mInstrumentation");
MyInstrumentation instrumentation1 = new MyInstrumentation(instrumentation);
ReflexUtil.setFieldObject(Activity.class,this,"mInstrumentation",instrumentation1);
2.2 对AMN进行Hook
如果大家去看execStartActivity()方法的源码,就可以看得到,execStartActivity()方法最终会走到ActivityManagerNative.getDefault().startActivity()方法。
try {
intent.setAllowFds(false);
intent.migrateExtraStreamToClipData();
int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
checkStartActivityResult(var16, intent);
} catch (RemoteException var14) {
;
}
继续看ActivityManagerNative的getDefault()方法。
public static IActivityManager getDefault() {
return (IActivityManager)gDefault.get();
}
public final T get() {
synchronized(this) {
if(this.mInstance == null) {
this.mInstance = this.create();
}
return this.mInstance;
}
}
可以看出IActivityManager是一个接口,gDefault.get()返回的是一个泛型,如果直接使用反射是无法入手的,所以我们这里要用动态代理方案。
首先,我们定义一个AmsHookHelperUtils类,在AmsHookHelperUtils类中处理反射代码。
public class AMNInvocationHandler implements InvocationHandler {
private String actionName = "startActivity";
private Object target;
public AMNInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals(actionName)) {
Log.d("AMNInvocationHandler", "I Am AMN Hook");
return method.invoke(target, args);
}
return method.invoke(target, args);
}
}
所有的代理类都要实现InvocationHandler接口,在invoke方法中method.invoke(target,args);
表示的就是执行被代理对象所对应的方法。
然后,我们将IActivityManager接口中gDefault字段替换为我们的代理类,如下。
ReflexUtil.setFieldObject("android.util.Singleton",gDefault,"mInstance",proxy);
我们定义一个AmsHookHelperUtil类,然后添加一个hook方法,里面使用代理的方式进行Hook。
public class AmsHookHelperUtil {
public static void hookAmn() throws ClassNotFoundException {
Object gDefault = ReflexUtil.getStaticFieldObject("android.app.ActivityManagerNative","gDefault");
Object mInstance = ReflexUtil.getFieldObject("android.util.Singleton",gDefault,"mInstance");
Class> classInterface = Class.forName("android.app.IActivityManager");
Object proxy = Proxy.newProxyInstance(classInterface.getClassLoader(),
new Class>[]{classInterface},new AMNInvocationHandler(mInstance));
ReflexUtil.setFieldObject("android.util.Singleton",gDefault,"mInstance",proxy);
}
}
三、如何启动一个未注册的Activity
如何启动一个未注册的Activity,首先我们了解Activity的启动流程,如果还不了解Activity启动流程的,可以参考:Android Activity启动流程分析。
假设,现在MainActivity,Main2Activity,Main3Activity,其中Main3Activity未注册,我们在MainActivity中启动Main3Activity,当启动Main3Activity的时候,AMS会在配置文件中检查,是否有Main3Activity的配置信息如果不存在则报错,存在则启动Main3Activity,这是我们已经知道的常规流程。
所以,如果要启动未注册的Activity,那么我们可以将要启动的Activity在发送给AMS之前,替换未已经注册Activity Main2Activity,这样AMS就可以检验通过,当AMS要启动目标Activity的时候再将Main2Activity替换为真正要启动的Activity即可,也是很多热修复空间的的Hook的原理。
我们按照上面逻辑先对startActivity方法进行Hook,这里采用对AMN Hook的方式。和上述代码一样,不一样的地方在于mInstance的代理类不同。首先, 新建一个AMNInvocationHanlder对象同样继承自InvocationHandler,只拦截startActivity方法。
if (method.getName().equals(actionName)){}
在这里我们要做的就是将要启动的Main3Activity替换为Main2Activity,这样能绕过AMS的检验,首先我们从目标方法中取出目标Activity。
Intent intent;
int index = 0;
for (int i = 0;i
你可能会问,怎么知道args中一定有intent类的参数。因为Java反射的invoke方法中最终会执行下面的代码:
return method.invoke(target,args);
而Android的startActivity()方法的源码如下。
int var16 = ActivityManagerNative.getDefault().startActivity(whoThread, intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null?target.mEmbeddedID:null, requestCode, 0, (String)null, (ParcelFileDescriptor)null, options);
所以说,args中肯定有个intent类型的参数,获取真实目标Activity之后,我们就可以获取目标的包名。
intent = (Intent) args[index];
String packageName = intent.getComponent().getPackageName();
接下来,我们新建一个Intent ,然后将intent设置为Main2Activity的替换者。
Intent newIntent = new Intent();
ComponentName componentName = new ComponentName(packageName,Main2Activity.class.getName());
newIntent.setComponent(componentName);
args[index] = newIntent;
这样目标Activity就成功替换了Main2Activity,不过这个替换者还要将原本的目标携带过去,等待真正打开的时候再替换回来,否则就真的启动这个替换者了。
newIntent.putExtra(AmsHookHelperUtils.TUREINTENT,intent);
startActivity(new Intent(this,Main3Activity.class));
接下来,我们要做的就是,如何将冒充者再重新替换为目标者。我们可以使用ActivityThread通过mH发消息给AMS实现替换。
synchronized(this) {
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
this.mH.sendMessage(msg);
}
然后,AMS收到消息后进行处理。
public void handleMessage(Message msg) {
ActivityThread.ActivityClientRecord data;
switch(msg.what) {
case 100:
Trace.traceBegin(64L, "activityStart");
data = (ActivityThread.ActivityClientRecord)msg.obj;
data.packageInfo = ActivityThread.this.getPackageInfoNoCheck(data.activityInfo.applicationInfo, data.compatInfo);
ActivityThread.this.handleLaunchActivity(data, (Intent)null);
Trace.traceEnd(64L);
mH是Handler类型的消息处理类,所以sendMessage方法会调用callback。新建hookActivityThread方法,首先我们获取当前的ActivityThread对象,然后获取对象的mH对象,将mH替换为我们的自己自定义的MyCallback。
Object currentActivityThread = Reflex.getStaticFieldObject("android.app.ActivityThread", "sCurrentActivityThread");
Handler mH = (Handler) Reflex.getFieldObject(currentActivityThread, "mH");
ReflexUtil.setFieldObject(Handler.class, mH, "mCallback", new MyCallback(mH));
自定义MyCallback需要处理Handler.Callback接口,然后处理handleMessage方法。
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 100:
handleLaunchActivity(msg);
break;
default:
break;
}
mBase.handleMessage(msg);
return true;
}
然后,获取传递过来的目标对象,从目标对象中取出携带过来的真实对象,并将intent修改为真实目标对象的信息,这样就可以启动真实的目标Activity。
Object obj = msg.obj;
Intent intent = (Intent) ReflexUtil.getFieldObject(obj, "intent");
Intent targetIntent = intent.getParcelableExtra(AmsHookHelperUtils.TUREINTENT);
intent.setComponent(targetIntent.getComponent());
以下是MyCallbackt的完整代码:
public class MyCallback implements Handler.Callback {
Handler mBase;
public MyCallback(Handler base) {
mBase = base;
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 100:
handleLaunchActivity(msg);
break;
default:
break;
}
mBase.handleMessage(msg);
return true;
}
private void handleLaunchActivity(Message msg) {
Object obj = msg.obj;
Intent intent = (Intent) ReflexUtil.getFieldObject(obj, "intent");
Intent targetIntent = intent.getParcelableExtra(AmsHookHelperUtils.TUREINTENT);
intent.setComponent(targetIntent.getComponent());
}
}
然后,再启动未注册的Main3Activity就可以成功启动了,是不是很简单。