Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider

VirtualApp源码(完整工程) VirtualApp源码(完整工程),基于android studio (ndk,sdk)开发,实现安卓双开。 立即下载

Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider

Provider 注册

回顾前面,Activity 启动的时候会检查 Application 是否初始化,会调用 bindApplication,里面执行了安装 Provider 的方法:

private void installContentProviders(Context app, List<ProviderInfo> providers) {
        long origId = Binder.clearCallingIdentity();
        Object mainThread = VirtualCore.mainThread();
        try {
            for (ProviderInfo cpi : providers) {
                if (cpi.enabled) {
                    ActivityThread.installProvider(mainThread, app, cpi, null);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

这里很简单,调用 ActivityThread.installProvider() 注册就这么完成了。

但是,仔细一想事情没那么简单,按照这个逻辑 ContentProvider 是在 Application 启动的时候注册的,那么如果 Application 没有启动,那么自然就没有注册了,这样的话其他 App 怎么找到 Provider 呢?

Activity 是因为有在 VA 的 Menifest 里注册的 StubActivity,这样启动 StubActivity 自然就启动了在“:p(n)”进程。那么对应的,VA 用了 StubContentProvider 么?确实是有的。

但是请不要误会了,这个注册在 “p(n)”进程的 StubContentProvider(继承自 StubContentProvider 的 C1,C2,Cn……) 并不是 StubActivity 那样的为了给插件 Provider 占坑的 Stub 组件。

StubContentProvider 的真正目的是为了让 AMS 通过 system_process 带起 “p(n)”进程,然后 VAMS 用过远程调用 StubProvider.call() 回插件 IClient 的 IBinder 句柄给 VAMS 持有。这样 VAMS 就可以远程调用插件进程 “p(n)”中的方法了。

事实上在前面第二张 App 启动时就有说过相关内容,但是为了避免大家误解还是有必要重复一下。

public class StubContentProvider extends ContentProvider {

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public Bundle call(String method, String arg, Bundle extras) {
        if ("_VA_|_init_process_".equals(method)) {
            return initProcess(extras);
        }
        return null;
    }

    private Bundle initProcess(Bundle extras) {
        ConditionVariable lock = VirtualCore.get().getInitLock();
        if (lock != null) {
            lock.block();
        }
        IBinder token = BundleCompat.getBinder(extras,"_VA_|_binder_");
        int vuid = extras.getInt("_VA_|_vuid_");
        VClientImpl client = VClientImpl.get();
        client.initProcess(token, vuid);
        Bundle res = new Bundle();
        BundleCompat.putBinder(res, "_VA_|_client_", client.asBinder());
        res.putInt("_VA_|_pid_", Process.myPid());
        return res;
    }

getContentProvider

这里依然 Hook 了 getContentProvider 方法:

 static class GetContentProvider extends MethodProxy {

        @Override
        public String getMethodName() {
            return "getContentProvider";
        }

        @Override
        public Object call(Object who, Method method, Object... args) throws Throwable {
            int nameIdx = getProviderNameIndex();
            String name = (String) args[nameIdx];
            int userId = VUserHandle.myUserId();
            // 远程调用 VPMS 从 VPackage 拿到 ProviderInfo
            ProviderInfo info = VPackageManager.get().resolveContentProvider(name, 0, userId);
            // 拿不到说明目标 App 未启动
            if (info != null && info.enabled && isAppPkg(info.packageName)) {
                // 远程调用 VAMS,然后 VAMS 再通过 AMS 远程调用注册在插件进程的 StubContentProvider.call 初始化插件进程
                int targetVPid = VActivityManager.get().initProcess(info.packageName, info.processName, userId);
                if (targetVPid == -1) {
                    return null;
                }
                args[nameIdx] = VASettings.getStubAuthority(targetVPid);
                Object holder = method.invoke(who, args);
                if (holder == null) {
                    return null;
                }
                // ContentProviderHolder 有两个成员变量provider、connection,provider 是目标 Provider 的 IBinder 句柄。
                // connection 则是 callback
                if (BuildCompat.isOreo()) {
                    IInterface provider = ContentProviderHolderOreo.provider.get(holder);
                    if (provider != null) {
                        // 这里是重点,远程调用了 VAMS 的 acquireProviderClient
                        provider = VActivityManager.get().acquireProviderClient(userId, info);
                    }
                    ContentProviderHolderOreo.provider.set(holder, provider);
                    ContentProviderHolderOreo.info.set(holder, info);
                } else {
                    IInterface provider = IActivityManager.ContentProviderHolder.provider.get(holder);
                    if (provider != null) {
                        provider = VActivityManager.get().acquireProviderClient(userId, info);
                    }
                    IActivityManager.ContentProviderHolder.provider.set(holder, provider);
                    IActivityManager.ContentProviderHolder.info.set(holder, info);
                }
                return holder;
            }
            Object holder = method.invoke(who, args);
            if (holder != null) {
                if (BuildCompat.isOreo()) {
                    IInterface provider = ContentProviderHolderOreo.provider.get(holder);
                    info = ContentProviderHolderOreo.info.get(holder);
                    if (provider != null) {
                        provider = ProviderHook.createProxy(true, info.authority, provider);
                    }
                    ContentProviderHolderOreo.provider.set(holder, provider);
                } else {
                    IInterface provider = IActivityManager.ContentProviderHolder.provider.get(holder);
                    info = IActivityManager.ContentProviderHolder.info.get(holder);
                    if (provider != null) {
                        provider = ProviderHook.createProxy(true, info.authority, provider);
                    }
                    IActivityManager.ContentProviderHolder.provider.set(holder, provider);
                }
                return holder;
            }
            return null;
        }


        public int getProviderNameIndex() {
            return 1;
        }

        @Override
        public boolean isEnable() {
            return isAppProcess();
        }
    }

这里主要做了几件事情:

通过 VPMS 拿到解析好的目标 ProviderInfo,启动目标 Provider 所在的进程,怎么启动?远程调用 VAMS,然后 VAMS 再通过 AMS 远程调用注册在插件进程的 StubContentProvider.call 初始化插件进程。这时候 VAMS 就会持有目标插件进程的 IClient 句柄,以备后续调用。
准备 ContentProviderHolder 相关的事情,ContentProviderHolder 有两个成员变量provider、connection,provider 是目标 Provider 的 IBinder 句柄。
远程调用了 VAMS 的 acquireProviderClient(), 将任务抛给了远端的 VAMS。
下面就来看一下 VAMS.acquireProviderClient()

@Override
    public IBinder acquireProviderClient(int userId, ProviderInfo info) {
        ProcessRecord callerApp;
        synchronized (mPidsSelfLocked) {
            callerApp = findProcessLocked(VBinder.getCallingPid());
        }
        if (callerApp == null) {
            throw new SecurityException("Who are you?");
        }
        String processName = info.processName;
        ProcessRecord r;
        synchronized (this) {
            r = startProcessIfNeedLocked(processName, userId, info.packageName);
        }
        if (r != null && r.client.asBinder().isBinderAlive()) {
            try {
                return r.client.acquireProviderClient(info);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

可以看到调用了 r.client.acquireProviderClient(info);
r.client 就是前面 initProcess 的时候保存下来的插件进程的 IClient 句柄,那么等于远程调用到了插件的 VClientImpl.acquireProviderClient():

注意现在开始流程是在目标 Provider 的 Client App 进程中

@Override
    public IBinder acquireProviderClient(ProviderInfo info) {
        if (mTempLock != null) {
            mTempLock.block();
        }
        // 这里检查 Application 是否启动,注意注册 Provider 的逻辑也在里面
        if (!isBound()) {
            VClientImpl.get().bindApplication(info.packageName, info.processName);
        }
        // 准备 ContentProviderClient
        IInterface provider = null;
        String[] authorities = info.authority.split(";");
        String authority = authorities.length == 0 ? info.authority : authorities[0];
        ContentResolver resolver = VirtualCore.get().getContext().getContentResolver();
        ContentProviderClient client = null;
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                client = resolver.acquireUnstableContentProviderClient(authority);
            } else {
                client = resolver.acquireContentProviderClient(authority);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        if (client != null) {
            // 反射获取 provider
            provider = mirror.android.content.ContentProviderClient.mContentProvider.get(client);
            client.release();
        }
        return provider != null ? provider.asBinder() : null;
    }

这里终于看到了调用 bindApplication 的地方,如果 provider 尚未注册,那么这里将会注册 provider。

最终通过反射 android.content.ContentProviderClient.mContentProvider 获取到了目标 provider 的句柄,然后 provider 沿着目标 Client App 进程——> VAMS 进程 ———-> 回到了调用 Client App 进程中,整个获取 provider 的过程正式完成。

到此,四大组件的代理全部完成,本次分析也基本结束,下一章会写一些总结和补充内容。

转载:https://blog.youkuaiyun.com/ganyao939543405/article/details/76253562

VirtualApp10:应用分身,虚拟多开,模拟用户操作。欢迎各类朋友交流 VirtualApp Android 10.0适应 本项目纯属个人学习修改,有疑问请TG电报群(Telegram)联系: ://t.me/auto_aid(VX:kook_bug)可以过钉钉,企业微信虚拟检测,也有自动化操作模拟操作,按键等 2020.6.4 1,修改原始系统不能反射导致的诸多问题2,修改android版本兼容问题3,修改nullporint客户端应用程序的问题 2020.6.9 1,修复多版本abi兼容问题2,修复AppOps的问题3,修复各类兼容小问题 2020.7.4 1,修复包含WebView的界面会报错2,android 10更多应用稳定兼容3,可以运行当前主 立即下载

相关推荐

所有应用双开、无限多开分身、隐私保护功能 for Android .rar

所有应用双开、无限多开分身、隐私保护功能 for Android

Android 多用户 —— 从入门到应用分身

https://mp.weixin.qq.com/s/CeoijS42tkNLejkHw0waOw 从Android L(5.0)开始引入多用户API。先不论权限问题,我们可以创建多个用户。Android为什么引入这样的多用户概念? 有至少以下2个意图: 实现"访客模式"这样的通常PC端支持的多用户模式。其特点设备可以支持多个人在不同时段登陆, 独享设备。比如上班时登陆A用户,回家给小孩玩,登陆B用户. A/B用户数据完全独立, 互不影响。 单纯为了"应用多开",原生的...

求变,从思想开始 1万+

基于Android 12的虚拟应用分身与模拟开发设计源码

该项目是一款基于Android 12平台的虚拟应用分身与模拟开发设计源码,共包含1250个文件,涵盖多种编程语言。其中,Java文件占836个,XML文件105个,AIDL文件86个,C/C++文件共68个,PNG图片文件54个,其他文件包括Gradle、Gitignore、C/C++头文件和属性文件等。该源码支持应用分身、设备模拟、WiFi模拟、虚拟多开、钉钉打卡、企微打卡、插件开发、XP插件、模拟用户操作、Java和C++ Hook等功能,适用于需要进行复杂模拟开发或应用分身技术的开发者和团队。

【使用VirtualApp无root对手游进行修改】

使用VirtualApp对手游进行修改

qq_45071266的博客 2929

AUTOSAR/汽车行业

AUTOSAR架构及汽车产业链相关介绍

m0_54254513的博客 1万+

数字IC后端实战:OCC电路时钟树综合的5个关键技巧(附Synopsys工具配置)

本文深入探讨了数字IC后端设计中OCC电路时钟树综合的实战技巧。针对工具自动平衡导致的绕线拥塞和时序问题,文章提出了分段处理的核心思路,并详细阐述了通过物理隔离、分段长树策略以及Synopsys工具链(如ICC2、PrimeTime)的精准配置来优化function clk与occ clk路径的关键方法,旨在提升时序收敛性和可制造性。

jwt8token的博客 855

【Android 插件化】VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )

一、目前的 API 现状、 二、安装应用源码分析、 1、安装按钮执行的操作、 2、返回到 HomeActivity 执行的操作、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 1946

解决 PyInstaller 与 `pathlib` 包不兼容的问题

为了避免 PyInstaller 和pathlib包的兼容性问题,最简单的解决办法就是卸载不必要的pathlib包。只需在命令行中运行卸载命令,然后重新打包即可。

Winfredzhang的博客 3225

android studio 修改包名,双开一起安装

1  对着项目目录   main ---&gt;java   下面的包名  如 com.aaa.bbb  ,选中 com 或者aaa  或者bbb, 分别重命名,变成如com.ccc.ddd 2 修改项目build.gradle 里面的    defaultConfig  的    applicationId  改成com.ccc.ddd 3修改AndroidManifest.xml的pack...

jinjiashui的博客 1527

Win10更新后黑屏只剩鼠标?火绒误杀explorer.exe的5种自救方案(附详细步骤)

本文详细介绍了Win10更新后出现黑屏只剩鼠标的5种专业修复方案,重点解决火绒误杀explorer.exe的问题。从基础的任务管理器操作到安全模式下的系统修复,再到注册表编辑和系统还原,提供了全面的解决方案,帮助用户快速恢复系统正常运行。

weixin_29306571的博客 636

当勒索软件盯上虚拟化层:从CVE-2025-22225漏洞看ESXi安全加固要点

本文深度剖析了针对VMware ESXi虚拟化层的CVE-2025-22225等漏洞链的攻击原理,揭示了攻击者如何利用VMCI、HGFS组件从虚拟机逃逸至宿主机。文章从实战角度出发,系统性地提出了ESXi安全加固的核心策略,包括禁用非必要服务、强化主机配置、实施网络分段、建立实时监控与自动化补丁管理体系,为企业构建纵深防御体系提供了具体可行的操作指南。

terraform7cloud的博客 853

Android 双开沙箱 VirtualApp 源码分析(一)

最近发现了一个非常好的开源项目,基本实现了一个 Android 上的沙箱环境,不过应用场景最多的还是应用双开。 VA github: https://github.com/asLody/VirtualApp VA 的源码注释: https://github.com/ganyao114/VA_Doc 第一章主要是分析一下项目的整体结构。包结构android.content主要是 PackageP

ganyao939543405的博客 4万+

看懂特斯拉IMU底层逻辑,才懂农业机器人不可替代的核心技术壁垒

在自动驾驶与具身智能赛道,特斯拉彻底重构了IMU(惯性测量单元)的技术定位,打破行业“IMU仅为辅助补盲配件”的固有认知,将其从底盘附属硬件升级为纯视觉感知体系的物理核心基座。依托500–1000Hz超高刷新率、无外部依赖的惯性感知能力、端到端神经网络融合架构,特斯拉实现了“视觉感知环境、IMU定义运动”的技术闭环,摆脱对激光雷达、高精地图、卫星定位的强依赖。这套技术逻辑落地农业机器人赛道,不再是技术优化,而是规模化落地的生存刚需。城市自动驾驶属于结构化稳定场景,IMU仅作误差修正的锦上添花;

getusushu的博客 49

Android Sandbox(沙箱)开源工具介绍

android sandbox 通过利用开源工具动态分析、静态分析android的相关应用,发现应用的具体行为,从而进行判断android应用的危险程度,本文重点介绍几款常用工具。 1、droidbox是基于TaintDroid系统构建的Sandbox,通过hook系统api对apk程序进行监控,随着android SDK的不断更新,其也要随之适配。 droidbox:http://code.google.com/p/droidbox/ taintbox:http://appanalysis.org/

#7的博客 3939

开源模型应用落地-qwen模型小试-Qwen3-8B-快速体验(一)

阿里云推出国内首个集成“快思考”与“慢思考”能力的 Qwen3-8B 大语言模型,以80亿参数和 128K 上下文支持,重塑AI应用边界并助力开发者高效部署

以微薄之力,予他人些许温暖. 2万+

SAP 系统间 SSO 的真正价值,从 SM59 到可信 RFC 的安全设计

SAP S/4HANA系统间Single Sign-On(SSO)的核心价值在于集中管理通信身份与信任关系,而非仅减少密码输入。传统固定账号密码方式在系统规模扩大后会面临密码管理、审计追踪等挑战。通过SM59配置可信RFC目标系统,结合SSL证书或SAP Trusted System等强认证机制,能有效降低安全凭据暴露风险。SSO配置需配合授权对象、角色管理等建立清晰的信任边界,实现系统间安全通信的同时保持可审计性。这种方案特别适用于Fiori前端与S/4HANA后端等场景,通过可信RFC传递用户身份,避免

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1062

修改Android源码实现原生应用双开,应用多开

1. 准备 把某系统双开的两个app的信息进行对比 1.1 目录的对比 1.1.1 data目录对比 原应用: /data/user/0/com.luoyesiqiu.crackme/files 被复制的应用: /data/user/999/com.luoyesiqiu.crackme/files 1.1.2 apk所在目录对比 原应用: /data/app/com.luoyesiqi...

weixin_30735391的博客 4972

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【Android 插件化】VirtualApp 接入 ( 在 VirtualApp 工程下创建 Module | 添加依赖 | 启动 VirtualApp 插件引擎 )

一、在 VirtualApp 工程下创建 Module、 二、添加依赖、 三、启动 VirtualApp 插件引擎、 四、博客源码、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 2141

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

android 沙盒 开源,沙盒选项  |  Android 开源项目  |  Android Open Source Project

SandboxOptionspublicfinalclassSandboxOptionsextends Objectjava.lang.Object↳com.android.tradefed.sandbox.SandboxOptionsClass that can receive and provide options to aSummaryFieldspublicstaticfinalStrin...

weixin_32712183的博客 442

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

系统安全的关键安全技术 Android中的沙箱模型

沙箱模型是业界保证系统安全的关键安全技术,已经在浏览器等领域得到了成功应用。作为优秀的开源移动平台操作系统,Android也有相应的沙箱模型,本文将对其进行介绍。   一、沙箱模型原理简介   现实中的沙箱(SandBox),是一种儿童玩具,类如KFC中一个装满小球的容器,儿童可以在其中随意玩耍,起到保护儿童的作用。(也可以理解为一种安全环境。)   近年来,随着网络安全问题的日

Yangchen 的专栏 1061

Android 系统分身及应用多开实战 frida hook

常见的分身/多开 工具修改包名,早的时候可以,现在的应用,换包名功能就残缺不能好好用了… 修改 Framework(Android多用户机制。例:小米分身、访客模式等),国产手机中挺常见。但如果系统不提供该功能,自己很难开分身,本文就是告诉你怎么开了它。 通过虚拟化技术实现(例:360分身大师、LBE平行空间),这个应用索要权限非常高,别轻易使用非官方的该类软件。 以插件机制运行 目前很多人都...

gallonyin的博客 2万+

Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service

上一章:Android 双开沙箱 VirtualApp 源码分析(三)App 启动原生 Service 创建过程首先有必要了解一下原生 framework 对 Service 的创建,因为在 VA 中启动 Service 和 Activity 有很大的区别。首先入口 ContextWrapper.startService():@Override public ComponentName st

ganyao939543405的博客 7800

Android 双开沙箱 VirtualApp 源码分析(三)App 启动

上一章:Android 双开沙箱 VirtualApp 源码分析(二) 在这之前,我们还是要先了解一下 VA Client Framework 和 VAService 之间的通讯方式VAService 与通讯VAService首先,VAService 是指 VA 仿造 Android 原生 framework 层 Service 实现的一套副本,举例有 VActivityManagerServic

ganyao939543405的博客 1万+

VirtualAPP,va,virtualapp源码

zh739115743的专栏 1169

Android应用分身功能介绍

作者 Steven.zhu 1     背景 从去年下半年360奇酷手机发布后,国内的手机新品发布,都会提到应用分身、微信双开等类似概念,有一些第三方应用也以分身/双开作为亮点抢占市场,一时间分身类应用成为主流手机的一个必备功能。 在了解和比较了国内的分身类产品后,我将介绍目前市面上主流的分身类产品,及其使用场景、实现原理和存在的风险整理出来,分享给大家、也作为知识的总结积累

maetelibom的博客 5万+
上一篇: Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver
下一篇: lxl 大厅协议 -- [libcef部分]
CoderAldrich
博客等级 码龄12年 431粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值