Hook启动插件中四大组件

发表于 2年以前  | 总阅读数:545 次

在上一节,我们主要介绍了如果通过反射来加载插件中的类,调用类中的方法;既然插件是一个apk,其实最重要的是启动插件中的Activity、Service等组件,因为像Activity这些组件,需要在清单文件中注册,否则启动的时候会报下面的错误

android.content.ActivityNotFoundException: 
Unable to find explicit activity class {com.lay.image_process/com.lay.image_process.MainActivity2};
have you declared this activity in your AndroidManifest.xml?

那么如果要启动插件中的Activity,就需要将其注册到宿主app的清单文件中

1 Activity的启动流程

其实想要启动插件中的四大组件,例如Activity,就需要熟悉Activity的启动流程,为什么在没有注册的情况下,不能启动

1.1 启动插件Activity思路梳理

开头我们提到,为什么没有注册过的Activity不能启动?是因为当系统启动一个Activity的时候,需要通过AMS检测,当前被启动的Activity是否被注册,如果没有注册,那么就会报错,所以我们需要采用Hook的方式来欺骗系统已达到我们的目的。

在应用层,我们能看到的就是,宿主app启动了插件中的Activity,其实在内部做了很多处理:\

(1)首先将插件中的Activity替换成宿主中已经注册过的ProxyActivity,在启动的时候,通过AMS检测发现当前启动的Activity已经被注册过了,那么就会继续往下执行; (2)当AMS检测完成之后,再将宿主中的ProxyActivity替换成插件中的Activity,最终启动的就是插件中的Activity。

//实际启动代码<br></br>

val intent = Intent()
intent.component = ComponentName("com.lay.plugin","com.lay.plugin.MainActivity")
startActivity(intent)

我们看到启动方式很简单,但是内部其实做了很多的Hook处理,如果想知道怎么用,那么就跟着这个思路一起走下去。

1.2 从源码理解Activity启动流程

对于Activity的启动流程,我想大家都非常了解了,记住关键的几个类Instrument、ActivityManagerService、ApplicationThread......,下面简单介绍下流程。

