BRANDSERVICE_PUBLISH_RESEARCH.md 7.18 KB

贴图号自动发布日报 — 逆向进度记录(进行中)

目标:把 lifelog/wechat-viewer 生成的每日日报(图片 + 文字)自动发到微信"贴图号" (一种手机端管理的品牌服务号内容发布功能,入口在"我的作品")。

约束:跟 RESEARCH.md(消息发送)同一台设备(Pixel 10 Pro Fold, root + Magisk + frida-server 17.9.10 常驻 :27042),WeChat 8.0.74。


一、已验证结论

1. 网络层 MITM 是死路(别再试)

用 phoneuse/mitm_proxy.py(已在设备装 CA + iptables NAT 重定向 uid 10336 的 80/443 到本地代理) 抓包,贴图号发布 + 定位请求全部是 POST szextshort.weixin.qq.com:443 /mmtls/<hex>—— 外层 TLS 解密后 payload 还是微信自己的 MMTLS 二进制包,内容不可读。跟 RESEARCH.md 当年 "IM 消息走 MMTLS 不走 OkHttp"的结论一致,贴图号也一样,不是 Finder 那种明文 CGI。

⚠️ 事故记录:第一次全量拦截微信 uid 的 80/443 时,真实打断了用户当场发布(提示"上传图片失败", 后来确认是定位请求被搅坏)。所有 CA/iptables/mitm_proxy 改动已完全清理干净 (CA 从 /system/etc/security/cacerts/ 删除,iptables 规则已 flush,进程已杀)。 以后不要再用全量网络重定向的方式碰这个设备的微信流量。

2. Activity 调用链(logcat 抓的,可信)

BizMyWorksFlutterViewActivity   (plugin.brandservice.ui.flutter,Flutter 写的"我的作品"列表)
  → MediaTabPickerUI            (plugin.gallery.picker.tab,原生选图)
  → AppBrandPluginUI            (plugin.appbrand.ui,交给小程序宿主进程)
  → TmplWebViewMMUI             (plugin.brandservice.ui.timeline.preload.ui,WebView 编辑器)
  → 回到 BizMyWorksFlutterViewActivity

3. 反编译找到的关键 hook 点(重大进展)

APK 反编译位置(设备上,Termux):~/wechat_apk/decompiled(apktool 3.0.2 反编译 ~/wechat_apk/base.apk,从 /data/app/~~xb1vAMX_YH46_JsgviBuTw==/com.tencent.mm-3EBNVMamk3aQotXae5mBQg==/base.apk 拷贝,261MB,17 个 dex)。这份反编译产物很大,没有也不应该进 git,留在设备上按需重新生成。

搜索 com/tencent/mm/plugin/brandservice/api/TransferRequestInfo 的引用方,定位到:

  • com/tencent/mm/plugin/lite/jsapi/comms/k2.smali —— LiteAppJsApiExtTransfer (lite = 微信小程序框架内部代号)。方法 a(String, JSONObject, boolean) 是小程序 JS 调 wx.xxx() 通用透传 CGI 的原生实现:从传入的 JSONObject 里取 reqUrl(明文 CGI 地址) / cgiCmdId / reqBody(明文请求体)/ method,塞进 TransferRequestInfo, 再交给 Ljs1/m;->c(TransferRequestInfo, callback) 发出去(发出去之后才会被包进 MMTLS)。
  • com/tencent/mm/plugin/lite/jsapi/comms/y7.smali —— LiteAppJsApiWebTransfer, 结构几乎一样,服务 WebView 侧(对应 TmplWebViewMMUI)。

这意味着:任何小程序(包括贴图号背后那个)要发后端请求,都要先把明文 reqUrl/reqBody 过这两个方法之一。Hook 这两个方法 = 不用管 MMTLS,直接看到发布请求的明文内容,且只读打印 不改行为,不会像网络层 MITM 那样有破坏性副作用。

Hook 脚本已写好并验证能用:wechat_send_internal/hook_lite_transfer.js (Java.perform 里对 k2/y7 的 a(String,JSONObject,boolean) 做 overload hook, 打印三个参数后照常调用原方法,纯观察不拦截)。


