Commit 6b47a431741f99a79979396fd8997d886698ed9d
1 parent
c3eb27d3
fix(wechat-spoof): 修复副设备登录设备名泄露 Pixel — resetprop 兜 native 读
微信在 native __system_property_get 读 ro.product.model,绕过所有 Xposed
Java hook,登录记录显示真机 "Pixel 10 Pro Fold" 触发预警。
- hookDeviceSpoof: SystemProperties.get 改模式匹配 ro.product.*.model
+ hook Settings.{Global,Secure,System}.getString(device_name/bluetooth_name)
+ hook BluetoothAdapter.getName() + 诊断日志(定位真实读取源用)
→ 真机验证这些 Java hook 全零命中,证实微信走 native 读
- 真正生效: wechat-model-spoof.sh 用 Magisk resetprop 改系统属性层
(装到 /data/adb/post-fs-data.d/,开机持久化),native 读也拿伪装值
→ 登录记录变 "samsung SM-X700",Pixel 泄露消失
- 代价:全机生效(getprop 也变),放弃"仅微信进程内",native 读无法 per-app
详见 memory wechat-pad-login-device-spoof。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Showing
2 changed files
with
87 additions
and
11 deletions
xhs_lsposed_a16_fix/wechat-model-spoof.sh
0 → 100644
| 1 | +#!/system/bin/sh | |
| 2 | +# 部署到 /data/adb/post-fs-data.d/wechat-model-spoof.sh (chmod 755)。 | |
| 3 | +# | |
| 4 | +# 微信副设备登录设备名泄露修复:微信在 native 层 __system_property_get 读 ro.product.model, | |
| 5 | +# 绕过 LSPosed Java hook(WeChatHook.hookDeviceSpoof 只改 Build.* / Java SystemProperties.get), | |
| 6 | +# 导致登录设备记录显示真机 "Pixel 10 Pro Fold",每次登录触发安全预警。 | |
| 7 | +# 这里在系统属性层把机型改成三星平板 SM-X700,native 读也会拿到伪装值 → 显示 "samsung SM-X700"。 | |
| 8 | +# 代价:全机生效(getprop 也变),非"仅微信进程内"——native prop 读无法 per-app hook, | |
| 9 | +# 只有 resetprop 兜得住。对这台专用 bot 手机可接受。详见 memory wechat-pad-login-device-spoof。 | |
| 10 | +for p in ro.product.model ro.product.product.model ro.product.vendor.model ro.product.odm.model \ | |
| 11 | + ro.product.system_ext.model ro.product.system_dlkm.model ro.product.vendor_dlkm.model \ | |
| 12 | + ro.product.bootimage.model ro.product.model_for_attestation; do | |
| 13 | + resetprop "$p" "SM-X700" | |
| 14 | +done | |
| 15 | +for p in ro.product.brand ro.product.product.brand ro.product.vendor.brand ro.product.odm.brand \ | |
| 16 | + ro.product.manufacturer ro.product.product.manufacturer; do | |
| 17 | + resetprop "$p" "samsung" | |
| 18 | +done | |
| 19 | +resetprop ro.product.marketname "Galaxy Tab S8" | ... | ... |
xhs_lsposed_a16_fix/wechathook_src/WeChatHook.java
| ... | ... | @@ -2727,19 +2727,21 @@ public class WeChatHook implements IXposedHookLoadPackage { |
| 2727 | 2727 | catch (Throwable t) { XposedBridge.log("[WeChatHook] spoof Build." + e.getKey() + " failed: " + t); } |
| 2728 | 2728 | } |
| 2729 | 2729 | // ro.product.* + ro.build.characteristics=tablet (the strong tablet signal). |
| 2730 | - final java.util.Map<String, String> P = new java.util.HashMap<>(); | |
| 2731 | - P.put("ro.product.brand", "samsung"); | |
| 2732 | - P.put("ro.product.manufacturer", "samsung"); | |
| 2733 | - P.put("ro.product.model", "SM-X700"); | |
| 2734 | - P.put("ro.product.device", "gts8wifi"); | |
| 2735 | - P.put("ro.product.name", "gts8wifixx"); | |
| 2736 | - P.put("ro.build.characteristics", "tablet"); | |
| 2730 | + // Pixel 有一大堆分区限定 prop(ro.product.{product,vendor,odm,system_ext,system_dlkm, | |
| 2731 | + // vendor_dlkm,bootimage}.model 全 = "Pixel 10 Pro Fold")。写死白名单必漏,改**模式匹配**: | |
| 2732 | + // 任何 ro.product.*.model / *marketname / *.brand 等一律盖,未来加分区也不漏。 | |
| 2737 | 2733 | Class<?> sysProp = XposedHelpers.findClass("android.os.SystemProperties", cl); |
| 2738 | 2734 | XC_MethodHook propHook = new XC_MethodHook() { |
| 2739 | 2735 | @Override protected void afterHookedMethod(MethodHookParam p) { |
| 2740 | - if (p.args == null || p.args.length == 0) return; | |
| 2741 | - Object k = p.args[0]; | |
| 2742 | - if (k != null && P.containsKey(k.toString())) p.setResult(P.get(k.toString())); | |
| 2736 | + if (p.args == null || p.args.length == 0 || p.args[0] == null) return; | |
| 2737 | + String k = p.args[0].toString(); | |
| 2738 | + String v = spoofProp(k); | |
| 2739 | + if (v != null) { | |
| 2740 | + // 诊断:记录微信实际读了哪个 model/name 源(找泄露源用,验证后可删) | |
| 2741 | + if (k.contains("model") || k.contains("marketname") || k.contains("name")) | |
| 2742 | + XposedBridge.log("[WeChatHook] spoofProp " + k + " → " + v); | |
| 2743 | + p.setResult(v); | |
| 2744 | + } | |
| 2743 | 2745 | } |
| 2744 | 2746 | }; |
| 2745 | 2747 | for (java.lang.reflect.Method m : sysProp.getDeclaredMethods()) { |
| ... | ... | @@ -2749,13 +2751,68 @@ public class WeChatHook implements IXposedHookLoadPackage { |
| 2749 | 2751 | XposedBridge.hookMethod(m, propHook); |
| 2750 | 2752 | } |
| 2751 | 2753 | } |
| 2754 | + // BluetoothAdapter.getName() 是设备名的另一条常见来源(走蓝牙服务 IPC,Settings hook 盖不到)。 | |
| 2755 | + try { | |
| 2756 | + Class<?> bt = XposedHelpers.findClass("android.bluetooth.BluetoothAdapter", cl); | |
| 2757 | + for (java.lang.reflect.Method m : bt.getDeclaredMethods()) { | |
| 2758 | + if ("getName".equals(m.getName()) && m.getParameterTypes().length == 0) { | |
| 2759 | + XposedBridge.hookMethod(m, new XC_MethodHook() { | |
| 2760 | + @Override protected void afterHookedMethod(MethodHookParam p) { | |
| 2761 | + XposedBridge.log("[WeChatHook] BluetoothAdapter.getName() intercepted"); | |
| 2762 | + p.setResult("Galaxy Tab S8"); | |
| 2763 | + } | |
| 2764 | + }); | |
| 2765 | + } | |
| 2766 | + } | |
| 2767 | + } catch (Throwable t) { XposedBridge.log("[WeChatHook] spoof BluetoothAdapter failed: " + t); } | |
| 2768 | + // 🔑 微信副设备「设备名」= Build.MANUFACTURER + Settings.Global "device_name", | |
| 2769 | + // 后者在 Pixel 上 = 真机营销名("Pixel 10 Pro Fold"),Build/prop 伪装盖不到它, | |
| 2770 | + // 每次登录都以 "samsung-Pixel10 Pro Fold" 触发预警。这里把 device_name/bluetooth_name | |
| 2771 | + // 一并伪装成三星平板名,让整串设备身份一致。 | |
| 2772 | + final java.util.Map<String, String> S = new java.util.HashMap<>(); | |
| 2773 | + S.put("device_name", "Galaxy Tab S8"); | |
| 2774 | + S.put("bluetooth_name", "Galaxy Tab S8"); | |
| 2775 | + XC_MethodHook settingsHook = new XC_MethodHook() { | |
| 2776 | + @Override protected void afterHookedMethod(MethodHookParam p) { | |
| 2777 | + if (p.args == null || p.args.length < 2) return; | |
| 2778 | + Object k = p.args[1]; // getString(ContentResolver, String name) | |
| 2779 | + if (k != null && S.containsKey(k.toString())) p.setResult(S.get(k.toString())); | |
| 2780 | + } | |
| 2781 | + }; | |
| 2782 | + for (String clsName : new String[]{"android.provider.Settings$Global", | |
| 2783 | + "android.provider.Settings$Secure", "android.provider.Settings$System"}) { | |
| 2784 | + try { | |
| 2785 | + Class<?> sc = XposedHelpers.findClass(clsName, cl); | |
| 2786 | + for (java.lang.reflect.Method m : sc.getDeclaredMethods()) { | |
| 2787 | + if ("getString".equals(m.getName()) | |
| 2788 | + && m.getParameterTypes().length == 2 | |
| 2789 | + && m.getParameterTypes()[1] == String.class) { | |
| 2790 | + XposedBridge.hookMethod(m, settingsHook); | |
| 2791 | + } | |
| 2792 | + } | |
| 2793 | + } catch (Throwable t) { XposedBridge.log("[WeChatHook] spoof " + clsName + " failed: " + t); } | |
| 2794 | + } | |
| 2752 | 2795 | XposedBridge.log("[WeChatHook] hookDeviceSpoof: → Samsung SM-X700 tablet in " + procName |
| 2753 | - + " (Build.MODEL now=" + XposedHelpers.getStaticObjectField(build, "MODEL") + ")"); | |
| 2796 | + + " (Build.MODEL now=" + XposedHelpers.getStaticObjectField(build, "MODEL") | |
| 2797 | + + ", device_name spoofed=Galaxy Tab S8)"); | |
| 2754 | 2798 | } catch (Throwable t) { |
| 2755 | 2799 | XposedBridge.log("[WeChatHook] hookDeviceSpoof failed in " + procName + ": " + t); |
| 2756 | 2800 | } |
| 2757 | 2801 | } |
| 2758 | 2802 | |
| 2803 | + /** 模式匹配伪装 ro.* 设备身份 prop。命中返回三星平板值,未命中返回 null(放行原值)。 */ | |
| 2804 | + private static String spoofProp(String k) { | |
| 2805 | + if (k == null) return null; | |
| 2806 | + if (k.equals("ro.build.characteristics")) return "tablet"; | |
| 2807 | + if (!k.startsWith("ro.product.")) return null; | |
| 2808 | + if (k.endsWith("marketname")) return "Galaxy Tab S8"; | |
| 2809 | + if (k.endsWith(".model")) return "SM-X700"; | |
| 2810 | + if (k.endsWith(".brand") || k.endsWith(".manufacturer")) return "samsung"; | |
| 2811 | + if (k.endsWith(".device")) return "gts8wifi"; | |
| 2812 | + if (k.endsWith(".name")) return "gts8wifixx"; | |
| 2813 | + return null; | |
| 2814 | + } | |
| 2815 | + | |
| 2759 | 2816 | private void hookInTabletEnv(ClassLoader cl) { |
| 2760 | 2817 | try { |
| 2761 | 2818 | Class<?> bkClass = XposedHelpers.findClass("com.tencent.mm.ui.bk", cl); | ... | ... |