赞
踩
启动没有在 AndroidManifest 中注册的 Activity,会报错:
android.content.ActivityNotFoundException: Unable to find explicit activity class {...}; have you declared this activity in your AndroidManifest.xml?
Android 使用的是 C/S 架构,我们的 app 是 client 客户端,内核是 Server 服务端。
Activity 是否注册的验证是在服务端进行的,所以我们客户端无法修改判断条件。但我们可以修改客户端的请求,让服务端以为我们要启动的是另外一个已注册的 Acitivity,在客户端得到启动许可后,再去启动真正的目标 Activity。
这一操作要通过 hook 来实现。
android 28 启动流程:
替换:
在 android 28 上,通过 ActivityManager.getService().startActivity()
来向服务端发起请求。所以我们要在这一步之前
,将要启动的 Activity 替换为已注册的 Activity。
恢复:
通过 mInstrumentation.newActivity()
来创建 Activity。所以我们要在这一步之前
,将要启动的 Activity 替换回未注册的 Activity。
本文选择的替换点是 ActivityManager.getService().startActivity()
,即要 hook ActivityManager.getService()
。
恢复点是:ActivityThread#mH.handleMessage()
,即要 hook ActivityThread#mH#mCallback
。
获取 AMSP:
android 26 及以上:
ActivityManager.getService()
android 26 以下:
ActivityManagerNative.getDefault()
处理启动 Activity 的 message:
android 28 及以上
handleMessage(ActivityThread.H.EXECUTE_TRANSACTION)
android 28 以下:
handleMessage(H.LAUNCH_ACTIVITY)
完整代码见 github.com/Gdeeer
新建三个 Activity:
AMSPHookHelper
class AMSPHookHelper { static final String EXTRA_TARGET_INTENT = "extra_target_intent"; static void hookAMSP() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { hookAMSPBefore26(); } else { hookAMSPSince26(); } } /** * android 26 以下版本 AMSP 的 hook */ private static void hookAMSPBefore26() { try { Class classActivityManagerNative = Class.forName("android.app.ActivityManagerNative"); Object gDefault = FieldUtils.readStaticField(classActivityManagerNative, "gDefault"); Object mInstance = FieldUtils.readField(gDefault, "mInstance"); Class classIActivityManager = Class.forName("android.app.IActivityManager"); Object proxy = Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[]{classIActivityManager}, new MockAMSP(mInstance) ); FieldUtils.writeField(gDefault, "mInstance", proxy); } catch (Exception e) { e.printStackTrace(); } } /** * android 26 及以上版本 AMSP 的 hook */ private static void hookAMSPSince26() { try { Object IActivityManagerSingleton = FieldUtils.readStaticField(ActivityManager.class, "IActivityManagerSingleton"); Object mInstance = FieldUtils.readField(IActivityManagerSingleton, "mInstance"); Class classIActivityManager = Class.forName("android.app.IActivityManager"); Object proxy = Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[]{classIActivityManager}, new MockAMSP(mInstance) ); FieldUtils.writeField(IActivityManagerSingleton, "mInstance", proxy); } catch (Exception e) { e.printStackTrace(); } } static void hookActivityThread() { try { Class classActivityThread = Class.forName("android.app.ActivityThread"); Object currentActivityThread = FieldUtils.readStaticField(classActivityThread, "sCurrentActivityThread"); Handler mH = (Handler) FieldUtils.readField(currentActivityThread, "mH"); FieldUtils.writeField(mH, "mCallback", new MockHCallback(mH)); } catch (Exception e) { e.printStackTrace(); } } }
StubActivityApplication
public class StubActivityApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
// 解除 android P 上的私有 api 限制,见http://weishu.me/2018/06/07/free-reflection-above-android-p/
Reflection.unseal(base);
// hook
AMSPHookHelper.hookAMSP();
AMSPHookHelper.hookActivityThread();
}
}
MockAMSP:
public class MockAMSP implements InvocationHandler { private static final String START_ACTIVITY = "startActivity"; private Object mBase; MockAMSP(Object base) { mBase = base; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (START_ACTIVITY.equals(method.getName())) { // 找到旧的 intent Intent raw; int index = 0; for (int i = 0; i < args.length; i++) { if (args[i] instanceof Intent) { index = i; break; } } raw = (Intent) args[index]; // 创建新的 intent Intent newIntent = new Intent(); String stubPackage = "com.gdeer.gdtesthub"; ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName()); newIntent.setComponent(componentName); newIntent.putExtra(AMSPHookHelper.EXTRA_TARGET_INTENT, raw); // 替换旧的 intent 为新的 intent args[index] = newIntent; // 调用 "startActivity" 方法 return method.invoke(mBase, args); } return method.invoke(mBase, args); } }
MockHCallback:
public class MockHCallback implements Handler.Callback { /** * android 28 以下,ActivityThread$H.LAUNCH_ACTIVITY = 100 */ private static final int LAUNCH_ACTIVITY = 100; /** * android 28 上,ActivityThread$H.EXECUTE_TRANSACTION = 159 */ private static final int EXECUTE_TRANSACTION = 159; private Handler mBase; MockHCallback(Handler base) { mBase = base; } @Override public boolean handleMessage(Message msg) { handleLaunchActivity(msg); mBase.handleMessage(msg); return true; } private void handleLaunchActivity(Message msg) { Log.d(TAG, "handleLaunchActivity"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (msg.what == EXECUTE_TRANSACTION) { handleLaunchActivitySince28(msg); } } else { if (msg.what == LAUNCH_ACTIVITY) { handleLaunchActivityBefore28(msg); } } } private void handleLaunchActivityBefore28(Message msg) { try { Object obj = msg.obj; if (obj != null) { Intent raw = (Intent) FieldUtils.readField(obj, "intent"); Intent target = raw.getParcelableExtra(AMSPHookHelper.EXTRA_TARGET_INTENT); if (target != null) { raw.setComponent(target.getComponent()); } } } catch (Exception e) { e.printStackTrace(); } } private void handleLaunchActivitySince28(Message msg) { try { Object mActivityCallbacks = FieldUtils.readField(msg.obj, "mActivityCallbacks"); if (mActivityCallbacks != null) { List<?> list = (List<?>) mActivityCallbacks; if (list.size() > 0) { Object listItem = list.get(0); Class classLaunchActivityItem = Class.forName("android.app.servertransaction.LaunchActivityItem"); if (listItem.getClass() == classLaunchActivityItem) { Intent raw = (Intent) FieldUtils.readField(listItem, "mIntent"); Intent target = raw.getParcelableExtra(AMSPHookHelper.EXTRA_TARGET_INTENT); raw.setComponent(target.getComponent()); } } } } catch (Exception e) { e.printStackTrace(); } } }
补充:FiledUtil 工具类
public class FieldUtils { private static Map<String, Field> sFieldCache = new HashMap<String, Field>(); private static String getKey(Class<?> cls, String fieldName) { StringBuilder sb = new StringBuilder(); sb.append(cls.toString()).append("#").append(fieldName); return sb.toString(); } private static Field getField(Class<?> cls, String fieldName, final boolean forceAccess) throws ReflectIllegalArgumentsException { Validate.assertTrue(cls != null, "The class must not be null"); Validate.assertTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty"); String key = getKey(cls, fieldName); Field cachedField; synchronized (sFieldCache) { cachedField = sFieldCache.get(key); } if (cachedField != null) { if (forceAccess && !cachedField.isAccessible()) { cachedField.setAccessible(true); } return cachedField; } // check up the superclass hierarchy for (Class<?> acls = cls; acls != null; acls = acls.getSuperclass()) { try { final Field field = acls.getDeclaredField(fieldName); // getDeclaredField checks for non-public scopes as well // and it returns accurate results if (!Modifier.isPublic(field.getModifiers())) { if (forceAccess) { field.setAccessible(true); } else { continue; } } synchronized (sFieldCache) { sFieldCache.put(key, field); } return field; } catch (final NoSuchFieldException ex) { // NOPMD // ignore } } // check the public interface case. This must be manually searched for // incase there is a public supersuperclass field hidden by a private/package // superclass field. Field match = null; for (final Class<?> class1 : ReflectUtils.getAllInterfaces(cls)) { try { final Field test = class1.getField(fieldName); Validate.assertTrue(match == null, "Reference to field %s is ambiguous relative to %s" + "; a matching field exists on two or more implemented interfaces.", fieldName, cls); match = test; } catch (final NoSuchFieldException ex) { // NOPMD // ignore } } synchronized (sFieldCache) { sFieldCache.put(key, match); } return match; } public static Object readField(final Field field, final Object target, final boolean forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException { Validate.assertTrue(field != null, "The field must not be null"); if (forceAccess && !field.isAccessible()) { field.setAccessible(true); } else { MemberUtils.setAccessibleWorkaround(field); } return field.get(target); } public static void writeField(final Field field, final Object target, final Object value, final boolean forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException { Validate.assertTrue(field != null, "The field must not be null"); if (forceAccess && !field.isAccessible()) { field.setAccessible(true); } else { MemberUtils.setAccessibleWorkaround(field); } field.set(target, value); } public static Object readField(final Field field, final Object target) throws IllegalAccessException, ReflectIllegalArgumentsException { return readField(field, target, true); } public static Field getField(final Class<?> cls, final String fieldName) throws ReflectIllegalArgumentsException { return getField(cls, fieldName, true); } public static Object readField(final Object target, final String fieldName) throws IllegalAccessException, ReflectIllegalArgumentsException { Validate.assertTrue(target != null, "target object must not be null"); final Class<?> cls = target.getClass(); final Field field = getField(cls, fieldName, true); Validate.assertTrue(field != null, "Cannot locate field %s on %s", fieldName, cls); // already forced access above, don't repeat it here: return readField(field, target, false); } public static Object readField(final Object target, final String fieldName, final boolean forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException { Validate.assertTrue(target != null, "target object must not be null"); final Class<?> cls = target.getClass(); final Field field = getField(cls, fieldName, forceAccess); Validate.assertTrue(field != null, "Cannot locate field %s on %s", fieldName, cls); // already forced access above, don't repeat it here: return readField(field, target, forceAccess); } public static void writeField(final Object target, final String fieldName, final Object value) throws IllegalAccessException, ReflectIllegalArgumentsException { writeField(target, fieldName, value, true); } public static void writeField(final Object target, final String fieldName, final Object value, final boolean forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException { Validate.assertTrue(target != null, "target object must not be null"); final Class<?> cls = target.getClass(); final Field field = getField(cls, fieldName, true); Validate.assertTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName); // already forced access above, don't repeat it here: writeField(field, target, value, forceAccess); } public static void writeField(final Field field, final Object target, final Object value) throws IllegalAccessException, ReflectIllegalArgumentsException { writeField(field, target, value, true); } public static Object readStaticField(final Field field, final boolean forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException { Validate.assertTrue(field != null, "The field must not be null"); Validate.assertTrue(Modifier.isStatic(field.getModifiers()), "The field '%s' is not static", field.getName()); return readField(field, (Object) null, forceAccess); } public static Object readStaticField(final Class<?> cls, final String fieldName) throws IllegalAccessException, ReflectIllegalArgumentsException { final Field field = getField(cls, fieldName, true); Validate.assertTrue(field != null, "Cannot locate field '%s' on %s", fieldName, cls); // already forced access above, don't repeat it here: return readStaticField(field, true); } public static void writeStaticField(final Field field, final Object value, final boolean forceAccess) throws IllegalAccessException, ReflectIllegalArgumentsException { Validate.assertTrue(field != null, "The field must not be null"); Validate.assertTrue(Modifier.isStatic(field.getModifiers()), "The field %s.%s is not static", field.getDeclaringClass().getName(), field.getName()); writeField(field, (Object) null, value, forceAccess); } public static void writeStaticField(final Class<?> cls, final String fieldName, final Object value) throws IllegalAccessException, ReflectIllegalArgumentsException { final Field field = getField(cls, fieldName, true); Validate.assertTrue(field != null, "Cannot locate field %s on %s", fieldName, cls); // already forced access above, don't repeat it here: writeStaticField(field, value, true); } public static Field getDeclaredField(final Class<?> cls, final String fieldName, final boolean forceAccess) throws ReflectIllegalArgumentsException { Validate.assertTrue(cls != null, "The class must not be null"); Validate.assertTrue(!TextUtils.isEmpty(fieldName), "The field name must not be blank/empty"); try { // only consider the specified class by using getDeclaredField() final Field field = cls.getDeclaredField(fieldName); if (!MemberUtils.isAccessible(field)) { if (forceAccess) { field.setAccessible(true); } else { return null; } } return field; } catch (final NoSuchFieldException e) { // NOPMD // ignore } return null; } public static void writeDeclaredField(final Object target, final String fieldName, final Object value) throws IllegalAccessException, ReflectIllegalArgumentsException { Validate.assertTrue(target != null, "target object must not be null"); final Class<?> cls = target.getClass(); final Field field = getDeclaredField(cls, fieldName, true); Validate.assertTrue(field != null, "Cannot locate declared field %s.%s", cls.getName(), fieldName); // already forced access above, don't repeat it here: writeField(field, target, value, false); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。