【Android安全-安卓逆向之插件化技术学习】此文章归类为:Android安全。
从12年的概念提出,到16年的把花齐放,件化技术引领着Android技术的进步。
现在出现的flutter 等等 已经让插件化技术变得没那么重要
大型应用模块化拆分、按需加载功能、第三方服务集成
多开app,无需安装运行app的框架
我所了解到virtualapp,edxposed等等,可以说实现安卓app的多开,那时候我就想自己实现一个简单的demo,可以多开简单的app就够了。
于是开始了的安卓学习之旅,本以为学完了安卓基础,就可以自己完成开发,结果就只能望着android studio 发呆 ------原来这个是高级的开发知识。
继而开始了我的安卓源码学习之路。
读了很多教程与安卓开发的书籍才知道,要实现多开要先学会插件化开发
那我们一起进入这个旅程吧------------
源码基于 安卓12
从开发者熟悉的的startActivity开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  | 5687  5688      /**5689       * Same as {@link #startActivity(Intent, Bundle)} with no options5690       * specified.5691       *5692       * @param intent The intent to start.5693       *5694       * @throws android.content.ActivityNotFoundException5695       *5696       * @see #startActivity(Intent, Bundle)5697       * @see #startActivityForResult5698       */5699      @Override5700      public void startActivity(Intent intent) {5701          this.startActivity(intent, null);5702      } | 
继续跟踪这个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47  | 5704      /**5705       * Launch a new activity.  You will not receive any information about when5706       * the activity exits.  This implementation overrides the base version,5707       * providing information about5708       * the activity performing the launch.  Because of this additional5709       * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not5710       * required; if not specified, the new activity will be added to the5711       * task of the caller.5712       *5713       * <p>This method throws {@link android.content.ActivityNotFoundException}5714       * if there was no Activity found to run the given Intent.5715       *5716       * @param intent The intent to start.5717       * @param options Additional options for how the Activity should be started.5718       * See {@link android.content.Context#startActivity(Intent, Bundle)}5719       * Context.startActivity(Intent, Bundle)} for more details.5720       *5721       * @throws android.content.ActivityNotFoundException5722       *5723       * @see #startActivity(Intent)5724       * @see #startActivityForResult5725       */5726      @Override5727      public void startActivity(Intent intent, @Nullable Bundle options) {5728          if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)5729                  && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) {5730              if (TextUtils.equals(getPackageName(),5731                      intent.resolveActivity(getPackageManager()).getPackageName())) {5732                  // Apply Autofill restore mechanism on the started activity by startActivity()5733                  final IBinder token =5734                          mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);5735                  // Remove restore ability from current activity5736                  mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);5737                  mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY);5738                  // Put restore token5739                  intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);5740                  intent.putExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY, true);5741              }5742          }5743          if (options != null) {5744              startActivityForResult(intent, -1, options);5745          } else {5746              // Note we want to go through this call for compatibility with5747              // applications that may have overridden the method.5748              startActivityForResult(intent, -1);5749          }5750      } | 
然后就是startActivityForResult方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36  |    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,5400              @Nullable Bundle options) {5401          if (mParent == null) {5402              options = transferSpringboardActivityOptions(options);5403              Instrumentation.ActivityResult ar =5404                  mInstrumentation.execStartActivity(5405                      this, mMainThread.getApplicationThread(), mToken, this,5406                      intent, requestCode, options);5407              if (ar != null) {5408                  mMainThread.sendActivityResult(5409                      mToken, mEmbeddedID, requestCode, ar.getResultCode(),5410                      ar.getResultData());5411              }5412              if (requestCode >= 0) {5413                  // If this start is requesting a result, we can avoid making5414                  // the activity visible until the result is received.  Setting5415                  // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the5416                  // activity hidden during this time, to avoid flickering.5417                  // This can only be done when a result is requested because5418                  // that guarantees we will get information back when the5419                  // activity is finished, no matter what happens to it.5420                  mStartedActivity = true;5421              }5422  5423              cancelInputsAndStartExitTransition(options);5424              // TODO Consider clearing/flushing other event sources and events for child windows.5425          } else {5426              if (options != null) {5427                  mParent.startActivityFromChild(this, intent, requestCode, options);5428              } else {5429                  // Note we want to go through this method for compatibility with5430                  // existing applications that may have overridden it.5431                  mParent.startActivityFromChild(this, intent, requestCode);5432              }5433          }5434      } | 
主要的代码就是这个玩意了 :mInstrumentation.execStartActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43  | 1710      public ActivityResult execStartActivity(1711              Context who, IBinder contextThread, IBinder token, Activity target,1712              Intent intent, int requestCode, Bundle options) {1713          IApplicationThread whoThread = (IApplicationThread) contextThread;1714          Uri referrer = target != null ? target.onProvideReferrer() : null;1715          if (referrer != null) {1716              intent.putExtra(Intent.EXTRA_REFERRER, referrer);1717          }1718          if (mActivityMonitors != null) {1719              synchronized (mSync) {1720                  final int N = mActivityMonitors.size();1721                  for (int i=0; i<N; i++) {1722                      final ActivityMonitor am = mActivityMonitors.get(i);1723                      ActivityResult result = null;1724                      if (am.ignoreMatchingSpecificIntents()) {1725                          result = am.onStartActivity(intent);1726                      }1727                      if (result != null) {1728                          am.mHits++;1729                          return result;1730                      } else if (am.match(who, null, intent)) {1731                          am.mHits++;1732                          if (am.isBlocking()) {1733                              return requestCode >= 0 ? am.getResult() : null;1734                          }1735                          break;1736                      }1737                  }1738              }1739          }1740          try {1741              intent.migrateExtraStreamToClipData(who);1742              intent.prepareToLeaveProcess(who);1743              int result = ActivityTaskManager.getService().startActivity(whoThread,1744                      who.getOpPackageName(), who.getAttributionTag(), intent,1745                      intent.resolveTypeIfNeeded(who.getContentResolver()), token,1746                      target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);1747              checkStartActivityResult(result, intent);1748          } catch (RemoteException e) {1749              throw new RuntimeException("Failure from system", e);1750          }1751          return null;1752      } | 
这里就调用 ActivityTaskManager.getService().startActivity方法了 这里就开始 与 ams沟通
AMS 的内部就不记录了
后面会调用 ActivityThread的 H 对象 进行沟通
1 2 3 4  | @Overridepublic void scheduleTransaction(ClientTransaction transaction) {    mH.sendMessage(H.EXECUTE_TRANSACTION, transaction);  // 发送消息到应用进程} | 
1 2 3 4 5 6 7 8 9 10  |     case EXECUTE_TRANSACTION:2213                      final ClientTransaction transaction = (ClientTransaction) msg.obj;2214                      mTransactionExecutor.execute(transaction);2215                      if (isSystem()) {2216                          // Client transactions inside system process are recycled on the client side2217                          // instead of ClientLifecycleManager to avoid being cleared before this2218                          // message is handled.2219                          transaction.recycle();2220                      }2221                      // TODO(lifecycler): Recycle locally scheduled transactions. | 
我查看了TransactionExecutor 的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47  |      /** Transition the client through previously initialized state sequence. */205      private void performLifecycleSequence(ActivityClientRecord r, IntArray path,206              ClientTransaction transaction) {207          final int size = path.size();208          for (int i = 0, state; i < size; i++) {209              state = path.get(i);210              if (DEBUG_RESOLVER) {211                  Slog.d(TAG, tId(transaction) + "Transitioning activity: "212                          + getShortActivityName(r.token, mTransactionHandler)213                          + " to state: " + getStateName(state));214              }215              switch (state) {216                  case ON_CREATE:217                      mTransactionHandler.handleLaunchActivity(r, mPendingActions,218                              null /* customIntent */);219                      break;220                  case ON_START:221                      mTransactionHandler.handleStartActivity(r, mPendingActions,222                              null /* activityOptions */);223                      break;224                  case ON_RESUME:225                      mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,226                              r.isForward, "LIFECYCLER_RESUME_ACTIVITY");227                      break;228                  case ON_PAUSE:229                      mTransactionHandler.handlePauseActivity(r, false /* finished */,230                              false /* userLeaving */, 0 /* configChanges */, mPendingActions,231                              "LIFECYCLER_PAUSE_ACTIVITY");232                      break;233                  case ON_STOP:234                      mTransactionHandler.handleStopActivity(r, 0 /* configChanges */,235                              mPendingActions, false /* finalStateRequest */,236                              "LIFECYCLER_STOP_ACTIVITY");237                      break;238                  case ON_DESTROY:239                      mTransactionHandler.handleDestroyActivity(r, false /* finishing */,240                              0 /* configChanges */, false /* getNonConfigInstance */,241                              "performLifecycleSequence. cycling to:" + path.get(size - 1));242                      break;243                  case ON_RESTART:244                      mTransactionHandler.performRestartActivity(r, false /* start */);245                      break;246                  default:247                      throw new IllegalArgumentException("Unexpected lifecycle state: " + state);248              }249          }250      } | 
这里就进入
1 2  | mTransactionHandler.handleLaunchActivity(r, mPendingActions,218                              null /* customIntent */); | 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57  | /**3758       * Extended implementation of activity launch. Used when server requests a launch or relaunch.3759       */3760      @Override3761      public Activity handleLaunchActivity(ActivityClientRecord r,3762              PendingTransactionActions pendingActions, Intent customIntent) {3763          // If we are getting ready to gc after going to the background, well3764          // we are back active so skip it.3765          unscheduleGcIdler();3766          mSomeActivitiesChanged = true;3767  3768          if (r.profilerInfo != null) {3769              mProfiler.setProfiler(r.profilerInfo);3770              mProfiler.startProfiling();3771          }3772  3773          if (r.mPendingFixedRotationAdjustments != null) {3774              // The rotation adjustments must be applied before handling configuration, so process3775              // level display metrics can be adjusted.3776              overrideApplicationDisplayAdjustments(r.token, adjustments ->3777                      adjustments.setFixedRotationAdjustments(r.mPendingFixedRotationAdjustments));3778          }3779  3780          // Make sure we are running with the most recent config.3781          mConfigurationController.handleConfigurationChanged(null, null);3782  3783          if (localLOGV) Slog.v(3784              TAG, "Handling launch of " + r);3785  3786          // Initialize before creating the activity3787          if (ThreadedRenderer.sRendererEnabled3788                  && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {3789              HardwareRenderer.preload();3790          }3791          WindowManagerGlobal.initialize();3792  3793          // Hint the GraphicsEnvironment that an activity is launching on the process.3794          GraphicsEnvironment.hintActivityLaunch();3795  3796          final Activity a = performLaunchActivity(r, customIntent);3797  3798          if (a != null) {3799              r.createdConfig = new Configuration(mConfigurationController.getConfiguration());3800              reportSizeConfigurations(r);3801              if (!r.activity.mFinished && pendingActions != null) {3802                  pendingActions.setOldState(r.state);3803                  pendingActions.setRestoreInstanceState(true);3804                  pendingActions.setCallOnPostCreate(true);3805              }3806          } else {3807              // If there was an error, for any reason, tell the activity manager to stop us.3808              ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED,3809                      null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY);3810          }3811  3812          return a;3813      } | 
继续跟进
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133  | 3512      /**  Core implementation of activity launch. */3513      private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {3514          ActivityInfo aInfo = r.activityInfo;3515          if (r.packageInfo == null) {3516              r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,3517                      Context.CONTEXT_INCLUDE_CODE);3518          }3519  3520          ComponentName component = r.intent.getComponent();3521          if (component == null) {3522              component = r.intent.resolveActivity(3523                  mInitialApplication.getPackageManager());3524              r.intent.setComponent(component);3525          }3526  3527          if (r.activityInfo.targetActivity != null) {3528              component = new ComponentName(r.activityInfo.packageName,3529                      r.activityInfo.targetActivity);3530          }3531  3532          ContextImpl appContext = createBaseContextForActivity(r);3533          Activity activity = null;3534          try {3535              java.lang.ClassLoader cl = appContext.getClassLoader();3536              activity = mInstrumentation.newActivity(3537                      cl, component.getClassName(), r.intent);3538              StrictMode.incrementExpectedActivityCount(activity.getClass());3539              r.intent.setExtrasClassLoader(cl);3540              r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),3541                      appContext.getAttributionSource());3542              if (r.state != null) {3543                  r.state.setClassLoader(cl);3544              }3545          } catch (Exception e) {3546              if (!mInstrumentation.onException(activity, e)) {3547                  throw new RuntimeException(3548                      "Unable to instantiate activity " + component3549                      + ": " + e.toString(), e);3550              }3551          }3552  3553          try {3554              Application app = r.packageInfo.makeApplication(false, mInstrumentation);3555  3556              if (localLOGV) Slog.v(TAG, "Performing launch of " + r);3557              if (localLOGV) Slog.v(3558                      TAG, r + ": app=" + app3559                      + ", appName=" + app.getPackageName()3560                      + ", pkg=" + r.packageInfo.getPackageName()3561                      + ", comp=" + r.intent.getComponent().toShortString()3562                      + ", dir=" + r.packageInfo.getAppDir());3563  3564              if (activity != null) {3565                  CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());3566                  Configuration config =3567                          new Configuration(mConfigurationController.getCompatConfiguration());3568                  if (r.overrideConfig != null) {3569                      config.updateFrom(r.overrideConfig);3570                  }3571                  if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "3572                          + r.activityInfo.name + " with config " + config);3573                  Window window = null;3574                  if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {3575                      window = r.mPendingRemoveWindow;3576                      r.mPendingRemoveWindow = null;3577                      r.mPendingRemoveWindowManager = null;3578                  }3579  3580                  // Activity resources must be initialized with the same loaders as the3581                  // application context.3582                  appContext.getResources().addLoaders(3583                          app.getResources().getLoaders().toArray(new ResourcesLoader[0]));3584  3585                  appContext.setOuterContext(activity);3586                  activity.attach(appContext, this, getInstrumentation(), r.token,3587                          r.ident, app, r.intent, r.activityInfo, title, r.parent,3588                          r.embeddedID, r.lastNonConfigurationInstances, config,3589                          r.referrer, r.voiceInteractor, window, r.configCallback,3590                          r.assistToken, r.shareableActivityToken);3591  3592                  if (customIntent != null) {3593                      activity.mIntent = customIntent;3594                  }3595                  r.lastNonConfigurationInstances = null;3596                  checkAndBlockForNetworkAccess();3597                  activity.mStartedActivity = false;3598                  int theme = r.activityInfo.getThemeResource();3599                  if (theme != 0) {3600                      activity.setTheme(theme);3601                  }3602  3603                  if (r.mActivityOptions != null) {3604                      activity.mPendingOptions = r.mActivityOptions;3605                      r.mActivityOptions = null;3606                  }3607                  activity.mLaunchedFromBubble = r.mLaunchedFromBubble;3608                  activity.mCalled = false;3609                  if (r.isPersistable()) {3610                      mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);3611                  } else {3612                      mInstrumentation.callActivityOnCreate(activity, r.state);3613                  }3614                  if (!activity.mCalled) {3615                      throw new SuperNotCalledException(3616                          "Activity " + r.intent.getComponent().toShortString() +3617                          " did not call through to super.onCreate()");3618                  }3619                  r.activity = activity;3620                  mLastReportedWindowingMode.put(activity.getActivityToken(),3621                          config.windowConfiguration.getWindowingMode());3622              }3623              r.setState(ON_CREATE);3624  3625              // updatePendingActivityConfiguration() reads from mActivities to update3626              // ActivityClientRecord which runs in a different thread. Protect modifications to3627              // mActivities to avoid race.3628              synchronized (mResourcesManager) {3629                  mActivities.put(r.token, r);3630              }3631  3632          } catch (SuperNotCalledException e) {3633              throw e;3634  3635          } catch (Exception e) {3636              if (!mInstrumentation.onException(activity, e)) {3637                  throw new RuntimeException(3638                      "Unable to start activity " + component3639                      + ": " + e.toString(), e);3640              }3641          }3642  3643          return activity;3644      } | 
这里进行一个简单的总结
startActivity方法的调用链开发者常用的startActivity(Intent intent)方法,实际上调用的是startActivity(Intent intent, Bundle options),将options设为null。
在startActivity(Intent intent, Bundle options)方法中,会先处理与自动填充(Autofill)相关的逻辑。若满足特定条件,会在启动的 Activity 上应用自动填充恢复机制。之后,根据options是否为null,调用startActivityForResult(intent, -1, options)或startActivityForResult(intent, -1)方法 。
startActivityForResult方法的处理当mParent为null时,会调用mInstrumentation.execStartActivity方法,并传入一系列参数,包括当前上下文、应用线程、Activity 令牌等。同时,如果启动请求需要结果(requestCode >= 0),会标记mStartedActivity为true,以避免在结果返回前 Activity 闪烁。
当mParent不为null时,会调用mParent.startActivityFromChild方法,同样会根据options是否为null进行不同的处理。
execStartActivity方法与 AMS 交互在mInstrumentation.execStartActivity方法中,首先会处理ActivityMonitor相关逻辑,检查是否有匹配的监视器并进行相应操作。
接着,通过ActivityTaskManager.getService().startActivity方法与 Activity 管理服务(AMS)进行跨进程通信,将启动 Activity 的请求发送给 AMS。AMS 接收到请求后,会进行一系列系统层面的处理,如任务调度、Activity 栈管理等,不过网页中未详细记录 AMS 内部的处理过程。
AMS 处理完启动请求后,会通过 ActivityThread 的H对象发送消息。H类接收到EXECUTE_TRANSACTION消息后,会调用mTransactionExecutor.execute(transaction)方法。
performLifecycleSequence方法根据不同的生命周期状态(如ON_CREATE、ON_START等),调用mTransactionHandler相应的方法来处理 Activity 的生命周期转换。例如,在ON_CREATE状态时,会调用mTransactionHandler.handleLaunchActivity方法。
handleLaunchActivity与performLaunchActivity方法handleLaunchActivity方法会进行一些准备工作,如取消垃圾回收任务、处理配置变更等,然后调用performLaunchActivity方法来实际启动 Activity。
performLaunchActivity方法负责创建 Activity 实例,首先获取 Activity 的相关信息,如ActivityInfo、PackageInfo等。接着,使用ClassLoader加载 Activity 类,并通过mInstrumentation.newActivity方法创建 Activity 实例。然后,创建应用上下文ContextImpl,并将 Activity 与上下文、应用等进行关联。最后,调用mInstrumentation.callActivityOnCreate方法,触发 Activity 的onCreate方法,完成 Activity 的初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85  |     public static void hook()    {        //开启 欺骗 ams        Method execStartActivity = null;        try {            execStartActivity = Instrumentation.class.getDeclaredMethod("execStartActivity",  Context.class,//0                    IBinder.class,//1                    IBinder.class,//2                    Activity.class,//3                    Intent.class,//4                    int.class,//5                    Bundle.class);        } catch (Exception e) {            throw new RuntimeException(e);        }        CalvinBridge.hookMethod(execStartActivity, new CA_MethodHook() {            @Override            public void beforeHookedMethod(MethodHookParam param) throws Throwable {                Log.e("hook", "hook 测试成功!"+param.args[3]);                Intent oriIntent =(Intent) param.args[4];                Intent intent = new Intent((Activity) param.args[3], ProxyActivity.class);                intent.setComponent(new ComponentName(BaseData.oriAppName,BaseData.proxyActivity));                intent.putExtra("oriIntent",oriIntent);                param.args[4] = intent;            }        });        //欺骗 app        try {            Class<?> aClass = ReflectUtils.loadClass("android.app.ActivityThread");            Class<?> ActivityClientRecord = ReflectUtils.loadClass("android.app.ActivityThread");            CalvinBridge.hookAllMethods(aClass, "performLaunchActivity", new CA_MethodHook() {                @Override                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {                    Log.e(TAG, "createBaseContextForActivity " + param.args[0].getClass());                    Object arg = param.args[0];                    Object activityInfo = ReflectUtils.getField(arg, "activityInfo");                    Object LoadedApk = ReflectUtils.getField(arg, "packageInfo");                    ReflectUtils.printFieldValues(LoadedApk);                    ReflectUtils.setField(LoadedApk, "mApplication", null);                    ReflectUtils.setField(LoadedApk, "mApplicationInfo", packageInfo.applicationInfo);                    //设置资源//                        fs.setFieldValue(LoadedApk, "mResources", apkResources);                    //设置 Intent Intent intent = Intent { cmp=com.ywl.fva/.ui.ProxyActivity (has extras) }                    Intent FakeIntent =(Intent) ReflectUtils.getField(arg, "intent");                    if(FakeIntent != null){                        Intent oriIntent = (Intent)FakeIntent.getParcelableExtra("oriIntent");                        if(oriIntent != null){                            Log.e(TAG, "替换 Intent intent = " + oriIntent);                            ReflectUtils.setField(arg, "intent", oriIntent);                        }                    }                    //设置 主题 theme//                        ReflectUtils.setFieldValue(activityInfo, "theme", packageInfo.applicationInfo.theme);                    ReflectUtils.setField(activityInfo, "theme", packageInfo.applicationInfo.theme);//                    super.beforeHookedMethod(param);                }            });        } catch (Exception e) {        }        // 处理包名        Method getPackageName = ReflectUtils.getMethod(context.getClass(), "getPackageName");        CalvinBridge.hookMethod(getPackageName, new CA_MethodHook() {            @Override            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {                super.beforeHookedMethod(param);            }            @Override            protected void afterHookedMethod(MethodHookParam param) throws Throwable {//                    Log.e(TAG, "getPackageName " + param.getResult());//                    param.setResult("com.crack.testdemo");                param.setResult(packageInfo.packageName);                super.afterHookedMethod(param);            }        });    } | 
我使用侵入式 hook ,这样才会显得我不专业,但也显得 我们的攻击方式很明确
欺上瞒下的思路。
与PathClassLoader不同,DexClassLoader支持从APK、JAR或ZIP文件中加载Dex文件,无需预装到系统
通过构造函数指定插件Dex路径、优化目录、父加载器等参数,实现动态加载:
1  | DexClassLoader loader = new DexClassLoader(dexPath, optimizedDir, null, getClassLoader()); | 
双亲委派模型:优先通过父加载器加载类,避免重复加载。若未找到,则调用findClass方法从Dex中解析类
Dex文件解析:Dex文件包含所有类的索引信息,通过DexPathList将Dex转换为Element数组,逐个加载类
从这里可以了解到,要实现dex的加载 可以利用双亲委派机制,或者修改 dexClassloader的DexPathList,我这里有两个思路:
思路一: 我觉得可以加载 dexclassloader 直接把 原来的dexclassloader的父亲classloader 修改为插件classloader 以链表的样子插入进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31  |   public static void addPluginApkDex(File apk) {        // 优化后的输出目录        File optimizedDir = context.getDir("oapk", Context.MODE_PRIVATE);        DexClassLoader dexClassLoader = new DexClassLoader(                apk.getAbsolutePath(), // 替换为 APK 文件路径                optimizedDir.getAbsolutePath(),                null,                context.getClassLoader()        );        Field Field_parent;        try {            Field_parent = ClassLoader.class.getDeclaredField("parent");            Field_parent.setAccessible(true);            Object parent = Field_parent.get(context.getClassLoader());            Field_parent.set(context.getClassLoader(), dexClassLoader);            Field_parent.set(dexClassLoader,parent );        } catch (Exception e) {//            throw new RuntimeException(e);            Log.e(TAG, "addPluginApkDex: ", e);        }        Class<?> aClass = null;        try {            aClass = context.getClassLoader().loadClass("com.crack.testdemo.MainActivity");        } catch (ClassNotFoundException e) {        }        Log.d(TAG, "addPluginApkDex: "+aClass);        //我认为这样做 可以优先加载 插件的 类 这个样 在插件中 就不会出现与 宿主app 拥有相同的sdk 冲突了    } | 
思路二: 可以让直接 将插件dexclassloader 的DexPathList  copy到 程序的dexclassloader 的DexPathList中去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77  | public static void addDexs(File dir) {        //合并 dex 文件//        File dir = new File(context.getFilesDir(), appItem.getPackageName());        File[] dex_s = dir.listFiles(new FilenameFilter() {            @Override            public boolean accept(File dir, String name) {                return name.contains(".dex");            }        });        // 优化后的输出目录        File optimizedDir = context.getDir("odex", Context.MODE_PRIVATE);        ArrayList<Object[]> allElements = new ArrayList<>();        int ElementsLen = 0;        Field pathList = null;        for (File dex : dex_s) {            // 创建DexClassLoader            DexClassLoader dexClassLoader = new DexClassLoader(                    dex.getAbsolutePath(),                    optimizedDir.getAbsolutePath(),                    null,                    context.getClassLoader()            );            // 获取BaseDexClassLoader中的DexPathList字段            Class<?> aClass = fs.getClass("dalvik.system.BaseDexClassLoader");            try {                pathList = aClass.getDeclaredField("pathList");            } catch (NoSuchFieldException e) {                throw new RuntimeException(e);            }            pathList.setAccessible(true);            Object pathList_obj = null;            try {                pathList_obj = pathList.get(dexClassLoader);            } catch (IllegalAccessException e) {                throw new RuntimeException(e);            }            Object[] dexElements = (Object[])fs.getFieldValue(pathList_obj, "dexElements");            allElements.add(dexElements);            ElementsLen += dexElements.length;        }        //加上原本的        ClassLoader oriclassLoader = context.getClassLoader();        Object pathList_obj =null;        try {            pathList_obj = pathList.get(oriclassLoader);        } catch (IllegalAccessException e) {            throw new RuntimeException(e);        }        Object[] dexElements = (Object[])fs.getFieldValue(pathList_obj, "dexElements");        //app 自身的dex        allElements.add(dexElements);        ElementsLen += dexElements.length;        Object[] NewDexElements =(Object[]) Array.newInstance(                dexElements.getClass().getComponentType(),                ElementsLen        );        int index = 0;        for (Object[] allElement : allElements) {            System.arraycopy(allElement, 0, NewDexElements, index, allElement.length);            index += allElement.length;        }        //添加完毕 开始设置 变量        try {            Field dexElements1 = pathList_obj.getClass().getDeclaredField("dexElements");            dexElements1.setAccessible(true);            dexElements1.set(pathList_obj, NewDexElements);        } catch (Exception e) {            throw new RuntimeException(e);        }        Log.d(TAG, "handle: dexElements 变量设置完毕!");    } | 
这里 加载外部dex 结束
这里还有资源的加载 我写到另一个文章中去了
这里我出现的问题好多
比如 资源冲突等等
其实这里可以使用io重定向解决是比较好的方式 ,但是 楼主 为了简单省事,我的处理方案就是 提前加载so文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35  | public static void hook(File dir) {      //加载so文件   思路 预加载so文件      File soDir = new File(dir, "lib/arm64-v8a");      if(soDir.exists()){          File[] files = soDir.listFiles(new FilenameFilter() {              @Override              public boolean accept(File dir, String name) {                  return name.contains(".so");              }          });          if(files != null){              for (File file : files) {                  System.load(file.getAbsolutePath());                  Log.d(TAG, "so 加载:"+file.getAbsolutePath());              }          }else {              Log.d(TAG, "so 文件不存在");          }          //拦截 so加载函数          try {              Class<?> System = context.getClassLoader().loadClass("java.lang.System");              Method loadLibrary = System.getDeclaredMethod("loadLibrary", String.class);              CalvinBridge.hookMethod(loadLibrary, new CA_MethodHook() {                  @Override                  protected void beforeHookedMethod(MethodHookParam param) throws Throwable {                      Log.d(TAG, "loadLibrary: "+param.args[0]);                      param.args[0] = "c";                  }              });          } catch (Exception e) {              throw new RuntimeException(e);          }      }  } | 
这里 Service 的加载 与activity差不的,只不过 service 没有 生命周期,与ams交互 感觉就是走个形式,那这里 我也就走个形式 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41  |     public static  void hook(){        //开启 欺骗 ams        //startService(Intent service)  类 android.app.        Method startService = ReflectUtils.getMethod(ContextWrapper.class, "startService", Intent.class);        CalvinBridge.hookMethod(startService, new CA_MethodHook() {            @Override            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {                Log.e(TAG, "startService: " );                ReflectUtils.printFieldValues(param.args[0]);                Intent oriintent =(Intent) param.args[0];//                intentList.contains(oriintent)                map.put("ori", oriintent);                Intent Fakeintent = new Intent(BaseData.baseActivity, ProxyService.class);                Fakeintent.setComponent(new android.content.ComponentName(BaseData.oriAppName,"com.crack.vapp.service.ProxyService"));                Fakeintent.putExtra("oriIntent",oriintent);                param.args[0] = Fakeintent;                ReflectUtils.printFieldValues(param.args[0]);                super.beforeHookedMethod(param);            }        });        // hookService  handleCreateService        Class<?> ActivityThread = ReflectUtils.loadClass("android.app.ActivityThread");        CalvinBridge.hookAllMethods(ActivityThread, "handleCreateService", new CA_MethodHook() {            @Override            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {                Log.e(TAG, "handleCreateService: " );                ReflectUtils.printFieldValues(param.args[0]);                Object info = ReflectUtils.getField(param.args[0], "info");                ReflectUtils.printFieldValues(info);                if(info != null){                   ReflectUtils.setField(info, "name", map.get("ori").getComponent().getClassName());                   ReflectUtils.setField(info, "packageName", map.get("ori").getComponent().getPackageName());                }                super.beforeHookedMethod(param);            }        });    } | 
这样做 只能启动一个 service 因为会把之前的覆盖掉!
反正我也只是学习 涨涨见识 。 就这样无论吞枣的过去吧 哈哈。
后面还有 广播 内容提供者的插件化 这些 我了解原理了 就不写了
网络上还有一堆 好文 关于插件化的。
就这样, 劈里啪啦的代码一顿乱写,就把这个残疾版的玩具多开app写出来了。也是 对我半个月学习的总结吧。当然 这个项目写的很垃圾 完全不能与 网上大佬相比。
要提升 我的想法也很多,比如
加入 去签名的逻辑 我之前写的文章,
在 用io重定向将文件进行隔离,设置好activityThread类的对象 ,
这里 是我Vapp的项目地址 :
支持 安卓12 ,可以加载 安卓android studio 编写出来的小demo 实际测试 很多app都加载不了
更多【Android安全-安卓逆向之插件化技术学习】相关视频教程:www.yxfzedu.com