Modo proxy y aplicación Hook para startActivity

definición

En resumen, deje que la clase de proxy y la clase de destino implementen la misma interfaz y deje que la clase de proxy contenga un objeto de la clase de destino, de modo que el acceso a la clase de proxy se pueda transferir al acceso a la clase de destino. . Podemos lograr algunas mejoras funcionales en la clase de proxy sin modificar la clase de destino, mejorando así indirectamente la funcionalidad de la clase de destino.

El diagrama UML es el siguiente:
Insertar descripción de la imagen aquí
implementación del código básico:

public interface Subject {
    void request()
}

===================================================================================
public class RealSubject implements Subject {
    @Override
    public void request() {
        // 完成一些事情
    }
}

===================================================================================\
public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject subject) {
        this.realSubject = subject;
    }

    @Override
    public void request() {
        realSubject.request();
    }
}

Varios métodos de aplicación del modo proxy:

  1. Agente remoto (AIDL)
    Insertar descripción de la imagen aquí

El método add en el Proxy escribe los parámetros en los datos. A través del método transact de mRemote, los datos y la respuesta se envían al otro extremo de AIDL. En el otro extremo de AIDL, los parámetros se extraen de los datos a través de El método onTransact de la clase Stub y calculado. Después de obtener el resultado, pase el resultado a través de la función de devolución de llamada de respuesta.

  1. Agente de protección (control de autoridad):
    El jefe otorga temporalmente su autoridad para revisar las solicitudes de licencia a su secretaria mientras se encuentra en un viaje de negocios.

  2. Agente virtual (marcador de posición de imagen),
    por ejemplo: visualización de imágenes de WeChat, cuando la red no es buena, primero use una imagen pequeña borrosa para ubicar el lugar y luego reemplace la imagen pequeña anterior después de descargar la imagen grande 4. Desarrollo
    Insertar descripción de la imagen aquí
    colaborativo (Clase simulada)
    A debe usarse El método en B, pero B aún no se ha desarrollado, entonces B proporcionará un método falso, devolverá el valor codificado y luego lo reemplazará una vez que se complete el desarrollo.

5. Agregar registro
Hay un método doSomething en Class1. Queremos registrar una línea de registro antes y después de la ejecución de doSomething. Podemos diseñar un Class1Proxy para realizar esta función, de modo que no afecte a Class1.

public interface Class1Interface {
    public void doSomething();
}
public class Class1 implements Class1Interface{
    @Override
    public void doSomething() {
        System.out.println("class1 doSomething");
    }
}
public class Class1Proxy implements Class1Interface{
    Class1 class1 = new Class1();

    @Override
    public void doSomething() {
        System.out.println("Begin log.");
        class1.doSomething();
        System.out.println("End log.");
    }
}

A continuación, podemos usar Class1Proxy para reemplazar Class1

Class1Proxy proxy = new Class1Proxy();
proxy.doSomething();

El método de implementación anterior es el modo proxy estático y su desventaja es que

  1. Proxy estático Si se agrega un nuevo método a la interfaz, además de todas las clases de implementación (clases temáticas reales) que necesitan implementar este método, todas las clases de proxy también deben implementar este método. Aumenta la complejidad del mantenimiento del código.
  2. El objeto proxy solo sirve un tipo de objeto, si desea servir varios tipos de objetos. Se deben proporcionar servidores proxy para cada tipo de objeto. Los servidores proxy estáticos no son compatibles cuando el tamaño del programa es ligeramente mayor.

Por ejemplo, si necesitamos imprimir registros antes de la ejecución de doSomething en Class2, necesitamos implementar otra clase Class2Proxy, y la cantidad de clases Proxy será grande.

proxy dinámico

El proxy estático del que hablamos anteriormente ya tiene el archivo de clase de la clase de proxy antes de que se ejecute el código; mientras que el proxy dinámico genera dinámicamente el objeto de clase de proxy lanzándolo cuando se ejecuta el código y determina a quién representar como proxy.
Es decir, no sabemos a quién representar durante la fase de codificación, decidiremos a quién representar cuando se esté ejecutando el código.
Java proporciona métodos Proxyde clase newProxyInstancepara generar objetos proxy y también proporciona interfaces proxy dinámicas InvocationHandlerpara transmitir métodos que requieren implementación de proxy.
Echemos un vistazo a newProxyInstancela declaración del método:

