小白为能够看懂的插件化DroidPlugin原理(三)– 如何阻止startActivity方法。Activity的启动过程。

小白为能够看懂的插件化DroidPlugin原理(三)– 如何阻止startActivity方法。Activity的启动过程。

  前言:于前面片篇稿子被分头介绍了动态代理、反射机制及Hook机制,如果对这些还免顶了解之童鞋建议先失参考一下前方少首文章。经过了面前两篇文章的陪衬,终于可以玩点真刀实弹的了,本篇将会见由此
Hook 掉 startActivity 方法的一个微例子来介绍如何寻找来相当的 Hook
切入点。 开始之前我们要掌握之一点哪怕是,其实在 Android 里面启动一个
Activity 可以通过简单种方式贯彻,一栽是咱们常因此的调用
Activity.startActivity 方法,一种植是调用 Context.startActivity
方法,两种艺术相比,
第一栽启动Activity的方法更加简单,所以先为第一种乎例。

于平时起步Activity时,我们常常因此之凡在Activity中直接调用startActivity(intent)就可启动Activity,或者是通过context来启动Activity。归根结底调用的是ContextImpl类的startActivity(intent)方法。所以我们直接由ContextImpl的startActivity(intent)方法开始,而startActivity(intent)方法最终调用了startActivity(intent,
bundle)。以下是源码(本篇文章的源码的基于Android
26之,不同版本代码间可能稍出入,但是原理是相同的):

  本系列文章的代码已经上传至github,下载地址:https://github.com/lgliuwei/DroidPluginStudy 本篇文章对应的代码在 com.liuwei.proxy_hook.hook.activityhook
包内,下载下来对照代码看文章效果会再次好!

@Override
public void startActivity(Intent intent, Bundle options) {
    warnIfCallingFromSystemProcess();

    // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
    // generally not allowed, except if the caller specifies the task id the activity should
    // be launched in.
    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
            && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
        throw new AndroidRuntimeException(
                "Calling startActivity() from outside of an Activity "
                + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                + " Is this really what you want?");
    }
    mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity) null, intent, -1, options);
}

一、Hook 掉 Activity 的 startActivity
的方法

认清了瞬间要是自表启动Activity,但是却无安装Intent
的flag为FLAG_ACTIVITY_NEW_TASK的,会丢弃来非常。我们平常吗会就此Application作为Context去启动Activity,如果无装Intent的flag为FLAG_ACTIVITY_NEW_TASK,会弃来方的那个,这点是内需专注的。
正规的流水线下是移动mMainThread.getInstrumentation().execStartActivity方法。先记住这无异步,到时候还要回去看。我们事先失看一下Activity的startActivity方法及Context的startActivity方法有啊分别,Activity的startActivity方法最终调用了startActivityForResult的方法,我们一直看该办法好了:

  于 Hook Activity 的 startActivity
