
前阵子做了一次安卓端应用隐私审计的小项目顺手把一直想补的坑也填了用基于 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 ProfileShelter 的最新版本在 F-Droid 和 GitHub Releases 都能找到建议优先装 F-Droid 版本至少签名来源可追溯。安装后第一次启动Shelter 会提示设置设备管理员。这一步要仔细看好弹窗内容它明确告知你 Shelter 需要设备管理员权限来管理资料不能在主空间和资料空间之间自由移动应用。确认后进入系统激活流程点击创建工作资料系统会弹出页面向导要求输入一个工作账号名称实际上只是资料名字比如我用的是 sdcard 里看不出来的中性名。创建完工作资料后系统会自动在资料里安装一份 Shelter。切回主空间的 Shelter这时界面会多出主空间和工作资料两个页面可以互相切换管理。实测中第二个步骤偶尔会卡住表现是资料创建成功但资料里的 Shelter 一直没装好。这种情况不用重来直接进系统设置里的应用—工作资料手动从应用商店搜索 Shelter 装一遍或者用 adb 指定 user 安装。3.3 把目标应用装进工作资料安装方式有三种按推荐度排Shelter 主空间页面选择应用直接点列表里已安装的应用Shelter 会调起安装器并选择用户 10工作资料作为安装目标。注意这里安装的是 APK 副本不是迁移应用数据主空间的应用保留不动。应用商店双账号在 Play 商店里切换用户到工作资料然后搜索目标应用安装。这个方式适合 Play 商店本身已经支持多用户下载的场景。国内商店有些并不区分用户建议忽略此方法。adb 安装到指定 useradb 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 目录但有三种路径可以操作adb shell run-as package_name仅适用于 debuggable 应用。通过 adb backup 方式导出应用私有数据无 root 可用但 Android 12 之后对非 backup 白名单应用会收紧。让目标应用走一次登录流程然后用 Shelter 暂停应用再用系统备份恢复接口把数据换出来分析。实测项目里常用的是第一种和第二种组合。如果应用是 testing 包或者允许备份拿到的目录结构是明文可见的直接看shared_prefs/*.xml和databases/*.db。当年跑一个内部项目的加固包时我就在一个login_info.xml里看到了完整明文密码字段下面是一段脱敏化示例map string nameusernametest_user_001/string string namepasswordPssw0rd_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(fadb shell {cmd}, shellTrue, checkTrue) pytest.mark.parametrize(space, [0, USER_WORK]) def test_profile_space_available(space): out subprocess.run( fadb shell pm list packages --user {space} | grep {PACKAGE}, shellTrue, capture_outputTrue, textTrue ) assert PACKAGE in out.stdout def test_password_not_in_shared_prefs(): # 从工作资料目录导出 xml 并检查是否存在明文密码字段 result subprocess.run( fadb pull /data/user/{USER_WORK}/{PACKAGE}/shared_prefs/login.xml ., shellTrue, capture_outputTrue, textTrue ) content open(login.xml, encodingutf-8).read() assert password not in content or Pss 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 回归脚本就足够搭起一个扎实的测试环境了。