如何拦截Activity的启动(一)

要把正常的apk当做一个插件启动起来,首先我们拿到这个插件的Launcher Activity,然后启动宿主APP中的Proxy Activity,在这里面调用插件的Activity的生命周期回调。结果会报Null Pointer Exception,原因在于调用插件Activity的onCreate时会调用到super.onCreate,里面需要用到的一些上下文没有初始化过,所以为null。这里确实没有初始化过,我们拿到插件的Activity类后只是调用了其构造函数,并没有初始化上下文。但是如果插件的Activity都是继承自宿主APP提供的一个PluginBaseActivity就不会有这种问题,因为调用super.onCreate时会调到宿主的PluginBaseActivity的onCreate,里面是空的什么也没有做,所以不会崩溃。仔细想想确实该如此,因为super.onCreate在Proxy Activity中已经做过了,所以插件的Activity就不用再做一次了。但是我们这里插件的Activity并没有继承自PluginBaseActivity,所以还是会调到super.onCreate的,怎么解决这个问题呢?

要解决问题需要对Activity启动过程非常熟悉,所以接下来我们仔细研究一下Activity的启动过程。

首先入口是startActivity函数,里面调到了startActivityForResult,接下来会调到mInstrumentation.execStartActivity:

public ActivityResult execStartActivity(
    Context who, IBinder contextThread, IBinder token, Activity target,
    Intent intent, int requestCode) {
    ..........
    ActivityManagerNative.getDefault()
            .startActivity(whoThread, intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    null, 0, token, target != null ? target.mEmbeddedID : null,
                    requestCode, false, false);
    ..........
}

这里又调到了ActivityManagerNative的startActivity,我们先看看ActivityManagerNative.getDefault是个什么东西:

private static IActivityManager gDefault;

static public IActivityManager getDefault() {
    if (gDefault != null) {
        return gDefault;
    }
    IBinder b = ServiceManager.getService("activity");
    gDefault = asInterface(b);
    return gDefault;
}

static public IActivityManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    IActivityManager in =
        (IActivityManager) obj.queryLocalInterface(descriptor);
    if (in != null) {
        return in;
    }
    return new ActivityManagerProxy(obj);
}

原来是先从ServiceManager中查到ActivityManagerService的IBinder,然后封装了一层ActivityManagerProxy,这个IBinder就保存在ActivityManagerProxy的mRemote中。IBinder负责直接与/dev/binder交互,属于”物理层”,而ActivityManagerProxy是在“物理层”上封装的“业务层”,只要准备好业务相关的数据即可,至于怎么给数据发出去是“物理层”的事。

我们看看ActivityManagerProxy中的startActivity的实现:

public int startActivity(IApplicationThread caller, Intent intent,
        String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
        IBinder resultTo, String resultWho,
        int requestCode, boolean onlyIfNeeded,
        boolean debug) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    intent.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeTypedArray(grantedUriPermissions, 0);
    data.writeInt(grantedMode);
    data.writeStrongBinder(resultTo);
    data.writeString(resultWho);
    data.writeInt(requestCode);
    data.writeInt(onlyIfNeeded ? 1 : 0);
    data.writeInt(debug ? 1 : 0);
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
    reply.readException();
    int result = reply.readInt();
    reply.recycle();
    data.recycle();
    return result;
}

可见这里就是准备好两个parcel,一个是发出去的,一个是接收返回值的,具体和/dev/binder交互是通过mRemote。我们来看看这里发出的数据是在哪里处理的,由于这是ActivityManagerService的IBinder,所以处理肯定是在ActivityManagerService的onTransact中:

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    try {
        return super.onTransact(code, data, reply, flags);
    } catch (RuntimeException e) {
        // The activity manager only throws security exceptions, so let's
        // log all others.
        if (!(e instanceof SecurityException)) {
            Slog.e(TAG, "Activity Manager Crash", e);
        }
        throw e;
    }
}

这里调到了父类的onTransact,而ActivityManagerService的父类是ActivityManagerNative,搞了半天又绕回来了。

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    switch (code) {
    case START_ACTIVITY_TRANSACTION: {
        data.enforceInterface(IActivityManager.descriptor);
        IBinder b = data.readStrongBinder();
        IApplicationThread app = ApplicationThreadNative.asInterface(b);
        Intent intent = Intent.CREATOR.createFromParcel(data);
        String resolvedType = data.readString();
        Uri[] grantedUriPermissions = data.createTypedArray(Uri.CREATOR);
        int grantedMode = data.readInt();
        IBinder resultTo = data.readStrongBinder();
        String resultWho = data.readString(); 
        int requestCode = data.readInt();
        boolean onlyIfNeeded = data.readInt() != 0;
        boolean debug = data.readInt() != 0;
        int result = startActivity(app, intent, resolvedType,
                grantedUriPermissions, grantedMode, resultTo, resultWho,
                requestCode, onlyIfNeeded, debug);
        reply.writeNoException();
        reply.writeInt(result);
        return true;
    }
    ..........
}

