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

【VirtualAPP 双开系列08】如何实现多开 - UID 目录: 1. Android UID 介绍 2.多开基本原理 3. 常用的多开方案 4. VirtualAPP 如何实现多开 1. Android UID 介绍 Android 系统中修改了 Linux 的 UID 的含义。由于 Android 是单用户系统,不需要支持多用户登陆,因此传统的 UID 系统就失去了原来的意义。Android 的开发者巧妙地修改了 UID 的含义:每个 APP 对应一个 UID—用 UID 对应用程序进行管理。 Android 中查看 UID 的方法: .. 阅读详情

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

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

包结构

在这里插入图片描述

android.content

主要是 PackageParser,该类型覆盖了系统的隐藏类 android.content.pm.PackageParser

com.lody.virtual

这里就是框架的主体代码了

client

运行在客户端的代码,指加载到 VA 中的子程序在被 VA 代理(hook)之后,所运行的代码

hook

hook java 层函数的一些代码

ipc

伪造的一些 framework 层的 IPC 服务类,诸如 ActivityManager,ServiceManager 等等,使用 VXXXXX 命名。hook 之后,子程序就会运行到这里而·不是原来真正的系统 framework 代码。

stub

系统四大组件的插桩,如提前注册在 Menifest 里的几十个 StubActivity。

remote

一些可序列化 Model,继承于 Parcelable

server

server 端代码,VA 伪造了一套 framework 层系统 service 的代码,他在一个独立的服务中记录管理组件的各种 Recorder,其逻辑其实与系统原生的相近,通过 Binder 与 client 端的 ipc 包中的 VXXXXManager 通讯。诸如 AMS(VAMS),PMS(VPMS)。

mirror

系统 framework 的镜像,实现了与 framework 层相对应的结构,封装了反射获取系统隐藏字段和方法的,便于直接调用获取或者赋值以及调用方法。

Base 一些基础措施的封装

Mirror framework 层镜像

成员变量 Field 映射

根据成员变量类型,映射类型分为几个基本数据类型和对象引用类型。下面就以对象引用类型为例,其他类型类似。类型 RefObject 代表映射 framework 层同名的泛型类型成员变量。

// Field 映射
@SuppressWarnings("unchecked")
public class RefObject<T> {

    // framework 层对应的 Field
    private Field field;

    public RefObject(Class<?> cls, Field field) throws NoSuchFieldException {
        // 获取 framework 中同名字段的 field
        this.field = cls.getDeclaredField(field.getName());
        this.field.setAccessible(true);
    }

    // 获取变量值
    public T get(Object object) {
        try {
            return (T) this.field.get(object);
        } catch (Exception e) {
            return null;
        }
    }
    // 赋值
    public void set(Object obj, T value) {
        try {
            this.field.set(obj, value);
        } catch (Exception e) {
            //Ignore
        }
    }
}

以 framework 层中隐藏类 LoadedApk 来说:

public class LoadedApk {
    public static Class Class = RefClass.load(LoadedApk.class, "android.app.LoadedApk");
    public static RefObject<ApplicationInfo> mApplicationInfo;
    @MethodParams({boolean.class, Instrumentation.class})
    public static RefMethod<Application> makeApplication;

mApplicationInfo 就是 LoadedApk 中私有 ApplicationInfo 类型字段的同名映射。

当你引用 LoadedApk Mirror 类时,类加载器加载该类,执行静态成员的初始化表达式 RefClass.load(LoadedApk.class, “android.app.LoadedApk”); Mirror 类中的同名字段将被反射赋值。

下面看一下 RefClass.load() 函数

public static Class load(Class mappingClass, Class<?> realClass) {
        // 获取 Mirror 类的所有字段
        Field[] fields = mappingClass.getDeclaredFields();
        for (Field field : fields) {
            try {
                // 必须是 static 变量
                if (Modifier.isStatic(field.getModifiers())) {
                    // 从预设的 Map 中找到 RefXXXX 的构造器
                    Constructor<?> constructor = REF_TYPES.get(field.getType());
                    if (constructor != null) {
                        // 赋值
                        field.set(null, constructor.newInstance(realClass, field));
                    }
                }
            }
            catch (Exception e) {
                // Ignore
            }
        }
        return realClass;
    }

最后调用的话 MirrorClass.mirrorField.get(instance),MirrorClass.mirrorField.set(instance),就相当于直接调用 framework 层的隐藏字段了。

Method 映射

其实与 Field 类似,只是 Field 主要是一个 call 即调用方法。

@MethodParams({File.class, int.class})
public static RefMethod<PackageParser.Package> parsePackage;

表现在 Mirror 类型中也是一个字段,不过要在字段上边加上注解以标注参数类型。
当然还有一种情况,参数类型也是隐藏的,则要使用全限定名表示