当调用Instrument的execStartActivity方法时,通过ActivityTaskManager.getService()获取AMS服务,调用AMS的startActivity方法,并返回检测的结果。


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);
    }
    // ......

    try {
        intent.migrateExtraStreamToClipData(who);
        intent.prepareToLeaveProcess(who);
        //这里就是通过ActivityTaskManager获取AMS服务,调用AMS的startActivity方法,并返回结果
        int result = ActivityTaskManager.getService().startActivity(whoThread,
                who.getOpPackageName(), who.getAttributionTag(), 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;
}

我们看一下checkStartActivityResult方法究竟做了什么?我们看一下START_CLASS_NOT_FOUND这个结果,最终抛出的错误是不是就是文章开头的错误


public static void checkStartActivityResult(int res, Object intent) {
    if (!ActivityManager.isStartResultFatalError(res)) {
        return;
    }

    switch (res) {
        case ActivityManager.START_INTENT_NOT_RESOLVED:
        case ActivityManager.START_CLASS_NOT_FOUND:
            if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
                throw new ActivityNotFoundException(
                        "Unable to find explicit activity class "
                        + ((Intent)intent).getComponent().toShortString()
                        + "; have you declared this activity in your AndroidManifest.xml?");
            throw new ActivityNotFoundException(
                    "No Activity found to handle " + intent);
        case ActivityManager.START_PERMISSION_DENIED:
            throw new SecurityException("Not allowed to start activity "
                    + intent);
        case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
            throw new AndroidRuntimeException(
                    "FORWARD_RESULT_FLAG used while also requesting a result");
        case ActivityManager.START_NOT_ACTIVITY:
            throw new IllegalArgumentException(
                    "PendingIntent is not an activity");
        case ActivityManager.START_NOT_VOICE_COMPATIBLE:
            throw new SecurityException(
                    "Starting under voice control not allowed for: " + intent);
        case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:
            throw new IllegalStateException(
                    "Session calling startVoiceActivity does not match active session");
        case ActivityManager.START_VOICE_HIDDEN_SESSION:
            throw new IllegalStateException(
                    "Cannot start voice activity on a hidden session");
        case ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION:
            throw new IllegalStateException(
                    "Session calling startAssistantActivity does not match active session");
        case ActivityManager.START_ASSISTANT_HIDDEN_SESSION:
            throw new IllegalStateException(
                    "Cannot start assistant activity on a hidden session");
        case ActivityManager.START_CANCELED:
            throw new AndroidRuntimeException("Activity could not be started for "
                    + intent);
        default:
            throw new AndroidRuntimeException("Unknown error code "
                    + res + " when starting " + intent);
    }
}

也就是说在这个位置就已经检测完成,当前启动的Activity是否在清单文件中注册,也就是说,Hook点要在这个检测方法之前,完成对插件Activity的替换,这是Hook点1.

我们接着往下看AMS里主要做了啥

public class ActivityTaskManagerService extends IActivityTaskManager.Stub

ActivityTaskManagerService继承自IActivityTaskManager.Stub接口,作为一个服务端的角色,接受客户端的请求,例如启动Activity,AMS除了做Intent启动的Activity检测之外,还做了什么事呢?

在ATMS中调用startActivity方法,最终是调用了startActivityAsUser方法,这个方法中,首先检查了调用者的权限,然后调用了ActivityStartController一系列方法,用于启动前的任务栈处理

private int startActivityAsUser(IApplicationThread caller, String callingPackage,
        @Nullable String callingFeatureId, Intent intent, String resolvedType,
        IBinder resultTo, String resultWho, int requestCode, int startFlags,
        ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
    assertPackageMatchesCallingUid(callingPackage);
    enforceNotIsolatedCaller("startActivityAsUser");
    //检查调用者的权限
    userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
            Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

    // TODO: Switch to user app stacks here.
    return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
            .setCaller(caller)
            .setCallingPackage(callingPackage)
            .setCallingFeatureId(callingFeatureId)
            .setResolvedType(resolvedType)
            .setResultTo(resultTo)
            .setResultWho(resultWho)
            .setRequestCode(requestCode)
            .setStartFlags(startFlags)
            .setProfilerInfo(profilerInfo)
            .setActivityOptions(bOptions)
            .setUserId(userId)
            .execute();

}

再深入的源码,大家可以自己去查看,最终AMS会判断启动这个Activity的进程是否启动,没有启动的话就启动app进程,如果已经启动了会调用realStartActivityLocked方法


void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
    // Is this activity's application already running?
    final WindowProcessController wpc =
            mService.getProcessController(r.processName, r.info.applicationInfo.uid);

    boolean knownToBeDead = false;
    if (wpc != null && wpc.hasThread()) {
        try {
            realStartActivityLocked(r, wpc, andResume, checkConfig);
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception when starting activity "
                    + r.intent.getComponent().flattenToShortString(), e);
        }

        // If a dead object exception was thrown -- fall through to
        // restart the application.
        knownToBeDead = true;
    }

    r.notifyUnknownVisibilityLaunchedForKeyguardTransition();

    final boolean isTop = andResume && r.isTopRunningActivity();
    mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
}

在realStartActivityLocked方法中,首先会创建一个启动Activity的Transaction,会通过ApplicationThread回调给客户端启动Activity,在AMS和客户端通信时,有两个对象需要注意:ActivityThread和ApplicationThread,ApplicationThread其实是ActivityThead在AMS的代理对象,AMS通过调用ApplicationThread与ActivityThead建立通信


boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
        boolean andResume, boolean checkConfig) throws RemoteException {

    // The LaunchActivityItem also contains process configuration, so the configuration change
    // from WindowProcessController#setProcess can be deferred. The major reason is that if
    // the activity has FixedRotationAdjustments, it needs to be applied with configuration.
    // In general, this reduces a binder transaction if process configuration is changed.
    //......

        // Create activity launch transaction.
        final ClientTransaction clientTransaction = ClientTransaction.obtain(
                proc.getThread(), r.appToken);

        final boolean isTransitionForward = r.isTransitionForward();
        clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                System.identityHashCode(r), r.info,
                // TODO: Have this take the merged configuration instead of separate global
                // and override configs.
                mergedConfiguration.getGlobalConfiguration(),
                mergedConfiguration.getOverrideConfiguration(), r.compat,
                r.getFilteredReferrer(r.launchedFromPackage), task.voiceInteractor,
                proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(),
                results, newIntents, r.takeOptions(), isTransitionForward,
                proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
                r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
                r.getLaunchedFromBubble()));

        // Schedule transaction.
        mService.getLifecycleManager().scheduleTransaction(clientTransaction);
}

所以AMS这里的作用就是:检测Activity合法性、检测app进程是否启动、通过ApplicationThead与ActivityThead建立通信。

private class ApplicationThread extends IApplicationThread.Stub {

    @Override
    public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        ActivityThread.this.scheduleTransaction(transaction);
    }

我们看下服务端的代理对象ApplicationThread,主要是处理AMS端的请求,例如realStartActivityLocked方法中创建的启动Activity的ClientTransaction,接收到之后,交给了ActivityThread处理。

首先我们看下ActivityThread是什么,我们看到它是继承自ClientTransactionHandler,也就是说,在App进程内的处理,都是通过Handler来发送消息

public final class ActivityThread extends ClientTransactionHandler
public abstract class ClientTransactionHandler {

    // Schedule phase related logic and handlers.

    /** Prepare and schedule transaction for execution. */
    void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }
}

对于EXECUTE_TRANSACTION消息类型,就是用来处理AMS端发送来的消息

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;

最终是调用了TransactionExecutor的execute方法,在execute方法中,调用executeCallbacks方法,我们可以在realStartActivityLocked方法中看到,在transaction添加了callback,所以这个方法就是执行这些callback

public void execute(ClientTransaction transaction) {
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");

    executeCallbacks(transaction);

    executeLifecycleState(transaction);
    mPendingActions.clear();
    if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
}

executeCallbacks方法比较简单,就是遍历获取所有的callback,然后调用callback的execute方法,也就是LaunchActivityItem的execute方法


public void executeCallbacks(ClientTransaction transaction) {
    final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
    if (callbacks == null || callbacks.isEmpty()) {
        // No callbacks to execute, return early.
        return;
    }

    final int size = callbacks.size();
    for (int i = 0; i < size; ++i) {
        final ClientTransactionItem item = callbacks.get(i);
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
        final int postExecutionState = item.getPostExecutionState();
        final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                item.getPostExecutionState());
        if (closestPreExecutionState != UNDEFINED) {
            cycleToPath(r, closestPreExecutionState, transaction);
        }

        item.execute(mTransactionHandler, token, mPendingActions);
        item.postExecute(mTransactionHandler, token, mPendingActions);
        if (r == null) {
            // Launch activity request will create an activity record.
            r = mTransactionHandler.getActivityClient(token);
        }

        if (postExecutionState != UNDEFINED && r != null) {
            // Skip the very last transition and perform it by explicit state request instead.
            final boolean shouldExcludeLastTransition =
                    i == lastCallbackRequestingState && finalState == postExecutionState;
            cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
        }
    }
}

在LaunchActivityItem的execute方法中,执行了ClientTransactionHandler的handleLaunchActivity方法,也就是ActivityThread的handleLaunchActivity方法,这里才是真正要启动这个Activity。


@Override
public void execute(ClientTransactionHandler client, IBinder token,
        PendingTransactionActions pendingActions) {
    Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
    ActivityClientRecord r = client.getLaunchingActivity(token);
    client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}

在performLaunchActivity方法中,通过Instrument类来创建新的Activity,并执行Activity的onCreate、onStart、onResume方法等,Activity就算是正式启动,所以第二个Hook点,我们知道了吗?

因为在performLaunchActivity方法调用之前,Activity都不算是真正地启动,也就是说,将宿主中的ProxyActivity替换成插件的Activity就需要在performLaunchActivity方法之前

