Log.v(“ActivityManager”, "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v(“ActivityManager”, "default service = " + am);
}
return am;
}
};
}
同理我们看到 ActivityManagerNative 的 gDefault 是一个静态变量,因此,我们可以尝试 hook gDefault.
public static void hookAmsBefore26() throws Exception {
// 第一步:获取 IActivityManagerSingleton
Class<?> forName = Class.forName(“android.app.ActivityManagerNative”);
Field defaultField = forName.getDeclaredField(“gDefault”);
defaultField.setAccessible(true);
Object defaultValue = defaultField.get(null);
Class<?> forName2 = Class.forName(“android.util.Singleton”);
Field instanceField = forName2.getDeclaredField(“mInstance”);
instanceField.setAccessible(true);
Object iActivityManagerObject = instanceField.get(defaultValue);
// 第二步:获取我们的代理对象,这里因为 IActivityManager 是接口,我们使用动态代理的方式
Class<?> iActivity = Class.forName(“android.app.IActivityManager”);
InvocationHandler handler = new AMSInvocationHandler(iActivityManagerObject);
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{iActivity}, handler);
// 第三步:偷梁换柱,将我们的 proxy 替换原来的对象
instanceField.set(defaultValue, proxy);
}
到此,hook Activity 的三种方式已讲解完毕
启动一个没有在 AndroidManifest 声明的 Activity
我们知道,当我们启动一个没有在 AndroidManifest 中声明的 activity,会抛出 ActivityNotFoundException 异常。
Caused by: android.content.ActivityNotFoundException: Unable to find explicit activity class {com.xj.hookdemo/com.xj.hookdemo.activityhook.TargetAppCompatActivity}; have you declared this activity in your AndroidManifest.xml?
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2124)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1802)
at android.app.Activity.startActivityForResult(Activity.java:4514)
at android.support.v4.app.BaseFragmentActivityApi16.startActivityForResult(BaseFragmentActivityApi16.java:54)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:67)
at android.app.Activity.startActivityForResult(Activity.java:4472)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:720)
at android.app.Activity.startActivity(Activity.java:4833)
at android.app.Activity.startActivity(Activity.java:4801)
at com.xj.hookdemo.activityhook.TestStartActivityNoRegister.onB
从报错的堆栈中,我们非常定位到 Instrumentation.execStartActivity 方法
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String resultWho,
Intent intent, int requestCode, Bundle options, UserHandle user) {
----- // 省略若干代码
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManager.getService()
.startActivityAsUser(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, resultWho,
requestCode, 0, null, options, user.getIdentifier());
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException(“Failure from system”, e);
}
return null;
}
在该方法中,调用 startActivityAsUser 方法通过传入的 intent 获取 result,再通过 checkStartActivityResult 方法,判断 result 是否合法。
而我们知道我们启动的 activity 信息都储存在 intent 中,那么我们若想要 启动一个没有在 AndroidManifest 声明的 Activity,那我们只需要在 某个时机,即调用 startActivity 方法之前欺骗 AMS 我们的 activity 已经注册(即替换 intent),这样就不会抛出 ActivityNotFoundException 异常。
在前面的时候,我们已经讲解到如何 hook ams,这里我们不再具体讲述,主要步骤如下
-
第一步, API 26 以后,hook android.app.ActivityManager.IActivityManagerSingleton, API 25 以前,hook android.app.ActivityManagerNative.gDefault
-
第二步,获取我们的代理对象,这里因为是接口,所以我们使用动态代理的方式
-
第三步:设置为我们的代理对象
private static void hookAMS(Context context) throws ClassNotFoundException,
NoSuchFieldException, IllegalAccessException {
// 第一步, API 26 以后,hook android.app.ActivityManager.IActivityManagerSingleton,
// API 25 以前,hook android.app.ActivityManagerNative.gDefault
Field gDefaultField = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Class<?> activityManager = Class.forName(“android.app.ActivityManager”);
gDefaultField = activityManager.getDeclaredField(“IActivityManagerSin