static Object newProxyInstance(
	ClassLoader loader,
	Class<?>[] interfaces,
	InvocationHandler h)

Descripción de parámetros:

  • El cargador se establece en el cargador de clases correspondiente al objeto de destino (objeto proxy)
  • interfaces se establece en el tipo de interfaz implementado por el objeto de destino (objeto proxy)
  • h se establece en un objeto de clase que implementa la interfaz InvocationHandler e inyectamos el objeto de destino a través de su constructor.
 Class1Interface class1 = new Class1();
 Class1Interface class1Proxy = (Class1Interface) Proxy.newProxyInstance(class1.getClass().getClassLoader(), class1.getClass().getInterfaces(), new InvocationHandlerForTest(class1));
 class1Proxy.doSomething();

================================================================================
public class InvocationHandlerForTest implements InvocationHandler {
    private Object realSubject; // 真实的主题对象

    public InvocationHandlerForTest(Object realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志开始");
        Object result = method.invoke(realSubject,args);
        System.out.println("日志结束");
        return result;
    }
}

El objeto creado a través del método Proxy.newProxyInstance es un objeto que implementa la interfaz Class1Interface. De esta manera, hemos implementado el objeto proxy de Class1. Si ahora necesitamos proxy Class2, entonces podemos directamente:

Class2Interface class2 = new Class2();
Class2Interface class2Proxy = (Class2Interface) Proxy.newProxyInstance(class2.getClass().getClassLoader(), class2.getClass().getInterfaces(), new InvocationHandlerForTest(class2));
class2Proxy.work();

Tenga en cuenta que en la línea de código método.invoke (objetivo, objetos), el objetivo es el objeto de clase 1 y los objetos son los parámetros requeridos por el método doSomthing de clase 1. A continuación, llamar al método doSomething de clase1Proxy llamará al método doSomething de clase1 .

Ventajas de los proxies dinámicos
  1. Todas las funciones de proxy se pueden completar a través de una clase de proxy y todos los métodos declarados en la interfaz se transfieren a un método centralizado del procesador de invocación (InvocationHandler.invoke). Cuando hay una gran cantidad de métodos de interfaz, podemos manejarlos de manera flexible sin tener que transferir cada método como un proxy estático.
  2. La aplicación del proxy dinámico hace que nuestras responsabilidades de clase sean más únicas y más reutilizables.
Desventajas de los proxies dinámicos

Las clases no pueden ser proxy, solo las interfaces pueden ser proxy. Si nuestra clase no implementa ninguna interfaz, entonces no podemos usar este método para el proxy dinámico (porque la clase $Proxy() integra Proxy y no se permite la integración de Java. múltiples clases principales ).

Podemos utilizar el objeto proxy generado por el método proxy dinámico Proxy.newProxyInstance para reemplazar el objeto original. Esta tecnología se llama tecnología Hook en el complemento.

Aplicación de gancho

Suponemos que se inicia una SecondActivity en MainActivity, pero finalmente se inicia ThirdActivity.
Lo primero que debemos saber es el proceso básico de ejecución de startActivity en Actividad.

Dos formas de método startActivity:

  1. Utilice startActivity que viene con Activity
Intent intent= new Intent(MainActivity.this ,,SecondActivity.class) ;
startActivity(intent);
  1. Utilice el método startActivity de Contexto
Intent intent= new Intent(MainActivity.this ,,SecondActivity.class) ;
startActivity(intent);

Los dos métodos tienen el mismo objetivo: se ejecutarán en el Activitymétodo startActivityForResulty finalmente mInstrumentation.execStartActivityse llamarán y execStartActivityluego se entregarán al método ActivityTaskManager.getService().startActivitypara su procesamiento.
Esta es la primera mitad de startActivity:

MainActivity notifica a los cajeros automáticos que inicien SecondActivity

ATMS recibe la solicitud de MainActivity, luego mService.getLifecycleManager().scheduleTransactionregresa al proceso de la APLICACIÓN a través del método, envía ActivityThread.H.EXECUTE_TRANSACTIONel mensaje, ejecuta el ActivityThreadmétodo handleLaunchActivity()y finalmente ejecuta el método llamado en este método para completar el inicio. Esta es la segunda mitad de startActivity:ActivityThreadmInstrumentation.callActivityOnCreate();SecondActivityonCreate()