 @MethodReflectParams({"android.content.pm.PackageParser$Package", "int"})
public static RefMethod<Void> collectCertificates;

Java 层 Hook

位于 com.lody.virtual.client.hook

Java 层使用了 Java 自带的动态代理

  1. MethodProxy
    Hook 点的代理接口,动态代理中的 call。

重要的是这三个方法:

public boolean beforeCall(Object who, Method method, Object... args) {
        return true;
    }

public Object call(Object who, Method method, Object... args) throws Throwable {
        return method.invoke(who, args);
    }

public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {
        return result;
    }

以 hook getServices 为例:

static class GetServices extends MethodProxy {
        @Override
        public String getMethodName() {
            return "getServices";
        }

        @Override
        public Object call(Object who, Method method, Object... args) throws Throwable {
            int maxNum = (int) args[0];
            int flags = (int) args[1];
            return VActivityManager.get().getServices(maxNum, flags).getList();
        }

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

A. getMethodName 是要 Hook 的方法名
B. Hook getServices 之后发现,真正返回服务的方法变成了仿造的 VActivityManager 对象。而在后面我们会知道这些服务最后都会从 VAMS 中获取,而不是原来的 AMS。
C. 实现了 isEnable 方法,这是 Hook 开关,如果返回 false 则不 Hook 该方法,而在这里的条件是,只有在子程序环境中 Hook,而宿主即框架是不需要 Hook 的,框架仍然需要连接真正的 AMS 以获取在系统 AMS 中注册的“外部” service。

  1. 那么上面这个 call 在哪里被调用呢?
    MethodInvocationStub
    这个桩对应一个需要 Hook 的类,各种 Method 可以在内部添加.
    我们需要专注这个方法
/**
     * Add a method proxy.
     *
     * @param methodProxy proxy
     */
    public MethodProxy addMethodProxy(MethodProxy methodProxy) {
        if (methodProxy != null && !TextUtils.isEmpty(methodProxy.getMethodName())) {
            if (mInternalMethodProxies.containsKey(methodProxy.getMethodName())) {
                VLog.w(TAG, "The Hook(%s, %s) you added has been in existence.", methodProxy.getMethodName(),
                        methodProxy.getClass().getName());
                return methodProxy;
            }
            mInternalMethodProxies.put(methodProxy.getMethodName(), methodProxy);
        }
        return methodProxy;
    }

这个也是关于动态代理的知识,这里的区别其实就是 Lody 对他做了一些接口的抽象,和一些诸如 Log 的封装。

添加 Hook Method 的方式有两个,一是调用 addMethodProxy,二是在 Stub 上添加 @Inject 注解,具体见下一段。

  1. 关于 MethodProxies
    叫这个名字的类很多,一个 MethodProxies 对应一个需要 Hook 的 framework 类型,需要 Hook 的方法以内部类(MethodProxy)的形式罗列在内部。
@Inject(MethodProxies.class)
public class LibCoreStub extends MethodInvocationProxy<MethodInvocationStub<Object>> {

将要 Hook 的方法集合 MethodProxies 绑定到 Stub 上。然后就是 Stub 对自己头上注解的解析,最终还是会调用到内部的 addMethodProxy 方法。

protected void onBindMethods() {

        if (mInvocationStub == null) {
            return;
        }
        Class<? extends MethodInvocationProxy> clazz = getClass();
        Inject inject = clazz.getAnnotation(Inject.class);
        if (inject != null) {
            Class<?> proxiesClass = inject.value();
            Class<?>[] innerClasses = proxiesClass.getDeclaredClasses();
            // 遍历内部类
            for (Class<?> innerClass : innerClasses) {
                if (!Modifier.isAbstract(innerClass.getModifiers())
                        && MethodProxy.class.isAssignableFrom(innerClass)
                        && innerClass.getAnnotation(SkipInject.class) == null) {
                    addMethodProxy(innerClass);
                }
            }

        }
    }

运行时结构

这点很重要,VA 在运行时并不是一个简单的单进程的库,其需要在系统调用到其预先注册的 Stub 组件之后接手系统代理 Client App 的 四大组件,包括生命周期等一切事物。
VA 参照原生系统 framework 仿造了一套 framework service,还有配套在 client 端的 framework 库。

