☰
安卓多开隐私保护实测:基于Work Profile的方案、测试与避坑
2026/10/8 15:31:35 网站建设 项目流程

前阵子做了一次安卓端应用隐私审计的小项目,顺手把一直想补的坑也填了:用基于 Android 自带工作资料(Work Profile)的开源方案做应用双开,并且在多开环境下把应用的密码存储、数据隔离、权限边界挨个做了一遍实测。整个项目做完之后有个很直接的感受——今天的免费多开不是没有好东西,而是你的多开姿势,基本决定了隐私保护一半以上的效果。这篇就把我所用的方案、搭建过程、测试思路和踩坑记录全部摊开讲。

先说结论性的背景:日常生活里我们多开的需求往往比想象中更硬核——工作微信和个人微信号要分开、手游需要小号养号、社交 App 要同时挂多个账号、平板和手机要跑同一套应用但不同数据。市面上一堆挂着"免费"名头的多开工具,真正用下来,要么时不时弹广告,要么把应用数据加密后闭源处理,甚至有些还会上报安装列表和账号信息。真正想兼顾"免费""开源""隐私隔离"三个条件,可行方案其实不多,绕来绕去最后都会回到 Android 原生的 Work Profile 机制,以及围绕它做的 Shelter/Insular 这类开源客户端。本文记录的,就是这套路线的完整实测。

1. 免费多开与隐私保护,为什么必须放在一起测

1.1 多开需求远不止"开两个微信"

在我们的认知里,多开往往是灰色需求,但实际上它覆盖的场景非常广。比如很多人的手机里同时有工作钉钉、企业微信、个人微信,Android 单用户体系下如果只有一个微信入口,工作群和家庭群的消息会混在一起,尴尬事谁遇到谁知道。再比如说部分海外社交 App 只允许单设备登录,切换账号就要重新验证短信,多开后两个账号并存,切换成本大幅下降。还有一些工具类 App 本身限制了免费账号的设备绑定数,多开可以绕开这种限制——这种用法虽然不一定符合开发者意图,但确实是需求。

这次项目的测试目标也很明确:把无root环境下的免费多开方案跑通,并且针对"多开后的 App 是否更容易泄露密码、是否做到数据隔离"做一轮真实测试。所以这不是一篇单纯介绍"装哪个软件能开两个分身"的水文,而是把多开和隐私审计串在一起看的完整记录。

1.2 "免费"的代价往往体现在隐私上

过去试过几个主流的"多开分身"型工具,它们的免费策略基本逃不出两种模式:一是通过内置广告变现,二是通过统计 SDK 采集用户画像。Google Play 上很多所谓 Clone App 类应用,实际会请求"读取已安装应用列表""获取设备标识""修改系统设置"等敏感权限,你用它做分身,它则把分身环境里所有应用的包名、启动时间、使用频率一并打包拿走。

这类工具通常还要求开启"允许创建快捷方式"或"后台弹出界面"权限,不然分身应用无法正常显示通知。权限一旦放开,就不再只是简单多开了,它本质上变成了一层代理——你在这个代理层里登录的任何账号、输入的密码、收发的验证码,理论上都经过它。别说闭源工具,就是开源项目你也很难逐行审计完所有代码。所以我的基本判断是:如果在意密码和账号数据,就不要让第三方多开器待在你和应用之间。这也是为什么我最终选择了系统层方案——绕开中间人,从架构上解决问题。

1.3 测试对象与评判标准

这次实测涉及的方案主要有三类:第一类是基于系统工作资料(Work Profile)的方案,代表是开源的 Shelter 和 Insular;第二类是手机厂商自带的"应用分身"功能(比如 MIUI 的双开,其实内部也是走多用户机制);第三类是传统 VirtualApp 系的多开工具。评判标准围绕四个维度:是否免费开源、是否与应用存在中间转发层、数据隔离是否可验证、在无 root 环境下是否稳定。测试过的密码存储场景则单独列入后续章节。

最终跑下来的结论很明确:Shelter/Insular 这类 Work Profile 方案是当前免费多开里隐私边界最干净的一条路。它没有中间层,所有多开应用都直接跑在 Android 系统的单独用户空间内,连 APK 解析、进程创建都由系统完成。下面详细展开。