Cajeros automáticos notifica el proceso de APP para iniciar SecondActivity

Los lugares a los que podemos enganchar en la primera mitad incluyen:

  • Activitymétodo startActivityForResult;
  • Activitycampos mInstrumentation;
  • ActivityTaskManager.getService()El objeto obtenido por el método.

Los lugares que se pueden enganchar en la segunda mitad incluyen:

  • Objeto mInstrumentation de ActivityThread, método newActivity correspondiente y método callActivityOnCreate.
Anular startActivityForResult de la actividad

Cree una clase base Activity-BaseActivity para todas las actividades en la aplicación y anule el método startActivityForResult en BaseActivity.

Enganche el campo mInstrumentation de Actividad

Hay un campo mInstrumentation en Actividad. El método startActivityForResult de Actividad llamará al método ejecutarStartActivity de mInstrumentation:

# Activity.java
private Instrumentation mInstrumentation;

public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
    if(this.mParent == null) {
        ActivityResult ar = this.mInstrumentation.execStartActivity(this, this.mMainThread.getApplicationThread(), this.mToken, this, intent, requestCode, options);
        if(ar != null) {
            this.mMainThread.sendActivityResult(this.mToken, this.mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
        }

        if(requestCode >= 0) {
            this.mStartedActivity = true;
        }
    } else if(options != null) {
        this.mParent.startActivityFromChild(this, intent, requestCode, options);
    } else {
        this.mParent.startActivityFromChild(this, intent, requestCode);
    }

}

Primero obtenemos esta variable privada mediante reflexión.

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 获取Activity中的Instrumentation对象,私有变量,通过反射获取
        Instrumentation instrumentation = (Instrumentation) RefInvoke.getFieldObject(Activity.class,this,"mInstrumentation");
        // 创建Instrumentation代理对象
        InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
        // 替换掉MainActivity中的mInstrumentation对象为代理对象
        RefInvoke.setFieldObject(Activity.class,this,"mInstrumentation",instrumentationProxy);

        setContentView(R.layout.activity_main);
        findViewById(R.id.turn_to_other_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}
public class InstrumentationProxy extends Instrumentation {
    private static final String TAG = InstrumentationProxy.class.getSimpleName();
    private Instrumentation mInstrumentation;

    public InstrumentationProxy(Instrumentation mInstrumentation) {
        this.mInstrumentation = mInstrumentation;
    }

    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,
                                            Intent intent, int requestCode, Bundle options){
        Log.d(TAG, "XXX到此一游!");
        ComponentName componentName = intent.getComponent();
        String className = componentName.getClassName();
        // 判断目标activity是不是SecondActivity,替换SecondActivity为ThirdActivity
        if(className.equals("com.chinatsp.hookstartactivity.SecondActivity")){
            intent.setClassName(who,ThirdActivity.class.getCanonicalName());
        }
      
        // 由于这个方法是隐藏的,因此需要使用反射调用;
        Class[] p1 = {Context.class, IBinder.class,
                IBinder.class, Activity.class,
                Intent.class, int.class, Bundle.class};
        Object[] v1 = {who, contextThread, token, target,
                intent, requestCode, options};
        return (ActivityResult) RefInvoke.invokeInstanceMethod(
                mInstrumentation, "execStartActivity", p1, v1);
    }
}

De esta manera, conectamos con éxito el método startActivity. Debido a que mInstrumentation es un objeto, aquí se utiliza el modo proxy estático.

Enganche el getService de los cajeros automáticos

InstrumentaionEl execStartActivitymétodo eventualmente llegará al ActivityTaskManager.getService().startActivitymétodo:

# Instrumentation.java

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

    try {
		// 流程就从Launcher进程进入到AMS所在的SystemServer进程了
        int result = ActivityTaskManager.getService().startActivity(whoThread,
                who.getBasePackageName(), who.getAttributionTag(), intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
       ...
    }
    return null;
}

//=========ActivityTaskManager.java=========
public static IActivityTaskManager getService() {
    return IActivityTaskManagerSingleton.get();
}

@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton = new Singleton<IActivityTaskManager>() {
    @Override
    protected IActivityTaskManager create() {
        final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
        return IActivityTaskManager.Stub.asInterface(b);
    }
};