2 Hook方式实现插件四大组件启动

通过上面的源码,我们知道了AMS在什么时机去检测Activity合法性,以及Activity什么时候真正地启动,所以我们分步骤,先处理AMS合法性校验问题。

2.1 Hook AMS

首先我们先看一下,如何通过Hook的方式,欺骗AMS过Activity合法性检测这一关,因为从前面的源码我们知道,调用startActivity最终会在Instrument的execStartActivity方法中检测,就是下面这两行代码


//  位置①
int result = ActivityTaskManager.getService().startActivity(whoThread,
        who.getOpPackageName(), who.getAttributionTag(), intent,
        intent.resolveTypeIfNeeded(who.getContentResolver()), token,
        target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
checkStartActivityResult(result, intent);

首先我们先看一下ActivityTaskManager.getService()最终返回的是一个IActivityTaskManager代理对象,是从IActivityTaskManagerSingleton中取出来的;


public static IActivityTaskManager getService() {
    return IActivityTaskManagerSingleton.get();
}

@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
        new Singleton<IActivityTaskManager>() {
            @Override
            protected IActivityTaskManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                return IActivityTaskManager.Stub.asInterface(b);
            }
        };

所以想要代替系ActivityTaskManager.getService()的返回值,首先反射获取IActivityTaskManagerSingleton这个属性


val clazz4ATM = Class.forName("android.app.ActivityTaskManager")
val filed = clazz4ATM.getDeclaredField("IActivityTaskManagerSingleton")
filed.isAccessible = true
//获取ActivityTaskManager中IActivityTaskManagerSingleton属性的值
val IActivityTaskManagerSingletonField = filed.get(null)

我们看到,IActivityTaskManagerSingleton其实是一个单例,需要通过get方法才能获取真正的实例,那么我们先看下Singleton是啥


public abstract class Singleton<T> {

    @UnsupportedAppUsage
    public Singleton() {
    }

    @UnsupportedAppUsage
    private T mInstance;

    protected abstract T create();

