如何实现不安装xpoedinstall激活xposed模块

简介: xpoedinstall及安装器,它的作用有几个:安装xposed框架、激活xposed模块、搜索模块等等如果我们已经安装了xposed框架,或者说我们自己二次开发了这个框架,通过手动的方式安装。然后需要使用我们自己的xposed模块,这时xposedinstall的作用就只剩激活模块了,那么我们是否可以摆脱它来激活模块?

前言


xpoedinstall及安装器,它的作用有几个:安装xposed框架、激活xposed模块、搜索模块等等

如果我们已经安装了xposed框架,或者说我们自己二次开发了这个框架,通过手动的方式安装。然后需要使用我们自己的xposed模块,这时xposedinstall的作用就只剩激活模块了,那么我们是否可以摆脱它来激活模块?


源码


先来看看它如何激活模块的

在它的源码中有一个ModuleUtil类,其中


public void setModuleEnabled(String packageName, boolean enabled) {
    if (enabled)
        mPref.edit().putInt(packageName, 1).apply();
    else
        mPref.edit().remove(packageName).apply();
}
复制代码


可以看到将激活的模块的包名存到了sharepreference中,然后还有另外一个类


public synchronized void updateModulesList(boolean showToast) {
    try {
        Log.i(XposedApp.TAG, "updating modules.list");
        int installedXposedVersion = XposedApp.getInstalledXposedVersion();
        PrintWriter modulesList = new PrintWriter(MODULES_LIST_FILE);
        PrintWriter enabledModulesList = new PrintWriter(XposedApp.ENABLED_MODULES_LIST_FILE);
        List<InstalledModule> enabledModules = getEnabledModules();
        for (InstalledModule module : enabledModules) {
            if (module.minVersion > installedXposedVersion || module.minVersion < MIN_MODULE_VERSION)
                continue;
            modulesList.println(module.app.sourceDir);
            try {
                String installer = mPm.getInstallerPackageName(module.app.packageName);
                if (!PLAY_STORE_PACKAGE.equals(installer)) {
                    enabledModulesList.println(module.app.packageName);
                }
            } catch (IllegalArgumentException ignored) {
                // In rare cases, the package might not be installed anymore at this point,
                // so the PackageManager can't return its installer package name.
            }
        }
        modulesList.close();
        enabledModulesList.close();
        FileUtils.setPermissions(MODULES_LIST_FILE, 00664, -1, -1);
        FileUtils.setPermissions(XposedApp.ENABLED_MODULES_LIST_FILE, 00664, -1, -1);
        if (showToast)
            showToast(R.string.xposed_module_list_updated);
    } catch (IOException e) {
        Log.e(XposedApp.TAG, "cannot write " + MODULES_LIST_FILE, e);
        Toast.makeText(mApp, "cannot write " + MODULES_LIST_FILE + e, Toast.LENGTH_SHORT).show();
    }
}
复制代码


这里有一个getEnableModules函数,如下:


public List<InstalledModule> getEnabledModules() {
    LinkedList<InstalledModule> result = new LinkedList<InstalledModule>();
    for (String packageName : mPref.getAll().keySet()) {
        InstalledModule module = getModule(packageName);
        if (module != null)
            result.add(module);
        else
            setModuleEnabled(packageName, false);
    }
    return result;
}
复制代码


可以看到这个函数正是从刚才sharepreference中读取激活的模块包名

然后回到updateModulesList中,可以看到将这些激活的模块的sourceDir保存到MODULES_LIST_FILE这个文件中,即:


private static final String MODULES_LIST_FILE = XposedApp.BASE_DIR + "conf/modules.list";
复制代码


以上就是xposedinstaller激活模块的流程,可以看到就是将要激活的模块的apk地址保存到一个modules.list文件中。看来真正激活使用模块是在xposedbridge中,在它的main函数中(XposedBridge类)


protected static void main(String[] args) {
   // Initialize the Xposed framework and modules
   try {
      if (!hadInitErrors()) {
         initXResources();
         SELinuxHelper.initOnce();
         SELinuxHelper.initForProcess(null);
         runtime = getRuntime();
         XPOSED_BRIDGE_VERSION = getXposedVersion();
         if (isZygote) {
            XposedInit.hookResources();
            XposedInit.initForZygote();
         }
         XposedInit.loadModules();
      } else {
         Log.e(TAG, "Not initializing Xposed because of previous errors");
      }
   } catch (Throwable t) {
      Log.e(TAG, "Errors during Xposed initialization", t);
      disableHooks = true;
   }
   // Call the original startup code
   if (isZygote) {
      ZygoteInit.main(args);
   } else {
      RuntimeInit.main(args);
   }
}
复制代码


主要是XposedInit.loadModules()这句,这个函数的代码:


static void loadModules() throws IOException {
   final String filename = BASE_DIR + "conf/modules.list";
   BaseService service = SELinuxHelper.getAppDataFileService();
   if (!service.checkFileExists(filename)) {
      Log.e(TAG, "Cannot load any modules because " + filename + " was not found");
      return;
   }
   ClassLoader topClassLoader = XposedBridge.BOOTCLASSLOADER;
   ClassLoader parent;
   while ((parent = topClassLoader.getParent()) != null) {
      topClassLoader = parent;
   }
   InputStream stream = service.getFileInputStream(filename);
   BufferedReader apks = new BufferedReader(new InputStreamReader(stream));
   String apk;
   while ((apk = apks.readLine()) != null) {
      loadModule(apk, topClassLoader);
   }
   apks.close();
}
复制代码