2. 拆解 Shelter/Insular:不复制进程,只借用系统隔离能力

2.1 工作资料机制的核心原理

要理解 Shelter 为什么"干净",首先得搞明白 Android 的多用户机制。Android 从很早就支持多用户(Multi-User),只是普通用户除了"访客模式"之外很少接触到。工作资料(Work Profile)本质上是这套多用户机制在企业设备管理场景下的正式封装。你在设备上创建一个"工作资料"后,系统会为它分配独立的 user id、独立的应用数据目录、独立的共享存储区域。主空间里装的应用和资料里装的应用,虽然共享同一个系统内核,但运行时的进程 UID、文件系统权限、SharedPreferences 存储路径都是隔离的。

Shelter 做的就是把这个企业场景的能力搬到个人设备上。它在你的主用户空间安装一个管理端 App,以"设备管理员插件"的身份申请创建和管理工作资料。创建完成后,Shelter 自己会跑到资料里作为一个控制入口,你就可以在主空间和资料之间安装、冻结、解冻应用。这里有个关键点:它不是用 Hook 或代理去克隆 APK,也不是复制进程,而是直接引导你在系统工作资料里安装一份原版 App。原版 App 跑起来之后,看到的是标准 Android 环境,它甚至不知道自己"多开了"。

2.2 为什么这种方案对隐私保护最友好

从数据流的角度看:传统双开工具想实现"多开",必须先接管目标 APK 的加载过程,多数实现方式是在自己进程内做动态代理,目标 App 的 Activity、Service 实际由宿主进程承载。这意味着目标 App 的所有 Intent、私有文件读写、网络请求,都需要经过这一层转发。如果转发层有 bug 或恶意代码,密码、Cookie、token 全部可见。

而 Work Profile 方案里,Shelter 只负责"安装"这个动作。应用一旦在资料里启动,它就是一个独立进程,直接与系统 AMS、PMS 通信,中间没有任何旁路。它的数据落在 /data/user/10/package/ 这种独立 user 目录下,和主空间的 /data/user/0/ 完全不互通。我把两个空间的 App 登录相同账号后备份数据对比过,除了包名一样,其他能访问到的文件全部分开。这种隔离不需要你对每个应用做适配,是系统提供的强制边界。

2.3 与常见多开工具的参数对比

这里直接给一张我整理过的对比表,方便你理解差异:

维度Work Profile 方案(Shelter/Insular)手机厂商自带分身VirtualApp 系工具
免费程度完全免费、开源系统内置,免费免费版带广告,高级版付费
中间转发层无,直接系统多用户无,多数走系统多用户有,目标 App 进程在宿主内
数据隔离完整 UID/目录隔离完整隔离依赖自身沙箱,存在绕过风险
Root 要求不需要不需要多数不需要,部分功能要 root
稳定性依赖系统版本,高厂商定制,高脆弱,部分 App 检测到会闪退
隐私采集无内置 SDK厂商可能采集,视ROM而定商业化 SDK,采集风险最高

2.4 方案边界:不是无懈可击

也必须说清楚这套方案的局限。最明显的一点是:不是所有应用都愿意待在"工作资料"里。有些应用(比如部分银行客户端)会检查当前是否处于 Device Admin 激活状态,或者检查自身所在 user id 的 owner 是否为企业场景,一旦发现就拒绝登录。另外,工作资料与主空间的应用数据隔离也意味着登录态不互通,如果你需要"同一个 App 快速切换主号和小号",Work Profile 方案做不到——它更像是"两个井水不犯河水的独立空间",而不是"一个应用同时开 20 个分身"。

冻结功能也要提一下。Shelter 里可以冻结资料里的应用,冻结后进程被杀、数据保留。这既是为了省电,也是隐私保护手段——你完全可以做到"不用时不激活",把敏感 App 保持在静默状态,连后台扫描的机会都没有。但对于依赖实时推送的 App,冻结会导致推送收不到,实测时微信电话会有延迟,需要注意。

3. 从零搭建:一套具备隐私边界的分身环境

3.1 前置条件与设备准备