方法之前,我们第一明确一下咱们的对象,我们先通过追踪源码找有
startActivity
调用的实在由作用的法,然后想方把对象措施阻碍截掉,并出口我们的平等漫漫 Log
信息。

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
        @Nullable Bundle options) {
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
        if (requestCode >= 0) {        
            mStartedActivity = true;
        }

        cancelInputsAndStartExitTransition(options);
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {       
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

  我们先行来一步步解析 startActivity 的源码,随手写一个 startActivity
的示范,按停 command 键( windows 下仍停 control )用鼠标点击
startActivity的办法即可跳反到艺术中。

如果mParent不为空,则执行mParent的startActivityFromChild方法:

  startActivity(Intent intent) 源码如下:

public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
        int requestCode, @Nullable Bundle options) {
    options = transferSpringboardActivityOptions(options);
    Instrumentation.ActivityResult ar =
        mInstrumentation.execStartActivity(
            this, mMainThread.getApplicationThread(), mToken, child,
            intent, requestCode, options);
    if (ar != null) {
        mMainThread.sendActivityResult(
            mToken, child.mEmbeddedID, requestCode,
            ar.getResultCode(), ar.getResultData());
    }
    cancelInputsAndStartExitTransition(options);
}
1 public void startActivity(Intent intent) {
2         this.startActivity(intent, null);
3 }

此地的尽逻辑与点mParent为空时的行逻辑是一模一样的。都是调用了Instrumentation类的execStartActivity方法。看到这里,我们还对照一下最好开头说到之ContextImpl里面的startActivity方法,同样都是实行了Instrumentation类的execStartActivity方法。所以我们即便集中地看Instrumentation类的execStartActivity方法吧,Instrumentation类的execStartActivity方法来无数独重构的点子,它们的逻辑都是一律的,所以我们不怕以里面的一个主持了。源码如下:

  就看 this.startActivity(intent, null) 方法源码:

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);
                ActivityResult result = null;
                if (am.ignoreMatchingSpecificIntents()) {
                    result = am.onStartActivity(intent);
                }
                if (result != null) {
                    am.mHits++;
                    return result;
                } else if (am.match(who, null, intent)) {
                    am.mHits++;
                    if (am.isBlocking()) {
                        return requestCode >= 0 ? am.getResult() : null;
                    }
                    break;
                }
            }
        }
    }
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        int result = ActivityManager.getService()
            .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 public void startActivity(Intent intent, @Nullable Bundle options) {
2         if (options != null) {
3             startActivityForResult(intent, -1, options);
4         } else {
5             // Note we want to go through this call for compatibility with
6             // applications that may have overridden the method.
7             startActivityForResult(intent, -1);
8         }
9 }

源码有硌长,我们挑重点来拘禁,和开行Activity相关的便是ActivityManager.getService().startActivity方法。该方式调用了ActivityManager.getService()的startActivity方法。在进ActivityManager之前,先看一下checkStartActivityResult方法:

  从上同样步传的参数 options 为 null
我们就是可知道这无异于步调用了 startActivityForResult(intent, -1) 的代码。

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

  startActivityForResult(Intent intent, int requestCode) 源码如下:

凡免是观了一个万分熟悉的挺,have you declared this activity in your
AndroidManifest.xml?,这个深的意是咱并未在AndroidManifest中登记我们的Activity,所以抛出异常了。这说明了开行的Activity会通过ActivityManagerService进行局部校验,如果未符合要求就会丢来大。好了,让咱后续上ActivityManager类可以见见getService方法如下:

1 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
2         startActivityForResult(intent, requestCode, null);
3 }
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

  startActivityForResult(Intent intent, int requestCode, Bundle
options) 源码如下:

她而调用了IActivityManagerSingleton类的get()方法,而IActivityManagerSingleton类是一个Singleton类型的静态单例类,如下所示:

 1 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
 2             @Nullable Bundle options) {
 3         if (mParent == null) {
 4             options = transferSpringboardActivityOptions(options);
 5             Instrumentation.ActivityResult ar =
 6                 mInstrumentation.execStartActivity(
 7                     this, mMainThread.getApplicationThread(), mToken, this,
 8                     intent, requestCode, options);
 9             if (ar != null) {
10                 mMainThread.sendActivityResult(
11                     mToken, mEmbeddedID, requestCode, ar.getResultCode(),
12                     ar.getResultData());
13             }
14             if (requestCode >= 0) {
15                 // If this start is requesting a result, we can avoid making
16                 // the activity visible until the result is received.  Setting
17                 // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
18                 // activity hidden during this time, to avoid flickering.
19                 // This can only be done when a result is requested because
20                 // that guarantees we will get information back when the
21                 // activity is finished, no matter what happens to it.
22                 mStartedActivity = true;
23             }
24 
25             cancelInputsAndStartExitTransition(options);
26             // TODO Consider clearing/flushing other event sources and events for child windows.
27         } else {
28             if (options != null) {
29                 mParent.startActivityFromChild(this, intent, requestCode, options);
30             } else {
31                 // Note we want to go through this method for compatibility with
32                 // existing applications that may have overridden it.
33                 mParent.startActivityFromChild(this, intent, requestCode);
34             }
35         }
36 }
private static final Singleton<IActivityManager> IActivityManagerSingleton =
    new Singleton<IActivityManager>() {
        @Override
        protected IActivityManager create() {
            final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
            final IActivityManager am = IActivityManager.Stub.asInterface(b);
            return am;
        }
    };

   到马上无异步我们曾经见到了第一点,注意点代码块被革命的代码,其实