这里可以看到从modules.list文件中读取已激活模块的apk地址,然后执行loadModule将其加载进来,loadModule中会有一些判断,比如说是否有assets/xposed_init文件等等,如果都正常就会将这个apk的class加载进内存中,后续就会调用他们,实际上就是将模块的代码注入到xposedbridge中来使用。这部分代码就不细说了。


(实际上,真正hook都是在xposedbridge中的,如果我们直接在这里写hook代码也是可以的,只是这样就无法灵活修改了。所以我们将hook代码写在apk中,然后在这里将这些类加载进来,然后xposedbridge再调用它的hookLoadPackage之类的代码就可以了)


结论


所以通过上面我们可以知道,我们完全可以抛开xposedinstaller,当我们安装app并打开时,可以自己创建或修改modules.list文件,将自己app加进入就可以了。当然还要重启手机,从上面可以看到,因为加载模块的代码是在main函数中执行的,所以只有重启手机才会再次执行。


当然这个默认的modules.list我们可能需要root权限才能读写,但是如果我们自己二次开发xposed,可以在xposedbridge中将这个文件路径改成sd卡下可以任意读写的路径,这样我们的app就可以随意进行改写了。


或者我们提前准好这个文件手动放到手机中,文件内容是一个未安装的apk的路径,比如/sdcard/123.apk,这样不需要安装应该就可以直接使用。这个我没有测试,效果待验证。


目录
相关文章
|
存储 缓存 Rust
Polars (最强Pandas平替)
Polars (最强Pandas平替)
1174 1
|
机器学习/深度学习 人工智能 达摩院
如何打造真人化高表现力的语音合成系统
音合成技术作为人机交互的重要环节,终极目标即达到媲美真人的合成效果。高表现力语音合成逐渐成为未来的趋势。高表现力语音有三个显著的特点:韵律自然、情感风格丰富和音质清澈。 需要认识到的是当下的技术水平在韵律自然表示、情感风格丰富度上和真人之间还存在着较大的、人耳容易分辨的差距。 因此,我们针对这三个特点,进行算法上的探索,形成达摩院第五代语音合成技术——基于韵律建模的 SAM-BERT、情感语音合成 Emotion TTS 和高清语音合成 HiFi-TTS 的 Expressive-TTS。
1781 0
如何打造真人化高表现力的语音合成系统
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
561 6
|
9月前
|
前端开发 JavaScript 网络架构
约定式路由生成神器:vite-plugin-pages
这篇文章介绍了如何使用vite-plugin-pages插件在Vite项目中实现约定式路由自动生成,包括搭建工程、安装插件、配置vite.config.ts文件以及创建和挂载路由。
681 0
|
11月前
|
运维 数据挖掘 API
官方api和第三方api有什么区别
官方API与第三方API是电商商家常用的两种接口类型。官方API由电商平台提供,功能全面、集成度高、安全性强且稳定性好,适合处理核心业务;而第三方API由外部服务商提供,专注特定功能如支付、物流等,灵活性和扩展性更强,但安全性和稳定性参差不齐。商家应根据自身需求、预算及技术能力选择合适的API,确保数据安全与合规性,同时优化运营流程、提升用户体验并拓展业务功能,在竞争中占据优势。
|
人工智能 算法 数据挖掘
AAAI 2025| S5VH: 基于选择性状态空间的高效自监督视频哈希
随着短视频、流媒体平台的爆发式增长,如何高效地索引和检索视频数据成为计算机视觉和多媒体领域的重要研究问题。视频哈希(Video Hashing) 是一种通过学习紧凑的二进制编码来高效索引和检索视频的技术,其核心目标是使哈希码的汉明距离(Hamming Distance)能够准确地反映视频之间的语义相似性。
343 18
|
机器学习/深度学习
《揭秘机器学习中的交叉验证:模型评估的基石》
交叉验证是机器学习中评估模型性能的关键技术,旨在提高模型的可靠性和泛化能力。通过将数据集划分为多个子集,交叉验证有效抵御过拟合风险,最大化数据利用效率,并精准筛选最优模型。常见的方法包括K折交叉验证、留一交叉验证、分层交叉验证和嵌套交叉验证,每种方法适用于不同场景,确保模型在实际应用中表现优异。
1629 1
|
存储 并行计算 安全
超级计算机:探索宇宙奥秘与解决复杂问题的利器
【9月更文挑战第14天】超级计算机作为计算科学的巅峰之作,以其卓越的计算能力和庞大的数据存储能力,助力科学家深入探索宇宙奥秘并解决复杂问题。本文介绍了超级计算机的定义、发展历程及关键技术,并详细探讨了其在宇宙学模拟、生物学研究、气候研究和工程技术等领域的广泛应用。尽管面临能耗、数据传输等挑战,但其未来前景光明,有望在量子计算等前沿技术推动下继续引领科技创新。
|
存储 编解码 网络协议
FFmpeg学习笔记(三):FFmpeg和FFplay参数介绍
这篇文章是关于FFmpeg和FFplay参数的介绍,包括如何使用这些参数进行视频流的捕获、处理和播放。
593 0
|
Ubuntu 数据安全/隐私保护
ubuntu默认root密码
安装完Ubuntu后忽然意识到没有设置root密码,不知道密码自然就无法进入根用户下。到网上搜了一下,原来是这麽回事。Ubuntu的默认root密码是随机的,即每次开机都有一个新的root密码。我们可以在终端输入命令 sudo passwd,然后输入当前用户的密码,enter,终端会提示我们输入新的密码并确认,此时的密码就是root新密码。
19849 1