动手之前先确认设备条件:Android 系统建议 8.0 以上,DPM(设备管理)API 在 5.0 以后就有,但真正稳定好用还是得 Android 8.0 之后。手机需要能在"设置—账号—添加工作资料"这个流程里走通。部分国产 ROM 对这个入口做了隐藏,比如某些 MIUI 版本,需要在设置里搜索"工作资料"才能唤起。如果你的设备实在找不到入口,也可以在 Shelter 首次启动时让它直接尝试创建,它会拉起系统界面引导你完成。

设备不需要 root,但要注意:如果手机里已经激活了其他设备管理员应用(比如企业 Intune、MDM 管控),Shelter 可能创建失败。因为一个设备上只能有一个"激活的设备管理组件",这是个系统级限制,无解。另外部分 ROM 的省电策略很激进,Shelter 被系统杀死后会导致冻结状态无法自动恢复,建议在电池策略里给 Shelter 设为不限制。

3.2 安装 Shelter 并创建 Work Profile

Shelter 的最新版本在 F-Droid 和 GitHub Releases 都能找到,建议优先装 F-Droid 版本,至少签名来源可追溯。安装后第一次启动,Shelter 会提示"设置设备管理员"。这一步要仔细看好弹窗内容,它明确告知你 Shelter 需要设备管理员权限来管理资料,不能在主空间和资料空间之间自由移动应用。确认后进入系统激活流程:

  1. 点击创建"工作资料",系统会弹出页面向导,要求输入一个"工作账号"名称,实际上只是资料名字,比如我用的是 sdcard 里看不出来的中性名。
  2. 创建完工作资料后,系统会自动在资料里安装一份 Shelter。
  3. 切回主空间的 Shelter,这时界面会多出"主空间"和"工作资料"两个页面,可以互相切换管理。

实测中第二个步骤偶尔会卡住,表现是资料创建成功但资料里的 Shelter 一直没装好。这种情况不用重来,直接进系统设置里的"应用—工作资料",手动从应用商店搜索 Shelter 装一遍,或者用 adb 指定 user 安装。

3.3 把目标应用装进工作资料

安装方式有三种,按推荐度排:

  1. Shelter 主空间页面选择应用:直接点列表里已安装的应用,Shelter 会调起安装器并选择"用户 10"(工作资料)作为安装目标。注意这里安装的是 APK 副本,不是迁移应用数据,主空间的应用保留不动。
  2. 应用商店双账号:在 Play 商店里切换用户到工作资料,然后搜索目标应用安装。这个方式适合 Play 商店本身已经支持多用户下载的场景。国内商店有些并不区分用户,建议忽略此方法。
  3. adb 安装到指定 user:adb shell pm install-user --user 10 app.apk,这个办法适合批量安装、自动化脚本场景。

日常最常用的还是第一种。装完以后,工作资料里会生成一个独立的桌面图标,点击图标打开的应用就是"第二个实例"。从外观上看,它和主空间图标几乎没有差别,只是系统会在图标左下角加上一个公文包小标志(如果系统没有禁用工作资料图标角标的话)。

3.4 冻结、解冻与网络权限收紧

安装完成后别急着登录账号,先把隐私相关配置做好。Shelter 的"冻结"按钮在应用详情页,点击后资料里的目标进程会被立即 kill 并锁定,需要再次打开时通过解冻动作唤醒。对于不常用的分身账号,强烈建议长期保持冻结状态。

网络权限层面,Shell 下还可以用appops单独设置。如果某 App 的多开实例不需要联网,比如只是离线记账工具,可以执行:

adb shell appops set --user 10 <package_name> INTERNET deny

这样工作资料内的该应用就无法发起任何网络请求,即使它有后台恶意行为,数据也出不去。注意--user 10不是固定的,不同 ROM 上 Work Profile 的 user id 可能不同,可以在 Shelter 的设置里查看当前资料的 user id。实测常见值是 10 或 11。

还有一个容易被忽略的点:工作资料里的应用,个别权限与主空间是分开授予的。比如定位权限,你在主空间给了"仅前台允许",资料里默认是"每次询问";短信权限在资料里默认拒绝。这套独立的授权模型很有用,它相当于自动给分身应用套了一层更严格的权限约束。

3.5 正常使用时遇到的意外情况

整套方案日常用了半个多月,遇到三个真实问题。

