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万+

A robust and easy to implement method for IMU calibration without external equipments(IMU标定)

A robust and easy to implement method for IMU calibration without external equipments 1 标定原理1 亲试可用,还有动态演示效果不错。相比商用产品的简易标定流程,该标定方法有以下几点不同: 给出了加速度计和陀螺仪更复杂的模型。 利用Allan方差的定义,标定陀螺仪的偏置。 大量冗余数据,保证最优化收敛。利用数十...

擦擦擦大侠的博客 3947

深度学习基于Resnet18的图像多分类--训练自己的数据集(超详细 含源码)

深度学习基于Resnet18的图像多分类--训练自己的数据集(超详细 含源码)

她是不会无条件的!! 2万+

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

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

CoderAldrich的专栏 616

论文解读——YOLOv11(目标检测)

YOLOv11提供了多种型号和功能,以满足不同应用场景的需求。对象检测对象检测是YOLOv11的基本功能之一。在训练时,YOLOv11能够检测图像中的对象,并输出对象的边界框和类别信息。这一功能在自动驾驶、智能安防等领域具有广泛应用。图像分割图像分割是YOLOv11的另一个重要功能。与对象检测不同,图像分割不仅检测对象的边界框,还能够对图像中的对象进行像素级别的分割。这使得YOLOv11在医疗影像分析、工业制造等领域具有更高的应用价值。姿态检测姿态检测是YOLOv11的一个独特功能。

qq_38220914的博客 4457

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

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

CoderAldrich的专栏 682

【2023版】基于JT808/JT1078协议的车载视频监控平台开发实战-开源架构解析

本文详细解析了基于JT808/JT1078协议的车载视频监控平台开发实战,涵盖协议核心作用、开源架构设计、开发避坑指南及二次开发建议。重点介绍了JT1078协议在音视频传输中的优化方案,以及海量位置数据存储、高可用架构等关键技术点,为开发者提供全面的实战参考。

weixin_26965807的博客 234

基于MATLAB/Simulink与WSL2的ROS2实时联合仿真实践

本文详细介绍了基于MATLAB/Simulink与WSL2的ROS2实时联合仿真实践。通过环境配置、PX4仿真环境搭建、自定义消息生成、Simulink通信实现及实时优化等步骤,提供了完整的无人机位置控制案例,帮助开发者实现高精度的实时联合仿真。

red88的博客 202

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

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

CoderAldrich的专栏 657

Scratch离线版部署指南:解决资源缺失与页面显示异常问题

本文提供了一份详尽的Scratch离线版部署指南,旨在解决网络依赖导致的资源缺失与页面显示异常问题。通过从基础文件获取、自动化资源抓取到生产环境部署的完整流程,帮助教育工作者和开发者在无网络环境下搭建功能完整的本地Scratch编辑器,确保编程教学的顺利进行。

wine的专栏 1260

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

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

CoderAldrich的专栏 445

解码器选型参数,结构原理,工艺与注意问题总结

解码器芯片(Decoder Chip)是电子设备中的一个重要组件,通常用于处理、解释和输出存储在某种编码格式中的数据或信号。本文对解码器的工作原理,结构特点,工艺流程,选型参数及设计注意事项进行总结。

fydar的博客 1169

【软考高项】【计算专题】- 3 - 进度类 - 双代号网络图

共需压缩 20 天。在双代号网络图中,工作一般使用箭线表示,任意一条箭线都需要占用时间、消耗资源,工作名称写在箭线的上方,而消耗的时间则写在箭线的下方或者工作后面。反映的是前后工作的交接点,节点中的编号可以任意编写,但应保证后续工作的节点比前面节点的编号大,即上图图中的1⃣️4⃣️ 即表示 B 活动和 D 活动间有依赖关系,D 活动的开始,依赖 B 活动的结束。B活动完成了一半, 各计划的实际支出为A活动6500元, B活动1000元, C活动280元, D。

欢迎来到我的博客,一起探索代码里的世界! 4559

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

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

cf125313的专栏 1342

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

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

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

gitblog_00055的博客 1468

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

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

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

gitblog_00013的博客 904

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

上一篇: 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、付费专栏及课程。

余额充值