startActivity 真正调用的是 mInstrumentation.execStartActivity(…)
方法,mInstrumentation 是
Activity 的一个私有变量。接下来的职责将移得非常简单,回忆一下上同篇博文《小白为会看明白插件化DroidPlugin原理(二)–
反射机制及Hook入门》着的方案一,在轮换汽车引擎时我们累原来的汽车引擎类创建了一个新类,然后在初引擎类中梗阻了最好酷快之办法,这里的笔触是平等的,我们直接新建一个后续
Instrumentation 的新类,然后还写 execStartActivity()
。对这起无明了的童鞋建议重新看无异周上一致篇博文《小白也克看明白插件化DroidPlugin原理(二)–
反射机制和Hook入门》。代码如下:

俺们看一下Singleton类的定义:

 1 public class EvilInstrumentation extends Instrumentation {
 2     private Instrumentation instrumentation;
 3     public EvilInstrumentation(Instrumentation instrumentation) {
 4         this.instrumentation = instrumentation;
 5     }
 6     public ActivityResult execStartActivity(
 7             Context who, IBinder contextThread, IBinder token, Activity target,
 8             Intent intent, int requestCode, Bundle options) {
 9         Logger.i(EvilInstrumentation.class, "请注意! startActivity已经被hook了!");
10         try {
11             Method execStartActivity = Instrumentation.class.getDeclaredMethod("execStartActivity", Context.class,
12                     IBinder.class, IBinder.class, Activity.class,
13                     Intent.class, int.class, Bundle.class);
14             return (ActivityResult)execStartActivity.invoke(instrumentation, who, contextThread, token, target,
15                     intent, requestCode, options);
16         } catch (Exception e) {
17             e.printStackTrace();
18         }
19 
20         return null;
21     }
22 }
public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

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

   重写工作已举行截止了,接着我们经过反射机制用新建的
EvilInstrumentation 替换掉 Activity 的 mInstrumentation
变量,具体代码如下:

其间的get()方法返回了mInstance对象,而mInstance对象又由于create()方法创建的。我们可以看出地方IActivityManagerSingleton对象的create()方法创建了一个IActivityManager类型的目标。而IActivityManager是一个接口,其具体的实现类似是ActivityManagerService,所以最终调用的是ActivityManagerService的startActivity方法:

 1 public static void doActivityStartHook(Activity activity){
 2         try {
 3             Field mInstrumentationField = Activity.class.getDeclaredField("mInstrumentation");
 4             mInstrumentationField.setAccessible(true);
 5             Instrumentation originalInstrumentation = (Instrumentation)mInstrumentationField.get(activity);
 6             mInstrumentationField.set(activity, new EvilInstrumentation(originalInstrumentation));
 7         } catch (Exception e) {
 8             e.printStackTrace();
 9         }
10 }
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
            resultWho, requestCode, startFlags, profilerInfo, bOptions,
            UserHandle.getCallingUserId());
}

   这对于我们吧既好是轻车熟路了,很快便描写了了,然后我们当 Activity
的 onCreate() 方法中需要调用一下 doActivityStartHook 即可完成对
Activity.startActivity 的 hook。MainActivity 的代码如下:

再就是调用了startActivityAsUser方法,而startActivityAsUser方法又调用了那重构的同名方法,而其重构的点子以调用了ActivityStarter的startActivityMayWait方法,经过内部频繁调用,然后以调用了ActivityStackSupervisor类的法门,经过了ActivityStarter类和ActivityStackSupervisor类的反复调用,最后执行之是ActivityStackSupervisor类中之realStartActivityLocked方法,那么我们直接扣这个方式吧:

 1 public class MainActivity extends Activity {
 2     private Button btn_start_by_activity;
 3     @Override
 4     protected void onCreate(Bundle savedInstanceState) {
 5         super.onCreate(savedInstanceState);
 6         setContentView(R.layout.activity_main);
 7         // hook Activity.startActivity()的方法时不知道这行代码为什么放在attachBaseContext里面不行?
 8         // 调试发现,被hook的Instrumentation后来又会被替换掉原来的。
10         ActivityThreadHookHelper.doActivityStartHook(this);
11         btn_start_by_activity = (Button) findViewById(R.id.btn_start_by_activity);
12         btn_start_by_activity.setOnClickListener(new View.OnClickListener() {
13             @Override
14             public void onClick(View v) {
15                 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
16                 startActivity(intent);
17             }
18         });
19     }
20 }
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {
    ...省略
    app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    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.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                    r.persistentState, results, newIntents, !andResume,
                    mService.isNextTransitionForward(), profilerInfo);
    ...省略
}

   程序运行之后,点击启动 Activity 的按钮将出口以下 Log:

以realStartActivityLocked方法被调用了app.thread.
scheduleLaunchActivity方法,而app.thread的色是IApplicationThread,其实现类是ActivityThread内部类ApplicationThread,所以我们去ApplicationThread中找到scheduleLaunchActivity方法:

   [EvilInstrumentation] :
请注意! startActivity已经被hook了!

@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
        int procState, Bundle state, PersistableBundle persistentState,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

    updateProcessState(procState, false);

    ActivityClientRecord r = new ActivityClientRecord();

    r.token = token;
    r.ident = ident;
    r.intent = intent;
    r.referrer = referrer;
    r.voiceInteractor = voiceInteractor;
    r.activityInfo = info;
    r.compatInfo = compatInfo;
    r.state = state;
    r.persistentState = persistentState;

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

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

    r.profilerInfo = profilerInfo;

    r.overrideConfig = overrideConfig;
    updatePendingConfiguration(curConfig);

    sendMessage(H.LAUNCH_ACTIVITY, r);
}

   到是结束我们已 hook 了 Activity 的 startActivity
方法,非常简单,代码量也很少,但咱也甚随意的觉察这种方式要以各一个
Activity 的 onCreate 方法中调用一次 doActivityStartHook
方法,显然这不是一个好的方案,所以我们在搜寻 hook
点时得要顾尽量找有在过程面临维系无变换或未容易让转的变量,就像单例和静态变量。

末调用了sendMessage方法,而以sendMessage方法以调用了那个同名重构方法sendMessage:

  问题1:在此间有几许值得一提,我们以
doActivityStartHook(…) 方法的调用如果放置  MainActivity
的 attachBaseContext(…) 方法被替换工作拿无见面生效,为什么?

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    if (DEBUG_MESSAGES) Slog.v(
        TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
        + ": " + arg1 + " / " + obj);
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}

  调试发现,我们在
attachBaseContext(..) 里面执行了 doActivityStartHook(…) 方法后确实将
Activity 的 mInstrumentation 变量换成了俺们好之
EvilInstrumentation,但程序执行到 onCreate() 方法后即会见意识此时候
mInstrumentation 变成了网协调的 Instrumentation
对象了。这时候我们可以确信的是 mInstrumentation 变量一定是在
attachBaseContext() 之后为初始化或者赋值的。带在此目标我们好轻松就以
Activity 源码的 attach() 方法中找到如下代码:

说到底经调用了mH的sendMessage方法将一个Message对象上加到消息队列,因为mH的类型的Handler类,所以最后见面当mH的handleMessage方法中处理该信息,我们看一下handleMessage方法被对于msg.what为H.LAUNCH_ACTIVITY的处理逻辑:

  Activity.attach() 的源码如下(注意第8推行及第26行):

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        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;
        ...省略
    }
}
 1   final void attach(Context context, ActivityThread aThread,
 2             Instrumentation instr, IBinder token, int ident,
 3             Application application, Intent intent, ActivityInfo info,
 4             CharSequence title, Activity parent, String id,
 5             NonConfigurationInstances lastNonConfigurationInstances,
 6             Configuration config, String referrer, IVoiceInteractor voiceInteractor,
 7             Window window) {
 8         attachBaseContext(context);
 9 
10         mFragments.attachHost(null /*parent*/);
11 
12         mWindow = new PhoneWindow(this, window);
13         mWindow.setWindowControllerCallback(this);
14         mWindow.setCallback(this);
15         mWindow.setOnWindowDismissedCallback(this);
16         mWindow.getLayoutInflater().setPrivateFactory(this);
17         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
18             mWindow.setSoftInputMode(info.softInputMode);
19         }
20         if (info.uiOptions != 0) {
21             mWindow.setUiOptions(info.uiOptions);
22         }
23         mUiThread = Thread.currentThread();
24 
25         mMainThread = aThread;
26         mInstrumentation = instr;
27         mToken = token;
28         mIdent = ident;
29         mApplication = application;
30         mIntent = intent;
31         mReferrer = referrer;
32         mComponent = intent.getComponent();
33         mActivityInfo = info;
34         mTitle = title;
35         mParent = parent;
36         mEmbeddedID = id;
37         mLastNonConfigurationInstances = lastNonConfigurationInstances;
38         if (voiceInteractor != null) {
39             if (lastNonConfigurationInstances != null) {
40                 mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
41             } else {
42                 mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
43                         Looper.myLooper());
44             }
45         }
46 
47         mWindow.setWindowManager(
48                 (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
49                 mToken, mComponent.flattenToString(),
50                 (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
51         if (mParent != null) {
52             mWindow.setContainer(mParent.getWindow());
53         }
54         mWindowManager = mWindow.getWindowManager();
55         mCurrentConfig = config;
56     }

其间又调用了handleLaunchActivity方法:

  至此,问题1算凡是找到了答案。

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;

    if (r.profilerInfo != null) {
        mProfiler.setProfiler(r.profilerInfo);
        mProfiler.startProfiling();
    }

    // Make sure we are running with the most recent config.
    handleConfigurationChanged(null, null);

    if (localLOGV) Slog.v(
        TAG, "Handling launch of " + r);

    // Initialize before creating the activity
    WindowManagerGlobal.initialize();

    Activity a = performLaunchActivity(r, customIntent);

    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

        if (!r.activity.mFinished && r.startsNotResumed) {      
            performPauseActivityIfNeeded(r, reason);

            if (r.isPreHoneycomb()) {
                r.state = oldState;
            }
        }
    } else {
        // If there was an error, for any reason, tell the activity manager to stop us.
        try {
            ActivityManager.getService()
                .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

二、Hook 掉 Context 的 startActivity
的方法

里调用了performLaunchActivity方法创建了一个Activity对象,然后回调Activity的生命周期方法,我们看一下performLaunchActivity方法:

  文章开始我们就是说 Android 中产生只简单栽启动 Activity 的法,一栽是
Activity.startActivity 另一样种植是
Context.startActivity,但需留意的常常,我们当使 Context.startActivity
启动一个 Activity 的上以 flags 指定为 FLAG_ACTIVITY_NEW_TASK。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                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);
    }

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }

    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
        if (localLOGV) Slog.v(
                TAG, r + ": app=" + app
                + ", appName=" + app.getPackageName()
                + ", pkg=" + r.packageInfo.getPackageName()
                + ", comp=" + r.intent.getComponent().toShortString()
                + ", dir=" + r.packageInfo.getAppDir());

        if (activity != null) {
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);
            if (r.overrideConfig != null) {
                config.updateFrom(r.overrideConfig);
            }
            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                    + r.activityInfo.name + " with config " + config);
            Window window = null;
            if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                window = r.mPendingRemoveWindow;
                r.mPendingRemoveWindow = null;
                r.mPendingRemoveWindowManager = null;
            }
            appContext.setOuterContext(activity);
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback);

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

            activity.mCalled = false;
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            if (!activity.mCalled) {
                throw new SuperNotCalledException(
                    "Activity " + r.intent.getComponent().toShortString() +
                    " did not call through to super.onCreate()");
            }
            r.activity = activity;
            r.stopped = true;
            if (!r.activity.mFinished) {
                activity.performStart();
                r.stopped = false;
            }
            if (!r.activity.mFinished) {
                if (r.isPersistable()) {
                    if (r.state != null || r.persistentState != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
                    }
                } else if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            if (!r.activity.mFinished) {
                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state,
                            r.persistentState);
                } else {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onPostCreate()");
                }
            }
        }
        r.paused = true;

        mActivities.put(r.token, r);

    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to start activity " + component
                + ": " + e.toString(), e);
        }
    }

    return activity;
}

  于联网下的辨析面临待查阅 Android 源码,先引进两独翻 Android
