{"id":1835,"date":"2023-03-29T21:45:52","date_gmt":"2023-03-29T13:45:52","guid":{"rendered":"https:\/\/www.appblog.cn\/?p=1835"},"modified":"2023-04-22T09:17:33","modified_gmt":"2023-04-22T01:17:33","slug":"startup-process-for-android-hook-activity","status":"publish","type":"post","link":"https:\/\/www.appblog.cn\/index.php\/2023\/03\/29\/startup-process-for-android-hook-activity\/","title":{"rendered":"Android Hook-Activity\u7684\u542f\u52a8\u6d41\u7a0b"},"content":{"rendered":"<h2>\u4e24\u79cd\u542f\u52a8Activity\u7684\u65b9\u5f0f\u6e90\u7801\u8ffd\u8e2a<\/h2>\n<p>\u6e90\u7801\u57fa\u4e8e SDK 28 ~ Android 9.0<\/p>\n<h3>\u65b9\u5f0f1\uff1a\u4f7f\u7528Activity\u81ea\u5e26\u7684startActivity<\/h3>\n<h4>\u4ee3\u7801\u793a\u4f8b<\/h4>\n<p><!-- more --><\/p>\n<pre><code class=\"language-java\">private void startActivityByActivity() {\n    Intent i = new Intent(MainActivity.this, Main2Activity.class);\n    startActivity(i);\n}<\/code><\/pre>\n<h4>\u7a0b\u5e8f\u6267\u884c\u8d70\u5411\u56fe<\/h4>\n<p>\u4ee3\u7801\u8ffd\u8e2a\uff1a<code>Activity.java<\/code><\/p>\n<pre><code class=\"language-java\">@Override\npublic void startActivity(Intent intent) {\n    this.startActivity(intent, null);\n}<\/code><\/pre>\n<pre><code class=\"language-java\">@Override\npublic void startActivity(Intent intent, @Nullable Bundle options) {\n    if (options != null) {\n        startActivityForResult(intent, -1, options);\n    } else {\n        \/\/ Note we want to go through this call for compatibility with\n        \/\/ applications that may have overridden the method.\n        startActivityForResult(intent, -1);\n    }\n}<\/code><\/pre>\n<pre><code class=\"language-java\">public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,\n        @Nullable Bundle options) {\n    if (mParent == null) {\n        options = transferSpringboardActivityOptions(options);\n        Instrumentation.ActivityResult ar =\n            mInstrumentation.execStartActivity(\n                this, mMainThread.getApplicationThread(), mToken, this,\n                intent, requestCode, options);\n        if (ar != null) {\n            mMainThread.sendActivityResult(\n                mToken, mEmbeddedID, requestCode, ar.getResultCode(),\n                ar.getResultData());\n        }\n        if (requestCode &gt;= 0) {\n            \/\/ If this start is requesting a result, we can avoid making\n            \/\/ the activity visible until the result is received.  Setting\n            \/\/ this code during onCreate(Bundle savedInstanceState) or onResume() will keep the\n            \/\/ activity hidden during this time, to avoid flickering.\n            \/\/ This can only be done when a result is requested because\n            \/\/ that guarantees we will get information back when the\n            \/\/ activity is finished, no matter what happens to it.\n            mStartedActivity = true;\n        }\n\n        cancelInputsAndStartExitTransition(options);\n        \/\/ TODO Consider clearing\/flushing other event sources and events for child windows.\n    } else {\n        if (options != null) {\n            mParent.startActivityFromChild(this, intent, requestCode, options);\n        } else {\n            \/\/ Note we want to go through this method for compatibility with\n            \/\/ existing applications that may have overridden it.\n            mParent.startActivityFromChild(this, intent, requestCode);\n        }\n    }\n}<\/code><\/pre>\n<p>\u8fd9\u91cc\u6709\u4e2a<code>if (mParent == null)<\/code>\u5224\u5b9a\uff0c\u5148\u770b<code>true<\/code>\u5206\u652f\uff0c\u53d1\u73b0\u4e00\u4e2a\u5751\uff0c<code>mInstrumentation.execStartActivity<\/code>\u8fd9\u91cc\u5c45\u7136\u4e0d\u80fd\u7ee7\u7eed\u5f80\u4e0b\u7d22\u5f15\uff1f\u5f88\u5947\u602a\uff0c\u4e0d\u8fc7\u4e0d\u91cd\u8981\uff0c\u6211\u4eec\u76f4\u63a5\u8fdb\u5165<code>Instrumentation.java<\/code>\u53bb\u627e\u8fd9\u4e2a\u65b9\u6cd5:<\/p>\n<pre><code class=\"language-java\">public ActivityResult execStartActivity(\n        Context who, IBinder contextThread, IBinder token, Activity target,\n        Intent intent, int requestCode, Bundle options) {\n    IApplicationThread whoThread = (IApplicationThread) contextThread;\n    Uri referrer = target != null ? target.onProvideReferrer() : null;\n    if (referrer != null) {\n        intent.putExtra(Intent.EXTRA_REFERRER, referrer);\n    }\n    if (mActivityMonitors != null) {\n        synchronized (mSync) {\n            final int N = mActivityMonitors.size();\n            for (int i=0; i&lt;N; i++) {\n                final ActivityMonitor am = mActivityMonitors.get(i);\n                ActivityResult result = null;\n                if (am.ignoreMatchingSpecificIntents()) {\n                    result = am.onStartActivity(intent);\n                }\n                if (result != null) {\n                    am.mHits++;\n                    return result;\n                } else if (am.match(who, null, intent)) {\n                    am.mHits++;\n                    if (am.isBlocking()) {\n                        return requestCode &gt;= 0 ? am.getResult() : null;\n                    }\n                    break;\n                }\n            }\n        }\n    }\n    try {\n        intent.migrateExtraStreamToClipData();\n        intent.prepareToLeaveProcess(who);\n        int result = ActivityManager.getService()\n            .startActivity(whoThread, who.getBasePackageName(), intent,\n                    intent.resolveTypeIfNeeded(who.getContentResolver()),\n                    token, target != null ? target.mEmbeddedID : null,\n                    requestCode, 0, null, options);\n        checkStartActivityResult(result, intent);\n    } catch (RemoteException e) {\n        throw new RuntimeException(&quot;Failure from system&quot;, e);\n    }\n    return null;\n}<\/code><\/pre>\n<p>\u5728\u8fd9\u4e2a<code>execStartActivity<\/code>\u4e2d\uff0c\u53ef\u4ee5\u627e\u5230\u5173\u952e\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-java\">int result = ActivityManager.getService()\n    .startActivity(whoThread, who.getBasePackageName(), intent,\n            intent.resolveTypeIfNeeded(who.getContentResolver()),\n            token, target != null ? target.mEmbeddedID : null,\n            requestCode, 0, null, options);\ncheckStartActivityResult(result, intent);<\/code><\/pre>\n<p>\u901a\u8fc7\u8fd9\u79cd\u65b9\u5f0f\u542f\u52a8Activity\uff0c\u6700\u7ec8\u7684\u6267\u884c\u6743\u88ab\u4ea4\u7ed9\u4e86<code>ActivityManager.getService()<\/code>\uff08\u5373AMS\uff09\uff0c\u5b83\u7684\u4f5c\u7528\u662f\u542f\u52a8\u4e00\u4e2aActivity\u5e76\u4e14\u8fd4\u56de<code>result<\/code>\uff0c\u7136\u540e<code>checkStartActivityResult(result, intent);<\/code>\u8fd9\u53e5\u8bdd\uff0c\u5bf9\u5f53\u524d\u7684\u8df3\u8f6c\u610f\u56fe<code>intent<\/code>\u8fdb\u884c\u68c0\u6d4b\uff1b<\/p>\n<pre><code class=\"language-java\">\/** @hide *\/\npublic static void checkStartActivityResult(int res, Object intent) {\n    if (!ActivityManager.isStartResultFatalError(res)) {\n        return;\n    }\n\n    switch (res) {\n        case ActivityManager.START_INTENT_NOT_RESOLVED:\n        case ActivityManager.START_CLASS_NOT_FOUND:\n            if (intent instanceof Intent &amp;&amp; ((Intent)intent).getComponent() != null)\n                throw new ActivityNotFoundException(\n                        &quot;Unable to find explicit activity class &quot;\n                        + ((Intent)intent).getComponent().toShortString()\n                        + &quot;; have you declared this activity in your AndroidManifest.xml?&quot;);\n            throw new ActivityNotFoundException(\n                    &quot;No Activity found to handle &quot; + intent);\n        case ActivityManager.START_PERMISSION_DENIED:\n            throw new SecurityException(&quot;Not allowed to start activity &quot;\n                    + intent);\n        case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:\n            throw new AndroidRuntimeException(\n                    &quot;FORWARD_RESULT_FLAG used while also requesting a result&quot;);\n        case ActivityManager.START_NOT_ACTIVITY:\n            throw new IllegalArgumentException(\n                    &quot;PendingIntent is not an activity&quot;);\n        case ActivityManager.START_NOT_VOICE_COMPATIBLE:\n            throw new SecurityException(\n                    &quot;Starting under voice control not allowed for: &quot; + intent);\n        case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:\n            throw new IllegalStateException(\n                    &quot;Session calling startVoiceActivity does not match active session&quot;);\n        case ActivityManager.START_VOICE_HIDDEN_SESSION:\n            throw new IllegalStateException(\n                    &quot;Cannot start voice activity on a hidden session&quot;);\n        case ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION:\n            throw new IllegalStateException(\n                    &quot;Session calling startAssistantActivity does not match active session&quot;);\n        case ActivityManager.START_ASSISTANT_HIDDEN_SESSION:\n            throw new IllegalStateException(\n                    &quot;Cannot start assistant activity on a hidden session&quot;);\n        case ActivityManager.START_CANCELED:\n            throw new AndroidRuntimeException(&quot;Activity could not be started for &quot;\n                    + intent);\n        default:\n            throw new AndroidRuntimeException(&quot;Unknown error code &quot;\n                    + res + &quot; when starting &quot; + intent);\n    }\n}<\/code><\/pre>\n<p><code>have you declared this activity in your AndroidManifest.xml<\/code>\u8fd9\u53e5\u5f02\u5e38\u5e94\u8be5\u5f88\u719f\u6089\u4e86\u5427\uff1f\u542f\u52a8\u4e00\u4e2a\u6ca1\u6709\u6ce8\u518c\u7684Activity\u7684\u62a5\u9519<\/p>\n<p>\u518d\u770b\u4e2a<code>if (mParent == null)<\/code>\u7684<code>false<\/code>\u5206\u652f:<\/p>\n<pre><code class=\"language-java\">public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,\n        int requestCode, @Nullable Bundle options) {\n    options = transferSpringboardActivityOptions(options);\n    Instrumentation.ActivityResult ar =\n        mInstrumentation.execStartActivity(\n            this, mMainThread.getApplicationThread(), mToken, child,\n            intent, requestCode, options);\n    if (ar != null) {\n        mMainThread.sendActivityResult(\n            mToken, child.mEmbeddedID, requestCode,\n            ar.getResultCode(), ar.getResultData());\n    }\n    cancelInputsAndStartExitTransition(options);\n}<\/code><\/pre>\n<p>\u63a7\u5236\u6743\u4f9d\u7136\u662f\u4ea4\u7ed9\u4e86<code>mInstrumentation.execStartActivity()<\/code>\uff0c\u5269\u4f59\u7684\u4ee3\u7801\u7d22\u5f15\u548c\u4e0a\u9762\u7684\u4e00\u6837<\/p>\n<p>\u6240\u4ee5\uff0c\u4ee3\u7801\u7d22\u5f15\u7684\u7ed3\u8bba\uff0c\u6309\u7167\u4e00\u5f20\u56fe\u6765\u8868\u793a\u5c31\u662f\uff1a<\/p>\n<p><iframe id=\"embed_dom\" name=\"embed_dom\" frameborder=\"0\" style=\"display:block;width:720px; height:640px;\" src=\"https:\/\/www.processon.com\/embed\/5e8c6b40e4b034045683d7b6\"><\/iframe><\/p>\n<h3>\u65b9\u5f0f2\uff1a\u4f7f\u7528applictonContext\u7684startActivity<\/h3>\n<pre><code class=\"language-java\">private void startActivityByApplicationContext() {\n    Intent i = new Intent(MainActivity.this, Main2Activity.class);\n    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n    getApplicationContext().startActivity(i);\n}<\/code><\/pre>\n<p>\u5728\u65b9\u5f0f1\u4e2d\u5df2\u7ecf\u5c55\u793a\u4e86\u6e90\u7801\u7d22\u5f15\u7684\u65b9\u5f0f\uff0c\u6240\u4ee5\u8fd9\u91cc\u4e0d\u518d\u8d58\u8ff0\u8d34\u56fe\u3002\u76f4\u63a5\u7ed9\u51fa\u4ee3\u7801\u7d22\u5f15\u7ed3\u8bba\u56fe\uff1a<\/p>\n<p><iframe id=\"embed_dom\" name=\"embed_dom\" frameborder=\"0\" style=\"display:block;width:720px; height:640px;\" src=\"https:\/\/www.processon.com\/embed\/5e8c6b40e4b034045683d7b6\"><\/iframe><\/p>\n<h3>\u7ed3\u8bba<\/h3>\n<p>\u4e24\u5f20\u56fe\u5bf9\u6bd4\uff0c\u6211\u4eec\u5f88\u5bb9\u6613\u5f97\u51fa\u4e00\u4e2a\u7ed3\u8bba\uff1a<\/p>\n<ul>\n<li>\u542f\u52a8Activity\u7684\u6700\u7ec8\u6267\u884c\u6743\uff0c\u90fd\u88ab\u4ea4\u7ed9\u4e86<code>Instrumentation.java<\/code>\u7c7b<\/li>\n<li>\u65b9\u5f0f1\uff1a<code>Activity.startActivity<\/code>\u7684\u6700\u7ec8\u6267\u884c\u8005\u662f\u5b83\u7684<code>mInstrumentation<\/code>\u6210\u5458\uff0c<code>mInstrumentation<\/code>\u7684\u6301\u6709\u8005\u662fActivity\u81ea\u8eab<\/li>\n<li>\u65b9\u5f0f2\uff1a<code>getApplicationContext().startActivity(i)<\/code>\u7684\u6700\u7ec8\u6267\u884c\u8005\u662f\uff1a<code>ActivityThread<\/code>\u7684<code>mInstrumentation<\/code>\u6210\u5458\uff0c\u6301\u6709\u8005\u662f<code>ActivityThread<\/code>\u4e3b\u7ebf\u7a0b<\/li>\n<\/ul>\n<p>\u4e24\u79cd\u65b9\u5f0f\u90fd\u53ef\u4ee5\u628a<code>mInstrumentation<\/code>\u5f53\u4f5chook\u5207\u5165\u70b9\uff0c\u5c06\u5b83\u4ece\u5b83\u7684\u6301\u6709\u8005\u4e2d&quot;\u5077\u6881\u6362\u67f1&quot;<\/p>\n<h2>\u7b2c\u4e00\u79cd\u542f\u52a8\u65b9\u5f0f\u7684hook\u65b9\u6848<\/h2>\n<p>\u521b\u5efa\u4e00\u4e2a<code>HookActivityHelper.java<\/code>\uff0c\u7136\u540e\u4e09\u6b65\u8d70\uff1a<\/p>\n<p>\uff081\uff09\u627e\u5230<code>hook<\/code>\u70b9\uff0c\u4ee5\u53ca<code>hook<\/code>\u5bf9\u8c61\u7684\u6301\u6709\u8005\uff0c\u4e0a\u6587\u4e2d\u5df2\u7ecf\u8bf4\u660e\uff1a<code>hook<\/code>\u70b9\u662fActivity\u7684<code>mInstrumentation<\/code>\u6210\u5458\uff0c\u6301\u6709\u8005\u5c31\u662fActivity<\/p>\n<pre><code class=\"language-java\">Field mInstrumentationField = Activity.class.getDeclaredField(&quot;mInstrumentation&quot;);\nmInstrumentationField.setAccessible(true);\nInstrumentation base = (Instrumentation) mInstrumentationField.get(activity);<\/code><\/pre>\n<p><code>base<\/code>\u662f\u7cfb\u7edf\u539f\u6765\u7684\u6267\u884c\u903b\u8f91\uff0c\u5b58\u8d77\u6765\u540e\u9762\u7528\u5f97\u7740.<\/p>\n<p>\uff082\uff09\u521b\u5efa<code>Instrumentation<\/code>\u4ee3\u7406\u7c7b\uff0c\u7ee7\u627f<code>Instrumentation<\/code>\uff0c\u7136\u540e\u91cd\u5199<code>execStartActivity<\/code>\u65b9\u6cd5\uff0c\u52a0\u5165\u81ea\u5df1\u7684\u903b\u8f91\uff0c\u7136\u540e\u518d\u6267\u884c\u7cfb\u7edf\u7684\u903b\u8f91<\/p>\n<pre><code class=\"language-java\">private static class ProxyInstrumentation extends Instrumentation {\n    public ProxyInstrumentation(Instrumentation base) {\n        this.base = base;\n    }\n\n    Instrumentation base;\n\n    public ActivityResult execStartActivity(\n            Context who, IBinder contextThread, IBinder token, Activity target,\n            Intent intent, int requestCode, Bundle options) {\n\n        Log.d(&quot;ProxyInstrumentation&quot;, &quot;\u6211\u4eec\u81ea\u5df1\u7684\u903b\u8f91&quot;);\n\n        \/\/\u8fd9\u91cc\u8fd8\u8981\u6267\u884c\u7cfb\u7edf\u7684\u539f\u672c\u903b\u8f91\uff0c\u4f46\u662f\u7a81\u7136\u53d1\u73b0\uff0c\u8fd9\u4e2aexecStartActivity\u5c45\u7136\u662fhide\u7684\uff0c\u53ea\u80fd\u53cd\u5c04\u54af\n        try {\n            Class&lt;?&gt; InstrumentationClz = Class.forName(&quot;android.app.Instrumentation&quot;);\n            Method execStartActivity = InstrumentationClz.getDeclaredMethod(&quot;execStartActivity&quot;,\n                    Context.class, IBinder.class, IBinder.class, Activity.class,\n                    Intent.class, int.class, Bundle.class);\n            return (ActivityResult) execStartActivity.invoke(base, \n                        who, contextThread, token, target, intent, requestCode, options);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return null;\n    }\n}<\/code><\/pre>\n<p>\uff083\uff09\u7528\u4ee3\u7406\u7c7b\u5bf9\u8c61\u66ff\u6362<code>hook<\/code>\u5bf9\u8c61<\/p>\n<pre><code class=\"language-java\">ProxyInstrumentation proxyInstrumentation = new ProxyInstrumentation(base);\nmInstrumentationField.set(activity, proxyInstrumentation);<\/code><\/pre>\n<p>\uff084\uff09\u5b8c\u6574\u8f85\u52a9\u7c7b<\/p>\n<pre><code class=\"language-java\">import android.app.Activity;\nimport android.app.Instrumentation;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.util.Log;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\npublic class ActivityHookHelper {\n\n    public static void hook(Activity activity) {\n\n        \/\/\u76ee\u6807:Activity\u7684mInstrumentation\u6210\u5458\n        try {\n            \/\/1.\u62ff\u5230\u8981hook\u7684\u5bf9\u8c61\uff1aActivity\u7684mInstrumentation\u6210\u5458\n            Field mInstrumentationField = Activity.class.getDeclaredField(&quot;mInstrumentation&quot;);\n            mInstrumentationField.setAccessible(true);\n            Instrumentation base = (Instrumentation) mInstrumentationField.get(activity);\n\n            \/\/2.\u6784\u5efa\u81ea\u5df1\u7684\u4ee3\u7406\u5bf9\u8c61\uff0c\u8fd9\u91ccInstrumentation\u662f\u4e00\u4e2aclass\uff0c\u800c\u4e0d\u662f\u63a5\u53e3\uff0c\u6240\u4ee5\u53ea\u80fd\u7528\u521b\u5efa\u5185\u90e8\u7c7b\u7684\u65b9\u5f0f\u6765\u505a\n            ProxyInstrumentation proxyInstrumentation = new ProxyInstrumentation(base);\n\n            \/\/3.\u66ff\u6362\n            mInstrumentationField.set(activity, proxyInstrumentation);\n\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static class ProxyInstrumentation extends Instrumentation {\n        public ProxyInstrumentation(Instrumentation base) {\n            this.base = base;\n        }\n\n        Instrumentation base;\n\n        public ActivityResult execStartActivity(\n                Context who, IBinder contextThread, IBinder token, Activity target,\n                Intent intent, int requestCode, Bundle options) {\n\n            Log.d(&quot;ActivityHook&quot;, &quot;\u6211\u4eec\u81ea\u5df1\u7684\u903b\u8f91&quot;);\n\n            \/\/\u8fd9\u91cc\u8fd8\u8981\u6267\u884c\u7cfb\u7edf\u7684\u539f\u672c\u903b\u8f91\uff0c\u4f46\u662f\u7a81\u7136\u53d1\u73b0\uff0c\u8fd9\u4e2aexecStartActivity\u5c45\u7136\u662fhide\u7684\uff0c\u53ea\u80fd\u53cd\u5c04\u54af\n            try {\n                Class&lt;?&gt; InstrumentationClz = Class.forName(&quot;android.app.Instrumentation&quot;);\n                Method execStartActivity = InstrumentationClz.getDeclaredMethod(&quot;execStartActivity&quot;,\n                        Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);\n                return (ActivityResult) execStartActivity.invoke(base, who, contextThread, token, target, intent, requestCode, options);\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n\n            return null;\n        }\n\n    }\n}<\/code><\/pre>\n<p>\uff085\uff09\u5982\u4f55\u4f7f\u7528: \u5728MainActivity\u7684<code>onCreate<\/code>\u4e2d\u52a0\u5165\u4e00\u884c<code>ActivityHookHelper.hook(this)<\/code><\/p>\n<pre><code class=\"language-java\">public class MainActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        ActivityHookHelper.hook(this);\n        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                startActivityByActivity();\n            }\n        });\n\n    }\n\n    private void startActivityByActivity() {\n        Intent i = new Intent(MainActivity.this, Main2Activity.class);\n        startActivity(i);\n    }\n\n}<\/code><\/pre>\n<p>\u6548\u679c\uff1a\u8df3\u8f6c\u4f9d\u7136\u6b63\u5e38\uff0c\u5e76\u4e14logcat\u4e2d\u53ef\u4ee5\u53d1\u73b0\u4e0b\u9762\u7684\u65e5\u5fd7.<\/p>\n<pre><code>cn.appblog.activityhookdemo D\/ActivityHook: \u6211\u4eec\u81ea\u5df1\u7684\u903b\u8f91<\/code><\/pre>\n<p>OK\uff0c\u63d2\u5165\u81ea\u5df1\u7684\u903b\u8f91\uff0c\u6210\u529f<\/p>\n<h2>\u7b2c\u4e8c\u79cd\u542f\u52a8\u65b9\u5f0f\u7684hook\u65b9\u6848<\/h2>\n<p>\u521b\u5efa<code>ApplicationContextHookHelper.java<\/code>\uff0c\u7136\u540e\u540c\u6837\u662f\u4e09\u6b65\u8d70\uff1a<\/p>\n<p>\uff081\uff09\u786e\u5b9a<code>hook<\/code>\u7684\u5bf9\u8c61\u548c\u8be5\u5bf9\u8c61\u7684\u6301\u6709\u8005<\/p>\n<p>\u9501\u5b9a<code>ActivityThread<\/code>\u7684<code>mInstrumentation<\/code>\u6210\u5458<\/p>\n<pre><code class=\"language-java\">\/\/1.\u4e3b\u7ebf\u7a0bActivityThread\u5185\u90e8\u7684mInstrumentation\u5bf9\u8c61\uff0c\u5148\u628a\u4ed6\u62ff\u51fa\u6765\nClass&lt;?&gt; ActivityThreadClz = Class.forName(&quot;android.app.ActivityThread&quot;);\n\/\/\u518d\u62ff\u5230sCurrentActivityThread\nField sCurrentActivityThreadField = ActivityThreadClz.getDeclaredField(&quot;sCurrentActivityThread&quot;);\nsCurrentActivityThreadField.setAccessible(true);\nObject activityThreadObj = sCurrentActivityThreadField.get(null); \/\/\u9759\u6001\u53d8\u91cf\u7684\u5c5e\u6027get\u4e0d\u9700\u8981\u53c2\u6570\uff0c\u4f20null\u5373\u53ef\n\/\/\u518d\u53bb\u62ff\u5b83\u7684mInstrumentation\nField mInstrumentationField = ActivityThreadClz.getDeclaredField(&quot;mInstrumentation&quot;);\nmInstrumentationField.setAccessible(true);\nInstrumentation base = (Instrumentation) mInstrumentationField.get(activityThreadObj); \/\/OK,\u62ff\u5230<\/code><\/pre>\n<p>\uff082\uff09\u521b\u5efa\u4ee3\u7406\u5bf9\u8c61\uff0c\u548c\u4e0a\u9762\u7684\u4ee3\u7406\u7c7b\u4e00\u6a21\u4e00\u6837\uff0c\u5c31\u4e0d\u91cd\u590d\u8d34\u4ee3\u7801\u4e86<\/p>\n<pre><code class=\"language-java\">\/\/2.\u6784\u5efa\u81ea\u5df1\u7684\u4ee3\u7406\u5bf9\u8c61\uff0c\u8fd9\u91ccInstrumentation\u662f\u4e00\u4e2aclass\uff0c\u800c\u4e0d\u662f\u63a5\u53e3\uff0c\u6240\u4ee5\u53ea\u80fd\u7528\u521b\u5efa\u5185\u90e8\u7c7b\u7684\u65b9\u5f0f\u6765\u505a\nProxyInstrumentation proxyInstrumentation = new ProxyInstrumentation(base);<\/code><\/pre>\n<p>\uff083\uff09\u66ff\u6362\u6389\u539f\u5bf9\u8c61<\/p>\n<pre><code class=\"language-java\">\/\/3.\u5077\u6881\u6362\u67f1\nmInstrumentationField.set(activityThreadObj, proxyInstrumentation);<\/code><\/pre>\n<p>\uff084\uff09\u5b8c\u6574\u8f85\u52a9\u7c7b<\/p>\n<pre><code class=\"language-java\">import android.app.Activity;\nimport android.app.Instrumentation;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.util.Log;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\npublic class ApplicationContextHookHelper {\n    public static void hook() {\n        \/\/ \u5982\u4f55\u53d6\u5f97ActivityThread\u597d\u50cf\u662f\u5355\u4f8b\u7684\u5427\u3002\u90a3\u5c31\u7b80\u5355\u4e86\uff0c\n        try {\n            \/\/1.\u4e3b\u7ebf\u7a0bActivityThread\u5185\u90e8\u7684mInstrumentation\u5bf9\u8c61\uff0c\u5148\u628a\u4ed6\u62ff\u51fa\u6765\n            Class&lt;?&gt; ActivityThreadClz = Class.forName(&quot;android.app.ActivityThread&quot;);\n            \/\/\u518d\u62ff\u5230sCurrentActivityThread\n            Field sCurrentActivityThreadField = ActivityThreadClz.getDeclaredField(&quot;sCurrentActivityThread&quot;);\n            sCurrentActivityThreadField.setAccessible(true);\n            Object activityThreadObj = sCurrentActivityThreadField.get(null);\/\/\u9759\u6001\u53d8\u91cf\u7684\u5c5e\u6027get\u4e0d\u9700\u8981\u53c2\u6570\uff0c\u4f20null\u5373\u53ef.\n            \/\/\u518d\u53bb\u62ff\u5b83\u7684mInstrumentation\n            Field mInstrumentationField = ActivityThreadClz.getDeclaredField(&quot;mInstrumentation&quot;);\n            mInstrumentationField.setAccessible(true);\n            Instrumentation base = (Instrumentation) mInstrumentationField.get(activityThreadObj);\/\/ OK,\u62ff\u5230\n\n            \/\/2.\u6784\u5efa\u81ea\u5df1\u7684\u4ee3\u7406\u5bf9\u8c61\uff0c\u8fd9\u91ccInstrumentation\u662f\u4e00\u4e2aclass\uff0c\u800c\u4e0d\u662f\u63a5\u53e3\uff0c\u6240\u4ee5\u53ea\u80fd\u7528\u521b\u5efa\u5185\u90e8\u7c7b\u7684\u65b9\u5f0f\u6765\u505a\n            ProxyInstrumentation proxyInstrumentation = new ProxyInstrumentation(base);\n\n            \/\/3.\u5077\u6881\u6362\u67f1\n            mInstrumentationField.set(activityThreadObj, proxyInstrumentation);\n\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n    }\n\n    private static class ProxyInstrumentation extends Instrumentation {\n        public ProxyInstrumentation(Instrumentation base) {\n            this.base = base;\n        }\n\n        Instrumentation base;\n\n        public ActivityResult execStartActivity(\n                Context who, IBinder contextThread, IBinder token, Activity target,\n                Intent intent, int requestCode, Bundle options) {\n\n            Log.d(&quot;ApplicationContextHook&quot;, &quot;\u6211\u4eec\u81ea\u5df1\u7684\u903b\u8f91&quot;);\n\n            \/\/\u8fd9\u91cc\u8fd8\u8981\u6267\u884c\u7cfb\u7edf\u7684\u539f\u672c\u903b\u8f91\uff0c\u4f46\u662f\u7a81\u7136\u53d1\u73b0\uff0c\u8fd9\u4e2aexecStartActivity\u5c45\u7136\u662fhide\u7684\uff0c\u53ea\u80fd\u53cd\u5c04\u54af\n            try {\n                Class&lt;?&gt; InstrumentationClz = Class.forName(&quot;android.app.Instrumentation&quot;);\n                Method execStartActivity = InstrumentationClz.getDeclaredMethod(&quot;execStartActivity&quot;,\n                        Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);\n                return (ActivityResult) execStartActivity.invoke(base, who, contextThread, token, target, intent, requestCode, options);\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n\n            return null;\n        }\n\n    }\n}<\/code><\/pre>\n<p>\uff085\uff09\u5982\u4f55\u4f7f\u7528: \u5728MainActivity\u7684<code>onCreate<\/code>\u4e2d\u52a0\u5165\u4e00\u884c<code>ApplicationContextHookHelper.hook()<\/code><\/p>\n<pre><code class=\"language-java\">public class MainActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main4);\n\n        ApplicationContextHookHelper.hook();\n        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                startActivityByApplicationContext();\n            }\n        });\n    }\n\n    private void startActivityByApplicationContext() {\n        Intent i = new Intent(MainActivity.this, Main2Activity.class);\n        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        getApplicationContext().startActivity(i);\n    }\n}<\/code><\/pre>\n<p>\u6548\u679c<\/p>\n<pre><code>cn.appblog.activityhookdemo D\/ApplicationContextHook: \u6211\u4eec\u81ea\u5df1\u7684\u903b\u8f91\ncn.appblog.activityhookdemo D\/ApplicationContextHook: \u6211\u4eec\u81ea\u5df1\u7684\u903b\u8f91<\/code><\/pre>\n<p>OK\uff0c\u7b2c\u4e8c\u79cd\u542f\u52a8\u65b9\u5f0f\uff0c\u6211\u4eec\u4e5f\u53ef\u4ee5\u52a0\u5165\u81ea\u5df1\u7684\u903b\u8f91\u4e86\uff0c<code>hook<\/code>\u6210\u529f\uff01<\/p>\n<h2>\u76ee\u524d\u65b9\u6848\u5f0a\u7aef\u5206\u6790<\/h2>\n<ul>\n<li>\u542f\u52a8\u65b9\u5f0f1\u7684<code>hook<\/code>\uff1a\u53ea\u662f\u5728\u9488\u5bf9\u5355\u4e2aActivity\u7c7b\uff0c\u6765\u8fdb\u884c<code>hook<\/code>\uff0c\u591a\u4e2aActivity\u5219\u9700\u8981\u5199\u591a\u6b21\uff0c\u6216\u8005\u5199\u5728<code>BaseActivity<\/code>\u91cc\u9762<\/li>\n<li>\u542f\u52a8\u65b9\u5f0f2\u7684<code>hook<\/code>\uff1a\u53ef\u4ee5\u9488\u5bf9\u5168\u5c40\u8fdb\u884c<code>hook<\/code>\uff0c\u65e0\u8bba\u591a\u5c11\u4e2aActivity\uff0c\u53ea\u9700\u8981\u8c03\u7528\u4e00\u6b21<code>ApplicationContextHookHelper.hook()<\/code>\u51fd\u6570\u5373\u53ef,\u4f46\u662f\uff0c\u5b83\u53ea\u80fd\u9488\u5bf9<code>getApplicationContext().startActivity(i)<\/code>\u666e\u901a\u7684<code>Activity.startActivity(i)<\/code>\u5219\u4e0d\u80fd\u8d77\u4f5c\u7528<\/li>\n<\/ul>\n<p>\u90a3\u4e48\u6709\u6ca1\u6709\u4e00\u79cd\u5b8c\u6574\u7684\u89e3\u51b3\u65b9\u6848\uff1a\u80fd\u591f\u5728\u5168\u5c40\u8d77\u4f5c\u7528\uff0c\u5e76\u4e14\u53ef\u4ee5\u5728\u4e24\u79cd\u542f\u52a8\u65b9\u5f0f\u4e0b\u90fd\u80fd<code>hook<\/code>\u3002\u56de\u987e\u4e4b\u524d\u7684\u4e24\u5f20\u4ee3\u7801\u7d22\u5f15\u7ed3\u8bba\u56fe\uff0c\u4f1a\u53d1\u73b0\uff0c\u4e24\u79cd\u542f\u52a8Activity\u7684\u65b9\u5f0f\uff0c\u6700\u7ec8\u90fd\u88ab\u6267\u884c\u5230\u4e86<code>AMS<\/code>\u5185\u90e8\uff0c\u4e0b\u4e00\u6b65\uff0c\u5c1d\u8bd5<code>hook AMS<\/code>\u3002<\/p>\n<h2>\u6700\u7ec8\u89e3\u51b3\u65b9\u6848<\/h2>\n<h3>\u6e90\u7801\u7d22\u5f15<\/h3>\n<p>\u4ee3\u7801\u7d22\u5f15\uff1a\u57fa\u4e8eSDK 28 ~ SDK 29<\/p>\n<p>\u53d6\u5f97AMS\uff08<code>ActivityManagerService<\/code>\u5b9e\u4f8b\uff09\u7684\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"language-java\">int result = ActivityManager.getService()\n    .startActivity(whoThread, who.getBasePackageName(), intent,\n            intent.resolveTypeIfNeeded(who.getContentResolver()),\n            token, target != null ? target.mEmbeddedID : null,\n            requestCode, 0, null, options);\ncheckStartActivityResult(result, intent);<\/code><\/pre>\n<p>\u5982\u679c\u53ef\u4ee5\u5728\u7cfb\u7edf\u63a5\u6536\u5230AMS\u5b9e\u4f8b\u4e4b\u524d\uff0c\u628a\u4ed6\u622a\u4e86\uff0c\u662f\u4e0d\u662f\u5c31\u53ef\u4ee5\u8fbe\u5230\u6211\u4eec\u7684\u76ee\u7684\uff1f\u8fdb\u53bb\u770b\u770b<code>getService<\/code>\u7684\u4ee3\u7801\uff1a<\/p>\n<p>SDK 28 ~ Android 9.0: ActivityManager.java<\/p>\n<pre><code class=\"language-java\">private static final Singleton&lt;IActivityManager&gt; IActivityManagerSingleton =\n        new Singleton&lt;IActivityManager&gt;() {\n            @Override\n            protected IActivityManager create() {\n                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);\n                final IActivityManager am = IActivityManager.Stub.asInterface(b);\n                return am;\n            }\n        };\n\n\/** @hide *\/\npublic static IActivityManager getService() {\n    return IActivityManagerSingleton.get();\n}<\/code><\/pre>\n<p>SDK 29 ~ Android 10.0: ActivityTaskManager.java<\/p>\n<pre><code class=\"language-java\">private static final Singleton&lt;IActivityTaskManager&gt; IActivityTaskManagerSingleton =\n        new Singleton&lt;IActivityTaskManager&gt;() {\n            @Override\n            protected IActivityTaskManager create() {\n                final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);\n                return IActivityTaskManager.Stub.asInterface(b);\n            }\n        };\n\n\/** @hide *\/\npublic static IActivityTaskManager getService() {\n    return IActivityTaskManagerSingleton.get();\n}<\/code><\/pre>\n<p>\u771f\u6b63\u7684<code>AMS<\/code>\u5b9e\u4f8b\u6765\u81ea\u4e00\u4e2a<code>Singleton<\/code>\u5355\u4f8b\u8f85\u52a9\u7c7b\u7684<code>create()<\/code>\u65b9\u6cd5\uff0c\u5e76\u4e14\u8fd9\u4e2a<code>Singleton<\/code>\u5355\u4f8b\u7c7b\uff0c\u63d0\u4f9b<code>get<\/code>\u65b9\u6cd5\uff0c\u83b7\u5f97\u771f\u6b63\u7684\u5b9e\u4f8b<\/p>\n<pre><code class=\"language-java\">\/**\n * Singleton helper class for lazily initialization.\n *\n * Modeled after frameworks\/base\/include\/utils\/Singleton.h\n *\n * @hide\n *\/\npublic abstract class Singleton&lt;T&gt; {\n    private T mInstance;\n\n    protected abstract T create();\n\n    public final T get() {\n        synchronized (this) {\n            if (mInstance == null) {\n                mInstance = create();\n            }\n            return mInstance;\n        }\n    }\n}<\/code><\/pre>\n<p>\u53c2\u8003\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.androidos.net.cn\/android\/9.0.0_r8\/xref\/frameworks\/base\/core\/java\/android\/util\/Singleton.java\">https:\/\/www.androidos.net.cn\/android\/9.0.0_r8\/xref\/frameworks\/base\/core\/java\/android\/util\/Singleton.java<\/a><\/p>\n<p>\u90a3\u4e48\uff0c\u6211\u4eec\u4ece\u8fd9\u4e2a\u5355\u4f8b\u4e2d\uff0c\u5c31\u53ef\u4ee5\u83b7\u5f97\u7cfb\u7edf\u5f53\u524d\u7684<code>AMS<\/code>\u5b9e\u4f8b\uff0c\u5c06\u5b83\u53d6\u51fa\u6765\uff0c\u7136\u540e\u4fdd\u5b58\u3002OK\uff0c\u786e\u8ba4\uff1a<\/p>\n<ul>\n<li><code>hook<\/code>\u5bf9\u8c61\uff1a<code>ActivityManager<\/code>\u7684<code>IActivityManagerSingleton<\/code>\u6210\u5458\u53d8\u91cf\u5185\u7684\u5355\u4f8b<code>mInstance<\/code><\/li>\n<li><code>hook<\/code>\u5bf9\u8c61\u7684\u6301\u6709\u8005\uff1a<code>ActivityManager<\/code>\u7684<code>IActivityManagerSingleton<\/code>\u6210\u5458\u53d8\u91cf<\/li>\n<\/ul>\n<h3>hook\u5b9e\u73b0<\/h3>\n<p>\uff081\uff09\u627e\u5230hook\u5bf9\u8c61\uff0c\u5e76\u4e14\u5b58\u8d77\u6765<\/p>\n<pre><code class=\"language-java\">\/\/1.\u628ahook\u7684\u5bf9\u8c61\u53d6\u51fa\u6765\u4fdd\u5b58\n\/\/\u77ee\u6cb9\uff0c\u9759\u6001\u7684\u8036\uff0c\u5f00\u5fc3\nClass&lt;?&gt; ActivityManagerClz = Class.forName(&quot;android.app.ActivityManager&quot;);\nMethod getServiceMethod = ActivityManagerClz.getDeclaredMethod(&quot;getService&quot;);\nfinal Object IActivityManagerObj = getServiceMethod.invoke(null); \/\/OK\uff0c\u5df2\u7ecf\u53d6\u5f97\u8fd9\u4e2a\u7cfb\u7edf\u81ea\u5df1\u7684AMS\u5b9e\u4f8b<\/code><\/pre>\n<p>\uff082\uff09\u521b\u5efa\u81ea\u5df1\u7684\u4ee3\u7406\u7c7b\u5bf9\u8c61\uff0cIActivityManager\u662f\u4e00\u4e2aAIDL\u751f\u6210\u7684\u52a8\u6001\u63a5\u53e3\u7c7b\uff0c\u6240\u4ee5\u5728\u7f16\u8bd1\u65f6\uff0candroidStudio\u4f1a\u627e\u4e0d\u5230\u8fd9\u4e2a\u7c7b\uff0c\u6240\u4ee5\uff0c\u5148\u53cd\u5c04\uff0c\u7136\u540e\u7528Proxy\u8fdb\u884c\u521b\u5efa\u4ee3\u7406\u3002<\/p>\n<pre><code class=\"language-java\">\/\/2.\u73b0\u5728\u521b\u5efa\u6211\u4eec\u7684AMS\u5b9e\u4f8b\n\/\/\u7531\u4e8eIActivityManager\u662f\u4e00\u4e2a\u63a5\u53e3\uff0c\u90a3\u4e48\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528Proxy\u7c7b\u6765\u8fdb\u884c\u4ee3\u7406\u5bf9\u8c61\u7684\u521b\u5efa\n\/\/\u7ed3\u679c\u88ab\u6446\u4e86\u4e00\u9053\uff0cIActivityManager\u8fd9\u73a9\u610f\u5c45\u7136\u8fd8\u662f\u4e2aAIDL\uff0c\u52a8\u6001\u751f\u6210\u7684\u7c7b\uff0c\u7f16\u8bd1\u5668\u8fd8\u4e0d\u8ba4\u8bc6\u8fd9\u4e2a\u7c7b\uff0c\u600e\u4e48\u529e\uff1f\u53cd\u5c04\u54af\nClass&lt;?&gt; IActivityManagerClz = Class.forName(&quot;android.app.IActivityManager&quot;);\nObject proxyIActivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), \n    new Class[]{IActivityManagerClz}, new InvocationHandler() {\n        @Override\n        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n            \/\/proxy\u662f\u521b\u5efa\u51fa\u6765\u7684\u4ee3\u7406\u7c7b\uff0cmethod\u662f\u63a5\u53e3\u4e2d\u7684\u65b9\u6cd5\uff0cargs\u662f\u63a5\u53e3\u6267\u884c\u65f6\u7684\u5b9e\u53c2\n            if (method.getName().equals(&quot;startActivity&quot;)) {\n                Log.d(&quot;GlobalActivityHook&quot;, &quot;\u5168\u5c40hook \u5230\u4e86 startActivity&quot;);\n            }\n            return method.invoke(IActivityManagerObj, args);\n        }\n    }\n);<\/code><\/pre>\n<p>\uff083\uff09\u5077\u6881\u6362\u67f1\uff1a\u8fd9\u6b21\u6709\u70b9\u590d\u6742\uff0c\u4e0d\u518d\u662f\u7b80\u5355\u7684<code>field.set<\/code>\uff0c\u56e0\u4e3a\u8fd9\u6b21\u7684<code>hook<\/code>\u5bf9\u8c61\u88ab\u5305\u88f9\u5728\u4e86\u4e00\u4e2a<code>Singleton<\/code>\u91cc<\/p>\n<pre><code class=\"language-java\">\/\/3.\u5077\u6881\u6362\u67f1,\u8fd9\u91cc\u6709\u70b9\u7ea0\u7ed3\uff0c\u8fd9\u4e2a\u5b9e\u4f8b\u5c45\u7136\u88ab\u85cf\u5728\u4e86\u4e00\u4e2a\u5355\u4f8b\u8f85\u52a9\u7c7b\u91cc\u9762\nField IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField(&quot;IActivityManagerSingleton&quot;);\nIActivityManagerSingletonField.setAccessible(true);\nObject IActivityManagerSingletonObj = IActivityManagerSingletonField.get(null);\n\/\/\u53cd\u5c04\u521b\u5efa\u4e00\u4e2aSingleton\u7684class\nClass&lt;?&gt; SingletonClz = Class.forName(&quot;android.util.Singleton&quot;);\nField mInstanceField = SingletonClz.getDeclaredField(&quot;mInstance&quot;);\nmInstanceField.setAccessible(true);\nmInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);<\/code><\/pre>\n<p>\u4f7f\u7528\u65b9\u6cd5\uff1a\u8001\u6837\u5b50\uff0c\u5728<code>Activity onCreate<\/code>\u91cc\u9762\u52a0\u5165<code>GlobalActivityHookHelper.hook()<\/code><br \/>\n\u8fd0\u884c\u8d77\u6765\uff0c\u9884\u671f\u7ed3\u679c\u5e94\u8be5\u662f\uff1a\u80fd\u591f\u5728logcat\u4e2d\u770b\u5230\u65e5\u5fd7\uff1a<\/p>\n<pre><code>GlobalActivityHook - \u5168\u5c40hook \u5230\u4e86 startActivity<\/code><\/pre>\n<p>\u5982\u679c\u6ca1\u6709\u770b\u5230\u8fd9\u4e2a\u65e5\u5fd7\uff0c\u90a3\u4e48\u539f\u56e0\u5c31\u662f\uff1a\u6211\u4eec\u5199\u7684<code>hook<\/code>\u4ee3\u7801\u5e76\u6ca1\u6709\u517c\u5bb9\u6027\uff0c\u9700\u8981\u9002\u914dAndroid\u7248\u672c<\/p>\n<h3>\u9002\u914d\u89e3\u51b3<\/h3>\n<p>\u4ee5 SDK 23 ~ Android 6.0 \u4e3a\u4f8b<\/p>\n<p>\uff081\uff09\u627e\u5230SDK 23\u7684\u6e90\u7801<\/p>\n<p>\u6ce8\u610f\uff0c\u524d\u65b9\u6709\u5751\uff0c\u5982\u679c\u76f4\u63a5\u628aAndroid Studio combileSDK\u6539\u621023\uff0c\u4f1a\u51fa\u73b0\u5f88\u591a\u672a\u77e5\u95ee\u9898\uff0c\u6240\u4ee5\u4e0d\u5efa\u8bae\u8fd9\u4e48\u505a\u3002\u4f46\u662f\u6211\u4eec\u4e00\u5b9a\u8981\u770bSDK 23\u7684\u6e90\u7801\uff0c\u600e\u4e48\u529e\uff1f<\/p>\n<p>\u5728\u7ebf\u67e5\u770b\u6e90\u7801 &#8211; <a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.androidos.net.cn\/sourcecode\">https:\/\/www.androidos.net.cn\/sourcecode<\/a><br \/>\n\u4ece\u8c37\u6b4c\u5b98\u7f51\u4e0b\u8f7dSDK 23\u7684\u6e90\u7801\uff0c\u7136\u540e\u7528SourceInsight\u67e5\u770b\uff09<\/p>\n<p>\uff082\uff09\u67e5\u770b<code>getService<\/code>\u65b9\u6cd5\u4e0d\u5b58\u5728\u7684\u539f\u56e0\uff0c\u4e24\u4e2a\u7248\u672c28 \u548c 23\uff0c\u5728\u8fd9\u4e00\u5757\u4ee3\u7801\u4e0a\u6709\u4ec0\u4e48\u4e0d\u540c<\/p>\n<p>\uff083\uff09\u6539\u9020<code>GlobalActivityHookHelper.java<\/code>\uff0c\u5224\u5b9a\u5f53\u524d\u8bbe\u5907\u7684\u7cfb\u7edf\u7248\u672c\u53f7\uff0c\u8ba9\u5b83\u53ef\u4ee5\u517c\u5bb9\u6240\u6709\u7248\u672c<\/p>\n<p>\u6309\u7167\u4e0a\u9762\u7684\u6b65\u9aa4\uff1a<\/p>\n<p>\u53d1\u73b0SDK 23\u91cc\u9762\uff1a<code>Instrumentation<\/code>\u7c7b\u7684<code>execStartActivitiesAsUser(Context who, IBinder contextThread, IBinder token, Activity target, Intent[] intents, Bundle options, int userId)<\/code>\u65b9\u6cd5\u91cc\uff0c\u83b7\u53d6<code>AMS<\/code>\u5b9e\u4f8b\u7684\u65b9\u5f0f\u5b8c\u5168\u4e0d\u540c<\/p>\n<pre><code class=\"language-java\">int result = ActivityManagerNative.getDefault()\n    .startActivity(whoThread, who.getBasePackageName(), intent,\n            intent.resolveTypeIfNeeded(who.getContentResolver()),\n            token, target != null ? target.mEmbeddedID : null,\n            requestCode, 0, null, options);\ncheckStartActivityResult(result, intent);<\/code><\/pre>\n<p>\u53c2\u8003\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.androidos.net.cn\/android\/6.0.1_r16\/xref\/frameworks\/base\/core\/java\/android\/app\/Instrumentation.java\">https:\/\/www.androidos.net.cn\/android\/6.0.1_r16\/xref\/frameworks\/base\/core\/java\/android\/app\/Instrumentation.java<\/a><\/p>\n<p>\u5b83\u662f\u4f7f\u7528<code>ActivityManagerNative.getDefault()<\/code>\u6765\u83b7\u5f97\u7684\uff0c\u8fdb\u53bb<code>ActivityManagerNative<\/code>\u7ee7\u7eed\u627e\uff1a<\/p>\n<pre><code class=\"language-java\">private static final Singleton&lt;IActivityManager&gt; gDefault = new Singleton&lt;IActivityManager&gt;() {\n    protected IActivityManager create() {\n        IBinder b = ServiceManager.getService(&quot;activity&quot;);\n        if (false) {\n            Log.v(&quot;ActivityManager&quot;, &quot;default service binder = &quot; + b);\n        }\n        IActivityManager am = asInterface(b);\n        if (false) {\n            Log.v(&quot;ActivityManager&quot;, &quot;default service = &quot; + am);\n        }\n        return am;\n    }\n};\n\n\/**\n * Retrieve the system&#039;s default\/global activity manager.\n *\/\nstatic public IActivityManager getDefault() {\n    return gDefault.get();\n}<\/code><\/pre>\n<p>\u53c2\u8003\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.androidos.net.cn\/android\/6.0.1_r16\/xref\/frameworks\/base\/core\/java\/android\/app\/ActivityManagerNative.java\">https:\/\/www.androidos.net.cn\/android\/6.0.1_r16\/xref\/frameworks\/base\/core\/java\/android\/app\/ActivityManagerNative.java<\/a><\/p>\n<p>OK\uff0c\u627e\u5230\u4e86\u533a\u522b\uff0c\u786e\u5b9a\u7ed3\u8bba\uff1aSDK 28\u548c23\u5728\u8fd9\u5757\u4ee3\u7801\u4e0a\u7684\u533a\u522b\u5c31\u662f<strong>\u83b7\u5f97<code>AMS<\/code>\u5b9e\u4f8b\u7684\u7c7b\u540d\u548c\u65b9\u6cd5\u540d\u90fd\u4e0d\u540c<\/strong>\u3002\u53e6\u5916\uff0c\u8fd9\u4e2a\u53d8\u5316\u662f\u5728SDK 26\u7248\u672c\u4fee\u6539\u7684\uff0c\u6240\u4ee526\u548c26\u4ee5\u540e\u662f\u901a\u8fc7<code>ActivityManager.getService()<\/code>\u6765\u83b7\u53d6\uff0c26\u4ee5\u524d\u4f7f\u7528<code>ActivityManagerNative.getDefault()<\/code>\u6765\u83b7\u5f97<br \/>\n\u8c03\u6574\u5f53\u524d\u7684<code>hook<\/code>\u65b9\u6cd5\uff0c\u4fee\u6539\u4e3a\u4e0b\u9762\u8fd9\u6837\uff1a<\/p>\n<pre><code class=\"language-java\">import android.os.Build;\nimport android.util.Log;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\n\npublic class GlobalActivityHookHelper {\n\n    \/\/\u8bbe\u5907\u7cfb\u7edf\u7248\u672c\u662f\u4e0d\u662f\u5927\u4e8e\u7b49\u4e8e26\n    private static boolean ifSdkOverIncluding26() {\n        int SDK_INT = Build.VERSION.SDK_INT;\n        if (SDK_INT &gt; 26 || SDK_INT == 26) {\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    public static void hook() {\n\n        try {\n            Class&lt;?&gt; ActivityManagerClz;\n            final Object IActivityManagerObj;\n            if (ifSdkOverIncluding26()) {\n                ActivityManagerClz = Class.forName(&quot;android.app.ActivityManager&quot;);\n                Method getServiceMethod = ActivityManagerClz.getDeclaredMethod(&quot;getService&quot;);\n                IActivityManagerObj = getServiceMethod.invoke(null);\/\/OK\uff0c\u5df2\u7ecf\u53d6\u5f97\u8fd9\u4e2a\u7cfb\u7edf\u81ea\u5df1\u7684AMS\u5b9e\u4f8b\n            } else {\n                ActivityManagerClz = Class.forName(&quot;android.app.ActivityManagerNative&quot;);\n                Method getServiceMethod = ActivityManagerClz.getDeclaredMethod(&quot;getDefault&quot;);\n                IActivityManagerObj = getServiceMethod.invoke(null);\/\/OK\uff0c\u5df2\u7ecf\u53d6\u5f97\u8fd9\u4e2a\u7cfb\u7edf\u81ea\u5df1\u7684AMS\u5b9e\u4f8b\n            }\n\n            \/\/2.\u73b0\u5728\u521b\u5efa\u6211\u4eec\u7684AMS\u5b9e\u4f8b\n            \/\/\u7531\u4e8eIActivityManager\u662f\u4e00\u4e2a\u63a5\u53e3\uff0c\u90a3\u4e48\u5176\u5b9e\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528Proxy\u7c7b\u6765\u8fdb\u884c\u4ee3\u7406\u5bf9\u8c61\u7684\u521b\u5efa\n            \/\/ \u7ed3\u679c\u88ab\u6446\u4e86\u4e00\u9053\uff0cIActivityManager\u8fd9\u73a9\u610f\u5c45\u7136\u8fd8\u662f\u4e2aAIDL\uff0c\u52a8\u6001\u751f\u6210\u7684\u7c7b\uff0c\u7f16\u8bd1\u5668\u8fd8\u4e0d\u8ba4\u8bc6\u8fd9\u4e2a\u7c7b\uff0c\u600e\u4e48\u529e\uff1f\u53cd\u5c04\u54af\n            Class&lt;?&gt; IActivityManagerClz = Class.forName(&quot;android.app.IActivityManager&quot;);\n            Object proxyIActivityManager = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IActivityManagerClz}, new InvocationHandler() {\n                @Override\n                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n                    \/\/proxy\u662f\u521b\u5efa\u51fa\u6765\u7684\u4ee3\u7406\u7c7b\uff0cmethod\u662f\u63a5\u53e3\u4e2d\u7684\u65b9\u6cd5\uff0cargs\u662f\u63a5\u53e3\u6267\u884c\u65f6\u7684\u5b9e\u53c2\n                    if (method.getName().equals(&quot;startActivity&quot;)) {\n                        Log.d(&quot;GlobalActivityHook&quot;, &quot;\u5168\u5c40hook \u5230\u4e86 startActivity&quot;);\n                    }\n                    return method.invoke(IActivityManagerObj, args);\n                }\n            });\n\n            \/\/3.\u5077\u6881\u6362\u67f1,\u8fd9\u91cc\u6709\u70b9\u7ea0\u7ed3\uff0c\u8fd9\u4e2a\u5b9e\u4f8b\u5c45\u7136\u88ab\u85cf\u5728\u4e86\u4e00\u4e2a\u5355\u4f8b\u8f85\u52a9\u7c7b\u91cc\u9762\n            Field IActivityManagerSingletonField;\n            if (ifSdkOverIncluding26()) {\n                IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField(&quot;IActivityManagerSingleton&quot;);\n            } else {\n                IActivityManagerSingletonField = ActivityManagerClz.getDeclaredField(&quot;gDefault&quot;);\n            }\n\n            IActivityManagerSingletonField.setAccessible(true);\n            Object IActivityManagerSingletonObj = IActivityManagerSingletonField.get(null);\n            Class&lt;?&gt; SingletonClz = Class.forName(&quot;android.util.Singleton&quot;);\/\/\u53cd\u5c04\u521b\u5efa\u4e00\u4e2aSingleton\u7684class\n            Field mInstanceField = SingletonClz.getDeclaredField(&quot;mInstance&quot;);\n            mInstanceField.setAccessible(true);\n            mInstanceField.set(IActivityManagerSingletonObj, proxyIActivityManager);\n\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n}<\/code><\/pre>\n<p>\u6210\u529f\uff0c\u5b9e\u73b0\u4e86\u5168\u5c40\u8303\u56f4\u5185\u7684<code>startActivity<\/code>\u52a8\u4f5c\u7684<code>hook<\/code><\/p>\n<h2>hook\u5f00\u53d1\u53ef\u80fd\u7684\u5751<\/h2>\n<p>\uff081\uff09Android Studio\u9605\u8bfb\u6e90\u7801\u5f88\u591a\u7c7b\u65e0\u6cd5\u7d22\u5f15\uff0c\u8fd9\u662f\u56e0\u4e3a\u6709\u4e00\u4e9b\u7c7b\u662f<code>@hide<\/code>\u7684\uff0c\u65e0\u6cd5Ctrl\u70b9\u8fdb\u53bb<\/p>\n<p>\u89e3\u51b3\u65b9\u6848\uff1a<code>Ctrl+shift+R<\/code>\u8f93\u5165\u7c7b\u540d\uff0c\u624b\u52a8\u8fdb\u5165<\/p>\n<p>\uff082\uff09Android Studio\u9605\u8bfb\u6e90\u7801\u76f4\u63a5\u62a5\u7ea2\uff1a\u6216\u8005\u4e00\u4e9b\u662fAIDL\u52a8\u6001\u751f\u6210\u7684\u63a5\u53e3\uff0c\u65e0\u6cd5\u76f4\u63a5\u67e5\u770b\uff0c\u6bd4<code>IActivityManager<\/code><\/p>\n<p>\u89e3\u51b3\u65b9\u6848\uff1a\u8fd9\u79cd\u63a5\u53e3\u4e0d\u7528\u7ba1\u5b83\uff0c\u5982\u679c\u975e\u8981\u7528\u5230\u5b83\uff0c\u90a3\u5c31\u4f7f\u7528<code>\u672c\u7c7b\u7684\u5305\u540d+IActivityManager<\/code>\u4f5c\u4e3a\u5168\u9650\u5b9a\u540d\uff0c\u53bb\u53cd\u5c04\u521b\u5efa\u5b83<\/p>\n<p>\uff083\uff09<code>hook<\/code>\u5f00\u53d1\uff0c\u662f\u5b66\u4e60\u6e90\u7801\u601d\u60f3\uff0c\u6539\u53d8\u6e90\u7801\u6267\u884c\u6d41\u7a0b\u3002\u6240\u4ee5\uff0c\u5728\u591a\u4e2a\u7248\u672c\u7684\u8bbe\u5907\u4e0a\u8fd0\u884c\uff0c\u5f88\u5bb9\u6613\u53d1\u751f\u4e0d\u517c\u5bb9\u7684\u60c5\u51b5<\/p>\n<p>\u89e3\u51b3\u65b9\u6848\uff1a\u627e\u5230\u4e0d\u517c\u5bb9\u7684\u8bbe\u5907\u7248\u672c\uff0c\u6839\u636e\u62a5\u7684\u5f02\u5e38\uff0c\u53c2\u7167\u6e90\u7801\u7684\u7248\u672c\u53d8\u8fc1\u505a\u51fa\u76f8\u5e94\u7684\u517c\u5bb9\u6027\u6539\u52a8<\/p>\n<p>\u672c\u6587\u8f6c\u8f7d\u81f3\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.jianshu.com\/p\/efce746836f5\">https:\/\/www.jianshu.com\/p\/efce746836f5<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u4e24\u79cd\u542f\u52a8Activity\u7684\u65b9\u5f0f\u6e90\u7801\u8ffd\u8e2a \u6e90\u7801\u57fa\u4e8e SDK 28 ~ Android 9.0 \u65b9\u5f0f1\uff1a\u4f7f\u7528Act [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[311],"tags":[60,310],"class_list":["post-1835","post","type-post","status-publish","format-standard","hentry","category-android-advance","tag-activity","tag-hook"],"_links":{"self":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1835","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/comments?post=1835"}],"version-history":[{"count":0,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/1835\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/media?parent=1835"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/categories?post=1835"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/tags?post=1835"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}