这里首先从parcel中取出各类数据,然后调用startActivity,这个startActivity的实现又回到了ActivityManagerService中:

public final int startActivity(IApplicationThread caller,
        Intent intent, String resolvedType, Uri[] grantedUriPermissions,
        int grantedMode, IBinder resultTo,
        String resultWho, int requestCode, boolean onlyIfNeeded,
        boolean debug) {
    return mMainStack.startActivityMayWait(caller, intent, resolvedType,
            grantedUriPermissions, grantedMode, resultTo, resultWho,
            requestCode, onlyIfNeeded, debug, null, null);
}

这个mMainStack是个ActivityStack:

final int startActivityMayWait(IApplicationThread caller,
        Intent intent, String resolvedType, Uri[] grantedUriPermissions,
        int grantedMode, IBinder resultTo,
        String resultWho, int requestCode, boolean onlyIfNeeded,
        boolean debug, WaitResult outResult, Configuration config) {

    intent = new Intent(intent);

    ResolveInfo rInfo =
        AppGlobals.getPackageManager().resolveIntent(
                intent, resolvedType,
                PackageManager.MATCH_DEFAULT_ONLY
                | ActivityManagerService.STOCK_PM_FLAGS);

    ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;

    if (aInfo != null) {
        intent.setComponent(new ComponentName(
                aInfo.applicationInfo.packageName, aInfo.name));
    }

    ..........

    int res = startActivityLocked(caller, intent, resolvedType,
                grantedUriPermissions, grantedMode, aInfo,
                resultTo, resultWho, requestCode, callingPid, callingUid,
                onlyIfNeeded, componentSpecified);

    .......... 

    return res;
}

这里resolveIntent找到匹配的Activity,然后调用startActivityLocked启动Activity:

final int startActivityLocked(IApplicationThread caller,
        Intent intent, String resolvedType,
        Uri[] grantedUriPermissions,
        int grantedMode, ActivityInfo aInfo, IBinder resultTo,
        String resultWho, int requestCode,
        int callingPid, int callingUid, boolean onlyIfNeeded,
        boolean componentSpecified) {

    ..........

    return startActivityUncheckedLocked(r, sourceRecord,
            grantedUriPermissions, grantedMode, onlyIfNeeded, true);
}

final int startActivityUncheckedLocked(ActivityRecord r,
            ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
            int grantedMode, boolean onlyIfNeeded, boolean doResume) {
    ..........

    startActivityLocked(r, newTask, doResume);
    return START_SUCCESS;
}

private final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume) {
    ..........

    if (doResume) {
        resumeTopActivityLocked(null);
    }
}

final boolean resumeTopActivityLocked(ActivityRecord prev) {
    ..........

    startSpecificActivityLocked(next, true, true);

    ..........
}

private final void startSpecificActivityLocked(ActivityRecord r,
        boolean andResume, boolean checkConfig) {

    ..........

    if (app != null && app.thread != null) {
        realStartActivityLocked(r, app, andResume, checkConfig);
        return;
    }

    mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
            "activity", r.intent.getComponent(), false);
}

为了启动一个Activity真是费尽周折,这里最后调到了realStartActivityLocked,如果进程还没启动则还要调用startProcessLocked启动一个新进程。先来看看realStartActivityLocked:

final boolean realStartActivityLocked(ActivityRecord r,
        ProcessRecord app, boolean andResume, boolean checkConfig)
        throws RemoteException {

    ..........

    app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
            System.identityHashCode(r),
            r.info, r.icicle, results, newIntents, !andResume,
            mService.isNextTransitionForward());

    return true;
}

这里scheduleLaunchActivity调到了ApplicationThreadProxy中,这是ApplicationThreadNative的内部类。

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
        List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
        throws RemoteException {
    Parcel data = Parcel.obtain();
    data.writeInterfaceToken(IApplicationThread.descriptor);
    intent.writeToParcel(data, 0);
    data.writeStrongBinder(token);
    data.writeInt(ident);
    info.writeToParcel(data, 0);
    data.writeBundle(state);
    data.writeTypedList(pendingResults);
    data.writeTypedList(pendingNewIntents);
    data.writeInt(notResumed ? 1 : 0);
    data.writeInt(isForward ? 1 : 0);
    mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
            IBinder.FLAG_ONEWAY);
    data.recycle();
}

看来又涉及到了Binder,这里应该是ApplicationThread的service,我们找对应的onTranct,在ApplicationThreadNative中:

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    switch (code) {

    case SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION:
    {
        data.enforceInterface(IApplicationThread.descriptor);
        Intent intent = Intent.CREATOR.createFromParcel(data);
        IBinder b = data.readStrongBinder();
        int ident = data.readInt();
        ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
        Bundle state = data.readBundle();
        List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
        List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
        boolean notResumed = data.readInt() != 0;
        boolean isForward = data.readInt() != 0;
        scheduleLaunchActivity(intent, b, ident, info, state, ri, pi,
                notResumed, isForward);
        return true;
    }

    ..........
}

