揭秘 Device Faker 的 COW 属性伪造引擎:mmap 写时复制如何实现零驻留机型伪装

原创2026-10-25 14:05:56888 阅读

揭秘 Device Faker 的 COW 属性伪造引擎:mmap 写时复制如何实现零驻留机型伪装

Device Faker 是一个基于 Zygisk 的 Android 机型伪装模块,核心能力是为不同应用单独配置设备型号、品牌与系统属性。它的属性伪造默认由 COW 属性伪造引擎驱动:通过 mmap 写时复制(Copy-On-Write)技术,在进程私有内存中克隆系统属性区并原地打补丁,实现 per-process 隔离与模块零驻留——应用进程内读到伪装机型,而全局状态与他人完全不受影响。

Device Faker 机型伪装模块主题插画:霓虹蓝紫光环中的手机轮廓

为什么机型伪装必须"伪装属性"

Android 应用的机型检测通常有两条读取路径:

  • Java 层:android.os.Build 的静态字段(Build.MODEL、Build.FINGERPRINT 等)
  • Native 层:__system_property_get() 等 C 函数,直接读取 /dev/__properties__/ 下的属性区文件

Device Faker 对 Java 层采用 JNI 字段覆写(见 src/hooks.rs),而 Native 层的属性值则交给 COW 引擎处理。这也是选择它的根本原因:只要属性区在进程内存中的内容被改写,所有 native 读取路径都会拿到伪装值,无需修改任何函数指针。

COW 原理:给属性区做一份"进程私有副本"

引擎核心实现在 src/cow_props.rs。传统做法是把共享属性区重新映射为可写(MAP_PRIVATE 原地 COW),但这会让该映射在 /proc/self/maps 中从 r--s 变成 rw-p,反而暴露出新的检测指纹。

Device Faker 的 COW 引擎走得更彻底,分四步完成:

第一步:定位属性区与 context 节点

Android 的 bionic 库用一组 context 节点管理属性区,每个节点内含一个指向属性区内存起点的指针(prop_area_)。src/prop_backend.rs 在运行期解码 AArch64 指令、扫描内存结构,解析出节点数组基址、元素步长和指针偏移——不硬编码任何结构偏移。这一步是整个"私有副本"方案的地基。

第二步:整块克隆到匿名映射

对需要改写的属性区,模块用 MAP_PRIVATE | MAP_ANONYMOUS 分配一块匿名内存,把真实属性区逐字节复制进去(含头部、trie 树和全部属性节点)。原始 /dev/__properties__/* 映射保持 r--s 只读形态,纹丝不动。

第三步:把 context 节点"指向"副本

解析好的 context 节点指针被批量改写,从真实属性区起点指向私有副本(PropBackend::repoint_area,见 src/prop_backend.rs)。此后本进程内所有属性读取自动走副本,而副本是进程私有的,其他进程和全局 getprop 看到的仍是真实值——per-process 隔离由此成立。

第四步:在副本上原地打补丁

  • 已有属性:直接在副本上原位更新值,长值也支持原地扩展(见 src/cow_props.rs)
  • 不存在的属性:在副本中用 bump allocator 插入新的 trie 节点,让新属性"凭空出现"
  • 跨区补丁:OnePlus/OPPO 等设备的 prefix routing 可能从多个分区属性区读同一 key,引擎会扫描并 patch 所有相关 build area,保证各读取路径一致

全部写完后,副本被 mprotect 降回只读,并打上中性的 <a href="https://link.gitcode.com/i/824cb55f2c8084735e056a99c926d260" target="_blank">anon:linker_alloc] 匿名名(定义见 [src/cow_props.rs)——这个名字与系统动态链接器本就大量产生的映射同形,不会成为新指纹。

零驻留的关键:写完即卸载

COW 引擎的价值不止于"隔离",还有"不留痕迹"。模块的统一执行流(见 src/lib.rs)是:

① JNI 覆写 Build 字段 → ② COW 属性伪造 → ③ DlClose 卸载模块

关键点在第 ③ 步:伪装完成后立即 DlClose 卸载模块自身的 so 文件。内存中的副本随应用进程生死与共、进程退出即消失,系统里没有任何常驻的伪装模块。这正是"零驻留属性伪装"的完整含义:

维度 传统全局 resetprop 方案 Device Faker COW 引擎
影响范围 全系统,getprop 可见 仅目标应用进程
/proc/self/maps 可能出现 rw-p 等异常 原始属性区保持 r--s,副本中性伪装
模块驻留 需常驻进程/文件 写完即 DlClose,零驻留
恢复机制 需要手动/脚本恢复 进程退出自动消失,无需恢复

降级路径与兜底策略

健壮性是这套引擎的另一亮点:

  • context 解析失败:自动降级回原地 COW remap(maps 中会重新出现 rw-p),并在日志中打 WARN 说明原因(见 src/cow_props.rs)
  • SELinux 拒绝映射的区:属性对本进程本就不可观察,静默跳过,不产生泄漏面
  • COW 无法处理的属性:严格模式下不再偷偷降级为全局 resetprop,而是打日志提示用户显式开启 companion_resetprop

这种"宁可日志告警、也不破坏隔离承诺"的设计,保证了 COW 模式的 per-process 语义始终成立。

如何选择 COW 与 companion 两种模式

默认配置下所有应用自动走 COW 引擎,无需任何设置。若需要全局一致的伪装(例如 getprop 子进程也要读到伪装值),可在 config.toml 中为对应应用开启:

[[apps]]
package = "com.example.app"
companion_resetprop = true

此时全部属性改由 companion 进程直写属性区,应用退出或退后台约 2 秒后自动恢复原始值。两种模式的取舍详见 docs/CONFIG.md 的「属性伪造机制」一节,完整字段说明可参考 docs/CONFIG.md。

写在最后

Device Faker 的 COW 属性伪造引擎把"内存副本 + 指针重定向"这对组合拳打得很细:运行期解析不硬编码偏移、私有副本消除 rw-p 指纹、中性匿名名避免新暴露面、写完即 DlClose 实现零驻留。对新手而言,理解这套机制的最佳路径是阅读 src/cow_props.rs 顶部注释与 src/prop_backend.rs,再结合 module/config.toml 中的架构说明,即可完整掌握"mmap 写时复制 → 进程私有副本 → 零驻留机型伪装"的全链路。

登录后查看全文
device_faker