第一个是通知栏角标与消息预览。工作资料应用的通知默认会以"工作"渠道展示,部分 ROM 会把它们和主空间通知分流排列。如果你的 ROM 版本较老,可能出现资料应用收不到推送的问题,这是因为工作资料的同步开关没打开。解决路径在系统设置—工作资料—联系人/设备/应用数据同步里,手动打开即可。

第二个是文件共享。主空间和工作资料之间的文件系统完全隔离,用系统相册往资料应用里传图片会很痛苦。Shelter 自带的文件选择器可以进行"安全文件交换",它会把所选文件复制到资料空间的暂存目录,再由目标应用读取。实测传照片、文档都没问题,但大视频文件会比较慢,因为等于多了一次完整拷贝。

第三个是"账号选择器"弹窗。Android 系统在处理工作资料里的认证时,会额外弹出一个"选择工作资料账号还是主空间账号"的界面。这个对普通用户不算大问题,但如果你是开发者在代码里直接调用 AccountManager,需要注意处理"多次账号选择"的 UI 回调。

4. 多开环境下的隐私测试:密码明文与数据隔离取证

4.1 针对"手机 App 登录密码是否明文存储"的审计思路

热身词里有一条非常典型的搜索:测试:手机 App 登录密码是否明文存储。这正好是隐私保护测试的核心命题。常规审计思路分两层:静态代码层和数据落盘层。

静态代码层的核心办法是反编译。把多开实例对应的 APK 拉回电脑,用 apktool 解包,重点看smali代码里是否存在SharedPreferences存储、SQLiteDatabase.insert、FileOutputStream.write的调用位置。尤其关注登录接口成功回调里是否有putString("token", password)类似逻辑。再搜一下有没有AES、RSA、Cipher、encrypt这些关键词,如果完全没有,说明密码极可能明文存储或只是做了简单编码。

数据落盘层的办法更直接:绕过多开工具,直接看工作资料空间里的文件。无 root 设备上对 release App 不一定能进 data 目录,但有三种路径可以操作:

  1. adb shell run-as <package_name>,仅适用于 debuggable 应用。
  2. 通过 adb backup 方式导出应用私有数据,无 root 可用,但 Android 12 之后对非 backup 白名单应用会收紧。
  3. 让目标应用走一次登录流程,然后用 Shelter 暂停应用,再用系统备份恢复接口把数据换出来分析。