    @UnsupportedAppUsage
    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

其实还是比较简单的一个单例类,通过get方法拿到的其实就是mInstance,也就是说getService()拿到的就是这个mInstance,只不过是对IActivityTaskManager做了一层封装。


//从Singleton中获取对象
val singletonClazz = Class.forName("android.util.Singleton")
val mInstance = singletonClazz.getDeclaredField("mInstance")
mInstance.isAccessible = true
val taskManagerInstance = mInstance.get(IActivityTaskManagerSingletonField)

那么之前通过反射,拿到了Singleton对象,那么现在通过反射获取mInstance属性就拿到了getService()的返回值。

既然我们要hook系统的执行方式,当我们调用startActivity的时候,系统流程一步一步执行到位置①,这个时候,我们把getService()的返回值替换,换成我们自己处理过的IActivityTaskManager,从而

2.1.1 动态代理在Hook中的使用

因为我们想要替换getService()的返回值,而且返回值是一个IActivityTaskManager接口对象,其实第一时间就会想到动态代理,那么动态代理是什么原理呢?


interface proxyInterface{
    fun execute()
}

例如有一个接口,如果我们想要执行execute方法,那么就需要实现一个具体类,然后调用execute方法,这样其实在程序运行之前,就已经知道执行者是谁了;而动态代理则是程序在运行时,动态生成一个对象,这个时候才能知道执行者是谁,例如:


val instance = Proxy.newProxyInstance(classLoader, arrayOf(proxyInterface::class.java),object : InvocationHandler{
    override fun invoke(
        proxy: Any?,
        method: java.lang.reflect.Method?,
        args: Array<out Any>?
    ): Any {

        //
        Log.e("TAG","执行execute方法之前")

        return method?.invoke(proxy,args)!!
    }

})
(instance as proxyInterface).execute()

通过Proxy调用newProxyInstance方法,传入的参数为类加载器、接口的class对象,需要实现一个接口InvocationHandler,其中method为调用者调用的某个方法,这个方法调用之前,可以插入一些逻辑判断,更为灵活;

这样就意味着,我们可以创建一个IActivityTaskManager的动态代理对象,替换系统的getService()返回的IActivityTaskManager对象,那么在动态代理对象调用startActivity之前,可以对入参做处理。


val proxyClazz = Class.forName("android.app.IActivityTaskManager")
val newTaskManagerInstance = Proxy.newProxyInstance(Thread.currentThread().contextClassLoader, arrayOf(proxyClazz),object : InvocationHandler{
    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any {

        //做相应参数的处理

        return method?.invoke(taskManagerInstance,args)!!
    }

})
//替换
mInstance.set(singletonClazz,newTaskManagerInstance)

因为IActivityTaskManager中的方法非常多,因此需要根据方法名去过滤,只能对startActivity方法进行处理,当startActivity执行时,首先会将Intent类型参数取出来,把Intent替换成宿主中的ProxyActivity,但是真实的Intent不能丢掉,等合适的时间会重置回来,因此可以保存在代理的Intent中


method?.let {
    if (it.name == "startActivity") {
        //修改Intent的值
        args?.let { args ->

            var targetIndex = 0
            for (index in args.indices) {
                if (args[index] is Intent) {
                    targetIndex = index
                    break
                }
            }
            val oldIntent = args[targetIndex] as Intent
            val intentProxy = Intent()
            intentProxy.component = ComponentName(
                "com.lay.image_process",
                "com.lay.image_process.ProxyActivity"
            )
            intentProxy.putExtra("old_intent", oldIntent)
            args[targetIndex] = intentProxy
        }
    }
}

这个时候,当我们启动插件中的Activity时,启动的就是这个代理Activity;这里需要说明一点就是,因为源码是Java写的,所以我们在Hook时,尽量还是用跟源码一致的语言,否则可能会出现类型不匹配的问题,详细源码见附录1

2.2 Hook ActivityThread

通过前面的源码,我们知道在AMS的realStartActivityLocked方法中(辛苦伙伴们自己爬楼),是创建了一个ClientTransaction对象,然后在ClientTransaction对象中添加了callback,在启动Activity的时候,添加的callback对象为LaunchActivityItem,我们看下LaunchActivityItem源码


public class LaunchActivityItem extends ClientTransactionItem {

    @UnsupportedAppUsage
    private Intent mIntent;
    private int mIdent;
    @UnsupportedAppUsage
    private ActivityInfo mInfo;

其他的先不用看,第一个参数就是mIntent,这个mIntent其实就是在之前AMS中替换的Intent,现在的目标就是将其替换成插件中的Activity。

那么在哪拿到这个对象呢?我们继续爬楼,在之前提到ActivityThread其实是一个Handler,通过Message的tag来分别处理数据。

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;

当启动Activity的时候,AMS通过ApplicationThread代理发送的消息类型为EXECUTE_TRANSACTION,也就是说,我们可以Hook系统的Handler,当接收到EXECUTE_TRANSACTION消息时,把Intent给替换掉。

在Activity中有一个Handler类H,这个类就负责接收Message然后处理


class H extends Handler{
    public static final int EXECUTE_TRANSACTION = 159;
}

我们可以看到,在ActivityThread中是直接new出来,我们知道,当调用Handler的send方法时,在底层是调用了dispatchMessage方法从消息池中取出数据分发


@UnsupportedAppUsage
final H mH = new H();

因为空参构造方法,mCallback是空的,所以会直接走handleMessage方法,所以我们如果想拿消息自己去处理,那么可以自己创建一个mCallback对象赋值给mH的callback,那么是不就能走我们自己的逻辑处理了呢


public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

首先我们先把前面梳理的思路通过反射梳理出来,有几个属性先强调一下,一个是sCurrentActivityThread,这个是ActivityThread类中的一个属性,就是当前对象,通过获取这个属性的值,来获取ActivityThread中的非静态属性值;还有一个就是Handler中的mCallback,因为系统Handler创建时没有设置callback,所以我们自己创建了一个callback,并给它赋值


public static void hookHandler() {

    try {

        Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
        //获取到ActivityThread对象
        Field sCurrentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
        sCurrentActivityThreadField.setAccessible(true);
        Object sCurrentActivityThread = sCurrentActivityThreadField.get(null);

        //获取mH属性
        Field mHField = activityThreadClazz.getDeclaredField("mH");
        mHField.setAccessible(true);
        //获取mH Handler对象
        Object mH = mHField.get(sCurrentActivityThread);

        //反射Handler
        Class<?> handlerClazz = Class.forName("android.os.Handler");
        Field mCallbackField = handlerClazz.getDeclaredField("mCallback");
        mCallbackField.setAccessible(true);

        //创建callback对象
        Handler.Callback callback = new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {

                //处理Intent替换的逻辑
                doIntentReplace(msg)

                return false;
            }
        };
        //给系统Handler赋值
        mCallbackField.set(mH, callback);

    } catch (Exception exception) {

    }

}

好了,那么下面的逻辑,主要就是给Intent替换。我们主要关心EXECUTE_TRANSACTION这个tag的处理。


public class ClientTransaction implements Parcelable, ObjectPoolItem {

