android实现启动未声明的Activity

news2024/11/18 18:19:00

实现原理:首先创建一个占位StubActivity,这个Activity必须要添加声明,用来代替目标的Activity,然后在ActivityThread中的Handler回调中替换掉原来的Callback,改为自己的Callback,并在此修改成自己要启动的真正的Activity,最后启动StubActivity,在启动intent里面放进目标intent,等最后取出目标intent替换原来的intent即可。

另一个问题是,如何加载其它apk或dex文件中的activity?其实这个也很简单,先加载dex文件,得到一个ClassLoader,然后用这个类加载器替换LoadedApk中的ClassLoader,然后就能加载dex文件中的activity对象,用完记着换回去。

最后一个问题是,要实现在任何地方都能启动未注册的Activity,当然也包括正常的Activity,要实现的这样的目的,就需要拦截activity启动过程,我们直接hook掉系统的IActivityManager中的启动对象即可,IActivityManager(Android10以后是IActivityTaskManager)中有一个叫做Singleton的对象,这个对象里有个泛型参数,放的是IActivityManager或者IActivityTaskManager对象,替换成我们自己的代理对象,即可拦截系统AMS服务所有调用,比如拦截startActivity等。

完整代码如下:

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

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/706796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Appium自动化测试 —— 断言

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

六.函数的定义与调用

目录 一.内置函数&#xff1a; 二.标准库函数 三、自定义函数 1、函数定义 2、函数调用 3、函数参数 值传递&#xff1a; 引用传值&#xff1a; 4、函数返回多个值 5、defer语句 6、init函数&#xff1a; 一.内置函数&#xff1a; Go 语言拥有一些不需要进行导入操…

实现java代码加密,jar\war加密

Springboot 项目代码加密&#xff0c;对你的代码进行加密&#xff0c;市面工具无法实现反编译。加密 Class 文件中每个方法的 Java 字节码&#xff0c;运行时在 JVM实现动态解密。 支持的部署环境Windows/Linux/macOS支持的框架SpringMVC、SpringBoot、Maven场景java加固&…

JAVA-编程基础-08-try-catch性能探究

Lsion <dreamlison163.com>, v1.0.0, 2023.04.01 JAVA-编程基础-08-try-catch性能探究 文章目录 JAVA-编程基础-08-try-catch性能探究try-catch会影响性能吗&#xff1f; try-catch会影响性能吗&#xff1f; 在 for 循环里面搞了个 try-catch&#xff0c;不知道try-cat…

Java的SPI

JavaSPI&#xff0c;全称是ServiceProviderInterface。 它是一种基于接口的动态扩展机制&#xff0c;相当于Java里面提供了一套接口。然后第三方可以实现这个接口来完成功能的扩展和实现。 举个简单的例子。 在Java的SDK里面&#xff0c;提供了一个数据库驱动的接口java.sql.Dr…

ModaHub魔搭社区:向量数据库Milvus性能优化问题(一)

目录 性能优化问题 为什么重启 Milvus 服务端之后&#xff0c;第一次搜索时间非常长&#xff1f; 为什么搜索的速度非常慢&#xff1f; 如何进行性能调优&#xff1f; 应如何设置 IVF 索引的 nlist 和 nprobe 参数&#xff1f; 性能优化问题 为什么重启 Milvus 服务端之后…

如何创建你的第一个西门子200PLC程序

更多关于西门子S7-200PLC内容请查看&#xff1a;西门子200系列PLC学习课程大纲 创建西门子200PLC程序分五步&#xff1a;1.打开Micro/WIN软件&#xff1b;2.新建工程&#xff1b;3.打开程序编辑器&#xff1b;4.输入程序指令&#xff1b;5.保存程序。 我们以下图程序为例讲解西…

Unable to reload Maven project

原因就是IDEA的版本与Maven的版本冲突。 IDEA的日志报错如下&#xff1a; 我当时IDEA是2020版&#xff0c;Maven是3.8.5. 后来把Maven换成3.6.3版本就可以了。

途乐证券|沪深两市震荡整理 机器人概念成市场新热点

周四&#xff0c;A股三大指数维持窄幅震荡整理走势&#xff0c;沪深两市成交额仍维持在9000亿元下方&#xff0c;北向资金净卖出超70亿元。盘面上&#xff0c;减速器、机器人概念持续爆发&#xff0c;煤炭、电力板块领跌。 香港途乐证券有限公司&#xff08;191883.com&#xf…

