针对 m.ceair.com 移动端航班查询接口的完整逆向工程,实现纯 Python 协议请求绕过阿里云 WAF 多层防护(Web SDK 指纹 + Keepper 挑战 + 请求签名 + AES 加解密),并提供 Tkinter 桌面 GUI。
- 纯协议请求 — 不依赖浏览器自动化,直接 HTTP 请求获取航班数据
- 多层 WAF 绕过 — 阿里云 WAF(Web SDK + Keepper 挑战 + refer__1036 签名)
- AES 白盒加解密 — 请求体加密 + 响应体解密,调用 Node.js WASM 模块
- Tkinter 桌面 GUI — 城市选择 + 日期输入 + 航班列表展示
- AST 反混淆 — Babel 12 步流水线,还原控制流平坦化 / 字符串表 / 常量混淆
- 模块化架构 — JS 签名层与 Python 业务层分离,可单独复用
┌──────────────────────────────────────────────────────────────────┐
│ m.ceair.com 阿里云 WAF 防护 │
├──────────────┬──────────────────┬────────────────────────────────┤
│ 层 1: Cookie│ ssxmod_itna │ 浏览器指纹采集 (屏幕/WebGL/字体) │
│ │ ssxmod_itna2 │ LZ 压缩 + 随机种子签名 │
├──────────────┼──────────────────┼────────────────────────────────┤
│ 层 2: TLS │ Chrome 指纹校验 │ JA3/JA4 TLS 握手特征检测 │
├──────────────┼──────────────────┼────────────────────────────────┤
│ 层 3: Body │ M-CEAIR-ENCRYPTED│ WASM 白盒 AES-CBC 加密 │
│ │ req={base64} │ 固定 IV + 嵌入 WASM 的密钥 │
├──────────────┼──────────────────┼────────────────────────────────┤
│ 层 4: WAF │ _waf_bd8ce2ce37 │ Keepper 挑战 token (一次性) │
│ Challenge │ _waf_a86dfdc5f2 │ 时间戳绑定 │
├──────────────┼──────────────────┼────────────────────────────────┤
│ 层 5: 签名 │ refer__1036 │ LZ 压缩 (洗牌 alphabet) │
│ │ │ MD5 + 时间戳 + 指纹掩码 │
│ │ │ 7 字段联合签名 │
└──────────────┴──────────────────┴────────────────────────────────┘
┌──────────────┐
│ 发现目标接口 │
│ shoppingv2 │
└──────┬───────┘
│
┌──────────────▼──────────────┐
│ Charles / F12 抓包 │
│ 识别加密参数 & 防护层次 │
└──────────────┬──────────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │
┌───────▼───────┐ ┌───────▼───────┐ ┌───────▼───────┐
│ AES 加解密 │ │ WAF JS 提取 │ │ Cookie 分析 │
│ WASM wbsk_Wbox │ │ keepper 内联 │ │ ssxmod_itna │
│ 固定 IV 调用 │ │ 脚本还原 │ │ 指纹数据 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
│ ┌───────▼───────┐ │
│ │ AST 12步还原 │ │
│ │ encode→decode │ │
│ └───────┬───────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ 关键变量捕获 │ │
│ │ alphabet 洗牌 │ │
│ │ L3 指纹 枚举 │ │
│ └───────┬───────┘ │
│ │ │
└────────────────────────┼────────────────────────┘
│
┌────────▼────────┐
│ Python 集成 │
│ Execjs & 协议 │
└────────┬────────┘
│
┌────────▼────────┐
│ Tkinter GUI │
│ 终端用户交付 │
└─────────────────┘
| 层级 | 技术 | 用途 |
|---|---|---|
| 协议请求 | curl_cffi 0.15.0 |
HTTP/2 + TLS 指纹伪装 (Chrome 136) |
| JS 执行 | PyExecJS 1.5.1 |
Node.js 子进程运行签名算法 |
| AES 加解密 | Node.js + WASM | 白盒 AES-CBC (wbsk_Wbox) |
| AST 反混淆 | @babel/parser @babel/traverse @babel/generator |
JS 混淆代码 → 可读源码 |
| 日志 | loguru 0.7.3 |
GUI 模式日志输出 |
| GUI | Tkinter (内置) | 桌面查询工具 |
| 抓包分析 | Charles / F12 HAR | 请求对比 & 参数验证 |
问题: 网站使用 WASM 白盒 AES (wbsk_Wbox.js),密钥嵌入 WASM 二进制不可提取。
方案: 保留原始 wbsk_Wbox.js + enc_and_dec.js,通过 subprocess 调用 Node.js 执行加解密:
- 加密:
aes_encrypt(json_payload)→ Base64 密文 - 解密:
aes_decrypt(base64_response)→ JSON 明文 - 解决 Windows 命令行长度限制:
dec_file模式将密文写入临时文件
问题: 首次 POST 返回 Keepper HTML,内含一次性 challenge token,后续请求必须携带该 token 生成的签名。
方案: 两步协议流程:
- 首次 POST → 提取
<textarea id="renderData">中的_waf_bd8ce2ce37 - 生成
refer__1036签名 → 二次 POST 携带签名 _waf_a86dfdc5f2时间戳必须在收到 Keepper 响应之后获取
问题: WAF 的 v(z) 函数在运行时对 LZ 压缩 alphabet 进行 Fisher-Yates 洗牌,种子依赖 E[] 数组。使用标准 alphabet 会导致服务端解压失败。
方案: 浏览器 F12 日志断点捕获洗牌后的 alphabet:
WupoePhf73Q6wvF1jtJY9ig8bTk0zNmnEAlBR5VxU=crCyqaLDdGXM/2O_s4SKIZH
验证方法: v(z) 使用确定性 LCG (H() 函数),每次页面加载 alphabet 相同。
问题: en_refer1036.js 中 LK() 返回 encodeURIComponent(Lg),Python 的 session.post(params=...) 对参数值再编码一次。= → %3D → %253D。
方案: HAR 对比验证 → JS 中 LK() 返回值改为 Lg(不编码),由 Python HTTP 库处理编码。
问题: WAF JS 使用 while(!![]) + switch 控制流平坦化,-597 + 1*-937 + 110*15 整数混淆,LA(0x1a) 字符串表索引。
方案: 编写 Babel AST 12 步还原流水线,详见 AST 反混淆流水线。
问题: 服务端检测 JA3/JA4 TLS 指纹,错误的 Chrome 版本会被下发更严格的 aliyun_waf_aa/bb 挑战。
方案: curl_cffi 伪装 Chrome 136 TLS 指纹(最接近目标 Chrome 140)。
问题: Node.js execjs 环境中 L2() 函数检测到 global.Buffer 存在(浏览器无此对象),指纹异常。
方案: window.Buffer = undefined 清除 Node.js 特征。
encode.js (混淆WAF源码)
│
▼ 12步 Babel AST 变换
│
├─ 1. 元信息提取 (IIFE名/checksum/解码器/hash函数)
├─ 2. VM 沙箱加载解码器 (tk=276)
├─ 3. 解码调用替换: LA(0x1a) → "window"
├─ 4. Hex raw 清理: 0x1a → 26
├─ 5. 对象属性替换: tg.x → 160
├─ 6. 字符串拼接折叠: "push"+"s" → "pushs"
├─ 7. 常量折叠: -597+1*-937+110*15 → 116
├─ 8. x 对象解析: x["key"] → x.key / 字面量
├─ 9. x wrapper 内联: x.ylVFE(a,b) → a==b
├─ 10. 控制流平坦化还原: while(!![]){switch...} → 顺序语句
├─ 11. 死代码删除: try{push(175)...finally{pop()}}
└─ 12. 未使用变量清理 (5轮)
│
▼
decode.js (可读源码)
│
▼ 手动调整
en_refer1036.js (execjs 可用)
反混淆效果统计:
| 指标 | 还原前 | 还原后 |
|---|---|---|
| 可读性 | 完全不可读 | 函数名/逻辑清晰 |
| 控制流 | while+switch 平坦化 | 顺序语句 |
| 字符串 | 全为 LA(0xNN) 调用 | 明文字符串 |
| 常量 | 三个整数运算拼接 | 单个数值 |
| x 对象调用 | 大量 x.xxx() wrapper | 内联为原生运算 |
dh/
├── main.py # 协议请求主程序 (命令行)
├── tk_dh.py # Tkinter GUI 桌面工具
├── en_refer1036.js # refer__1036 签名生成 (execjs)
├── ssxmod.js # ssxmod Cookie 生成 (execjs)
├── enc_and_dec.js # AES 加解密 Node.js 脚本
├── wbsk_Wbox.js # WASM 白盒 AES 模块
├── requirements.txt # Python 依赖
├── .gitignore # Git 忽略规则
├── decode.js # WAF 原始内联脚本 (仅供参考)
├── README.md # 本文档
├── AST_refer1036/ # AST 反混淆工具链 (WAF)
├── AST_jquery/ # AST 反混淆工具链 (ssxmod 风格)
│ ├── ast.js # Babel 12 步还原
│ ├── encode.js # 混淆源码 (输入)
│ ├── decode.js # 还原后 (输出)
│ └── test_verify.js # 功能验证
├── m.ceair.com.har # 浏览器抓包 (调试用)
└── m.ceair.com_*.har # 代理抓包 (调试用)
- Python 3.10+
- Node.js 22+
- Windows (Tkinter GUI)
git clone https://github.com/luminouspathway/dh_inquire.git
cd dh_inquire
pip install -r requirements.txt命令行模式:
python main.pyGUI 模式:
python tk_dh.pymain.py 中需修改:
CWD = r'你的路径\enc_and_dec.js 所在目录'MIT License — 仅供学习交流,请勿用于非法用途。
本项目仅用于技术研究与学习,严禁用于任何商业用途或非法爬取行为。使用者须自行承担一切法律责任,开发者概不负责。
本项目是对阿里云 WAF 防护体系的完整逆向分析,核心技术点包括白盒 AES、运行时 Alphabet 洗牌、控制流平坦化 AST 还原、TLS 指纹模拟等。