    /** A list of individual callbacks to a client. */
    @UnsupportedAppUsage
    private List<ClientTransactionItem> mActivityCallbacks;

    /**
     * Add a message to the end of the sequence of callbacks.
     * @param activityCallback A single message that can contain a lifecycle request/callback.
     */
    public void addCallback(ClientTransactionItem activityCallback) {
        if (mActivityCallbacks == null) {
            mActivityCallbacks = new ArrayList<>();
        }
        mActivityCallbacks.add(activityCallback);
    }
}

当我们获取到Message携带的对象,其实就是ClientTransaction,我们看下源码,当调用addCallback的时候,就是将ClientTransactionItem放在了mActivityCallbacks中,所以拿到mActivityCallbacks就能拿到我们想要的LaunchActivityItem

private static void doIntentReplace(Message msg) {
        switch (msg.what) {
            case 159:
                //获取
                Class<?> transactionClazz = msg.obj.getClass();
                try {
                    Field mActivityCallbacksField = transactionClazz.getDeclaredField("mActivityCallbacks");
                    mActivityCallbacksField.setAccessible(true);
                    List callbacks = (List) mActivityCallbacksField.get(msg.obj);

                    for (int i = 0; i < callbacks.size(); i++) {
                        Object transactionItem = callbacks.get(i);
                        //判断是不是LaunchActivityItem
                        if (transactionItem.getClass().getName().equals("android.app.servertransaction.LaunchActivityItem")) {
                            //获取mIntent属性
                            Field mIntentField = transactionItem.getClass().getDeclaredField("mIntent");
                            mIntentField.setAccessible(true);
                            Log.e("TAG", "intent " + mIntentField.get(transactionItem));
                            Intent mIntent = (Intent) mIntentField.get(transactionItem);
                            Intent oldIntent = mIntent.getParcelableExtra("old_intent");
                            mIntentField.set(transactionItem, oldIntent);
//                            mIntent.setComponent(oldIntent.getComponent()); 这种方式同样有效
                        }
                    }


                } catch (Exception e) {
                    e.printStackTrace();
                }

                break;
        }
    }

这样我们拿到了LaunchActivityItem中的mIntent之后,这个其实是代理的Intent,需要从中取出我们之前在HookAMS的时候保存的真正的Intent,并赋值,这个时候,Activity启动的时候启动的就是插件中的Activity。

其实,我们在学习插件化的时候,其实知识点还是很多的,像:Android类加载机制、Activity的启动流程、Hook原理等,并且能够深入源码,这一块其实是面试的重点和难点;这一节我们已经启动了插件中的Activity,其实像Service、BroadcastReceiver等组件,原理是一致,像Activity已经启动了,但是资源文件并没有被加载,所以下一节将着重介绍如何加载插件中的资源。

附录1 Hook AMS(Android Q版本)


public static void HookAMS() {

    try {
        Class<?> atmClazz = Class.forName("android.app.ActivityTaskManager");
        Field iActivityTaskManagerSingletonField = atmClazz.getDeclaredField("IActivityTaskManagerSingleton");
        iActivityTaskManagerSingletonField.setAccessible(true);
        Object atmSingleton = iActivityTaskManagerSingletonField.get(null);

        Class<?> singletonClazz = Class.forName("android.util.Singleton");
        Field mInstanceField = singletonClazz.getDeclaredField("mInstance");
        mInstanceField.setAccessible(true);
        final Object atmService = mInstanceField.get(atmSingleton);

        Class<?> proxyClazz = Class.forName("android.app.IActivityTaskManager");
        Object newProxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{proxyClazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                if (method.getName().equals("startActivity")) {
                    int targetIndex = 0;
                    //修改Intent的值
                    for (int i = 0; i < args.length; i++) {
                        if (args[i] instanceof Intent) {
                            targetIndex = i;
                            break;
                        }
                    }
                    Intent oldIntent = (Intent) args[targetIndex];
                    Intent proxyIntent = new Intent();
                    proxyIntent.setComponent(new ComponentName("com.lay.image_process", "com.lay.image_process.MainActivity2"));
                    proxyIntent.putExtra("old_intent", oldIntent);
                    args[targetIndex] = proxyIntent;
                }
                Log.e("TAG", "atmService=" + atmService + " method=" + method + ", args=" + args);
                return method.invoke(atmService, args);
            }
        });