这里又调到了scheduleLaunchActivity,实现在ApplicationThread中,这是ActivityThread的内部类,继承自ApplicationThreadNative。

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
        List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) {
    ActivityClientRecord r = new ActivityClientRecord();

    r.token = token;
    r.ident = ident;
    r.intent = intent;
    r.activityInfo = info;
    r.state = state;

    r.pendingResults = pendingResults;
    r.pendingIntents = pendingNewIntents;

    r.startsNotResumed = notResumed;
    r.isForward = isForward;

    queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
}

看来是丢到Message Queue中处理了,我们看看handleMessage:

public void handleMessage(Message msg) {
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            ActivityClientRecord r = (ActivityClientRecord)msg.obj;

            r.packageInfo = getPackageInfoNoCheck(
                    r.activityInfo.applicationInfo);
            handleLaunchActivity(r, null);
        } break;
    .........
}

private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    Activity a = performLaunchActivity(r, customIntent);
    ..........
}

想必这个performLaunchActivity就是最终启动Activity的地方了,前面的那些过程不是我们关注的重点,这个函数我们需要仔细看看了,因为和插件非常相关。

private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        r.packageInfo = getPackageInfo(aInfo.applicationInfo,
                Context.CONTEXT_INCLUDE_CODE);
    }

    ComponentName component = r.intent.getComponent();
    if (component == null) {
        component = r.intent.resolveActivity(
            mInitialApplication.getPackageManager());
        r.intent.setComponent(component);
    }

    if (r.activityInfo.targetActivity != null) {
        component = new ComponentName(r.activityInfo.packageName,
                r.activityInfo.targetActivity);
    }

    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    Activity activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);
    r.intent.setExtrasClassLoader(cl);
    if (r.state != null) {
        r.state.setClassLoader(cl);
    }
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);

    if (activity != null) {
        ContextImpl appContext = new ContextImpl();
        appContext.init(r.packageInfo, r.token, this);
        appContext.setOuterContext(activity);
        CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
        Configuration config = new Configuration(mConfiguration);
        activity.attach(appContext, this, getInstrumentation(), r.token,
                r.ident, app, r.intent, r.activityInfo, title, r.parent,
                r.embeddedID, r.lastNonConfigurationInstance,
                r.lastNonConfigurationChildInstances, config);

        if (customIntent != null) {
            activity.mIntent = customIntent;
        }
        r.lastNonConfigurationInstance = null;
        r.lastNonConfigurationChildInstances = null;
        activity.mStartedActivity = false;
        int theme = r.activityInfo.getThemeResource();
        if (theme != 0) {
            activity.setTheme(theme);
        }

        activity.mCalled = false;
        mInstrumentation.callActivityOnCreate(activity, r.state);
        r.activity = activity;
        r.stopped = true;
        if (!r.activity.mFinished) {
            activity.performStart();
            r.stopped = false;
        }
        if (!r.activity.mFinished) {
            if (r.state != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
            }
        }
        if (!r.activity.mFinished) {
            activity.mCalled = false;
            mInstrumentation.callActivityOnPostCreate(activity, r.state);
        }
    }
    r.paused = true;

    mActivities.put(r.token, r);

    return activity;
}

这里通过Instrumentation.newActivity创建一个Activity:

public Activity newActivity(ClassLoader cl, String className,
        Intent intent)
        throws InstantiationException, IllegalAccessException,
        ClassNotFoundException {
    return (Activity)cl.loadClass(className).newInstance();
}

可见,其实就是传入DexClassLoader,先去加载class,然后调用构造函数创建一个Activity,和我们调起插件的Activity的过程是一样的。接下来就是要为这个Activity赋予上下文了,这些我们调起插件的时候是无法做到的,只能用Proxy Activity来代为完成。

好了,回到本文开始时提出的问题,如何解决插件Activity在onCreate时调用super.onCreate由于上下文未初始化导致的NPR。这个问题的解决关键就是为插件Activity赋予上下文,解决方案很简单,首先我们hook掉AMS,将要启动的intent改变为指向ProxyActivity,然后整个流程都会无比顺畅地走下去了,接下来我们要hook掉ActivityThread中的Handler,赋予Handler一个callback就可以拦截到消息,我们在这里判断当消息为LAUNCH_ACTIVITY时来处理我们自己的逻辑,首先通过msg.obj拿到ActivityClientRecord,里面有Intent,ActivityInfo之类的,不过最重要的是LoadedApk,因为新建Activity时就是用这个LoadedApk中的mClassLoader来加载的,而这个LoadedApk是通过getPackageInfoNoCheck函数来获取的,需要反射调用。最后通过反射给插件Apk的DexClassLoader写入LoadedApk中,则万事大吉了。

总的说来,所有的一切都是欺骗系统,在一些关键节点截获数据,精心地修改一番,再顺着正常的流程走下去,最终达到我们需要的效果,而系统毫不知情。不过缺陷就是需要适配不同的sdk版本。每次发布新的sdk,我们都要进行适配,很麻烦。

你可能感兴趣的:(android,hook)