【每日一题】Leetcode - 剑指 Offer 43. 1~n 整数中 1 出现的次数

题目 Leetcode - 剑指 Offer 43. 1&#xff5e;n 整数中 1 出现的次数 解题思路 分解数字中的每一位&#xff0c;判断记录 结果 class Solution {public int countDigitOne(int n) {int count 0;for (int i 1; i < n; i) {int localI i;while (localI / 10 ! 0) {in…

Leetcode-每日一题【143.重排链表】

题目 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; 请将其重新排列后变为&#xff1a; 不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5]输出&#xff1a;[1,5,2,…

Ubuntu 23.10 现在由Linux内核6.3提供支持

对于那些希望在Ubuntu上尝试最新的Linux 6.3内核系列的人来说&#xff0c;今天有一个好消息&#xff0c;因为即将发布的Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;已经重新基于Linux内核6.3。 Ubuntu 23.10的开发工作于4月底开始&#xff0c;基于目前的临时版本Ubu…

光模块封装类型有哪些?光模块“皮肤”大揭秘

什么是光模块&#xff1f; 光模块&#xff08;Optical Transceiver&#xff09;全称为光收发一体模块&#xff0c;它是光通信中的核心器件&#xff0c;能够完成信号的光-电/电-光转换过程&#xff0c;它由光电子器件、功能电路和光接口等部件组成&#xff0c;其中的光电子器件…

【STL】iterator adapters_反向迭代器的实现

放在专栏【C知识总结】&#xff0c;会持续更新&#xff0c;期待支持 本章相关文章&#xff1a; 【STL】容器适配器 【STL】list的模拟实现 【STL】vector的模拟实现 1、反向迭代器介绍 1.1、前言 在前文中我们已经讲过STL中的适配器概念&#xff0c;即在底层将一个类的接口转…

centos7卸载自带jdk(openjdk)

前提&#xff1a;root模式 1、查看自带jdk 首先确定我们有自带的jdk&#xff0c;然后搜索jdk java -version rpm -qa | grep jdk 2、使用如下命令卸载openjdk rpm -e --nodeps [name] 复制带有openjdk的那两行&#xff0c;有的人可能有多行。一条条执行。 最后java -ver…

win10/11环境解决fastboot模式看不到设备

问题描述&#xff1a; C:\Users\good>adb devices * daemon not running. starting it now on port 5037 * * daemon started successfully * List of devices attached ? device C:\Users\good>fastboot devices fastboot devices 不是内部或外部命令&#xff0…

茶油生产加工MES质量溯源平台源码(spring boot+mybatis+easyui+mysql+h5)

一、生产加工MES&#xff08;Manufacturing Execution System&#xff0c;简称MES&#xff09;是一种面向车间的生产过程管理与实时信息系统。它主要负责监控生产过程&#xff0c;管理生产资源&#xff0c;优化生产流程&#xff0c;提高生产效率和质量。MES系统需要与ERP系统、…

【阻塞队列】阻塞队列DelayedWorkQueue源码详解

目录 一、前言 二、ScheduledThreadPoolExecutor线程池 三、DelayedWorkQueue延迟阻塞队列 四、工作原理 五、源码分析 5.1 定义 5.2 成员属性 5.3 构造函数 5.4 入队方法 5.4.1 offer添加元素 5.4.2 扩容grow() 5.4.3 向上堆化siftUp 5.5 出队方法 5.5.1 take()…

LangChain 基于 ChatGPT 构建本地知识库问答应用

一、使用本地知识库构建问答应用 上篇文章基于 LangChain 的Prompts 提示管理构建特定领域模型&#xff0c;如果看过应该可以感觉出来 ChatGPT 还是非常强大的&#xff0c;但是对于一些特有领域的内容让 GPT 回答的话还是有些吃力的&#xff0c;比如让 ChatGPT 介绍下什么是 L…

二重积分的解题技巧

计算方法 本节内容一般都应该先画图再思考后续内容较为直观 基本口诀是&#xff1a;后积先定限&#xff0c;限内画条线&#xff0c;先交写下限&#xff0c;后交写上限&#xff08;且下限必须小于上限&#xff09; 结合下图进行解释&#xff0c;后积先定限&#xff0c;对于X-型来…