源码的网站:

该法的源码有硌长,我们分层次来拘禁:
1、首先通过调用mInstrumentation.newActivity方法创建了一个Activity对象,而mInstrumentation.newActivity方法创建对象的经过为酷简短,直接通过反射创建一个新的靶子,如下所示:

  http://androidxref.com

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

  http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/

2、接着通过调用r.packageInfo.makeApplication方法判断Application对象是不是有,存在则归,不存则新建一个Application对象并回调其onCreate方法:

  我们摸索着 hook 掉 Context.startActivity 方法,我们仍随手写一个
Context 方式启动 Activity 的示范,如下:

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }

    ...省略

    try {
        java.lang.ClassLoader cl = getClassLoader();
        if (!mPackageName.equals("android")) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                    "initializeJavaContextClassLoader");
            initializeJavaContextClassLoader();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        if (!mActivityThread.mInstrumentation.onException(app, e)) {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            throw new RuntimeException(
                "Unable to instantiate application " + appClass
                + ": " + e.toString(), e);
        }
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    if (instrumentation != null) {
        try {
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            if (!instrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to create application " + app.getClass().getName()
                    + ": " + e.toString(), e);
            }
        }
    }

    ...省略

    return app;
}
1 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
2 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3 getApplicationContext().startActivity(intent);

3、回调Activity对象的attach、onCreate方法:

   照着(一)中之姿势点入 startActivity() 方法中,由于 Context
是一个抽象类,所以我们得找到其的落实类似才能够看到实际的代码,通过查
Android 源码我们好当 ActivityTread 中但是知 Context 的贯彻类似是
ContextImpl。(在此大家先了解就同碰就算推行,具体的调用细节将会当生一致首博文中详尽介绍)

if (activity != null) {
    CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
    Configuration config = new Configuration(mCompatConfiguration);
    if (r.overrideConfig != null) {
        config.updateFrom(r.overrideConfig);
    }
    ...省略
    appContext.setOuterContext(activity);
    activity.attach(appContext, this, getInstrumentation(), r.token,
            r.ident, app, r.intent, r.activityInfo, title, r.parent,
            r.embeddedID, r.lastNonConfigurationInstances, config,
            r.referrer, r.voiceInteractor, window, r.configCallback);

    ...省略
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    if (!activity.mCalled) {
        throw new SuperNotCalledException(
            "Activity " + r.intent.getComponent().toShortString() +
            " did not call through to super.onCreate()");
    }
    r.activity = activity;
}

  源码地址:

交这里Activity就开动起来了。下面我们来回顾一下者的有所手续流程:
1、首先我们而开动的Activity会去ActivityManagerService中校检是否合法,如果未合法则抛来特别。
2、然后经回调ActivityThread中内部类ApplicationThread的scheduleLaunchActivity方法并发送一个消息及ActivityThread中的内类H中,而H是一个Handler类。
3、最后经过反射创建Activity对象和Application对象,并回调其中的生命周期方法,从而将Activity启动起来。

  http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/app/ActivityThread.java#2338

 1 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
 2         ...
 3             if (activity != null) {
 4                 Context appContext = createBaseContextForActivity(r, activity);
 5                 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
 6                 Configuration config = new Configuration(mCompatConfiguration);
 7         ...
 8 }
 9         ...
10 private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
11          ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
12          appContext.setOuterContext(activity);
13          Context baseContext = appContext;
14          ...
15 }

    现在咱们来查 ContextImpl.startActivity() 的源码。 

  源码地址:

  http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/app/ContextImpl.java#ContextImpl.startActivity%28android.content.Intent%29

