DEVICE_NAME_LEAK_FIX.md 7.47 KB

微信副设备登录「设备名泄露 Pixel」修复全记录(2026-07-11)

一次从「以为一个 hook 就完事」到「挖了四层才根治」的完整复盘。 配套代码:../xhs_lsposed_a16_fix/wechathook_src/WeChatHook.java(hookDeviceSpoof)、 ../xhs_lsposed_a16_fix/wechat-model-spoof.sh(resetprop 开机脚本)。 背景与平板登录逆向见同目录 LESSONS.md;memory wechat-pad-login-device-spoof。


1. 问题现象

微信「登录设备管理 / 账号与安全」里,本机登录记录显示:

你的微信号于 21:24 在 samsung-Pixel10 Pro Fold 设备上通过微信密码登录
  • brand 改到了(samsung,来自 LSPosed 对 Build.MANUFACTURER 的伪装)
  • 机型没改(还是真机 Pixel 10 Pro Fold)

→ 伪装只盖住一半,每次登录都以「新设备 / 异常设备」触发安全预警。

更诡异的后续现象:同一台机、同样的系统属性,火星人账号显示 SM-X700(对),小火子账号显示 Pixel(错) —— 按账号不同。这条线索最终揭穿了真正的根因(见 §4)。


2. 设备身份的「四层」结构(核心认知)

微信读取「机型」不是一条路,而是四层,任何一层没盖住都会从那层泄露:

层 读取源 谁能盖 本次是否泄露过
① Java android.os.Build.MODEL / Build.MANUFACTURER LSPosed Java hook manufacturer 已盖,model 未从这读
② native __system_property_get("ro.product.model") 及各分区变体 只有 Magisk resetprop(Java hook 盖不到 native) ✅ 泄露过
③ 系统设备名 Settings.Global "device_name" / Settings.Secure "bluetooth_name" settings put(改系统 settings 库) ✅ 泄露过
④ 微信自己的持久缓存 files/mmkv/mmkv.default(首装时算一次存下的机型种子)→ HARDWARE_INFO_MMKV(二级缓存) 删缓存逼重采集 ✅ 泄露过(最坑)

关键规律:微信登录注册读的是第 ④ 层缓存,不是每次实时读系统。 首次安装时把机型算一次存进 mmkv.default,之后一直复用。所以前三层全伪装对了,它照样发缓存里的 Pixel。


3. 排查过程:错误的弯路 vs 正确的落点

弯路 A(无效):以为是 Settings.Global device_name

加了 hook Settings.{Global,Secure,System}.getString 对 device_name/bluetooth_name 返回伪装值。 → 没用。微信不走 Java 的 getString 包装。

弯路 B(无效):以为是分区限定 prop 没盖全

把 SystemProperties.get 的 hook 从写死白名单改成模式匹配 ro.product.*.model,又加 BluetoothAdapter.getName() hook + 诊断日志。 → 登录时诊断零命中。证明微信根本没调 Java 层的 SystemProperties.get。

💡 诊断零命中 = 该立刻想到 native 读。这是本次最大的时间浪费点:Java 层怎么 hook 都不命中,就该马上转向 native / 缓存,而不是继续加 Java hook。

正确落点 ②(有效但不够):resetprop

getprop ro.product.model 全系统改成 SM-X700(native 读也拿到伪装值):

resetprop ro.product.model SM-X700   # + 全分区变体 + marketname

→ 机型(device type)字段变对了,但设备名字段仍 Pixel。

正确落点 ③(有效但不够):改系统 settings

settings put global device_name "Galaxy Tab S8"
settings put secure bluetooth_name "Galaxy Tab S8"

(持久化,存在 settings 库里,重启也在)

正确落点 ④(根治):清微信持久 MMKV 缓存

即使 ②③ 都做了,HARDWARE_INFO_MMKV 重新生成后仍写 samsung/Pixel 10 Pro Fold,而此时 getprop+settings 全系统已无任何 "Pixel 10 Pro Fold"。 → 说明 Pixel 来自微信自己的持久缓存种子 mmkv.default。

  • 只删 HARDWARE_INFO_MMKV 没用:它会从 mmkv.default 种子重新长出 Pixel。必须连 mmkv.default 一起删。
