如果启动一个未注册的Activity
简述
要启动未注册的Activity主要是要逃避AMS的检测,思路是,检测前要启动的Activity换成注册的,检测通过了,再在启动前换回来。这里主要是两个点。检测前,hookAMS
。检测后hookHandler
。hook点有很多尽量找静态变量、单例和public
hookAMS
1、android 11举例,启动acitivty是在ATMS中(11之前是AMS,这个自己可以去适配)
2、拿到ATMS的代理。
3、然后ATMS整个动态代理
在startActivity之前将Intent
偷梁换柱
4、换成已经注册的Activity之后记得原目标Acitivty存起来,在骗完AMS之后换回来
public static void hookAMS() {
// 10之前
try {
Class<?> clazz = Class.forName("android.app.ActivityTaskManager");
Field singletonField = clazz.getDeclaredField("IActivityTaskManagerSingleton");
singletonField.setAccessible(true);
Object singleton = singletonField.get(null);
Class<?> singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
Method getMethod = singletonClass.getMethod("get");
Object mInstance = getMethod.invoke(singleton);
Class IActivityTaskManagerClass = Class.forName("android.app.IActivityTaskManager");
Object mInstanceProxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[]{IActivityTaskManagerClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
int index = -1;
// 获取 Intent 参数在 args 数组中的index值
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
break;
}
}
// 生成代理proxyIntent -- 孙悟空(代理)的Intent
Intent proxyIntent = new Intent();
// 这个包名是宿主的
proxyIntent.setClassName("com.leo.amsplugin",
ProxyActivity.class.getName());
// 原始Intent能丢掉吗?保存原始的Intent对象
Intent intent = (Intent) args[index];
proxyIntent.putExtra(TARGET_INTENT, intent);
// 使用proxyIntent替换数组中的Intent
args[index] = proxyIntent;
}
// 原来流程
return method.invoke(mInstance, args);
}
});
// 用代理的对象替换系统的对象
mInstanceField.set(singleton, mInstanceProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
hookHandler
hookAMS完成,欺骗了AMS,接下来要把Intent中的原目标扶起回正位,
启动Activity要用handler,我们从这里hook吧
1、Activtiy thread 中的handler用来启动activity class H extends Handler
2、handlerMessage中的EXECUTE_TRANSACTION(159)来启动activity
3、
final ClientTransaction transaction = (ClientTransaction) msg.obj;--包含Intent
mTransactionExecutor.execute(transaction);--执行启动
launchActivityItem
中有Intent
,而ta继承于ClientTransactionItem
,而ClientTransaction
中包含List<ClientTransactionItem>
4、所以我只要拿到msg
就可以拿到Intent
。
msg.obj --> ClientTransaction --> List mActivityCallbacks(LaunchActivityItem)
--> private Intent mIntent 替换
5、handlerMessage(MSG)之前有个callback也可以拿到msg。则会callback是一个接口,如果重写这个接口可就可重新handlerMessage这个方法,然后操作msg。
6、ActivityThread当中,Handler的构建没有传参数。
...//去ActivityThread.java里看
@UnsupportedAppUsage
final H mH = new H();
...
class H extends Handler //也没写构造方法
...//去Handler.java里看
@Deprecated
public Handler() {
this(null, false);
}
7、实际上callback是看,那么我自己替换系统的call就可以啦
8、那我通过反射
拿Handler中的mCallback
,
public void hoodHandler() {
try {
Class<?> clazz = Class.forName("android.app.ActivityThread");
Field activityThreadField = clazz.getDeclaredField("sCurrentActivityThread");
activityThreadField.setAccessible(true);
Object activityThread = activityThreadField.get(null);
Field mHField = clazz.getDeclaredField("mH");
mHField.setAccessible(true);
final Handler mH = (Handler) mHField.get(activityThread);
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
mCallbackField.set(mH, new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 159:
// msg.obj = ClientTransaction
try {
// 获取 List<ClientTransactionItem> mActivityCallbacks 对象
Field mActivityCallbacksField = msg.obj.getClass()
.getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List mActivityCallbacks = (List) mActivityCallbacksField.get(msg.obj);
for (int i = 0; i < mActivityCallbacks.size(); i++) {
// 打印 mActivityCallbacks 的所有item:
//android.app.servertransaction.WindowVisibilityItem
//android.app.servertransaction.LaunchActivityItem
// 如果是 LaunchActivityItem,则获取该类中的 mIntent 值,即 proxyIntent
if (mActivityCallbacks.get(i).getClass().getName()
.equals("android.app.servertransaction.LaunchActivityItem")) {
Object launchActivityItem = mActivityCallbacks.get(i);
Field mIntentField = launchActivityItem.getClass()
.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
Intent proxyIntent = (Intent) mIntentField.get(launchActivityItem);
// 获取启动插件的 Intent,并替换回来
Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
if (intent != null) {
mIntentField.set(launchActivityItem, intent);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
return false;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
总结
一个分为两步
1、hookAMS主要就是逃避ams检测,让ams检测的是一个已经注册了的activity。
2、hookHandler在生成activity之前再把activity换回来。
所以一定要熟悉动态代理,反射和Activity的启动流程。
主要通过hook,核心在于hook点
插桩
1、尽量找 静态变量 单利
2、public
动态代理
AMS检测之前我改下
来源:juejin.cn/post/7243272599769055292