1 @Override
2 public void startActivity(Intent intent) {
3         warnIfCallingFromSystemProcess();
4         startActivity(intent, null);
5 }

  再进入 startActivity(intent, null) 查看源码如下:

 1 @Override
 2 public void startActivity(Intent intent, Bundle options) {
 3         warnIfCallingFromSystemProcess();
 4         if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
 5             throw new AndroidRuntimeException(
 6                     "Calling startActivity() from outside of an Activity "
 7                     + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
 8                     + " Is this really what you want?");
 9         }
10         mMainThread.getInstrumentation().execStartActivity(
11             getOuterContext(), mMainThread.getApplicationThread(), null,
12             (Activity)null, intent, -1, options);
13 }

   由方第四尽代码可以见见在代码中判断了 intent 的 flag
类型,如果非 FLAG_ACTIVITY_NEW_TASK
类型就见面扔来很。接着看红部分的重中之重代码,可以看出先从 ActivityTread
中拿走到了 Instrumentation 最后要调用了 Instrumentation 的
execStartActivity(…) 方法,我们今天要举行的即使是分析 ActivityTread
类,并想办法用我们友好写的 EvilInstrumentation 类将 ActivityTread 的
mInstrumentation 替换掉。

  源码地址:

  http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/app/ActivityThread.java#ActivityThread.0sCurrentActivityThread

  ActivityTread 部分代码如下:

206     private static ActivityThread sCurrentActivityThread;
207     Instrumentation mInstrumentation;
...
1597    public static ActivityThread currentActivityThread() {
1598        return sCurrentActivityThread;
1599    }
...
1797    public Instrumentation getInstrumentation()
1798    {
1799        return mInstrumentation;
1800    }

  这里要报告大家是,ActivityTread
即代表采取之主线程,而一个行使被只有来一个主线程,并且由源码可知,ActivityTreadd
的对象又是因静态变量的样式是的,太好了,这正是我们设物色的 Hook
点。废话不多说了,现在咱们惟有待用反射通过 currentActivityThread()
方法以到 ActivityThread 的目标,然后于用 mInstrumentation 替换成
EvilInstrumentation 即可,代码如下:

 1   public static void doContextStartHook(){
 2         try {
 3             Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
 4             Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
 5             Object activityThread = currentActivityThreadMethod.invoke(null);
 6 
 7             Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
 8             mInstrumentationField.setAccessible(true);
 9             Instrumentation originalInstrumentation = (Instrumentation)mInstrumentationField.get(activityThread);
10             mInstrumentationField.set(activityThread, new EvilInstrumentation(originalInstrumentation));
11         } catch (Exception e) {
12             e.printStackTrace();
13         }
14     }

    其实代码也不难理解,跟 Hook Activity 的 startActivity()
方法是一个思路,只是 Hook 的点不同而已。下面我们于 MainActivity
的 attachBaseContext() 方法中调用 doContextStartHook()
方法,并累加相关测试代码,具体代码如下:

 1 public class MainActivity extends Activity {
 2     private Button btn_start_by_context;
 3     @Override
 4     protected void onCreate(Bundle savedInstanceState) {
 5         super.onCreate(savedInstanceState);
 6         setContentView(R.layout.activity_main);
 7         btn_start_by_context = (Button) findViewById(R.id.btn_start_by_context);
 8         btn_start_by_context.setOnClickListener(new View.OnClickListener() {
 9             @Override
10             public void onClick(View v) {
11                 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
12                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
13                 getApplicationContext().startActivity(intent);
14             }
15         });
16     }
17     @Override
18     protected void attachBaseContext(Context newBase) {
19         super.attachBaseContext(newBase);
20         ActivityThreadHookHelper.doContextStartHook();
21     }
22 }

   点击按钮后翻 Log 输出如下:

   [EvilInstrumentation]
: 请注意! startActivity已经被hook了!

   看如此的 Log,说明我们曾经打响之 Hook 了
Context.startActivity()。而且 doContextStartHook()
方法就于程序开始之时节调用一赖即可,后面在次外的 Activity 中调用
Context.startActivity() 时此拦截工作都只是生效,这是因
Context.startActivity() 在实践启动 Activity 的操作时调是经过
ActivityTread 获取到 Instrumentation,然后再度调用
Instrumentation.execStartActivity() 方法,而 ActivityTread
在次中凡坐单例的款型在的,这虽是故。所以说调用
doContextStartHook() 方法极其好之机该是在 Application 中。

  注意!前方惊现彩蛋一朵!!

  将 doContextStartHook() 方法放入到了 MyApplication
