注册

如果启动一个未注册的Activity

简述


要启动未注册的Activity主要是要逃避AMS的检测,思路是,检测前要启动的Activity换成注册的,检测通过了,再在启动前换回来。这里主要是两个点。检测前,hookAMS。检测后hookHandler。hook点有很多尽量找静态变量单例public


hookAMS


1、android 11举例,启动acitivty是在ATMS中(11之前是AMS,这个自己可以去适配)


image.png


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 替换


image.png


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检测之前我改下


image.png


作者:KentWang
链接:https://juejin.cn/post/7243272599769055292
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册