本人小白,完全懵逼,希望大佬看了帖子指出错误和可以改正的地方谢谢。
=================================================================
## 一、先说样本是什么
手上有一批同一作者的软件,一共 **158 个 exe**,主题是「自媒体外推 / 自动化发布」,覆盖的平台一堆:知乎、百家号、搜狐号、网易号、B站、CSDN、简书、豆瓣、贴吧、喜马拉雅、爱卡汽车、脉脉、雪球、VK、Pinterest、Medium、wattpad……
三个特征一眼就认出来:
1. **全是易语言静态编译**的 32 位原生 exe;
2. 主窗体类名统一是 **`WTWindow`**,里面夹着易语言自绘控件 **`_EL_Label`**、`_EL_CommonDlg` 和 MFC 自绘控件 **`Afx:400000:...`**;
3. 登录界面是**易游 SDK 自带的那套**(`软件使用协议` 窗 → `用户登录` 窗 → 主界面)。
**0. 老规矩,先看壳。** 这批里约 2/3 有壳(UPX 之类),脱壳脚本我之前发过,这里不重复。没壳的直接拖进 OD 就能干活。
---
## 二、第一刀先砍错的:改本地跳转没用
最开始的直觉就是「找到判断卡密那句话,把跳转反过来」。**结果白干。**
因为这类程序的**判决在服务器**:
- 客户端只负责把卡密 POST 出去,然后把**服务器返回的错误码**翻译成中文提示。
- 我拿真卡密和白卡密各跑一遍,客户端弹的是「用户不存在」——**这个文案是服务器给的**,本地没有任何一处逻辑能把它变成「登录成功」。
所以**改本地判断、NOP 跳转、改返回值,全都无效**。这一步白费了我好几天,写出来给同样想抄近路的朋友省点时间。
---
## 三、把协议摸清楚:易游(eydata)的接口序列
挂上抓包(客户端会忽略主机名里的端口,所以还得在 80/443 上兜底监听),能看到完整顺序:
```
40377 动态配置 → 13 条 CRLF 分隔的接口路径 + 服务器时间
40375 公告 → 纯文本
40382 状态查询 → 状态值 | 时间
40383 账号密码 → <32 字符令牌> | 服务器时间
40386 状态 → 状态值 | 时间
40382 再查一次
```
**客户端的硬性要求**(这几条不对就直接失败):
| 要求 | 实测值 |
|---|---|
| 响应非空 | 是 |
| 令牌长度 | **必须正好 32 字符** |
| 服务器时间与网络时间差 | **≤ 2 秒** |
| 40382/40386 的状态值 | 真机对无效卡返回**负码**会让客户端直接失败 |
请求/响应的**循环密钥与偏移**我用双向往返给逆出来了,并且拿**真实抓包逐字节对齐验证**:真包解出来是
```
-102|2026-09-22 00:19:21
```
**`-102` 正好就是客户端显示「用户不存在」的那个服务端错误码。** 到这一步协议就完全确认了。
---
## 四、第二刀:为什么不能改 exe 本体
这批程序**约 2/3 带自校验**。我做了 4 组 A/B,每组跑 3 次:
| 改动 | 结果 |
|---|---|
| 原文件不动 | 3/3/3 秒正常进 |
| **只改 PE 头时间戳 4 字节** | 3 次**全部**失败,永远卡在「连接服务器…」 |
| 改判定门处的跳转 | 3 次**全部**失败 |
| 改代码节填充区 **1 个 dword** | 3 次**全部**失败 |
**只动 4 个字节就废。** 结论很硬:**磁盘上的文件必须保持原样,所有改动只能在内存里做。**
---
## 五、正解:把服务器也一起接管
既然判决在服务器,那就**让服务器变成我自己的**:
1. **本地起一个伪验证服务器**,按上面的协议应答「登录成功」;
2. **在目标进程里把它要连的主机地址改写成 `127.0.0.1`**(只改内存,磁盘文件一个字节不动);
3. **自动把界面走完**(点「同意以上协议」→ 填卡密 → 点「登录」)。
### 5.1 地址改写的细节(这里全是坑)
主机名是**等长**替换,长度受端口位数限制:
| 原串 | 替换成 | 长度 |
|---|---|---|
| `w<D>.eydata.net` | `127.0.0.1:<3位端口>` | 13 → 13 |
| `.eydata.net/` | `0.0.1:<5位端口>/` | 12 → 12 |
| `vip<D>.eydata.net` | `127.0.0.1:<5位端口>` | 15 → 15 |
| `https://vip` | `http://127.` | 11 → 11 |
另外两个必须处理的点:
- **客户端第一次连接走的是 IPv6**。只监听 IPv4 的话,把 `sockaddr_in6` 改写成 `::1` 之后它连不上来 —— 所以 IPv6 双栈监听也得起。
- 有的客户端会**忽略主机名里的端口**直接按 80/443 连,所以 80/443 也得兜底监听。
- 串是**按需解密**的:挂起状态下字符串还没解密,这时候扫描是扫不到的,得等壳跑完再扫。
### 5.2 界面自动驱动
易语言的按钮类名就是 `Button`,标题就是按钮文字,**直接发 `BM_CLICK`** 等于用户点击。
判定顺序必须是「**主界面 → 登录窗 → 协议窗**」。我原来把协议窗放前面,结果登录成功后登录窗消失、程序一直在协议窗分支里 `continue`,永远检测不到主界面,表现为「自动流程报未进入主界面,但主界面其实早出来了」。
---
## 六、易语言的几个特征,不知道会白折腾
### 6.1 调用约定跟常规 C 完全不一样
易语言的运行时调用长得是这样:
```
mov ebx, <运行时函数地址>
call <分派器>
```
分派器本身是通过 thunk 到的:`FF 25 <IAT槽>`。
**关键点**:`FF 25 ...` 是 **6 字节**指令。想在它上面做代码 Hook 会踩坏后面的指令;**只能改 IAT 槽里的指针**,这是唯一安全的位置。
### 6.2 混淆:无操作指令对
编译产物里到处插着这种对:
```
EB 01 <垃圾字节> ; jmp +1,跳过 1 字节垃圾
F9 72 01 ; stc; jb +1
F8 73 01 ; clc; jae +1
```
它们不影响执行,但**会让线性反汇编整个错位**。所以找特征的时候**按字节搜比按反汇编找靠谱得多**。
---
## 七、两个把我坑惨的教训(这段最值钱)
### 7.1 补丁点「多打」= 把程序改坏
「空信息框 → 点确定就退出」这个毛病是这批程序**自己**的缺陷,得找到那个守卫跳转改成无条件跳转。我一开始**把所有候选点都打上**,全量 158 个跑下来:
| 命中补丁点数 | 产品数 | 结果 |
|---|---|---|
| 1 个 | 109 | **全部稳定存活** |
| 2 个 | 6 | **一个都没活** |
| 3 个 | 8 | **一个都没活** |
| 4 个 | 2 | **一个都没活** |
**多出来的全是误命中**(同一段特征在别处也对上了),改了等于把别的判断逻辑弄坏。所以规则是:**只打离锚点最近的那一个**。
### 7.2 「替用户点确定」反而弄坏了本来能用的产品
这个是最大的坑,我一度把结论报错了,写出来提醒大家:
我装过一个**内联挂钩 `user32!MessageBoxA`** 的补丁:遇到空文本弹框就**直接返回 IDOK(1)**(等于替用户点了「确定」)。看着很聪明,实际上——
- 用户侧的真实描述是「**不点击弹窗就不会关闭**」,也就是说**点「确定」正是程序走退出分支的触发条件**;
- 于是这个补丁**把本来能用的产品全弄坏了**:登录后被自动「点掉」空框,程序立刻走退出分支,主界面再也出不来;
- 更坑的是**统计口径也错了**:这些产品因为提前退出、**根本没来得及弹框**,被记成了「不弹框」,于是得出「0 个还弹框」的假象,把问题藏了整整一天。
**怎么定位的**:拿历史版本做 A/B —— 某个时间点之前的构建**全部成功**,之后的**全部失败**;再把新增功能拆成独立开关逐个隔离,最后确认是内联钩子(只做记录、原样转发的记录桩是无辜的,也验证过)。
**结论**:真正解决「点弹窗就退出」的是**守卫分支补丁**,跟弹窗抑制无关。**别去替用户点按钮。**
---
## 八、结果和还没解决的部分
| 指标 | 结果 |
|---|---|
| 原始样本 | 158 个 |
| 能进主界面并稳定存活 | **137 个** |
| 交付文件完整性 | 逐文件 SHA256 与原件**完全一致**(137/137,0 异常) |
| 磁盘改动 | **零**(所有补丁只在进程内存) |
**还没解决的**:**26 个**产品启动后会弹一个「标题 `信息:`、正文为空」的小框。
我实测过:**点掉它不会关软件**(137 个逐个实测,点掉后 0 个退出),**不点也没事**;
但**框本身拦不掉** —— 它既不经过能挂的 IAT 槽,守卫补丁也匹配不到补丁点。
我试过「主界面已可见 + 框正文为空就自动点掉」,对 26 个复测直接**弄坏 11 个**(原因见 7.2),已回退。
要真消掉它,只能逐个产品反汇编找到弹框处改分支。
---
## 九、免责声明
本文**仅供技术学习与研究交流**,目的是理解网络验证机制的实现与防护思路,帮助开发者做出更健壮的产品。
文中所述方法针对的是**自己手上的样本**,**不提供任何成品、补丁、注册机,也不提供下载**。
请勿将本文技术用于任何未经授权的软件,请支持正版。一切后果由使用者自负。
=================================================================
**写得糙,步骤里跳过的细节不少,大佬们看出问题欢迎拍砖,我改。**