        mInstanceField.set(atmSingleton, newProxyInstance);

    } catch (Exception e) {
        Log.e("TAG", "e--" + e.getMessage());
        e.printStackTrace();
    }
}

附录2 Hook Handler(Android Q版本)


public static void hookHandler() {

        try {

            Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
            //获取到ActivityThread对象
            Field sCurrentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThreadField.setAccessible(true);
            Object sCurrentActivityThread = sCurrentActivityThreadField.get(null);

            //获取mH属性
            Field mHField = activityThreadClazz.getDeclaredField("mH");
            mHField.setAccessible(true);
            //获取mH Handler对象
            Object mH = mHField.get(sCurrentActivityThread);

            //反射Handler
            Class<?> handlerClazz = Class.forName("android.os.Handler");
            Field mCallbackField = handlerClazz.getDeclaredField("mCallback");
            mCallbackField.setAccessible(true);

            //创建callback对象
            Handler.Callback callback = new Handler.Callback() {
                @Override
                public boolean handleMessage(@NonNull Message msg) {

                    //处理Intent替换的逻辑
                    doIntentReplace(msg);
                    return false;
                }
            };
            //给系统Handler赋值
            mCallbackField.set(mH, callback);

        } catch (Exception exception) {

        }

    }

    private static void doIntentReplace(Message msg) {
        switch (msg.what) {
            case 159:
                //获取
                Class<?> transactionClazz = msg.obj.getClass();
                try {
                    Field mActivityCallbacksField = transactionClazz.getDeclaredField("mActivityCallbacks");
                    mActivityCallbacksField.setAccessible(true);
                    List callbacks = (List) mActivityCallbacksField.get(msg.obj);

                    for (int i = 0; i < callbacks.size(); i++) {
                        Object transactionItem = callbacks.get(i);
                        //判断是不是LaunchActivityItem
                        if (transactionItem.getClass().getName().equals("android.app.servertransaction.LaunchActivityItem")) {
                            //获取mIntent属性
                            Field mIntentField = transactionItem.getClass().getDeclaredField("mIntent");
                            mIntentField.setAccessible(true);
                            Log.e("TAG", "intent " + mIntentField.get(transactionItem));
                            Intent mIntent = (Intent) mIntentField.get(transactionItem);
                            Intent oldIntent = mIntent.getParcelableExtra("old_intent");
                            mIntentField.set(transactionItem, oldIntent);
//                            mIntent.setComponent(oldIntent.getComponent()); 这种方式同样有效
                        }
                    }


                } catch (Exception e) {
                    e.printStackTrace();
                }

                break;
        }
    }

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/TpLb-ne9v1QQrE6j6wmIng

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237227次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8063次阅读
 目录