二、Frida attach 的坑(重要,省得下次重新踩)

微信这种巨型 App,Java.perform 第一次要枚举当前进程里所有已加载的类, 如果对着一个已经跑了一段时间、加载了很多 class 的进程去 attach, 枚举耗时会超过 frida CLI 脚本加载的默认超时(Failed to load script: timeout was reached)。 已经在这台设备上反复验证:

  • 对存量的 com.tencent.mm:appbrand0/1/2(不管是空闲的还是当前 TOP 前台的)attach,全部超时。
  • 只有用 frida -U -W '.*appbrand.*' -l script.js(等一个全新 spawn 的匹配进程, 从它刚诞生、类还没加载多少的时候就注入)才成功过一次: Handling: Spawn(pid=22388, identifier="com.tencent.mm:appbrand1") Spawned `com.tencent.mm:appbrand1`. Resuming main thread! [hook] hooked com.tencent.mm.plugin.lite.jsapi.comms.k2.a(...) [hook] hooked com.tencent.mm.plugin.lite.jsapi.comms.y7.a(...)
  • -W PATTERN 的 PATTERN 是正则不是 glob(.*appbrand.*,不能写 *appbrand*)。
  • -W 只逮一次:撞见第一个匹配的 spawn 就处理,之后新冒出来的同名进程一律 "Ignoring", 不会连续抓多个。

微信这次的坑:贴图号这个功能背后同时用了不止一个 appbrand 进程(这次看到 appbrand0/1/2 三个都在跑),-W 只抓住了其中一个(appbrand1),但实际处理这次发布 JS 调用的 很可能是另外两个之一——它们当时已经不是"刚诞生"状态,attach 全部超时,没抓到任何 reqUrl/reqBody 输出。


三、下一步计划(还没做,下次接着来)

  1. 先把 3 个 appbrand 进程全部杀掉(su -c 'kill -9 <pid0> <pid1> <pid2>',杀子进程不影响 微信主进程/登录态,不用重启整个微信)。
  2. 同时开 3 个 frida -U -W '.*appbrand.*' -l wechat_send_internal/hook_lite_transfer.js 后台进程(各自输出到独立日志文件),寄希望于 3 个监听器分别逮住 3 个新冒出来的 appbrand 进程(不保证一定三个都能抓到不冲突,但比只开一个强)。
    • 后台常驻的技巧:nohup bash -c "yes | frida ... -l script.js" > log 2>&1 & (用 yes 管道防止 frida REPL 因为 stdin EOF 而退出导致 hook 被卸载)。
  3. 让用户重新走一遍"我的作品"发布流程(打开 → 选图 → 发布)。
  4. 检查三份日志里有没有 [hook] com.tencent.mm.plugin.lite.jsapi.comms.k2.a() called, 拿到里面的 reqUrl/cgiCmdId/reqBody JSON。
  5. 如果拿到了:reqBody 大概率是个 protobuf 或 JSON,需要再看一层怎么编码 (TransferRequestInfo 里 h 字段是 Lr45/y24 protobuf 对象,字段 d=reqUrl, e=reqBody, f=cgiCmdId, h=method),可能需要另外找 r45/y24.smali 看 protobuf schema,才能拼出一个能重放的请求。
  6. 如果这条路线折腾成本继续偏高(目前已经好几轮真人配合失败),跟用户确认是否值得 继续投入,或者退回到"半自动"方案(比如让人工点一下发布,只自动生成图片+文案, 不追求全自动免手工)。

四、现场状态(写这份记录时)

  • 设备上所有 mitm/iptables/CA 改动均已清理(见上文事故记录),微信网络恢复正常。
  • 设备上遗留:~/wechat_apk/(含 base.apk + decompiled/,可保留复用,不小,按需清理)。
  • 本次会话未留任何后台 frida/mitm 进程在跑(已逐一 kill 确认)。
  • wechat_send_internal/hook_lite_transfer.js 已提交到仓库(本次新增,还未 git add/commit, 待用户决定是否连同这份记录一起提交)。