//=================Singleton.java=========
        /**
         * Singleton helper class for lazily initialization.
         * ......
         */
        public abstract class Singleton<T> {
            private T mInstance;

            protected abstract T create();

            public final T get() {
                synchronized (this) {
                    if (mInstance == null) {
                        mInstance = create();
                    }
                    return mInstance;
                }
            }
        }

El método getService de ATMS devuelve un tipo IActivityTaskManager, que es una interfaz. Podemos usar el método Proxy.newProxyInstance para conectar este objeto de tipo de interfaz IActivityTaskManager a un objeto generado por nuestra clase de proxy personalizada AMSProxy.class:

public class AMSHookHelper {
    private static final String TAG = AMSHookHelper.class.getSimpleName();

    public static void hookAMS() throws ClassNotFoundException,ClassCastException{
        Object singletonObject = null;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityTaskManager","IActivityTaskManagerSingleton");
        }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityTaskManager","IActivityManagerSingleton");
        }else{
            // 获取AMN的gDefault单例,gDefault是final静态的
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative","gDefault");
        }

        // singletonObject 是一个android.util.Singleton<T>对象; 我们取出这个单例里面的mInstance字段
        Object mInstance = RefInvoke.getFieldObject("android.util.Singleton", singletonObject, "mInstance");

        // 创建一个这个对象的代理对象MockClass1, 然后替换这个字段, 让我们的代理对象帮忙干活
        Class<?> activityManagerInterface;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
            activityManagerInterface = Class.forName("android.app.IActivityTaskManager");
        }else {
            activityManagerInterface = Class.forName("android.app.IActivityManager");
        }

        // getInterfaces()方法和Java的反射机制有关。它能够获得这个对象所实现的接口。activityManager 本身就是activityManagerInterface 的Class
        Object activityManagerProxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{ activityManagerInterface },
                new AMSProxy(mInstance));

        RefInvoke.setFieldObject("android.util.Singleton", singletonObject,"mInstance",activityManagerProxy);
        Log.d(TAG, "hook activity manager success");

    }
}

public class AMSProxy implements InvocationHandler {
    private static final String TAG = AMSProxy.class.getSimpleName();

    private Object mAMSProxy;

    public AMSProxy(Object mAMSProxy) {
        this.mAMSProxy = mAMSProxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.d(TAG,"hey, baby,you are hooked!!");
        Log.d(TAG,"method:"+method.getName()+" called with args:" + Arrays.toString(args));
        if("startActivity".equals(method.getName())){
            int index = 0;
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof Intent) {
                    index = i;
                    break;
                }
            }
            // 原来的intent
            Intent intent = (Intent) args[index];
            Intent proxyIntent = new Intent(intent);
            // 实际跳转的页面Activity
            proxyIntent.setClassName("com.chinatsp.hookstartactivityamn", ThirdActivity.class.getCanonicalName());
            args[index] = proxyIntent;

            return method.invoke(mAMSProxy,args);
        }

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

Finalmente ejecute el método en el MainActivitymétodo :attachBaseContextAMSHookHelper.hookAMS