实测项目里常用的是第一种和第二种组合。如果应用是 testing 包或者允许备份,拿到的目录结构是明文可见的,直接看shared_prefs/*.xml和databases/*.db。当年跑一个内部项目的加固包时,我就在一个login_info.xml里看到了完整明文密码字段,下面是一段脱敏化示例:

<map> <string name="username">test_user_001</string> <string name="password">P@ssw0rd_2024</string> </map>

这种问题在业务类的管理端应用里出现频率非常高,原因是赶工期的后端同学直接在客户端做了"记住密码"功能。多开方案解决不了这个隐患,但测试框架可以把它自动暴露出来。

4.2 验证工作资料空间内多开数据是否真隔离

测试多开环境是否真的隔离,需要从系统层面取证。在 adb 里查看两个空间的应用 UID 和目录:

adb shell dumpsys package <package_name> | grep -E "userId|user 0|user 10" adb shell ls -la /data/user/0/<package_name>/shared_prefs adb shell ls -la /data/user/10/<package_name>/shared_prefs

正常工作下,/data/user/0和/data/user/10是两个完全独立的文件目录,inode 都不同。我把主空间应用 A 登录了账号 X,资料空间应用 A 登录了账号 Y,然后分别读取两个空间的shared_prefs,确认里面存的会话 ID、设备指纹完全不一样。这就是系统级隔离的实证。

更有意义的是"跨空间投递"测试:主空间的 A 应用尝试直接读取资料空间的文件路径,权限系统会返回 EPERM。因为两个空间的进程分别以 UID 10000+ 和 11000+ 运行,用户组不同,文件访问权限完全隔离。商业多开工具是没法给你这种保证的,甚至很多 VirtualApp 方案根本无法做到每个分身有独立 UID。

4.3 自动化回归:用 pytest 驱动整个隐私场景

测试阶段不能全靠手工点,我搭了一个轻量的 pytest 自动化脚本,用来重复执行"登录—切换空间—对比数据—清缓存"四步。核心逻辑是可以直接跑在真机上的,依赖 adb 和 uiautomator。下面是一个简化的测试骨架:

import subprocess import pytest PACKAGE = "com.example.target" USER_WORK = 10 def adb(cmd): subprocess.run(f"adb shell {cmd}", shell=True, check=True) @pytest.mark.parametrize("space", [0, USER_WORK]) def test_profile_space_available(space): out = subprocess.run( f"adb shell pm list packages --user {space} | grep {PACKAGE}", shell=True, capture_output=True, text=True ) assert PACKAGE in out.stdout def test_password_not_in_shared_prefs(): # 从工作资料目录导出 xml 并检查是否存在明文密码字段 result = subprocess.run( f"adb pull /data/user/{USER_WORK}/{PACKAGE}/shared_prefs/login.xml .", shell=True, capture_output=True, text=True ) content = open("login.xml", encoding="utf-8").read() assert "password" not in content or "P@ss" not in content

这个脚本的价值在于可以随时回归。日常跑完一轮测试,只要把断言条件换成具体业务字段,就能快速发现开发版本引入了什么新的敏感信息。当然,脚本仅仅能覆盖静态落地和系统配置层,真要审计网络流量,还得上抓包工具看 SSL pinning 之外的 HTTP 明文通道。因为多开实例和目标应用底层网络栈完全一致,这点和单开环境测起来没区别。

4.4 多开场景下还需要额外盯住的几个隐私细节

在 Work Profile 方案下,有几个细节是常规测试容易漏掉的。

一是剪贴板隔离。主空间复制的内容,工作资料应用默认是可以读取的,因为剪贴板属于系统全局服务。实测微信分身里粘贴主空间复制的验证码完全无阻碍。如果你对隐私要求极高,需要用 appops 禁止目标应用的读取剪贴板权限:adb shell appops set --user 10 <package_name> READ_CLIPBOARD deny,但要注意部分 ROM 不支持这个 op。

二是输入法联想记忆。工作资料里的输入法数据目录与主空间同样独立,但你切换输入法时,键盘应用会重新弹出"选择用户"的界面,容易造成误触。这个更多是体验问题,不是隐私泄露点,但测试时如果遇到输入法崩溃,第一反应就该查资料空间是否也安装了一份相同的输入法。

三是地理位置共享。系统对工作资料默认不共享定位授权,但某些 ROM 会默认允许"空白定位"或"精确位置关闭",应用在资料空间里拿到的定位精度和主空间不同。这会影响部分求打卡类应用的体验,但也反过来证明它隔离得更彻底。

四是备份恢复链。如果用户做了本地备份,多开空间的数据是否会被一并备份出来,取决于备份工具的工作方式。Shelter 本身不做备份,系统的一系列恢复操作针对 user 0 为主,工作资料用户空间需要单独指定。实测下来,这反而是一个更安全的设置——主空间恢复数据不会直接把工作资料的空间覆盖掉,避免了误恢复。

5. 应用反向识别多开环境的检测原理与测试边界

5.1 分身被检测的常见原因

不管是 Work Profile 方案还是 VirtualApp 方案,都会被一些高度敏感的应用尝试检测中。从原理上分,识别方式大概有三类:

第一类是环境特征检测。检测当前进程所在 user id 是否为 0,或者检测设备管理策略是否启用,这类检测最简单也很粗暴。dumpsys package里能看到应用请求 DeviceAdmin 相关信息,一些银行 App 会在启动时检查自身是否处于受管理设备环境,一旦发现就跑一个"禁止使用"的拦截页。

第二类是路径与挂载点检测。VirtualApp 方案因为要加载自己的虚拟文件系统,会改变 dex 的加载路径,或者留下与 packageName 不匹配的基带路径。一些加固方案会在 JNI 层读取/proc/self/maps,如果发现ueventd路径异常就判定为多开环境。这套检测对 Work Profile 方案无效,因为资料空间里的应用走的是完全正常的系统路径,没有挂载痕迹。

第三类是跨空间指纹比对。部分应用尝试读取Settings.Secure中的 ANDROID_ID,虽然两个空间共享同一设备硬件标识,但实测发现某些 ROM 会给不同 user 生成不同的安装标识组合。应用会把两个空间的安装标识差集作为判定依据。这个几乎没有绕开手段,因为系统确实会记录你创建过工作资料。

5.2 测试边界:哪些检测不该去绕过

在做隐私保护测试时,要明确一条底线:测试的目的是验证自己的数据安全,而不是为了欺骗目标应用。像银行 App 检测到工作资料环境后限制登录,这本质是安全策略,不该用坑蒙拐骗的方式绕过。项目里我们遇到类似的场景,处理方式是直接在文档里标注"该应用不支持多开环境,适合单实例使用",而不是花大力气做隐藏。

同样,测试也不该去引导用户 root 设备。无 root 情况下测多开是最安全的,一旦 root,应用层可检测的手段呈几何级增长,一切都是另一个故事。本项目全程保持无 root 状态,以便保证结论对普通用户可复现。

5.3 多开环境下的安全卫生习惯

Work Profile 方案虽然干净,但不代表你可以肆无忌惮地在里面装一堆来路不明的 APK。恰恰因为系统不会对工作资料的应用做额外的安全扫描(和主空间一样的扫描级别),资料空间反而是敏感数据的集中地。建议在日常使用中把下面几条当作纪律:

  • 只从官方商店或目标 App 官网下载安装包,绝对不碰破解版。
  • 对资料空间里每个 App 都单独收紧权限,尤其是存储、通讯录、电话。
  • 不常用的资料 App 一律冻结,降低后台数据访问窗口。
  • 涉及支付的 App,宁愿单开也不要放进多开空间,支付安全优先级最高。

我实际检测过一些从网上下载的"绿色版""去广告版" APK,它们在主空间安装时被手机管家的云查杀识别出来了,但在资料空间里如果禁用管家权限,系统可能静默放行。多开不是坏事,但多开环境天然更容易让用户放松警惕。

6. 多次实测后值得记住的经验清单

整个项目做完,有几个经验是反复踩坑后才真正内化下来的,单独写出来作为收尾。

第一,优先选用 F-Droid 版本。Shelter 的开源版本在 GitHub 和 F-Droid 上都有发布,但 GitHub Release 的 APK 可能更新更快,也更容易引入未充分测试的改动。长期稳定使用,选 F-Droid 的签名版本,至少能保证每行代码都可追溯。

第二,创建资料前先备份。一次系统 OTA 升级之后,我的工作资料曾经出现过"无法解锁"的故障,表现为资料图标一直转圈,Shelter 解冻失效。后来查下来是 ROM 的 DPM 状态丢失导致。解决方式是临时停用设备管理器再重新激活,这个操作不会删除资料内应用,但有一定概率需要重新登录所有账号。动手前先做一次完整备份,别省这一步。

第三,移动应用时先冻结再装。Shelter 里从主空间往资料空间安装应用,如果目标应用正在运行,安装过程中可能出现"签名冲突"或"当前用户已有该应用"的报错。解决方案是先冻结主空间的目标应用,让它完全退出进程,再执行安装。装完解冻主空间实例,两边互不干扰。

第四,自动化测试别只跑一遍。App 更新频繁,开发同学可能在某个版本里顺手改掉了敏感数据的存储方式。我的建议是把密码明文、权限收紧、隔离目录校验这三类断言做成持续回归的一部分,每次拿到新包就自动跑一遍。这套 Script 跑在 CI 里,比人工点按靠谱得多。

最后说一个小技巧:在 Shell 里指定工作资料 user id 时,不要手写死 10。不同厂商 ROM 上工作资料的 user id 可能是 9、10、11 不等,建议先执行adb shell pm list users看当前真实 user id。我就是因为一直写死--user 10,在一台 Android 13 的平板上反复出现"找不到用户"的问题,排查半天才意识到是 user id 不同。这个细节虽然小,但在脚本自动化场景里会成为最隐蔽的坑。

这套测试项目做下来最大的体会就是:隐私保护并不一定需要付出金钱成本,也不一定非要 root 才能折腾出花样,Android 系统自带的能力用好之后,效果反而比一堆第三方工具更让人放心。如果你手里正好有无 root 的安卓手机,又有多开加隐私审计的需求,Shelter 加一套 pytest 回归脚本,就足够搭起一个扎实的测试环境了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询