Android implements starting an undeclared Activity

Implementation principle: First create a placeholder StubActivity. This Activity must add a statement to replace the target Activity. Then replace the original Callback in the Handler callback in ActivityThread with your own Callback, and modify it here to your own. To start the real Activity, finally start the StubActivity, put the target intent in the startup intent, and finally take out the target intent to replace the original intent.

Another question is, how to load activities in other apk or dex files? In fact, this is also very simple. First load the dex file, get a ClassLoader, and then use this class loader to replace the ClassLoader in LoadedApk. Then you can load the activity object in the dex file. Remember to change it back after use.

The last problem is that in order to be able to start unregistered activities anywhere, including normal activities, to achieve this goal, we need to intercept the activity startup process. We directly hook the startup object in the system's IActivityManager. That's it. There is an object called Singleton in IActivityManager (IActivityTaskManager after Android 10). There is a generic parameter in this object, which is the IActivityManager or IActivityTaskManager object. Replace it with our own proxy object to intercept all system AMS services. Calls, such as intercepting startActivity, etc.

The complete code is as follows:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    private ClassLoader mDexClassLoader;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        findViewById(R.id.bn_start1).setOnClickListener(this);
        findViewById(R.id.bn_start2).setOnClickListener(this);

        HookHelper.getInstance().init(this).setHookEnabled(true);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bn_start1: //启动本地Activity测试
                //方式一,主动启动
                Intent intent = new Intent(this, TargetActivity.class);
                intent.putExtra("arg1", "123456"); //携带参数测试
                HookHelper.startActivity(this, intent);

                //方式二,被动拦截
                HookHelper.getInstance().hookAMS(true);
                startActivity(new Intent(this, TargetActivity.class));
                break;
            case R.id.bn_start2: //启动远程Activity测试
                HookHelper.getInstance().hookAMS(false);
                startRemoteActivity();
                break;
        }
    }

    /* 启动远程Activity */
    private void startRemoteActivity() {
        try {
            ClassLoader loader = getDexClassLoader();
            //替换系统默认ClassLoader为加载后的ClassLoader
            HookHelper.getInstance().changeClassLoader(loader);
            //获取远程Activity
            Class RemoteActivity = loader.loadClass("com.zwxuf.remotemodule.RemoteActivity");
            Intent intent = new Intent(this, RemoteActivity);
            HookHelper.startActivity(this, intent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private ClassLoader getDexClassLoader() {
        if (mDexClassLoader == null) {
            try {
                ApplicationInfo info = getPackageManager().getApplicationInfo("com.zwxuf.remotemodule", 0);
                mDexClassLoader = new DexClassLoader(info.sourceDir, info.sourceDir + ".tmp", null, getClassLoader());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return mDexClassLoader;
    }
}
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.util.Log;

import androidx.annotation.NonNull;

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

public class HookHelper {

    private static final String TAG = HookHelper.class.getSimpleName();

    public static HookHelper mInstance;

    private Object mOrigCallback;
    private ClassLoader mOrigClassLoader;
    private Object mLoadedApk;
    private Handler mHandler = new Handler(Looper.getMainLooper());
    private boolean mHookAMSEnabled;
    private Object mOrigAMS;

    public static HookHelper getInstance() {
        if (mInstance == null) {
            mInstance = new HookHelper();
        }
        return mInstance;
    }

    private HookHelper() {
    }

    public HookHelper init(Context context) {
        try {
            //保存LoadedApk对象
            if (mLoadedApk == null) {
                Field mLoadedApkField = Application.class.getDeclaredField("mLoadedApk");
                mLoadedApkField.setAccessible(true);
                mLoadedApk = mLoadedApkField.get((Application) context.getApplicationContext());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return this;
    }

    /**
     * 启用禁用hook
     *
     * @param enabled
     */
    public void setHookEnabled(boolean enabled) {
        if (enabled == (mOrigCallback != null)) {
            return;
        }
        try {
            Class<?> ActivityThread = Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThreadFiled = ActivityThread.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThreadFiled.setAccessible(true);
            Object sCurrentActivityThread = sCurrentActivityThreadFiled.get(null);
            Field mHField = ActivityThread.getDeclaredField("mH");
            mHField.setAccessible(true);
            Handler mH = (Handler) mHField.get(sCurrentActivityThread);
            Field mCallbackField = Handler.class.getDeclaredField("mCallback");
            mCallbackField.setAccessible(true);
            Object mCallback = mCallbackField.get(mH);
            if (enabled) {
                mOrigCallback = mCallback;
                mCallbackField.set(mH, new ProxyCallback());
            } else {
                mCallbackField.set(mH, mOrigCallback);
                mOrigCallback = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private class ProxyCallback implements Handler.Callback {

        @Override
        public boolean handleMessage(@NonNull Message msg) {
            if (msg.what == 100) { // LAUNCH_ACTIVITY
                try {
                    Object record = msg.obj;
                    Field intentField = record.getClass().getDeclaredField("intent");
                    intentField.setAccessible(true);
                    Intent intent = (Intent) intentField.get(record);
                    if (intent != null) restoreTargetIntent(intent);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        restoreClassLoader(); //恢复类加载器
                    }
                }, 500);
            } else if (msg.what == 159) { // LAUNCH_ACTIVITY
                try {
                    Object record = msg.obj;
                    Field fCallbacks = record.getClass().getDeclaredField("mActivityCallbacks");
                    fCallbacks.setAccessible(true);
                    List<?> lists = (List) fCallbacks.get(record);
                    if (lists != null) {
                        for (int i = 0; i < lists.size(); i++) {
                            Object item = lists.get(i);
                            Class itemClazz = item.getClass();
                            try {
                                Field mIntent = itemClazz.getDeclaredField("mIntent");
                                mIntent.setAccessible(true);
                                Intent intent = (Intent) mIntent.get(item);
                                if (intent != null) restoreTargetIntent(intent);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        restoreClassLoader(); //恢复类加载器
                    }
                }, 500);
            }
            return false;
        }
    }

    /**
     * 恢复目标intent
     *
     * @param intent
     */
    private static void restoreTargetIntent(Intent intent) {
        Intent targetIntent = intent.getParcelableExtra("targetIntent");
        if (targetIntent != null) {
            Parcel parcel = Parcel.obtain();
            targetIntent.writeToParcel(parcel, 0);
            parcel.setDataPosition(0);
            intent.readFromParcel(parcel);
            parcel.recycle();
        }
    }

    /**
     * 启动未注册的Activity
     *
     * @param context
     * @param intent
     */
    public static void startActivity(Context context, Intent intent) {
        Intent baseIntent = new Intent(context, StubActivity.class); //占位intent
        baseIntent.putExtra("targetIntent", intent); //存储目标intent
        context.startActivity(baseIntent);
    }

    public void changeClassLoader(ClassLoader loader) {
        ClassLoader mOldClassLoader = replaceClassLoader(loader);
        if (mOrigClassLoader == null) {
            mOrigClassLoader = mOldClassLoader;
        }
    }

    public void restoreClassLoader() {
        if (mOrigClassLoader != null) {
            replaceClassLoader(mOrigClassLoader);
            mOrigClassLoader = null;
        }
    }

    private ClassLoader replaceClassLoader(ClassLoader loader) {
        try {
            Field mClassLoaderField = mLoadedApk.getClass().getDeclaredField("mClassLoader");
            mClassLoaderField.setAccessible(true);
            ClassLoader oldClassLoader = (ClassLoader) mClassLoaderField.get(mLoadedApk);
            mClassLoaderField.set(mLoadedApk, loader);
            Log.i(TAG, "replaceClassLoader success");
            return oldClassLoader;
        } catch (Exception e) {
            Log.e(TAG, "replaceClassLoader:" + e.toString());
        }
        return null;
    }

    /**
     * 拦截AMS服务
     *
     * @param enabled
     */
    public void hookAMS(boolean enabled) {
        if (enabled == mHookAMSEnabled) return;
        try {
            Class parentClass = Class.forName("android.util.Singleton");
            Field mField = parentClass.getDeclaredField("mInstance");
            mField.setAccessible(true);
            Class hookClass;
            Object mSingleton;
            if (Build.VERSION.SDK_INT < 29) {
                hookClass = Class.forName("android.app.IActivityManager");
                mSingleton = getAMSingleton();
            } else {
                //android10以上
                hookClass = Class.forName("android.app.IActivityTaskManager");
                mSingleton = getATMSingleton();
            }
            if (enabled) {
                mOrigAMS = mField.get(mSingleton);
                InvocationHandlerProxy handlerProxy = new InvocationHandlerProxy(mOrigAMS);
                Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                        new Class<?>[]{hookClass},
                        handlerProxy);
                mField.set(mSingleton, proxy);
            }else {
                mField.set(mSingleton, mOrigAMS);
            }
            mHookAMSEnabled = enabled;
            Log.i(TAG, "hookAMS:" + enabled);
        } catch (Exception e) {
            Log.e(TAG, "hookAMS:" + e.toString());
        }
    }

    /**
     * 代理对象处理器
     */
    public class InvocationHandlerProxy implements InvocationHandler {

        Object original; //原始对象

        public InvocationHandlerProxy(Object original) {
            this.original = original;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("startActivity")) {
                if (args != null && args.length > 0) {
                    int index = -1;
                    for (int i = 0; i < args.length; i++) {
                        if (args[i] instanceof Intent) {
                            index = i;
                            break;
                        }
                    }
                    if (index != -1) {
                        Intent targetIntent = (Intent) args[index];
                        Intent intent = new Intent();
                        intent.putExtra("targetIntent", targetIntent);
                        intent.setClassName("com.zwxuf.mydemo", StubActivity.class.getName());
                        args[index] = intent;
                        Log.i(TAG, "replace intent success");
                    }
                }
            }
            return method.invoke(original, args);
        }
    }

    private Object getAMSingleton() {
        try {
            Class<?> CActivityManager = Class.forName("android.app.ActivityManager");
            Field FSingleton = CActivityManager.getDeclaredField("IActivityManagerSingleton");
            FSingleton.setAccessible(true);
            return FSingleton.get(null);
        } catch (Exception e) {
            Log.e(TAG, "getSingleton:" + e.toString());
        }
        return null;
    }

    private static Object getATMSingleton() {
        try {
            Class<?> CActivityTaskManager = Class.forName("android.app.ActivityTaskManager");
            Field FSingleton = CActivityTaskManager.getDeclaredField("IActivityTaskManagerSingleton");
            FSingleton.setAccessible(true);
            return FSingleton.get(null);
        } catch (Exception e) {
            Log.e(TAG, "getSingleton:" + e.toString());
        }
        return null;
    }

}
public class TargetActivity extends AppCompatActivity {


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_target);

        Intent intent = getIntent();
        String value = intent.getStringExtra("arg1");
        Log.i("WALX", String.valueOf(value));
    }
}

 

 

Guess you like

Origin blog.csdn.net/zzmzzff/article/details/131485356