public class MainActivity extends AppCompatActivity {

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        try {
           AMSHookHelper.hookAMN();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.turn_to_other_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

Enganche el campo Instrumentación de ActivityThread

Dentro de la Actividad, hay un campo mInstrumentation e implementamos Hook reemplazándolo con una clase InstrumentationProxy personalizada.

También hay un campo mInstrumentation dentro de ActivityThread, ActivityThread llamará al método newActivity de mInstrumentation para generar un objeto de actividad y luego llamará al método callActivityOnCreate de mInstrumentation para iniciar la actividad. Por lo tanto, también podemos reemplazar esta mInstrumentation con un objeto proxy personalizado para implementar Hook.

public class InstrumentationInActivityThreadHookHelper {
    private static final String TAG = InstrumentationInActivityHookHelper.class.getSimpleName();
    public static void hookInstrumentationInActivity() throws ClassNotFoundException{
        // 先获取到当前的ActivityThread
        Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread","currentActivityThread");
        // 拿到原始的Instrumentation字段
        Instrumentation instrumentation = (Instrumentation) RefInvoke.getFieldObject(currentActivityThread,"mInstrumentation");
        // 创建代理对象
        InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
        // 替换ActivityThread中的instrumentation为代理对象
        RefInvoke.setFieldObject(currentActivityThread,"mInstrumentation",instrumentationProxy);
    }
}
public class InstrumentationProxy extends Instrumentation {
    private static final String TAG = InstrumentationProxy.class.getSimpleName();
    // ActivityThread中原始的Instrumentation对象, 保存起来
    private Instrumentation mBase;

    public InstrumentationProxy(Instrumentation mBase) {
        this.mBase = mBase;
    }

    public Activity newActivity(ClassLoader cl, String className,
                                Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        // 原来的intent
        ComponentName componentName = intent.getComponent();
        Log.d(TAG, "到此一游!cl:"+cl+" ,className="+className
                + " ,component_packageName="+componentName.getPackageName()
                + " ,component_className="+componentName.getClassName());

        if("com.chinatsp.hookstartactivityamn.SecondActivity".equals(componentName.getClassName())){
            // 如果是跳转到SecondActivity,就替换为ThirdActivity
            className = ThirdActivity.class.getCanonicalName();
            intent.setComponent(new ComponentName(componentName.getPackageName(),"com.chinatsp.hookstartactivityamn.ThirdActivity"));
        }
        return mBase.newActivity(cl, className, intent);
    }

    public void callActivityOnCreate(Activity activity, Bundle bundle) {
        Log.d(TAG, "到此一游! bundle = " + bundle);

//        Class[] p1 = {Activity.class, Bundle.class};
//        Object[] v1 = {activity, bundle};
//        RefInvoke.invokeInstanceMethod(
//                mBase, "callActivityOnCreate", p1, v1);
        mBase.callActivityOnCreate(activity,bundle);
    }
}

callActivityOnCreateEl método no ha cambiado, el principal cambio está en newActivityel método.

Iniciar una actividad no declarada en AndroidManifest

Recuerde el diagrama de secuencia del inicio de la Actividad.
Insertar descripción de la imagen aquí
En el proceso normal, si la APLICACIÓN inicia una Actividad que no está declarada en AndroidManifest, AMS generará una excepción de Actividad no encontrada.
La verificación de AMS de si la Actividad está declarada en el archivo AndroidManifest se completa en el segundo paso. La idea básica:

  1. En el primer paso, antes de enviar la información de la actividad para iniciar, reemplace esta actividad con una StubActivity declarada en el archivo AndroidManifest para evitar la verificación de AMS. Durante el proceso de reemplazo, la información de la actividad original debe almacenarse en un paquete.
  2. En el quinto paso, cuando AMS notifica a la aplicación que inicie StubActivity, reemplazamos StubActivity con la Actividad original. La información de la Actividad original se almacena en el Paquete y se puede retirar.

La implementación del código del primer paso son en realidad varios métodos en la primera mitad del proceso Hook startActivity en la sección anterior. Aquí elegimos el método HookATMS:

public class AMSHookHelper {
    private static final String TAG = AMSHookHelper.class.getSimpleName();
    public static final String EXTRA_TARGET_INTENT = "extra_target_intent";

    /**
     * Hook AMS
     * 主要完成的操作是: 把真正要启动的Activity临时替换为在AndroidManifest.xml中声明好的Activity,进而骗过AMS
     */
    public static void hookAMN() throws ClassNotFoundException,NoSuchMethodException{
        // 获取AMN的gDefault对象实例
        Object singletonObject = null;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityTaskManager","IActivityTaskManagerSingleton");
        }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityTaskManager","IActivityManagerSingleton");
        }else{
            // 获取AMN的gDefault单例,gDefault是final静态的
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative","gDefault");
        }

        // singletonObject 是一个 android.util.Singleton<T>对象; 我们取出这个单例里面的mInstance字段
        Object mInstance = RefInvoke.getFieldObject("android.util.Singleton", singletonObject, "mInstance");

        // 创建一个这个对象的代理对象MockClass1, 然后替换这个字段, 让我们的代理对象帮忙干活
        Class<?> activityManagerInterface;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
            activityManagerInterface = Class.forName("android.app.IActivityTaskManager");
        }else {
            activityManagerInterface = Class.forName("android.app.IActivityManager");
        }

        Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{ activityManagerInterface },
                new AMSProxy(mInstance));

        RefInvoke.setFieldObject("android.util.Singleton", singletonObject, "mInstance",proxy);
    }
}

public class AMSProxy implements InvocationHandler {
    private static final String TAG = AMSProxy.class.getSimpleName();
    private Object mActivityManager;

