一、报错
启动没有在 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 启动流程:

3.1 hook 点的选择
替换:
在 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。
3.2 版本差异
获取 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:
- CommonActivity 已注册,用于点击跳转 TargetActivity
- StubActivity 已注册,用于占位
- TargetActivity 已注册,用于跳转
4.1 Hook
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();
}
}
4.2 替换点
MockAMSP:
public class MockAMSP implements InvocationHandler {
private static final String START_ACTIVITY = "startActivity";

本文介绍了一种在Android中启动未在AndroidManifest.xml中注册的Activity的方法,通过hook ActivityManager.getService()和ActivityThread#mH#mCallback,绕过系统验证,实现目标Activity的启动。

3972

被折叠的 条评论
为什么被折叠?



