热修复插件化原理分析

Android热修复原理解析

Android Hook 机制之简单实战

插件化的原理分析及实现

 

 

 

 

 

 

Hook IActivityManager .startActivity ActivityThread .mCallback


    Activity
   public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (mParent == null) {
            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());
            }

            Instrumentation
  public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {


binder跨进程通信, ActivityManagerProxy代理远程 通信到 ActivityManagerNative ActivityManagerService
    int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,


class ActivityManagerProxy implements IActivityManager
{
    public ActivityManagerProxy(IBinder remote)
    {
        mRemote = remote;
    }

    public IBinder asBinder()
    {
        return mRemote;
    }

    ActivityManagerProxy
    
    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {

  mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);

  ActivityManagerNative

public abstract class ActivityManagerNative extends Binder implements IActivityManager
{

 @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case START_ACTIVITY_TRANSACTION:
        {

    int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options);

                    ActivityManagerService
public final class ActivityManagerService extends ActivityManagerNative

    @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
            resultWho, requestCode, startFlags, profilerInfo, options,
            UserHandle.getCallingUserId());
    }
    
    
        ActivityStackSupervisor mStackSupervisor;
     return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
    
      int res = startActivityLocked(caller, intent, resolvedType, aInfo,
    
       err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                startFlags, true, options, inTask);
    
            resumeTopActivitiesLocked(targetStack, null, options);
    
    
            boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
       someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
    
       final ActivityStack stack = stacks.get(stackNdx);
       
         prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
       
       
         IApplicationThread thread;  // the actual proc...  may be null only if
         
         
       class ApplicationThreadProxy implements IApplicationThread {
       
         public final void schedulePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor);
        data.writeStrongBinder(token);
        data.writeInt(finished ? 1 : 0);
        data.writeInt(userLeaving ? 1 :0);
        data.writeInt(configChanges);
        data.writeInt(dontReport ? 1 : 0);
        mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null,
                IBinder.FLAG_ONEWAY);
        data.recycle();
    }
       
        public ApplicationThreadNative() {
        attachInterface(this, descriptor);
    }
    
    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case SCHEDULE_PAUSE_ACTIVITY_TRANSACTION:
        {
       
       
         schedulePauseActivity(b, finished, userLeaving, configChanges, dontReport);
       
       
       ActivityThread
       
       private class ApplicationThread extends ApplicationThreadNative {
       
       
          public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) {
            sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token,
                    (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
                    configChanges);
        }

 

  
  
  
    ActivityThread
 sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    
    case PAUSE_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
                            (msg.arg1&2) != 0);
                            
                            
  
  ActivityManagerProxy Binder代理  Remote进程 和本地进程通信 pauseActivity  
    ActivityManagerNative.getDefault().activityPaused(token);
    
    

  ActivityManagerService
   @Override
    public final void activityPaused(IBinder token) {
    
    
    
  
  ActivityStack
  private void completePauseLocked(boolean resumeNext) {
  
  
  ActivityStackSupervisor
  
  
   final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
   
      final ActivityManagerService mService;
      
  mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.ge
                
                
                
  ActivityManagerService本地进程
  
    private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
  
  
  启动Zygote进程fork一个app进程 ActivityThread
    Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
                    
            
            ZygoteInit
 private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        
           done = peers.get(index).runOnce();
        
            //fork一个新进程
           pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
        
           handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
        
           cloader = new PathClassLoader(parsedArgs.classpath,
                            ClassLoader.getSystemClassLoader());
                            
                            
                        ZygoteInit.invokeStaticMain(cloader, className, mainArgs);        
            cl = loader.loadClass(className);
        
                //调用ActivityThread的main方法
            m = cl.getMethod("main", new Class[] { String[].class });
        
            
                    
                    
  ActivityThread
  
      public static void main(String[] args) {
      
      
  //binderProxy代理 Remote进程(App进程)和本地进程通信  attachApplication
     final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
  
  
  
  ActivityManagerService本地进程
  
  
      @Override
    public final void attachApplication(IApplicationThread thread) {
    
     if (mStackSupervisor.attachApplicationLocked(app)) {
  
  ActivityStackSupervisor
  
 
  boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
            
            
  binder代理 本地进程和Remote进程(App进程)通信 lunch一个activity
    final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig)
            
            
  
  class ApplicationThreadProxy implements IApplicationThread {
  scheduleLaunchActivity为ApplicationThreadProxy远程代理的方法
  
    app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
    
    
  
  
  
  ActivityThread
  
  private class ApplicationThread extends ApplicationThreadNative {
  
     public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
     
     
   public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                
                
                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
  
     @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
            
               Activity a = performLaunchActivity(r, customIntent);
            
  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  
  
    java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            
              java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
            
             LoadedApk packageInfo;
             
             
             
             
             
             
             
             
             
             

    ActivityThread
    @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                
           sendMessage(H.LAUNCH_ACTIVITY, r);        
                
  handleLaunchActivity(r, null);

        Activity a = performLaunchActivity(r, customIntent);


   activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);

  return (Activity)cl.loadClass(className).newInstance();

    
    ActivityManagerService
    
        private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
    
                Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
                    
                    
                    
                    
                    
                    Process
                      public static final ProcessStartResult start(final String processClass,
                                  final String niceName,
                                  int uid, int gid, int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
    
    //通过 ZygoteInit创建fork新的进程 Process        Socket
     private static ProcessStartResult zygoteSendArgsAndGetResult(
    

 

 

热修复

ClassLoader 
DexClassLoader PathClassLoader


loadClass  findLoadedClass  findClass

DexClassLoader BaseDexClassLoader

DexPathList pathList


Element dexElements    dex

javac   .java  .class  d8 patch.dex


dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.github.xch168.hotfixdemo-2/base.apk"],
nativeLibraryDirectories=[/data/app/com.github.xch168.hotfixdemo-2/lib/arm64, /system/lib64, /vendor/lib64, /system/vendor/lib64, /product/lib64]]]


class dalvik.system.BaseDexClassLoader


DexPathList[[zip file "/data/app/com.github.xch168.hotfixdemo-2/base.apk"],
nativeLibraryDirectories=[/data/app/com.github.xch168.hotfixdemo-2/lib/arm64, /system/lib64, /vendor/lib64, /system/vendor/lib64, /product/lib64]]

zip file "/data/app/com.github.xch168.hotfixdemo-2/base.apk"


pathList dexElements

DexClassLoader


.loadClass        .newInstance

.invoke

loadClass

lo


    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = pathList.findClass(name);

        if (clazz == null) {
            throw new ClassNotFoundException(name);
        }

        return clazz;
    }


DexPathList pathList

parent.loadClass

  findBootstrapClassOrNull 
  
  
  findClass
  
DexPathList.findClass
    public Class findClass(String name) {
        for (Element element : dexElements) {
            DexFile dex = element.dexFile;

            if (dex != null) {
                Class clazz = dex.loadClassBinaryName(name, definingContext);
                if (clazz != null) {
                    return clazz;
                }
            }
        }

        return null;
    }
    
    dexElements
    
    PathClassLoader    dexElements
    
    dex =dexElements[0]        setField

 

1
接来下开始load我们刚刚写入在dex文件中的ClassStudent类:

Class<?> aClass = dexClassLoader.loadClass("ClassStudent");
1
然后我们对其进行初始化,并调用相关的get/set方法对其进行验证,在这里我传给ClassStudent对象一个字符串,然后调用它的get方法获取在方法内合并后的字符串:

    Object instance = aClass.newInstance();
    Method method = aClass.getMethod("setName", String.class);
    method.invoke(instance, "Sahadev");

    Method getNameMethod = aClass.getMethod("getName");
    Object invoke = getNameMethod.invoke(instance););
1
2
3
4
5
6
最后我们实现的代码可能是这样的:

    /**
     * 加载指定路径的dex
     *
     * @param apkPath
     */
    private void loadClass(String apkPath) {
        ClassLoader classLoader = getClassLoader();

        File file = new File(apkPath);

        try {

            DexClassLoader dexClassLoader = new DexClassLoader(apkPath, file.getParent() + "/optimizedDirectory/", "", classLoader);
            Class<?> aClass = dexClassLoader.loadClass("ClassStudent");
            mLog.i(TAG, "ClassStudent = " + aClass);

            Object instance = aClass.newInstance();
            Method method = aClass.getMethod("setName", String.class);
            method.invoke(instance, "Sahadev");

              伪代码相当于    void setName(string) {}

            Method getNameMethod = aClass.getMethod("getName");
            Object invoke = getNameMethod.invoke(instance);

         伪代码相当于    void getName() {}

 

            mLog.i(TAG, "invoke result = " + invoke);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

 

 

 

 

 

插件化原理分析
插件化要解决的三个核心问题:类加载、资源加载、组件生命周期管理。
类加载:Android中常用的两种类加载器:PathClassLoader和DexClassLoader,它们都继承于BaseDexClassLoader。
DexClassLoader的构造函数比PathClassLoader多了一个,optimizedDirectory参数,这个是用来指定dex的优化产物odex的路径,在源码注释中,指出这个参数从API 26后就弃用了。
PathClassLoader主要用来加载系统类和应用程序的类,在ART虚拟机上可以加载未安装的apk的dex,在Dalvik则不行。
DexClassLoader用来加载未安装apk的dex。
资源加载:Android系统通过Resource对象加载资源,因此只需要添加资源(即apk文件)所在路径到AssetManager中,即可实现对插件资源的访问。由于AssetManager的构造方法时hide的,需要通过反射区创建。
组件生命周期管理:对于Android来说,并不是说类加载进来就可以使用了,很多组件都是有“生命”的;因此对于这些有血有肉的类,必须给他们注入活力,也就是所谓的组件生命周期管理。
在解决插件中组件的生命周期,通常的做法是通过Hook相应的系统对象,实现欺上瞒下,后面将通过Activity的插件化来进行讲解。

    // 第一步:反射得到 ListenerInfo 对象
        Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
        //android.view.View$ListenerInfo android.view.View.getListenerInfo()
        getListenerInfo.setAccessible(true);
        Object listenerInfo = getListenerInfo.invoke(view);//View$ListenerInfo
        // 第二步:得到原始的 OnClickListener事件方法
        Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");//class android.view.View$ListenerInfo
        Field mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener");//public android.view.View$OnClickListener android.view.View$ListenerInfo.mOnClickListener
        mOnClickListener.setAccessible(true);
        View.OnClickListener originOnClickListener = (View.OnClickListener) mOnClickListener.get(listenerInfo);//MainActivity@4846
        // 第三步:用 Hook代理类 替换原始的 OnClickListener


        View.OnClickListener hookedOnClickListener = new HookedClickListenerProxy(originOnClickListener);
        mOnClickListener.set(listenerInfo, hookedOnClickListener);
        
        
        
           @UnsupportedAppUsage
    static public INotificationManager getService()
    {
        if (sService != null) {
            return sService;
        }
        IBinder b = ServiceManager.getService("notification");
        sService = INotificationManager.Stub.asInterface(b);
        return sService;
    }
    
    
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                          //Object

 

nvoke   newProxyInstance


InvocationHandler


reflectClas.getMethod


typeArguments


Java    Field.set 向对象的这个Field属性设置新值value


fields.get 


    try {
            ReflectUtil.hookNotificationManager(this);
        } catch (Exception e) {
            e.printStackTrace();
        }


        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
       createPrintingNotification(notificationManager);
    }

    public void killApp(View view) {
        android.os.Process.killProcess(android.os.Process.myPid());
    }
    private void createPrintingNotification(NotificationManager notificationManager) {
        Notification.Builder builder = new Notification.Builder(this)
                .setSmallIcon(R.drawable.ic_launcher_background);
        notificationManager.notify(0, builder.build());
    }


 private static String TAG = "robin";
    public static void hookNotificationManager(final Context context) throws Exception {
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        Method getService = NotificationManager.class.getDeclaredMethod("getService");
        getService.setAccessible(true);
        // 第一步:得到系统的 sService
        final Object sOriginService = getService.invoke(notificationManager);

        Class iNotiMngClz = Class.forName("android.app.INotificationManager");
        // 第二步:得到我们的动态代理对象
        Object proxyNotiMng = Proxy.newProxyInstance(context.getClass().getClassLoader(), new
                Class[]{iNotiMngClz}, new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Log.d(TAG, "invoke(). method:" + method);
                //android.app.INotificationManager$Stub$Proxy@84b2c77
                //public abstract void android.app.INotificationManager.enqueueNotificationWithTag(java.lang.String,java.lang.String,java.lang.String,int,android.app.Notification,int[],int) throws android.os.RemoteException
                //[0]com.github.xch168.hotfixdemo
                //[1]com.github.xch168.hotfixdemo
                //[2] 1
                //[3]Notification(pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0x00000000 vis=PRIVATE)
                String name = method.getName();
                Log.d(TAG, "invoke: name=" + name);
                if (args != null && args.length > 0) {
                    for (Object arg : args) {
                        Log.d(TAG, "invoke: arg=" + arg);
                    }
                }
                Toast.makeText(context.getApplicationContext(), "检测到有人发通知了", Toast.LENGTH_SHORT).show();
                // 操作交由 sOriginService 处理,不拦截通知
                return method.invoke(sOriginService, args);
                // 拦截通知,什么也不做
                //                    return null;
                // 或者是根据通知的 Tag 和 ID 进行筛选
            }
        });
        // 第三步:偷梁换柱,使用 proxyNotiMng 替换系统的 sService
        Field sServiceField = NotificationManager.class.getDeclaredField("sService");
        sServiceField.setAccessible(true);
        sServiceField.set(notificationManager, proxyNotiMng);

    }


invoke    newProxyInstance            InvocationHandler

forName Proxy
 

//获取PathClassLoader(BaseDexClassLoader)的DexPathList对象变量pathList
Object pathList = ReflectUtil.getFieldValue(BaseDexClassLoader.class, pathClassLoaderClass, "pathList");
//获取DexPathList的Element[]对象变量dexElements
Object[] dexElements = (Object[]) ReflectUtil.getFieldValue(pathList.getClass(),pathList,"dexElements");
 
//获取DexClassLoader(BaseDexClassLoader)的DexPathList对象变量pathList
Object pathList = ReflectUtil.getFieldValue(BaseDexClassLoader.class, dexClassLoaderClass, "pathList");
//获取DexPathList的Element[]对象变量dexElements
Object[] dexElements = (Object[]) ReflectUtil.getFieldValue(pathList.getClass(),pathList,"dexElements");
 
//合并到PathClassLoader
classes resources.arsc        res   assets\   src\jar 

libs\     gen\  .java\\\        bin\ META-INF\

AAPT(Android  AIDL工具会将所有的     JAVAC工具将R dex


apkbuilder脚本将资源文件和 jarsigner对apk进行签名。

R.java\\\    


Application Resources aapt R.java resource.arsc

打包资源文件:使用aapt(Android Asset Package Tool)把res目录下的资源生成相应的R.java文件和resource.arsc文件,同时为AndroidManifest.xml生成二进制的AndroidManifest.java文件。
处理aidl文件:使有aidl(Android Interface Denifition Language)把项目自定义的aidl文件生成相应的Java代码文件。
编译Java代码文件:使用javac(Java 编译器)把项目中所有的Java代码编译成class文件。包括Java源文件、aapt生成的R.java文件 以及 aidl生成的Java接口文件。
代码混淆处理:若配置了启用Proguard混淆,就会对代码进行混淆处理并生成proguardMapping.txt文件。
把class文件转成dex文件:使用dx.bat将所有的class文件(包括第三方库中的class文件)转换成dex文件。
生成apk文件:使用apkbuilder(主要用到的是sdk/tools/lib/sdklib.jar文件中的ApkBuilderMain类)将所有的dex文件、resource.arsc、res文件夹、assets文件夹、AndroidManifest.xml 打包为.apk文件。
apk文件签名:使用apksigner(Android官方针对apk签名及验证工具)或jarsigner(JDK提供针对jar包签名工具)对未签名的apk文件进行签名。
对齐处理:使用zipalign对签名后的apk文件进行对齐处理,以便在运行时可节省内存。


Proguard 
dx.bat将所有的class文件(包括第三方库中的class文件)转换成dex文件。


apkbuilder脚本将资源文件和

dex resource.arsc res assets\ AndroidManifest 
.apk

apksigner zipalign


ContextImpl

PluginManager getResources getAssets

scheduleTransaction

ClientTransaction本地


ClientTransactionHandler远程代理


从Activity的启动中可以发现,可以在ActivityManager.getService().startActivity()

这个地方把Intent送AMS检测之前替换调Intent(或Intent中要启动Activity的className),
和ClientTransaction.mActivityCallbacks,在ActivityThread.mH处理的msg.what是EXECUTE_TRANSACTION的时候,

取出mActivityCallbacks中第一个是LaunchActivityItem的元素,
获取LaunchActivityItem中的mIntent,替换mIntent中的占坑Activity,使用插件中的Activity。

(这里是针对SDK 28的Hook流程,SDK  25、26Hook有所不同,项目源码中做了3个版本的兼容处理)

Instrumentation.java
execStartActivity() -> ActivityManager.getService().startActivity()

ClientTransactionHandler.java
    scheduleTransaction() -> sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction) -> 
    ActivityThread.java
    ActivityThread.H.EXECUTE_TRANSACTION -> mTransactionExecutor.execute(transaction)

IActivityManagerSingleton

package com.cj.jplugin.hook;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class HookStartActivityApi25 extends BaseHookStartActivity {

    public static final int LAUNCH_ACTIVITY = 100;

    public HookStartActivityApi25(Context context, Class<?> proxyClass) {
        super(context, proxyClass);
    }

    @Override
    public void hookStartActivity() {
        try {
            Class<?> iamClass = Class.forName("android.app.IActivityManager");
            //interface android.app.IActivityManager
            Class<?> amnClass = Class.forName("android.app.ActivityManagerNative");//class android.app.ActivityManagerNative
            Field gDefaultField = amnClass.getDeclaredField("gDefault");//private static final android.util.Singleton android.app.ActivityManagerNative.gDefault
            gDefaultField.setAccessible(true);
            Object singleton = gDefaultField.get(null);//ActivityManagerNative

            Class<?> singletonClass = Class.forName("android.util.Singleton");//class android.util.Singleton
            Field mInstanceField = singletonClass.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);//private java.lang.Object android.util.Singleton.mInstance
            Object iam = mInstanceField.get(singleton);//ActivityManagerProxy类型

            Object iamProxy = Proxy.newProxyInstance(iam.getClass().getClassLoader(),
                    iam.getClass().getInterfaces(),
                    new StartActivityInvocationHandler(context, iam, proxyClass)
            );//android.app.ActivityManagerProxy@e5e80f
            //将Singleton中的mInstance替换成代理的iamProxy
            mInstanceField.set(singleton, iamProxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class StartActivityInvocationHandler implements InvocationHandler {
        private Object obj;
        private Context context;
        private Class<?> proxyClass;

        public StartActivityInvocationHandler(Context context, Object obj, Class<?> proxyClass) {
            this.context = context;
            this.obj = obj;
            this.proxyClass = proxyClass;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //替换Intent,过AndroidManifest.xml检测
            if ("startActivity".equals(method.getName())) {
                //1.首先获取原来的Intent
                Intent originIntent = (Intent) args[2];//第二个参数就是intent
                if(!isExistIntentActivity(originIntent)) {
                    //创建一个安全的Intent
                    Intent proxyIntent = new Intent(context, proxyClass);
                    //保存原来的Intent
                    proxyIntent.putExtra(EXTRA_ORIGIN_INTENT, originIntent);
                    //设置新的Intent过检测
                    args[2] = proxyIntent;
                }
            }
            return method.invoke(obj,args);
        }
    }

    @Override
    public void hookLaunchActivity() {
        try {
            // 获取ActivityThread
            Class<?> atClass = Class.forName("android.app.ActivityThread");///class android.app.ActivityThread
            Field sCurrentActivityThreadField = atClass.getDeclaredField("sCurrentActivityThread");//private static volatile android.app.ActivityThread android.app.ActivityThread.sCurrentActivityThread
            sCurrentActivityThreadField.setAccessible(true);
            Object at = sCurrentActivityThreadField.get(null);//ActivityThread
            //获取Handler mH
            Field mHField = atClass.getDeclaredField("mH");//final android.app.ActivityThread$H android.app.ActivityThread.mH
            mHField.setAccessible(true);
            Object mH = mHField.get(at);//Handler (android.app.ActivityThread$H) {f0bfc36}
            //给mH设置Callback
            Class<?> handlerClass = Class.forName("android.os.Handler");
            Field mCallbackField = handlerClass.getDeclaredField("mCallback");//final android.os.Handler$Callback android.os.Handler.mCallback
            mCallbackField.setAccessible(true);
            mCallbackField.set(mH,new HookCallback());//
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected boolean handleCallbackMessage(Message msg){
        if (msg.what == LAUNCH_ACTIVITY){
            return handleLaunchActivity(msg);
        }
        return false;
    }

    private boolean handleLaunchActivity(Message msg) {
        try {
            //获取ActivityClientRecord
            Object record = msg.obj;
            Field intentField = record.getClass().getDeclaredField("intent");
            intentField.setAccessible(true);
            //代理的Intent,proxyIntent
            Intent intent = (Intent) intentField.get(record);
            if(intent.hasExtra(EXTRA_ORIGIN_INTENT)) {
                //获取原始的Intent
                Intent originIntent = intent.getParcelableExtra(EXTRA_ORIGIN_INTENT);
                //防止为null
                if (originIntent != null) {
                    //将原始的Intent重新设置回去,这样才能跳转到原始的Activity
                    intentField.set(record, originIntent);
                }

                //解决AppCompatActivity重新检测清单文件注册问题
                handleAppCompatActivityCheck(intent);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 解决AppCompatActivity重新检测清单文件注册问题
     * 如果使用的Activity集成的是AppCompatActivity,它会重新检测该Activity是否注册,所以这里也需要hook
     * @param intent
     */
    protected void handleAppCompatActivityCheck(Intent intent){
        try {
            //解决AppCompatActivity重新检测清单文件注册问题
            Class<?> atClass = Class.forName("android.app.ActivityThread");
            Method getPackageManagerMethod = atClass.getDeclaredMethod("getPackageManager");
            getPackageManagerMethod.setAccessible(true);
            Object ipm = getPackageManagerMethod.invoke(null);
            Object ipmProxy = Proxy.newProxyInstance(ipm.getClass().getClassLoader(),
                    ipm.getClass().getInterfaces(), new PackageManagerInvocationHandler(ipm, intent.getComponent()));
            Field sPackageManagerField = atClass.getDeclaredField("sPackageManager");
            sPackageManagerField.setAccessible(true);
            sPackageManagerField.set(null, ipmProxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class PackageManagerInvocationHandler implements InvocationHandler{
        private Object obj;
        private ComponentName proxyClass;

        public PackageManagerInvocationHandler(Object obj,ComponentName proxyClass) {
            this.obj = obj;
            this.proxyClass = proxyClass;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("getActivityInfo".equals(method.getName())) {
                args[0] = proxyClass;
            }
            Object invoke = method.invoke(obj, args);
            /*if ("getActivityInfo".equals(method.getName())) {
                ai.parentActivityName = originClass.getClassName();
            }*/
            return invoke;
        }
    }

    class HookCallback implements Handler.Callback{

        @Override
        public boolean handleMessage(Message msg) {//onresume,各种生命周期都可以收到
            return handleCallbackMessage(msg);
        }

    }
}

  public void click(View view) {
        try {
            Class clazz = getClassLoader().loadClass("com.cj.oneplugin.SecondActivity");
            Intent intent = new Intent(this,clazz);
            startActivity(intent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值