https://blog.csdn.net/gdutxiaoxu/article/details/81459910
https://www.jianshu.com/p/e8f0be9e1c15?utm_source=desktop(启动未在清单里注册的activity的方案)
思路:
目录
1.hook startActivity方法
通过分析activity的启动源码,可以知道启动activity有两种方式。
所以对应的hook也有两种方式
1)hook activity
hook activity 的 mInstrumentation
- 第一步:拿到当前 activity 的 mInstrumentation
- 第二步:创建代理对象
- 第三步:将我们的代理替换原 activity 的 mInstrumentation
2)hook context
- 第一步:拿到ActivityThread(通过其静态方法currentActivityThread),再拿到 mInstrumentation
- 第二步:创建代理对象
- 第三步:将我们的代理替换原 activity 的 mInstrumentation
3)hook AMS
上面 hook activity 的两种方法其实都有一定缺陷,比如,第一种方法,只能 hook 住通过 Activity startActivity 的 activity。第二种方法,只能 hook 住通过 getApplicationContext().startActivity 启动的 activity。但是二者都是通过Instrumentation来启动activity的,所以分析 Instrumentation可追溯到IActivityManager,发现正好是个静态变量,就是ActivityManagerService的实例。
- 第一步, API29以后,hook android.app.ActivityTaskManager.IActivityTaskManagerSingleton;API 26 以后,hook android.app.ActivityManager.IActivityManagerSingleton; API 25 以前,hook android.app.ActivityManagerNative.gDefault
- 第二步,获取我们的代理对象,这里因为是接口,所以我们使用动态代理的方式
- 第三步:设置为我们的代理对象
/**
* 这里对AMS进行hook
* ActivityManager(ActivityManagerNative)里的IActivityManager是一个单例,用我们的代理对象替换它!
*
* @param context
*/
public static void hookAMS(Context context) {
try {
final Class<?> ActivityManagerClz;
final String getServiceMethodStr;
final String IActivityManagerSingletonFieldStr;
if (ifSdkOverIncluding29()) {//29的ams获取方式是通过ActivityTaskManager.getService()
ActivityManagerClz = Class.forName("android.app.ActivityTaskManager");
getServiceMethodStr = "getService";
IActivityManagerSingletonFieldStr = "IActivityTaskManagerSingleton";
} else if (ifSdkOverIncluding26()) {//26,27,28的ams获取方式是通过ActivityManager.getService()
ActivityManagerClz = Class.forName("android.app.ActivityManager");
getServiceMethodStr = "getService";
IActivityManagerSingletonFieldStr = "IActivityManagerSingleton";
} else {//25往下,是ActivityManagerNative.getDefault()
ActivityManagerClz = Class.forName("android.app.ActivityManagerNative");
getServiceMethodStr = "getDefault";
IActivityManagerSingletonFieldStr = "gDefault";
}
//这个就是ActivityManager实例
Object ActivityManagerObj = ReflectUtil.invokeStaticMethod(ActivityManagerClz, getServiceMethodStr);
//这个就是这个就是ActivityManager实例中的IActivityManager单例对象
Object IActivityManagerSingleton = ReflectUtil.staticFieldValue(ActivityManagerClz,
IActivityManagerSingletonFieldStr);
// 2.现在创建我们的IActivityManager实例
// 由于IActivityManager是一个接口,那么其实我们可以使用Proxy类来进行代理对象的创建
// 结果被摆了一道,IActivityManager这玩意居然还是个AIDL,动态生成的类,编译器还不认识这个类,怎么办?反射咯
Class<?> IActivityManagerClz;
if (ifSdkOverIncluding29()) {
IActivityManagerClz = Class.forName("android.app.IActivityTaskManager");
} else {
IActivityManagerClz = Class.forName("android.app.IActivityManager");
}
// 构建代理类需要两个东西用于创建伪装的Intent
String packageName = Util.getPMName(context);
String clz = Util.getHostClzName(context, packageName);
Object proxyIActivityManager =
Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{IActivityManagerClz},
new AMSProxyInvocation(ActivityManagerObj, packageName, clz));
//3.拿到AMS实例,然后用代理的AMS换掉真正的AMS,代理的AMS则是用 假的Intent骗过了 activity manifest检测.
//偷梁换柱
Field mInstanceField = ReflectUtil.findSingletonField("mInstance");
mInstanceField.set(IActivityManagerSingleton, proxyIActivityManager);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 把InvocationHandler的实现类提取出来,因为这里包含了核心技术逻辑,最好独立,方便维护
*/
private static class AMSProxyInvocation implements InvocationHandler {
Object amObj;
String packageName;//这两个String是用来构建Intent的ComponentName的
String clz;
public AMSProxyInvocation(Object amObj, String packageName, String clz) {
this.amObj = amObj;
this.packageName = packageName;
this.clz = clz;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e("GlobalActivityHook", "method.getName() = " + method.getName());
//proxy是创建出来的代理类,method是接口中的方法,args是接口执行时的实参
if (method.getName().equals("startActivity")) {
Log.d("GlobalActivityHook", "全局hook 到了 startActivity");
Intent currentRealIntent = null;//侦测到startActivity动作之后,把intent存到这里
int intentIndex = -1;
//遍历参数,找到Intent
for (int i = 0; i < args.length; i++) {
Object temp = args[i];
if (temp instanceof Intent) {
currentRealIntent = (Intent) temp;//这是原始的Intent,存起来,后面用得着
intentIndex = i;
break;
}
}
//构造自己的Intent,这是为了绕过manifest检测
Intent proxyIntent = new Intent();
ComponentName componentName = new ComponentName(packageName, clz);//用ComponentName重新创建一个intent
proxyIntent.setComponent(componentName);
proxyIntent.putExtra(TextActivity.ORIGINALLY_INTENT, currentRealIntent);//将真正的proxy作为参数,存放到extras中,后面会拿出来还原
args[intentIndex] = proxyIntent;//替换掉intent
//哟,已经成功绕过了manifest清单检测. 那么,我不能老让它跳到 伪装的Activity啊,我要给他还原回去,那么,去哪里还原呢?
//继续看源码。
}
return method.invoke(amObj, args);
}
}
2.启动没有在应用清单xml文件里注册的activity
1)hook ams startActivity,将intent偷梁换柱。
对于插件里的activity,即使其在插件apk的清单xml里注册过,在宿主apk里启动的时候仍然会报错:
android.content.ActivityNotFoundException: Unable to find explicit activity class {com.renxh.pluginapp/com.renxh.pluginapp.PluginActivity}; have you declared this activity in your AndroidManifest.xml?
解决思路:事先在宿主apk的清单文件里注册一个StubActivity(暗桩,占位,也可以动态的获取宿主apk的第一个activity),然后再通过hook ActivityManager的startActivity的方法,创建新的intent指向StubActivity,并绑定有intent(插件intent)信息。
2)hook H Handler,还原intent
通过activity的启动流程:
app 调用 startActivity 方法 -> Instrumentation 类通过 ActivityManagerNative 或者 ActivityManager( API 26以后)将启动请求发送给 AMS -> AMS 进行一系列检查并将此请求通过 Binder 派发给所属 app -> app 通过 Binder 收到这个启动请求 -> ActivityThread 中的实现将收到的请求进行封装后送入 Handler -> 从 Handler 中取出这个消息,开始 app 本地的 Activity 初始化和启动逻辑。
所以可以hook ActivityThread类中的H类mH。
public static void hookActivityThread_mH(Context context) {
try {
Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
Object sCurrentActivityThread = ReflectUtil.staticFieldValue(activityThreadClazz, "sCurrentActivityThread");
Handler mH = (Handler) ReflectUtil.fieldValue(sCurrentActivityThread, "mH");
Field mCallBackField = ReflectUtil.findField(Handler.class, "mCallback");
Handler.Callback callback;
if (ifSdkOverIncluding28()) {
//2.现在,造一个代理
// 他就是一个简单的Handler子类
callback = new ProxyHandlerCallback();//不需要重写全部mH,只需要对mH的callback进行重新定义
} else {
callback = new ActivityThreadHandlerCallBack(context);
}
//3.替换
//将Handler的mCallback成员,替换成创建出来的代理HandlerCallback
mCallBackField.set(mH, callback);
} catch (Exception e) {
e.printStackTrace();
}
}
注意,android28以后的Activity的H Handler,hook方式不一样。!!!!!!
android28以上的H Handler的hook:
/**
* 注意,这里有个坑
* android.os.handler 这个类有 3个 callback,按照优先级,依次是 msg的callback,自己成员变量mCallback,自己的成员方法 handleMessage()
*
* 其中,msg.callback一般很少用,但是它是最优先的,如果有一个Message.存在callback非空成员,那么它是先执行,后面两个就没戏了。
* 如果 handler自己的成员变量mCallback,非空,那么 handlerMessage()方法就没戏了
* 前面两个都执行,那么handlerMessage才会执行,
* 这个叫责任链模式?根据实际条件决定代码分支。
*/
private static class ProxyHandlerCallback implements Handler.Callback {
private int EXECUTE_TRANSACTION = 159;//这个值,是android.app.ActivityThread的内部类H 中定义的常量EXECUTE_TRANSACTION
@Override
public boolean handleMessage(Message msg) {
boolean result = false;//返回值,请看Handler的源码,dispatchMessage就会懂了
//Handler的dispatchMessage有3个callback优先级,首先是msg自带的callback,其次是Handler的成员mCallback,最后才是Handler类自身的handlerMessage方法,
//它成员mCallback.handleMessage的返回值为true,则不会继续往下执行 Handler.handlerMessage
//我们这里只是要hook,插入逻辑,所以必须返回false,让Handler原本的handlerMessage能够执行.
if (msg.what == EXECUTE_TRANSACTION) {//这是跳转的时候,要对intent进行还原
try {
//先把相关@hide的类都建好
Class<?> ClientTransactionClz = Class.forName("android.app.servertransaction.ClientTransaction");
Class<?> LaunchActivityItemClz = Class.forName("android.app.servertransaction.LaunchActivityItem");
Field mActivityCallbacksField = ClientTransactionClz.getDeclaredField("mActivityCallbacks");//ClientTransaction的成员
mActivityCallbacksField.setAccessible(true);
//类型判定,好习惯
if (!ClientTransactionClz.isInstance(msg.obj)) {
return true;
}
Object mActivityCallbacksObj = mActivityCallbacksField.get(msg.obj);//根据源码,在这个分支里面,msg.obj就是 ClientTransaction类型,所以,直接用
//拿到了ClientTransaction的List<ClientTransactionItem> mActivityCallbacks;
List list = (List) mActivityCallbacksObj;
if (list.size() == 0) {
return false;
}
Object LaunchActivityItemObj = list.get(0);//所以这里直接就拿到第一个就好了
if (!LaunchActivityItemClz.isInstance(LaunchActivityItemObj)) {
return true;
}
//这里必须判定 LaunchActivityItemClz,
// 因为 最初的ActivityResultItem传进去之后都被转化成了这LaunchActivityItemClz的实例
Field mIntentField = LaunchActivityItemClz.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
Intent mIntent = (Intent) mIntentField.get(LaunchActivityItemObj);
Bundle extras = mIntent.getExtras();
if (extras != null) {
Intent oriIntent = (Intent) extras.get(TextActivity.ORIGINALLY_INTENT);
//那么现在有了最原始的intent,应该怎么处理呢?
Log.d("1", "2");
mIntentField.set(LaunchActivityItemObj, oriIntent);
}
return result;
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
}
android28以下的H Handler的hook:
public static class ActivityThreadHandlerCallBack implements Handler.Callback {
private final Context mContext;
public ActivityThreadHandlerCallBack(Context context) {
mContext = context;
}
@Override
public boolean handleMessage(Message msg) {
int LAUNCH_ACTIVITY = 0;
try {
Class<?> clazz = Class.forName("android.app.ActivityThread$H");
LAUNCH_ACTIVITY = (int) ReflectUtil.staticFieldValue(clazz, "LAUNCH_ACTIVITY");
} catch (Exception e) {
}
if (msg.what == LAUNCH_ACTIVITY) {
handleLaunchActivity(mContext, msg);
}
return false;
}
}
private static void handleLaunchActivity(Context context, Message msg) {
try {
Object obj = msg.obj;
Intent proxyIntent = (Intent) ReflectUtil.fieldValue(obj, "intent");
//拿到之前真实要被启动的Intent 然后把Intent换掉
Intent originallyIntent = proxyIntent.getParcelableExtra(TextActivity.ORIGINALLY_INTENT);
if (originallyIntent == null) {
return;
}
proxyIntent.setComponent(originallyIntent.getComponent());
} catch (Exception e) {
e.printStackTrace();
}
}
3.总结
hook actiivty的启动
- hook ams
- hook ActivityThread的H Handler
- hook的时候,要注意各个android版本的兼容处理。
4.关于插件apk的自定义classloader加载activity的兼容性问题
在下面的文章中,我提到了自定义classloader加载activity的兼容性问题
系统创建插件activity时,用的是应用的classloader去加载,自然就会报找不到,于是hook掉ActivityThread的Instrumentation的newActivity方法,将应用的classloader替换成自定义的classloader:
@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return instrumentation.newActivity(PluginHelper.pluginClassLoader, className, intent);
}
发现插件Activity的生命周期方法onResume也走了,但是Activity却并没有显示,不知道是为何?求路过的高手解答。//TODO
如果把这个插件的activity替换成app应用内没有注册的activity,这个activity会正常显示。
待续......
0 条评论