通过hook系统源码,绕过AndroidManifest检测,亲测支持 android5.0~android 10.0系统(文末有 github 地址)
要绕过AndroidManifest检测就得先明白activity的跳转流程,从Activity的源码中可以看到所有的startActivity方法最终都走到了startActivityForResult();

继续到Instrumentation类跟进一下execStartActivity()
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
// 注释 1 android api 25 Instrumentation源码
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
在注释1处,android系统通过ActivityManagerNative.getDefault()获取到一个IActivityManager接口,然后调用了这个接口的startActivity()方法。这里的IActivityManager接口实际上就是AMS,activity的检查将会在AMS中进行。
在AMS检查完毕后,AMS会执行到ActivityStackSupervisor类中的scheduleLaunchActivity方法,就是通过这个方法回调到ActivityThread,然后在ActivityThread中去启动具体的Activity
具体AMS的调度流程可以参考:blog.csdn.net/kc58236582/…
那么activity启动的整体流程可以概括为
app进程 > AMS进程(AndroidManifest检测) > app进程
按照上面的流程来看,我们只需要在进入AMS之前把一个已经注册过的activity传给AMS就能骗过AMS的AndroidManifest检测,然后在回到app进程的时候,将原本要跳转的activity替换回来就能实现绕过AMS的AndroidManifest检测。
代码需要hook两个地方,一个ams入口处,一个ams出口处,由于android系统每个版本之间源码有差异所以需要做根据系统差异做兼容。
目前将android 5.0android7.1分为一个版本,android8.0android9.0分为一个版本,android10.0分为一个版本。
android 5.0~android7.1 版本:
通过反射得到AMS和ActivityManagerNative中的gDefault属性
if (OSVersion.isAndroidOS_21_22_23_24_25()) { //android 低版本 21~25
Class mActivityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
Method getDefault = mActivityManagerNativeClass.getDeclaredMethod("getDefault");
getDefault.setAccessible(true);
mIActivityManager = getDefault.invoke(null); //得到ams对象
Field gDefault = mActivityManagerNativeClass.getDeclaredField("gDefault");
gDefault.setAccessible(true);
mSingleton = gDefault.get(null); //得到当前ActivityManagerNative中的singleton属性
}
通过动态代理得到一个android.app.IActivityManager的代理对象,AMS实现了IActivityManager这个接口。
在这代理对象中,我们将startActivity方法进行hook,将其中的intent参数替换成已经在AndroidManifest中注册过的proxyActivity,并将原本的intent通过参数的方式带过去
if (OSVersion.isAndroidOS_26_27_28() || OSVersion.isAndroidOS_21_22_23_24_25()) {
Class mIActivityManagerProxy = Class.forName("android.app.IActivityManager"); // hook系统的IActivityManager
final Object finalMIActivityManager = mIActivityManager; // 这个其实就是AMS对象
Object proxy = Proxy.newProxyInstance(context.getClassLoader(), new Class[]{mIActivityManagerProxy}, new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
// 如果是startActivity方法
if ("startActivity".equals(method.getName())) {
//创建一个intent,使用AndroidManifest中已经注册过的ProxyActivity
//来绕过AMS的检测
//代理activity的intent
Intent proxyIntent = new Intent(context, ProxyActivity.class);
//目标activity的intent
Intent target = (Intent) objects[2];
//将startActivity方法的第三个参数替换成代理intent
objects[2] = proxyIntent;
//将原本的intent通过putExtra的方式添加到代理intent的参数中
proxyIntent.putExtra("target", target);
}
//用反射得到的AMS去执行startActivity方法
return method.invoke(finalMIActivityManager, objects);
}
});
}
然后将代理对象设置给 ActivityManagerNative类中的gDefault属性中的mInstance属性(这一段比较绕,需要结合 android.app.ActivityManagerNative 类和 android.util.Singleton 这个类源码才好理解)
/**
* 将Ams对象中的singleton属性设置为动态代理对象
*/
//得到singleton类
Class mSingletonClass = Class.forName("android.util.Singleton");
//得到singleton属性
Field mInstance = mSingletonClass.getDeclaredField("mInstance");
mInstance.setAccessible(true);
//把Ams中的singleton设置为动态代理对象
mInstance.set(mSingleton, proxy);
到这里AMS的入口处我们就hook完毕了,接下来再看看如何将proxyIntnet在ActivityThread中还原成原本的targetIntent
通过反射获取ActivityThread对象,并获取到其中的Handler对象mH
//获取ActivityThread对象
Class mActivityThreadClass = Class.forName("android.app.ActivityThread");
Field mActivityThreadField = mActivityThreadClass.getDeclaredField("sCurrentActivityThread");
mActivityThreadField.setAccessible(true);
Object mActivityThread = mActivityThreadField.get(null); //此时获取到的就是真正的ActivityThread对象
// 获取Handler对象
Field mHField = mActivityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(mActivityThread);//通过当前ActivityThread对象获取mH对象
在android5.0~android7.1的版本中ActivityThread是通过
public static final int LAUNCH_ACTIVITY = 100;
这个msg.what来启动一个activity的,来看看LAUNCH_ACTIVITY中做了些什么操作:
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
在LAUNCH_ACTIVITY中通msg.obj得到了一个ActivityClientRecord,ActivityClientRecord中就记录着activity的所有信息,我们需要通过ActivityClientRecord获取到intent,并将其替换成原本的targetIntent
那我们就来替换一下ActivityClientRecord吧,在替换ActivityClientRecord之前,我们要hook一下Handler.callback这个接口,因为Handler源码中有个dispatchMessage方法,是用来分发消息的,我们要将ActivityThread中的mH这个handler中的callback方法替换成我们自己的callback,以便处理LAUNCH_ACTIVITY消息
public void dispatchMessage(Message msg) {
/**
* 如果msg中自带callback不为空执行callback中的方法
*/
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果Handler的回调不为空
if (mCallback != null) {
// 执行Handler的回调
if (mCallback.handleMessage(msg)) {
// 如果是使用callback的方式,并在callback中返回true,在这里就会直接return,不会执行Handler的重载方法
return;
}
}
// 执行Handler的重载方法
handleMessage(msg);
}
}
替换Handler中的callback
//设置Handler中的callback
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
if (OSVersion.isAndroidOS_21_22_23_24_25()) {
//给ActivityThread中的mH设置callback
mCallbackField.set(mH, new MyCallback_21_22_23_24_25());
}
自定义的Handler.Callback,在callback中替换intent
private static final class MyCallback_21_22_23_24_25 implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 100) {
try {
Object mActivityClientRecord = msg.obj; //得到mActivityClientRecord对象
Field intentField = mActivityClientRecord.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
Intent proxyIntent = (Intent) intentField.get(mActivityClientRecord);
Intent targetIntent = proxyIntent.getParcelableExtra("target");
if (targetIntent != null) {
intentField.set(mActivityClientRecord, targetIntent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//这里记得要returen false 让handler继续执行下去
return false;
}
}
到这里android5.0~android7.1的绕过AndroidManifest注册就完成了,但是在android8.0到android9.0源码变了,需要做兼容。
android 8.0~android9.1 版本:
首先是ams的hook,源码中的方法名和属性名称都发生了变化,主要的类也发生了变化。
代码不在android.app.ActivityManagerNative类中了,放到了android.app.ActivityManager类中
代码中也要做相应的兼容
else if (OSVersion.isAndroidOS_26_27_28()) { //android 高版本 26~28
//反射 android.app.ActivityManager 类
Class mActivityManagerClass = Class.forName("android.app.ActivityManager");
//获取 getService 方法
Method mGetServiceMethod = mActivityManagerClass.getMethod("getService");
mGetServiceMethod.setAccessible(true);
mIActivityManager = mGetServiceMethod.invoke(null);
// 获取IActivityManagerSingleton属性
Field iActivityManagerSingletonField =
mActivityManagerClass.getDeclaredField("IActivityManagerSingleton");
iActivityManagerSingletonField.setAccessible(true);
mSingleton = iActivityManagerSingletonField.get(null);
}
AMS入口处的hook还好搞,但是AMS的出口处ActivityThread的代码变动得就有点多了
首先 LAUNCH_ACTIVITY 没了
然后ActivityClientRecord也没了。。。。
但是有一个
public static final int EXECUTE_TRANSACTION = 159;
看下 EXECUTE_TRANSACTION 执行了啥
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
// Client transactions inside system process are recycled on the client side
// instead of ClientLifecycleManager to avoid being cleared before this
// message is handled.
transaction.recycle();
}
// TODO(lifecycler): Recycle locally scheduled transactions.
break;
ClientTransaction是什么鬼,点进去看一下
发现ClientTransaction中有个mActivityCallbacks属性,还是个list,list类型为ClientTransactionItem,继续看ClientTransactionItem是个什么鬼
public abstract class ClientTransactionItem implements BaseClientRequest, Parcelable {
/** Get the state that must follow this callback. */
@LifecycleState
public int getPostExecutionState() {
return UNDEFINED;
}
// Parcelable
@Override
public int describeContents() {
return 0;
}
嗯?是个抽象类,那再看看那些类实现了这个抽象类,全局搜索一下ClientTransactionItem
看到LaunchActivityItem继承了这个抽象类,哦豁,名字都叫LaunchActivity了,应该是这里了,点进去看一眼
果然在LaunchActivityItem中发现了熟悉的intent属性,接下来进行替换targetIntent操作
Handler还是那个Handler,把自定义的callback改一下
if (OSVersion.isAndroidOS_21_22_23_24_25()) {
//低版本的callback
mCallbackField.set(mH, new MyCallback_21_22_23_24_25());
} else if (OSVersion.isAndroidOS_26_27_28() || OSVersion.isAndroidOS_29()) {
//高版本的callback
mCallbackField.set(mH, new MyCallback_26_27_28());
}
高版本的自定义callback
/**
* android 26~28的Handler回调
*/
private static final class MyCallback_26_27_28 implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 159) { // EXECUTE_TRANSACTION
try {
Object mClientTransaction = msg.obj; //得到ClientTransaction对象
Class mClientTransactionClass = Class.forName("android.app.servertransaction.ClientTransaction");
Field mActivityCallbacksField = mClientTransactionClass.getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
//得到mActivityCallbacks属性
List mActivityCallbacks = (List) mActivityCallbacksField.get(mClientTransaction);
//做一下空判断
if (mActivityCallbacks.size() == 0) {
return false;
}
//获取到第一个mActivityCallback
Object mLaunchActivityItem = mActivityCallbacks.get(0);
Class mLaunchActivityItemClass = Class.forName("android.app.servertransaction.LaunchActivityItem");
//做一下校验,看是不是属于LaunchActivityItem
if (mLaunchActivityItemClass.isInstance(mLaunchActivityItem)) {
//intent 替换操作
Field intentField = mLaunchActivityItemClass.getDeclaredField("mIntent");
intentField.setAccessible(true);
Intent proxyIntent = (Intent) intentField.get(mLaunchActivityItem);
Intent targetIntent = proxyIntent.getParcelableExtra("target");
if (targetIntent != null) {
intentField.set(mLaunchActivityItem, targetIntent);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
}
到这里高版本的AMS出口处hook就完成了,在测试机 8.0~9.0 系统上能运行,但是我自己手机是10.0,一跑crash了,真令人头大。
点开源码发现系统源码又变了
getService还是那个getService,但ActivityManager却不是那个ActivityManager了,变成了ActivityTaskManager
点进ActivityTaskManager.getService()发现返回的接口也变了
返回的接口变成了IActivityTaskManager,并且在代码中还搜索不到这个类,在android 10.0源码中找到了这个类
在android 10.0中IActivityTaskManager接口没有直接写在源码中,而是通过AIDL的方式去生成的,没事能生成就行,看了下目录,还是在android.app目录下,那就继续反射呗
Class mActivityTaskManagerClass = Class.forName("android.app.ActivityTaskManager");
Field iActivityTaskManagerSingletonField = mActivityTaskManagerClass.getDeclaredField("IActivityTaskManagerSingleton");
iActivityTaskManagerSingletonField.setAccessible(true);
mSingleton = iActivityTaskManagerSingletonField.get(null);
/**
* 这里由于10.0 hide 了getService 无法通过getService得到mIActivityTaskManager
*/
Method mGetServiceMethod = mActivityTaskManagerClass.getDeclaredMethod("getService");
mGetServiceMethod.setAccessible(true);
mIActivityTaskManager = mGetServiceMethod.invoke(null); //得到activity_task
这里由于 android 10 getService方法被hide了,反射一直无法获取到getService方法
/** @hide */
public static IActivityTaskManager getService() {
return IActivityTaskManagerSingleton.get();
}
但是 IActivityTaskManagerSingleton 这个属性并没有被hide,于是通过IActivityTaskManagerSingleton属性获取 IActivityTaskManager 接口
/**
* 这里由于10.0 hide 了getService 所以需要通过 Singleton 的get方法得到 mIActivityTaskManager
*/
Class SingletonClass = Class.forName("android.util.Singleton");
Method getMethod = SingletonClass.getDeclaredMethod("get");
getMethod.setAccessible(true);
mIActivityTaskManager = getMethod.invoke(mSingleton);
然后继续反射替换proxy对象
// 动态代理 IActivityTaskManager 接口
Class proxyIActivityTaskManagerClass = Class.forName("android.app.IActivityTaskManager");
final Object finalMIActivityTaskManager = mIActivityTaskManager;
Object proxyIActivityTaskManager = Proxy.newProxyInstance(context.getClassLoader(), new Class[]{proxyIActivityTaskManagerClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
//这里的流程就跟低版本流程一样了,替换intent
if ("startActivity".equals(method.getName())) {
Intent proxyIntent = new Intent(context, ProxyActivity.class);
Intent target = (Intent) objects[2];
objects[2] = proxyIntent;
proxyIntent.putExtra("target", target);
}
return method.invoke(finalMIActivityTaskManager, objects);
}
});
Class mSingletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = mSingletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
mInstanceField.set(mSingleton, proxyIActivityTaskManager);
到此android 10.0的 AMS 入口处hook就完成了,出口处相比8.0~9.0版本并没有变化,所以不需要做特别处理。
自此 android 5.0 ~ android 10.0 的绕过AndroidManifest跳转activity终于完成了(虽然并没什么卵用 =。=!)
在阅读android 10.0源码中还发现一个有趣的现象,感觉AMS在新版本中在被慢慢的拆分。
AMS在android中实在是太庞大了,四大组件都杂糅在AMS中,对于源码阅读很不方便,希望四大组件能拆分成对应的TASK_SERVICE吧!
最后项目源码:github.com/amusiaHzr/A…