  1. 首先来看一下系统原生的 framework 运作方式

简单来说,我们平时所用到的 app 运行空间中的 framework api 最终会通过 Binder 远程调用到 framework service 空间的远程服务。
而远程服务类似 AMS 中的 Recoder 中会持有 app 空间的 Ibinder token 句柄,通过 token 也可以让 framework service 远程调用到 app 空间。
在这里插入图片描述

  1. VA 环境下:
    而在 VA 环境下,情况其实也是类似,只不过在 framework service 和 client app 之间还有另外一个 VA 实现的 VAService,VAService 仿造了 framework service 的一些功能。
    因为在 VA 中运行的 Client App 都是没有(也不能注册)在 framework service 的,注册的只有 VA 预先注册在 Menifest 中的 Stub 而已。所以 frameservice 是无法像普通 App 一样管理 VA Client App 的会话的。
    这就要依靠 VA 仿造的另外一套 VAService 完成对 VA 中 Client App 的会话管理了。
    在这里插入图片描述
    需要注意的是 VA Client 获取 VA Service 的 IBinder 句柄是统一通过 IServiceFetcher 这个句柄,这个看上去有些奇怪。而获得 IServiceFetcher 本身的方式是通过 ContentProvider,可能是 Provider call 的性能较好一些。

作者:OSTCB
来源:优快云
原文:https://blog.youkuaiyun.com/ganyao939543405/article/details/76146760

VirtualApp技术黑产利用研究报告 一、 前言 VirtualApp(以下称VA)是一个App虚拟化引擎(简称VA)。VirtualApp创建了一个虚拟空间,你可以在虚拟空间内任意的安装、启动和卸载APK,这一切都与外部隔离,如同一个沙盒。运行在VA中的APK无需在Android系统中安装即可运行,也就是我们熟知的多开应用。 VA免安装运行APK的特性使得VA内应用与VA相比具有不同的应用特征,这使得VA可用于免杀。此外,VA对被多... 阅读详情

相关推荐

Android虚拟化技术方案技术评测

完成虚拟化一个应用,要做的核心事情只有一件:在运行子应用时,欺骗系统让系统误以为正在运行的是宿主,否则系统会毫不犹豫的将你杀死!!

sinat_23902497的博客 2397

VirtualApp深度解析:Android沙盒技术的革命性突破

VirtualApp(简称VA)是一款革命性的Android沙盒技术产品,通过独特的三层架构(VA Space、VA Framework、VA Native)重新定义了移动应用的多开、隔离和安全管控能力。作为运行于Android系统上的轻量级"Android虚拟机",VA提供了全面的应用隔离、免Root的完整Hook能力、多进程架构设计,并展现出卓越的性能、兼容性和安全性优势。 ## Virtu...

gitblog_00032的博客 3727

VirtualApp源码(完整工程)

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

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

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

ganyao939543405的博客 1万+

ESP32S3 Korvo2V3四通道AEC配置实战:基于AFE的回声消除优化

本文详细介绍了在ESP32S3 Korvo2V3平台上配置四通道AEC(回声消除)的实战经验。通过AFE算法优化和硬件连接调试,实现高质量回声消除,显著提升智能音箱和会议设备的语音交互体验。重点解析了四通道配置、数据格式对齐和参数调优等关键技术要点。

elastic6hunter的博客 932

vue3 web项目引入高拍仪

高拍仪的引入步骤就两步,安装SDK,然后根据官网的api进行开发。根据自己需要的功能,请求对应的接口。

qq_42480170的博客 4115

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

Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider Provider 注册 回顾前面,Activity 启动的时候会检查 Application 是否初始化,会调用 bindApplication,里面执行了安装 Provider 的方法: private void installContentProviders(Context app, List<...

CoderAldrich的专栏 616

用友U8接口开发全攻略:从EAI到OpenAPI的5种方式详解(附避坑指南)

本文深度解析了用友U8系统接口开发的五种核心方式:EAI、API、OpenAPI、SQL直连与内部CO组件。文章详细对比了各方式的适用场景、优缺点及实战操作,并提供了清晰的选型决策框架与避坑指南,帮助开发者根据具体业务需求选择最高效、最稳定的集成路径。

weixin_29322855的博客 680

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

Android 双开沙箱 VirtualApp 源码分析(二) VA 初始化 先看一下代码: VirtualCore.startup public void startup(Context context) throws Throwable { if (!isStartUp) { // 确保 MainThread if (Looper...

CoderAldrich的专栏 682

Windows 下安装 Hermes Agent 完整教程(含踩坑记录)

不支持原生 Windows,必须通过 WSL2 (Windows Subsystem for Linux 2) 运行。WSL2 是 Windows 内置的 Linux 兼容层,可以在 Windows 上运行完整的 Linux 环境。

weixin_50191542的博客 4271

OGRE 3D----2. QGRE + QQuickView

将 OGRE(面向对象图形渲染引擎)集成到使用 QQuickView 的 Qt Quick 应用程序中,可以在现代灵活的 UI 框架中提供强大的 3D 渲染功能。本文将指导您如何在 QQuickView 环境中设置 OGRE。

charce_you的专栏 913

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

Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver 方案猜测 同 Activity 一样,Client App 在 Menifest 中注册的静态广播外部 AMS 是无法知晓的,经过前几章的分析,相信大家已经是老司机了,我们可以先尝试提出自己的观点。 和 Activity 一样使用 Stub 组件占坑?仔细想一想是无法实现的,因为你无法预先确定 Cl...

CoderAldrich的专栏 657

软考「系统规划与管理师(第2版)」通关全攻略(2026年下半年)

它考的是一个IT管理者能不能用体系化的语言,把"我们为什么要建这个系统→怎么规划→怎么管好服务→怎么合规"讲圆。所以备考的核心策略也很明确——教材建框架 → 真题磨考点 → 案例练结构 → 论文备好真实项目素材 → 机考练打字。按计划走,不贪全、不盲刷、不死背,稳稳把三科都推过45分那条线。祝你通关 🎯。

软件行业技术文化交流。 3039

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

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

CoderAldrich的专栏 445

推荐系统[一]:超详细知识介绍,一份完整的入门指南,解答推荐系统相关算法流程、衡量指标和应用,以及如何使用jieba分词库进行相似推荐,业界广告推荐技术最新进展

推荐算法其实本质上是一种信息处理逻辑,当获取了用户与内容的信息之后,按照一定的逻辑处理信息后,产生推荐结果。热度排行榜就是最简单的一种推荐方法,它依赖的逻辑就是当一个内容被大多数用户喜欢,那大概率其他用户也会喜欢。但是基于粗放的推荐往往会不够精确,想要挖掘用户个性化的,小众化的兴趣,需要制定复杂的规则运算逻辑,并由机器完成。召回:当用户以及内容量比较大的时候,往往先通过召回策略,将百万量级的内容先缩小到百量级。

丨汀、的博客 7590

移动支付APP(仿支付宝)

Toast.makeText(PayActivity.this, "支付失败:" + payResponse.getMsg(), Toast.LENGTH_SHORT).show();Toast.makeText(PayActivity.this, "支付失败:" + t.getMessage(), Toast.LENGTH_SHORT).show();Toast.makeText(PayActivity.this, "支付失败", Toast.LENGTH_SHORT).show();

寒寒豆豆软件公司的博客 134

多开引擎virtualAPP适配安卓9到安卓11

github上的virtualApp只兼容到了安卓8,需要自己适配安卓9到11的hook机制。

cf125313的专栏 1342

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

【亲测免费】 探索VirtualApp:一款强大的安卓应用虚拟化框架

探索VirtualApp:一款强大的安卓应用虚拟化框架 在今天的数字化时代,Android应用程序已经成为我们日常生活中不可或缺的一部分。然而,随着应用数量的增长和功能复杂性的提升,有时我们需要一个工具来管理和优化这些应用。这就是的用武之地,它是一个开源的Android应用虚拟化框架,允许你在单个设备上运行多个应用实例。 项目简介 VirtualApp的核心理念是创建一个沙盒环境,每个应用程序都可...

gitblog_00055的博客 1468

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

VirtualApp快速上手:3分钟搭建你的第一个应用多开环境

还在为手机空间不足无法安装多个应用分身烦恼?想同时登录多个社交账号却受限于系统限制?本文将带你3分钟内使用VirtualApp(简称VA)搭建轻量级应用多开环境,让一部手机轻松实现应用双开/多开,无需复杂配置,零Root门槛。 读完本文你将学会: - 理解VirtualApp沙盒技术原理 - 3步完成应用多开环境搭建 - 掌握安装、启动、管理虚拟应用的核心操作 - 解决常见兼容性问题的实用技巧 ...

gitblog_00013的博客 904
上一篇: Panorama - Custom DOTAScenePanel
下一篇: Android 双开沙箱 VirtualApp 源码分析(二)
CoderAldrich
博客等级 码龄12年 431粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值