    public AMSProxy(Object mActivityManager) {
        this.mActivityManager = mActivityManager;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.d(TAG,method.getName());
        if("startActivity".equals(method.getName())){
            // 只拦截这个方法
            // 替换参数
            Intent raw;
            int index = 0;
            for(int i = 0;i<args.length;i++){
                if(args[i] instanceof Intent){
                    index = i;
                    break;
                }
            }
            raw = (Intent) args[index];
            Intent newIntent = new Intent();
            // 替身Activity的包名
            String stubPackage = raw.getComponent().getPackageName();
            // 把启动的Activity替换为StubActivity
            ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());
            newIntent.setComponent(componentName);

            // 把我们原始要启动的TargetActivity存起来
            newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT,raw);
            // 替换掉intent
            args[index] = newIntent;
            Log.d(TAG,"hook success");
            return method.invoke(mActivityManager,args);
        }
        return method.invoke(mActivityManager,args);
    }
}

El trabajo principal de AMSProxy es interceptar el método startActivity, extraer el Intent original de los parámetros, reemplazarlo con el newIntent que inicia StubActivity y guardar el Intent original en newIntent, que se usará más adelante para reemplazar StubActivity con TargetActivity.

El segundo paso del código de implementación es la segunda mitad del proceso Hook startActivity. Aquí, seleccione el objeto mInstrumentation de Hook ActivityThread:

public class AMSHookHelper {
    private static final String TAG = AMSHookHelper.class.getSimpleName();
    public static final String EXTRA_TARGET_INTENT = "extra_target_intent";
	...
    public static void hookInstrumentation(){
        // 先获取到当前的ActivityThread
        Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread","currentActivityThread");
        // 拿到原始的Instrumentation字段
        Instrumentation instrumentation = (Instrumentation) RefInvoke.getFieldObject(currentActivityThread,"mInstrumentation");
        // 创建代理对象
        InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
        // 替换ActivityThread中的instrumentation为代理对象
        RefInvoke.setFieldObject(currentActivityThread,"mInstrumentation",instrumentationProxy);
    }
}
public class InstrumentationProxy extends Instrumentation {
    private static final String TAG = InstrumentationProxy.class.getSimpleName();
    // ActivityThread中原始的Instrumentation对象, 保存起来
    private Instrumentation mBase;

    public InstrumentationProxy(Instrumentation mBase) {
        this.mBase = mBase;
    }

    public Activity newActivity(ClassLoader cl, String className,
                                Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        // 原来的intent
        ComponentName componentName = intent.getComponent();
        Log.d(TAG, "到此一游!cl:"+cl+" ,className="+className
                + " ,component_packageName="+componentName.getPackageName()
                + " ,component_className="+componentName.getClassName());
        // 获取存入的targetIntent,也就是本来要启动的intent
        Intent targetIntent = intent.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT);
        // 为空则说明要启动的是一个在AndroidManifest中声明过的Activity,走正常流程
        if(targetIntent==null){
            return mBase.newActivity(cl,className,intent);
        }
       String newClassName = targetIntent.getComponent().getClassName();
        return mBase.newActivity(cl, newClassName, targetIntent);
    }

    public void callActivityOnCreate(Activity activity, Bundle bundle) {
        Log.d(TAG, "到此一游! bundle = " + bundle);

//        Class[] p1 = {Activity.class, Bundle.class};
//        Object[] v1 = {activity, bundle};
//        RefInvoke.invokeInstanceMethod(
//                mBase, "callActivityOnCreate", p1, v1);
        mBase.callActivityOnCreate(activity,bundle);
    }

}

InstrumentationProxyEl trabajo principal es interceptar newActivityel método, porque callActivityOnCreateno hay ningún parámetro Intent en el método, sacar el Intent original del parámetro Intent y reemplazar los parámetros className e Intent con los TargetActivityobjetos className e Intent que originalmente queríamos iniciar.

Finalmente, ejecute la suma en el método MainActivityenattachBaseContextAMSHookHelper.hookAMN();AMSHookHelper.hookInstrumentation();

public class MainActivity extends AppCompatActivity {
    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        try{
            AMSHookHelper.hookAMN();
            AMSHookHelper.hookInstrumentation();
        }catch (ClassNotFoundException | NoSuchMethodException e){
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.turn_to_other_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

Código fuente

Supongo que te gusta

Origin blog.csdn.net/jxq1994/article/details/130765093
Recomendado
Clasificación