一、问题/背景:网吧名称为什么"读不到"
黑龙江的网吧客户机上,统一安装着一套文化经营技术监管系统,客户端位于 C:\\Program Files (x86)\\culclientYkt\\cclt.exe(ProductName=CulClient,版本 1.0.0.2,未数字签名)。它的任务是向监管端上报场所信息,同时在下班的运维场景里,我们希望能程序化读出"当前这台机器属于哪家网吧",从而自动加载该网吧的个性化配置(鼠标灵敏度、音量、桌面壁纸、软件清单等)。但第一次尝试就撞了墙——网吧名称拿不到明文:
- 客户端界面是 SOUI 自绘(文字画在窗口上,不经过标准窗口文本机制),GetWindowText 只能拿到 SOUI_DUMMY_WND 这类固定标题;
- 进程被 VMProtect 3.2–3.5 加壳(DIE 检测到 .vmp1 段 packed,熵 7.96),.text 代码和导入表(IAT)都被虚拟化,静态字符串全灭——搜索 网吧/场所/监管/一卡通 等关键词,ASCII 与 UTF-16 双通道零命中;
- 配置目录里的 package.json 是自定义 A-Z 替换密码密文(18716 字符、仅 26 个大写字母),不可解;
- 抓包看流量,全部走自定义二进制帧且连接 192.168.8.x 内网,无明文。
一句话总结背景:名称被"SOUI 自绘 + VMProtect + 混淆流量"三层封死,常规 GetWindowText / 静态 strings / 配置文件三路全断。
二、原理/思路:壳保护"代码",不保护"API 参数"
静态分析失败后,我换了一个关键视角:VMProtect 加密的是 .text 代码和 IAT,但它拦不住"API 调用参数"——因为参数必须是明文,被调用的系统 API 才能理解它。这条链上有一个天然突破口:托盘图标。
cclt.exe 在系统托盘常驻(有图标),而 Windows 画 tooltip 的前提是进程调用 Shell_NotifyIconW(NIM_ADD) 时,把提示文本放进 NOTIFYICONDATA.szTip[128] 字段——UTF-16 明文,否则 explorer 画不出来。壳保护代码,但保护不了这个必须暴露给系统的字段。
所以原理就一句话:szTip 明文 100% 存在于进程内存里,扫描内存即可,无需脱壳。
配套的还有第二条更稳的路(供正式产品用):虽然"名称"读不到,但监管客户端留下了机器可读的唯一标识——culcfg.ini 里的场所编号 num=(明文、ASCII、GBK 编码整体)。前 6 位就是区县代码(如 230225=甘南县)。编号 + 一份人工映射文件,就能全自动带出名称。
三、实现/步骤:从内存 szTip 到 rust-cafe-tune 自动化
3.1 内存扫描直接拿 szTip(取证路径)
cclt.exe 是 32 位 PE,无需调试器,用 PowerShell P/Invoke 三步扫描:
OpenProcess(PROCESS_VM_READ)
→ VirtualQueryEx 遍历所有 MEM_COMMIT 区域
→ ReadProcessMemory 读回,按 Unicode 解码
→ 搜索关键词(网吧/场所/监管/飞宇/电竞…)
一次命中(地址 0x014D0000,配置区):
黑龙江省互联网上网服务营业场所经营管理技术监管系统
V1 0 0 2 4
编号 2302252835 名称 甘南县飞宇网吧
结论:szTip = "甘南县飞宇网吧"——监管端登记的正式名称(与招牌大字"飞宇电竞"+小字"网吧"是同一家)。验证了"壳保护代码、不保护 API 参数"的推断。
3.2 产品化路径:编号 + 人工映射(rust-cafe-tune 的做法)
内存扫描是取证手段,不适合每次开机都跑。正式产品 rust-cafe-tune 选择了编号 + 映射文件:
culcfg.ini 的 num=2302252835(机器可读,GBK lossy 解码)
→ configs/ctconfig-2302252835/cafe-name.txt(人工填一次:飞宇电竞)
→ ct cafe-info 自动输出:编号 + 区县 + 名称
源码锚点(crates/ct-cli/src/commands.rs):
- detect_cafe_num()(L905):读 C:\\Program Files (x86)\\culclientYkt\\culcfg.ini,lossy 解码逐行找 num=;
- resolve_cafe()(L922):显式 –cafe 优先,否则探测编号 → 存在 configs/ctconfig-<编号>/ 目录则自动采用;
- cafe_info()(L938):输出编号/主机/IP/MAC/服务器/区县,再读 cafe-name.txt 补名称。
实测输出:
[cafe] 自动识别网吧配置: ctconfig-2302252835
[cafe] 场所编号: 2302252835
[cafe] 所在区县: 甘南县
[cafe] 网吧名称: 飞宇电竞
从此用户不必手动说"我在飞宇电竞"——ct 每次启动自动探测编号、自动加载对应网吧的 ctconfig-<编号>/ 配置目录(鼠标/音量/壁纸/软件清单全在里面)。
四、总结/验证:rust-cafe-tune 是"网吧个性化配置自动化"工具
本文验证了三条事实,并沉淀为一个开源工具:
rust-cafe-tune(crates/ct-cli,二进制 ct.exe)把第 3 条做成全自动:
- ct cafe-info —— 一键输出编号/区县/名称;
- ct –cafe <名> —— 手动指定;不指定则自动探测编号并加载 ctconfig-<编号>/ 配置;
- 每个网吧的个性化设置(鼠标加速度/滚动方向、显示器亮度、壁纸、音量、软件安装清单)独立成目录,还原卡重启后 ct 仍能自动归位;
- 跨网吧对比:每家 cclt.exe 的 SHA256 + szTip + 编号可做成对比表,判断是否同模板镜像。
这套"从死路(SOUI 自绘)到活路(API 参数明文 + 编号映射)"的排查,完整记录在仓库 docs/监管客户端分析记录.md 与 docs/cclt-sztip-分析.md。若你也在维护网吧/网咖的自动化部署,这可能是把"人工报网吧名"消灭掉的一条可行路径。