的 attachBaseContext() 里面后,代码如下:

1 public class MyApplication extends Application {
2     @Override
3     protected void attachBaseContext(Context base) {
4         super.attachBaseContext(base);
5         ActivityThreadHookHelper.doContextStartHook();
6     }
7 } 

  MainActivity 的代码如下:

 1 public class MainActivity extends Activity {
 2     private final static String TAG = MainActivity.class.getSimpleName();
 3     private Button btn_start_by_activity;
 4     private Button btn_start_by_context;
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_main);
 9         btn_start_by_activity = (Button) findViewById(R.id.btn_start_by_activity);
10         btn_start_by_context = (Button) findViewById(R.id.btn_start_by_context);
11         ActivityThreadHookHelper.doActivityStartHook(this);
12         btn_start_by_activity.setOnClickListener(new View.OnClickListener() {
13             @Override
14             public void onClick(View v) {
15                 Log.i(TAG, "onClick: Activity.startActivity()");
16                 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
17                 startActivity(intent);
18             }
19         });
20 
21         btn_start_by_context.setOnClickListener(new View.OnClickListener() {
22             @Override
23             public void onClick(View v) {
24                 Log.i(TAG, "onClick: Context.startActivity()");
25                 Intent intent = new Intent(MainActivity.this, OtherActivity.class);
26                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
27                 getApplicationContext().startActivity(intent);
28             }
29         });
30     }
31 }

   代码如齐,布局文件特别简单即无粘出来了,就是少单按钮,一个测试
Activity.startActivity() 方法,一个测试 Context.startActivity()
方法,然后以 MainActivity 的 onCreate() 中调用了 doActivityStartHook()
在 MyApplication 里面调用了 doContextStartHook(),
目前看来代码很正规,符合我们地方的思路,但楼主在点击按钮发现 Log
输出如下:

图片 1

  是的,Activity.startActivity 被 hook 的音信输出了有限次!为什么?

  我们不妨先猜想一下,一定是 Activity 的 mInstrumentation
对象在我们轮换之前就是已变为了 EvilInstrumentation, 然后我们而于
Activity.onCreate 方法调用了同样不好 doActivityStartHook(), 相当给我们以就此
EvilInstrumentation 又又写了 EvilInstrumentation 的 startActivity()
方法,所以造成 log 信息输出了有限糟糕。

  那问题还要来了,为什么 Activity 的 mInstrumentation
对象在咱们轮换之前即既改为了 EvilInstrumentation? 

  纵观代码,只来一个地方产生疑难,那就是咱坐
MyApplication.attachBaseContext() 方法中的 doContextStartHook()
起的打算!

  还是事先直接省略说一下实际的本质吧,结合上文所说,一个用内仅设有一个
ActivityTread 对象,也仅仅存在一个 Instrumentation
对象,这个 Instrumentation 是 ActivityTread 的分子变量,并当
ActivityTread 内成功初始化,在开行一个 Activity 的流程中大约在最终的岗位
ActivityTread 会回调 Activity 的 attach() 方法,并将好的
Instrumentation 对象传于 Activity。启动 Activity
的事无巨细流程以及调用细节将见面在生一致首博文介绍,敬请期待!

三、小结

  本篇文章通过拦截 Context.startActivity() 和 Activity.startActivity()
两独艺术,将直达亦然篇稿子中牵线的 Hook 技术实施 Activity
的开行流程中,同时经过这片只小例子初步询问了 Android
源码以及哪去选定一个适中的 Hook 点。想如果了解插件化的基本原理,熟悉
Activity 的起步流程是必不可少的,下一样首文章用会详细介绍 Activity
的启航流程,感兴趣的同班可以关心一下!

参考文章

  《Android插件化原理分析——Hook机制的动态代理》

  《Android应用程序的Activity启动过程简单介绍及上计划》

正文链接:http://www.cnblogs.com/codingblock/p/6666239.html

admin

网站地图xml地图