am force-stop com.tencent.mm
# 先备份可回滚
cp files/mmkv/mmkv.default            /data/local/tmp/mmkv.default.bak
cp files/mmkv/mmkv.default.crc        /data/local/tmp/mmkv.default.crc.bak
# 删种子 + 二级缓存
rm -f /data/data/com.tencent.mm/files/mmkv/{mmkv.default,mmkv.default.crc,HARDWARE_INFO_MMKV,HARDWARE_INFO_MMKV.crc}
am start -n com.tencent.mm/.ui.LauncherUI
# 重新登录 → HARDWARE_INFO_MMKV 重生成 = samsung/SM-X700 ✓

删 mmkv.default 实测不登出(登录态在账号专属存储 / keystore,不在这个默认 MMKV 里)。 不用 pm clear / 重装(那会清掉本地聊天记录 + auto-reply 依赖的 EnMicroMsg.db)。删这两个 MMKV 是外科手术版,够用。


4. 「按账号不同」现象的解释

  • 火星人 = SM-X700:早先某次(本地缓存恰好是 SM-X700 时)注册到微信服务器的旧记录。服务器端设备记录一旦建立就固定、登录不自动刷新。
  • 小火子 = Pixel:删旧记录后重登,新注册读的是当时的本地缓存 = Pixel,于是又写成 Pixel。

⇒ 服务器设备名 = 注册当刻的本地缓存值。所以根治必须先把本地缓存改对(清缓存重采集),每个账号再重新登录一次让服务器记录刷新。


5. 最终落地的完整解法(换机 / 重刷照做)

  1. ① Java 层:LSPosed 模块 WeChatHook.hookDeviceSpoof(Build.* = 三星平板 + ro.build.characteristics=tablet)。
  2. ② native 层:/data/adb/post-fs-data.d/wechat-model-spoof.sh(开机 resetprop ro.product.*.model = SM-X700 等,全机生效、持久)。
  3. ③ 系统设备名:settings put global device_name "Galaxy Tab S8" + settings put secure bluetooth_name "Galaxy Tab S8"(settings 库持久)。
  4. ④ 清微信持久缓存:删 files/mmkv/{mmkv.default,HARDWARE_INFO_MMKV}(+.crc)逼重采集。
  5. 每个账号重新登录一次,让服务器端设备记录刷新成 samsung SM-X700。

验证:cat files/mmkv/HARDWARE_INFO_MMKV | strings | grep -iE 'samsung|SM-X|pixel' 应只见 samsung / SM-X700。


6. 维护注意 / 坑

  • 别再 pm clear 或重装微信:否则第 ④ 层缓存会在某个「伪装没就位」的时刻重新焊死 Pixel。真要重装,先确认 ①②③ 都在,再装、再登录。
  • resetprop 是全机生效(getprop 也变 SM-X700,所有 app 都看到)——放弃了「仅微信进程内」,因为 native prop 读无法 per-app hook(除非上 frida / native inline hook,微信重度混淆 + 反调试,不值)。这台专用 bot 手机可接受。
  • 残留无害:WebPrefetchUserAgent / AppBrandNetworkConfigUserAgentHelper / *_SDKCgiWaitReportMap 里还有旧 Pixel,是 User-Agent / 网页缓存,与登录设备身份无关,随用自然刷新。
  • 登录态下别改设备身份:改 hook / resetprop / 清缓存都可能让服务器看到指纹跳变 → 踢下线 + 实名验证(见 LESSONS.md)。所有改动务必在没账号在线时做,改完再登录。

7. 一句话总结

伪装设备机型别只在 Java 层。App 可能走 native __system_property_get(要 resetprop)、可能读系统 settings 设备名(要改 settings 库)、更可能读它自己首装时存下的持久缓存(要删 mmkv.default 逼重采集)。四层缺一漏一处。诊断零命中就是 native / 缓存的信号,别在 Java 层死磕。