美文网首页
如何启动一个没有在AndroidManifest中注册的acti

如何启动一个没有在AndroidManifest中注册的acti

作者: 孤独的根号十二 | 来源:发表于2018-12-28 11:26 被阅读340次

    我们先来看一下activity启动的过程

    activity的startActivity:

    public void startActivity(Intent intent) { 
    this.startActivity(intent, null); 
    } 
    

    经过一系列调用,会调到startActivityForResult:
    其中调用了Instrumentation中的execStartActivity()方法

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

    execStartActivity:

    public ActivityResult execStartActivity( 
    Context who, IBinder contextThread, IBinder token, Activity target, 
    Intent intent, int requestCode, Bundle options) { 
    IApplicationThread whoThread = (IApplicationThread) contextThread; 
    ... 
    try { 
    intent.migrateExtraStreamToClipData(); 
    intent.prepareToLeaveProcess(who); 
    int result = ActivityManagerNative.getDefault() 
    .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; 
    } 
    
    

    其中启动activity的方法是ActivityManagerNative.getDefault().startActivity()。ActivityManagerNative继承了Binder,同时实现了IActivityManager接口,启动activity用到了Android中binder机制,getDefault方法实际上获得了系统ActivityManagerService
    checkStartActivityResult方法会检查,到这里我们已经找到的报错的地方

    getDefault:返回了IActivityManager的一个单例,有没有办法把这个单例替换掉,自己实现startActivity方法来绕过系统对activity的校验呢

    思路:通过动态代理的方式修改接口

    步骤

    1.在manifest中注册代理的activity

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

    2.通过反射修改ActivityManagerNative类的 gDefault属性

    public void hookStartActivity() throws Exception{
           // 1>:获取 ActivityManagerNative里面的 gDefault;
           Class<?> amnClass = Class.forName("android.app.ActivityManagerNative") ;
           // 通过 ActivityManagerNative 类 获取 gDefault属性
           Field gDefaultField = amnClass.getDeclaredField("gDefault");
           gDefaultField.setAccessible(true);  // 设置权限
           Object gDefault = gDefaultField.get(null) ;
    
           // 2>:获取gDefault中的 mInstance属性;
           Class<?> singletonClass = Class.forName("android.util.Singleton") ;
           Field mInstanceField = singletonClass.getDeclaredField("mInstance");
           mInstanceField.setAccessible(true);
           Object iamInstance = mInstanceField.get(gDefault);
    
           Object a;
           Class<?> iamClass = Class.forName("android.app.IActivityManager") ;
           a = Proxy.newProxyInstance(HookStartActivityUtil.class.getClassLoader(),
                   new Class[] {iamClass} ,
                   // InvocationHandler:必须有一个执行者,就是谁去执行这个方法
                   new StartActivityInvocationHandler(iamInstance)) ;
    
           // 3>:重新指定
           mInstanceField.set(gDefault,a);
       }
    

    3.设置代理

    private class StartActivityInvocationHandler implements InvocationHandler {
    
            // 这个才是方法的执行者
            private Object mObject ;
            // 通过构造方法把mObject传递进来
            public StartActivityInvocationHandler(Object object){
                this.mObject = object ;
            }
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                // 在这里可以 hook到 IActivityManager中所有的方法
                Log.e("TAG" , method.getName()) ;
    
                // 替换intent ,过AndroidManifest.xml 检测
                if (method.getName().equals("startActivity")){
                    // 1. 首先获取原来的intent
                    Intent originIntent = (Intent) args[2];
                    // 2. 创建一个安全的intent
                    Intent safeIntent = new Intent(mContext , mProxyClass) ;
                    // 3. 替换第二个参数
                    args[2] = safeIntent ;
                    // 4. 绑定原来的Intent
                    safeIntent.putExtra(EXTER_ORIGIN_INTENT , originIntent) ;
                }
    
                return method.invoke(mObject , args);
            }
    

    demo

    https://github.com/CodeHurricane/PlugInDemo

    相关文章

      网友评论

          本文标题:如何启动一个没有在AndroidManifest中注册的acti

          本文链接:https://www.haomeiwen.com/subject/nevrlqtx.html