Android之启动没有注册的Activity

在Activity中启动另一个activity的流程
image.png

首先工程的编译环境的是API 28,startActivity的源码跳转到Activity.java的方法中:

  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) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

代码跳转到 Instrumentation.java

    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);
            // 这里这里 这将是检查Activity的地方,所以下一步是去ActivityManager,注意是API 28
            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;
    }

代码跳转到ActivityManager

   /**
     * @hide
     */
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

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

hook点的选择原则上选单例和静态变量,所以这里就是被选中的hook点 IActivityManagerSingleton变量。IActivityManager根据源码IActivityManager.aidl,中的第三个参数是Intent.

IActivityManager.aidl源码

   int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,
           in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
           int flags, in ProfilerInfo profilerInfo, in Bundle options);

占坑Activity

   <activity android:name=".StubActivity" />

到这里就是启动activity到校验这个被启动的activity是否在AndroidManifest.xml中被注册,只要把没有注册的activity换成占坑的Activity。hook的代码:

try {
            Field enterField = null;
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
                Class activityManagerClass = Class.forName("android.app.ActivityManager");
                //Field IActivityManagerSingleton
                enterField = activityManagerClass.getDeclaredField("IActivityManagerSingleton");
            } else {
                Class activityManagerNative = Class.forName("android.app.ActivityManagerNative");
                enterField = activityManagerNative.getDeclaredField("gDefault");
            }

            enterField.setAccessible(true);
            //Singleton<IActivityManager>的实例,因为IActivityManagerSingleton是静态的
            Object singletonObject = enterField.get(null);


            Class singletonClass = Class.forName("android.util.Singleton");
            Field mInstanceField = singletonClass.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);
            // 获取singletonObject中变量mInstance的值即IActivityManager类型的实例
            final Object mIActivityManagerObject = mInstanceField.get(singletonObject);

            //IActivityManager 是接口,通过动态代理来处理
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            Class iActivityManagerInterface = Class.forName("android.app.IActivityManager");

            //生产IActivityManager的代理对象
            Object mIActivityManagerProxy = Proxy.newProxyInstance(
                    classLoader, new Class[]{iActivityManagerInterface}, new InvocationHandler() {
                   
                        @Override public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                            Log.e(TAG, "invoke:   ~~~~~~ " + method.getName());

                            if ("startActivity".equals(method.getName())) {
                                Log.i(TAG, "准备启动activity");
                                Intent rawIntent = null;
                                int index = 0;
                                for (int i = 0; i < args.length; i++) {
                                    if (args[i] instanceof Intent) {
                                        rawIntent = (Intent) args[i];
                                        index = i;
                                        break;
                                    }
                                }


                                // 将需要被启动的Activity替换成StubActivity
                                Intent newIntent = new Intent();
                                String stubPackage = "per.zkingcobra.demo.proxyapp";
                                newIntent.setComponent(new ComponentName(stubPackage, StubActivity.class.getName()));
//                              newIntent.setClassName(rawIntent.getComponent().getPackageName(), StubActivity.class.getName());
                                //把这个newIntent放回到args,达到了一个欺骗AMS的目的
                                newIntent.putExtra(EXTRA_TARGET_INTENT, rawIntent);
                                args[index] = newIntent;

                            }

                            return method.invoke(mIActivityManagerObject, args);
                        }
                    });

            //把我们的代理对象融入到framework
            //IActivityManager 在源码中是AIDL
            mInstanceField.set(singletonObject, mIActivityManagerProxy);

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

        }

在执行startActivity之前运行,这个时候跳转就会跳转到StubActivity。所以,需要在校验完成之后换回想要启动的Activity,根据后面的Activity启动流程由ApplicationThread—>ActivityThread.mH ----> sendMessage 启动Activity。因为版本的变化

     //已经没有了
    public static final int LAUNCH_ACTIVITY         = 100;
.....
   //Api 28中
   public static final int EXECUTE_TRANSACTION = 159;

这个已经没有了。这一部分重构成了状态模式。

zhuangtaimoshi.png

class H extends Handler{
.....
public void handleMessage(Message msg) {
case EXECUTE_TRANSACTION:
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    mTransactionExecutor.execute(transaction);
                    if (isSystem()) {
                        // Client transactions inside system process are recycled on the client side
                        // instead of ClientLifecycleManager to avoid being cleared before this
                        // message is handled.
                        transaction.recycle();
                    }
                    // TODO(lifecycler): Recycle locally scheduled transactions.
                    break;
  .....
  }
.....
}

所以,恢复Activity的hook处理也要做版本的兼容

try {

            Class launchActivityClass = Class.forName("android.app.ActivityThread");
            Field mCurrentActivityThreadFiled = launchActivityClass.getDeclaredField("sCurrentActivityThread");

            // 获取ActivityThread的实例
            mCurrentActivityThreadFiled.setAccessible(true);
            Object currentActivityThreadObject = mCurrentActivityThreadFiled.get(null);

            //获取H
            Field mHFiled = launchActivityClass.getDeclaredField("mH");
            //H的实例
            mHFiled.setAccessible(true);
            Handler hObject = (Handler) mHFiled.get(currentActivityThreadObject);

            //获取callback
            Field mCallbackField = Handler.class.getDeclaredField("mCallback");
            mCallbackField.setAccessible(true);

            // TODO 为什么不需要动态代理了?
            mCallbackField.set(hObject, new Handler.Callback() {
                @Override public boolean handleMessage(Message msg) {
                    Log.e(TAG, "handleMessage: " + msg.what);
                    switch (msg.what) {
                        case 100:
                        // TODO 需要处理
                         break;
                        case 159:
                           //恢复真身
                            Object obj = msg.obj;
                            Log.i(TAG, "handleMessage: obj=" + obj);
                            try {
                                Field mActivityCallbacksField = obj.getClass().getDeclaredField("mActivityCallbacks");
                                mActivityCallbacksField.setAccessible(true);
                                List mActivityCallbacks = (List) mActivityCallbacksField.get(obj);
                                Log.i(TAG, "handleMessage: mActivityCallbacks= " + mActivityCallbacks);
                            
                                if (mActivityCallbacks.size() > 0) {
                                    Log.i(TAG, "handleMessage: size= " + mActivityCallbacks.size());
                                    String className = "android.app.servertransaction.LaunchActivityItem";
                                    if (mActivityCallbacks.get(0).getClass().getCanonicalName().equals(className)) {
                                        Object object = mActivityCallbacks.get(0);
                                        Field intentField = object.getClass().getDeclaredField("mIntent");
                                        intentField.setAccessible(true);
                                        Intent intent = (Intent) intentField.get(object);
                                        Intent targetIntent = intent.getParcelableExtra(EXTRA_TARGET_INTENT);
                                        intent.setComponent(targetIntent.getComponent());
                                    }
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

                            break;
                    }
                    return false;
                }
            });


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

到现在就完成了API28下的无注册Activity启动,分析时序图,还有一种hook的方式,就是校验前和校验后都会经过Instrumentation.java,也可以实现无注册Activity启动,就不写了。
device-2019-05-28-165734 (1).gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值