首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
WEB安全
发新帖
28
24
[原创] 某书 X-s 签名逆向分析(含风控点)-有了 AI人人都能还原VMP
发表于: 2026-1-2 23:44
18868
[原创] 某书 X-s 签名逆向分析(含风控点)-有了 AI人人都能还原VMP
执着的猫
1
2026-1-2 23:44
18868
# 某书 X-s 签名逆向分析 - VM 保护下的 RC4 算法还原 ## 0x01 抓包定位 首先抓包分析 `/api/sns/web/v1/homefeed` 接口,发现请求头中有两个关键签名参数: - `X-s`: 主签名 - `X-t`: 时间戳 全局搜索 `X-s`,定位到签名生成位置: ```javascript try { var _ = "X-s", b = "X-t", x = getRealUrl(a, c, d), p = seccore_signv2; p && (r.headers[_] = p(x, s), r.headers[b] = +new Date + "") } ``` ## 0x02 签名函数分析 跟进 `seccore_signv2`: ```javascript function seccore_signv2(e, r) { var a = window.toString, c = e; // 拼接 url + body "[object Object]" === a.call(r) || "[object Array]" === a.call(r) || (void 0 === r ? "undefined" : (0, m._)(r)) === "object" && null !== r ? c += JSON.stringify(r) : "string" == typeof r && (c += r); var d = (0, h.Pu)([c].join("")), // MD5(url + body) s = (0, h.Pu)(e), // MD5(url) f = window.mnsv2(c, d, s), // ★ 核心加密 l = { x0: u.i8, // 版本号 "4.3.0" x1: "xxx-pc-web", // appId x2: window[u.mj] || "PC", // 平台 x3: f, // mns 签名 x4: r ? void 0 === r ? "undefined" : (0, m._)(r) : "" }; return "XYS_" + (0, h.xE)((0, h.lz)(JSON.stringify(l))) } ``` 签名流程很清晰:`url + body` → MD5 → **mnsv2 加密** → JSON 包装 → 自定义 Base64 核心就是 `window.mnsv2`。 ## 0x03 定位 VM 入口 这是最难的一步。`window.mnsv2` 只是个入口,实际执行的解释器藏在别处。 **VM 的典型特征**: - 一个超长的 Base64 串或十六进制字符串(字节码) - `while(true) + switch` 或多层 `if-else` 结构(解释器) - 虚拟寄存器、栈操作 我使用自研的逆向 MCP 工具,通过 Hook + 调用栈追踪: ```javascript // 发现 22 个随机命名的全局函数 for (k in window) { if (/^_[a-f0-9]{20,}$/.test(k)) console.log(k); } // Hook 其中一个,观察调用栈 hook_function("window._0c6b9e549fef9ab9b4798ad1f12ea82b", logStack=true) ``` 最终定位到 VM 文件: ``` https://fe-static.xxxcdn.com/.../ds.js ``` ## 0x04 AST 解混淆 VM 解释器代码当然是混淆的。使用 Babel AST 配合 AI 进行解混淆: 1. **字符串数组还原** - 提取字符串表,替换索引访问 2. **常量折叠** - 计算静态表达式 3. **控制流还原** - 分析 switch-case 状态机 4. **死代码移除** - 删除无用分支 解混淆后得到可读的解释器代码。 ## 0x05 字节码提取 在代码中找到这样的字符串: ```javascript var bytecode = "ABt7CAAUSAAACADfSAAACAD1SAAACAAH..." // 约 6KB ``` 这就是 VM 的字节码,通过自定义的 switch 基于栈来执行。 > 基于栈的 VM 只是一种实现方式,还有基于寄存器的 VM,原理类似。 ## 0x06 构建操作码表 分析解释器的 switch-case,构建操作码映射: | OpCode | 助记符 | 操作 | |--------|--------|------| | 0x00 | PUSH | 压栈 | | 0x01 | POP | 出栈 | | 0x02 | ADD | 加法 | | 0x03 | CALL | 调用函数 | | ... | ... | ... | 这一步用 AI 辅助分析非常高效。 ## 0x07 反编译字节码 有了操作码表,就可以对字节码进行反编译: ```asm ; mns0301 字节码反编译 0000: PUSH_CONST "xh`)" ; 魔数 0004: PUSH_LOCAL r0 0008: CALL build_header 000C: PUSH_CONST 135 0010: CALL alloc_buffer ... ``` 这里需要**多次动态调试**来验证反编译结果的正确性,同样交给 AI 完成。 ## 0x08 函数分割与分析 反编译产出一个大文件,需要分割成独立函数: ``` output/mns0301/functions/ ├── build_input.js # 构建 135 字节输入 ├── rc4_encrypt.js # RC4 加密 ├── custom_base64.js # 自定义 Base64 └── main.js # 主流程 ``` > 有些厂商会在这层再加**控制流平坦化**,这时需要在浏览器中 trace 执行流程。 ## 0x09 算法还原 最后,结合静态分析和动态调试,还原完整算法。 ### 输入数据结构 (135 bytes) ``` 偏移 大小 字段 说明 ───────────────────────────────────────────────── [0-3] 4B Header "xh`)" 魔数 [4-7] 4B Random 随机数 (little-endian) [8-15] 8B Timestamp1 当前时间戳 ms [16-23] 8B Timestamp2 页面加载时间戳 ms [24-27] 4B Field3 固定值 (15-17) [28-31] 4B Field4 计数器 [32-35] 4B Field5 计数器 [36-43] 8B Double 随机浮点数 [44-96] 53B a1 "4" + a1_cookie (53字节) [97] 1B 分隔符 '\n' [98-107] 10B xsecappid "xxx-pc-web" [108] 1B 分隔符 '\x01' [109-134] 26B Extra 1随机 + 17固定 + 8随机 ───────────────────────────────────────────────── ``` ### 加密流程 ``` 输入构建 (135B) → RC4 加密 (预置 S-box) → 自定义 Base64 → "mns0301_" + result ``` ### 预置 S-box (256 bytes) ```python SBOX = [108, 71, 200, 252, 102, 41, 228, 110, 198, 188, 243, 68, ...] ``` ### 自定义 Base64 表 ``` MfgqrsbcyzPQRStuvC7mn501HIJBo2DEFTKdeNOwxWXYZap89+/A4UVLhijkl63G ``` ### XYS_ 外层编码 **Base64 表**: ``` ZmserbBoHQtNP+wOcza/LpngG8yJq42KWYj0DSfdikx3VT16IlUAFM97hECvuRX5 ``` **JSON 结构**: ```json { "x0": "4.3.0", // 版本号 "x1": "xxx-pc-web", // appId "x2": "Mac OS", // 平台 "x3": "mns0301_...", // mns0301 签名 "x4": "object" // 参数类型 } ``` ## 0x0A 风控检测机制 ### 核心原理:签名即数据上报 关键点:**mns0301 使用 RC4 + 预置 S-box 加密,服务器持有相同的 S-box,可以完全解密还原原始数据**。 签名不只是"防篡改",更是"行为数据上报通道"。 ``` ┌─────────────────────────────────────────────────────────────┐ │ 风控数据流 │ ├─────────────────────────────────────────────────────────────┤ │ 浏览器端 服务器端 │ │ ──────── ──────── │ │ 采集行为数据 │ │ ↓ │ │ 写入 135B 输入结构 │ │ ↓ │ │ RC4 加密 (预置 S-box) │ │ ↓ │ │ Base64 编码 → X-s 签名 ──────→ Base64 解码 │ │ ↓ │ │ RC4 解密 (相同 S-box) │ │ ↓ │ │ 还原 135B 原始数据 │ │ ↓ │ │ 分析行为特征 → 风控判定 │ └─────────────────────────────────────────────────────────────┘ ``` ### 签名中携带的风控数据 回顾 135 字节输入结构,**关键字段都是风控相关**: ``` 偏移 字段 风控用途 ───────────────────────────────────────────────── [4-7] Random 请求唯一标识,防重放 [8-15] Timestamp1 当前时间戳,检测时间异常 [16-23] Timestamp2 页面加载时间,计算停留时长 [24-27] Field3 行为标记位 [28-31] Field4 ★ 点击计数器 [32-35] Field5 ★ mouseenter 计数器 [36-43] Double 随机数,增加熵值 ───────────────────────────────────────────────── ``` ### 服务器风控判定逻辑(推测) 服务器解密签名后,可以进行以下判断: | 检测项 | 正常用户 | 爬虫/脚本 | |--------|----------|-----------| | 页面停留时长 | > 几秒 | 0 或极短 | | 点击计数 | 有累积 | 始终为 0 | | mouseenter 计数 | 有累积 | 始终为 0 | | 点击/mouseenter 比例 | 接近 1:N | 异常比例 | | 请求间隔 | > 100ms | < 77ms 或完全一致 | | 时间戳连续性 | 递增 | 跳跃或回退 | ### 行为采集与请求的关联 **核心机制:行为数据全局累积,每次请求时打包进签名** ``` ┌──────────────────────────────────────────────────────────────┐ │ 页面生命周期 │ ├──────────────────────────────────────────────────────────────┤ │ 页面加载 │ │ ↓ │ │ VM 初始化,对关键 DOM 元素挂载监听器 │ │ │ │ │ ├── #search-input (搜索框,鼠标必经区域) │ │ ├── #header-area (头部,几乎必经) │ │ ├── #homefeed_recommend (首页推荐,主内容区) │ │ └── #explore-guide-refresh (刷新按钮) │ │ ↓ │ │ 用户操作页面 → 计数器累积到全局变量 │ │ ↓ │ │ 发起 API 请求时,签名函数读取计数器写入 X-s │ └──────────────────────────────────────────────────────────────┘ ``` **为什么选这些元素?** 都是用户正常浏览**必然会经过**的区域。如果一个"用户"发了 100 个请求,但从未触碰过这些核心元素 → 爬虫。 ### 服务器如何判定风控 服务器通过**对比多次请求中的计数器变化**来判断: ``` 正常用户: ─────────────────────────────────────────────────── 请求1: 解密 → 点击=0, mouseenter=2, 停留=2s (刚进入) 请求2: 解密 → 点击=3, mouseenter=8, 停留=15s (正常浏览) 请求3: 解密 → 点击=5, mouseenter=12, 停留=30s (持续交互) ↑ 计数器递增,停留时长增加 → ✅ 正常 爬虫/脚本: ─────────────────────────────────────────────────── 请求1: 解密 → 点击=0, mouseenter=0, 停留=0s 请求2: 解密 → 点击=0, mouseenter=0, 停留=0s 请求3: 解密 → 点击=0, mouseenter=0, 停留=0s ↑ 全是 0,没有任何页面交互 → ❌ 异常 ``` ### VM 中的采集代码 从反汇编还原的逻辑: ```javascript // 对关键元素添加监听 document.querySelector("#search-input") .addEventListener("mouseenter", () => { window._xxx[6] = Date.now(); // 记录进入时间 }) .addEventListener("click", () => { let interval = Date.now() - window._xxx[6]; if (interval >= 77) { // 间隔 >= 77ms 才计入 window._xxx[13]++; // 点击计数器 +1 } }); ``` ### 77ms 阈值的意义 ```asm ; 反汇编关键代码 PUSH_INT8 77 LT ; if (interval < 77) 不计入有效点击 ``` 人类点击的物理极限约 50-100ms,77ms 是一个合理的阈值: - **< 77ms**:判定为程序触发,不计入计数器 - **≥ 77ms**:判定为人类操作,计数器 +1 ### 纯协议爬虫的挑战 对于不使用浏览器、直接发 HTTP 请求的纯协议爬虫,这套风控机制带来了显著挑战: **问题:计数器不能恒定** ``` ❌ 错误做法 1:计数器始终为 0 ─────────────────────────────────────── 请求1: 点击=0, mouseenter=0 → 服务器:没有任何交互,爬虫 请求2: 点击=0, mouseenter=0 请求3: 点击=0, mouseenter=0 ❌ 错误做法 2:计数器固定值 ─────────────────────────────────────── 请求1: 点击=5, mouseenter=10 → 服务器:数值不变,爬虫 请求2: 点击=5, mouseenter=10 请求3: 点击=5, mouseenter=10 ❌ 错误做法 3:计数器随机但无规律 ─────────────────────────────────────── 请求1: 点击=3, mouseenter=8 → 服务器:数值跳跃/回退,爬虫 请求2: 点击=7, mouseenter=2 (mouseenter 不应该减少) 请求3: 点击=1, mouseenter=15 (点击不应该减少) ``` **纯协议必须维护会话状态** ```python class SessionState: def __init__(self): self.click_count = 0 self.mouseenter_count = 0 self.page_load_time = int(time.time() * 1000) def before_request(self): # 模拟用户行为:每次请求前增加计数器 self.click_count += random.randint(1, 3) self.mouseenter_count += random.randint(2, 5) def get_sign_params(self): return { 'click': self.click_count, 'mouseenter': self.mouseenter_count, 'page_load': self.page_load_time, 'current': int(time.time() * 1000) } ``` **这套风控的设计意图** | 爬虫类型 | 难度 | 原因 | |---------|------|------| | 简单脚本 | ❌ 无法绕过 | 计数器为 0,立即识别 | | 固定参数 | ❌ 无法绕过 | 多次请求数值不变 | | 随机参数 | ⚠️ 可能被识别 | 数值变化不符合真实规律 | | 状态维护 | ✅ 可绕过 | 需要理解业务逻辑,成本高 | | 浏览器自动化 | ✅ 可绕过 | 真实触发事件,但效率低 | **结论**:纯协议爬虫必须维护会话状态,模拟计数器的**单调递增**趋势,这大大增加了爬虫的开发成本。 ## 0x0B Python 实现 ```python from output.x_sign import XSign # 初始化 x_sign = XSign(a1="your_a1_cookie", platform="Mac OS") # 生成签名 url = "/api/sns/web/v1/homefeed" params = {"cursor_score": "", "num": 27} headers = x_sign.get_headers(url, params) # {'X-s': 'XYS_...', 'X-t': '1767359305641'} ``` ## 0x0C 总结 ### 逆向分析流程 ``` ┌─────────────────────────────────────────────────────────┐ │ 逆向分析流程 │ ├─────────────────────────────────────────────────────────┤ │ 抓包定位 → 函数分析 → 定位 VM 入口 │ │ ↓ │ │ AST 解混淆 → 提取字节码 → 构建操作码 │ │ ↓ │ │ 反编译 → 函数分割 → 算法还原 → Python 实现 │ └─────────────────────────────────────────────────────────┘ ``` ### 架构总览 ``` ┌─────────────────────────────────────────────────────────────┐ │ 某书签名系统 │ ├─────────────────────────────────────────────────────────────┤ │ seccore_signv2(url, body) │ │ │ │ │ ▼ │ │ ┌─────────┐ ┌─────────────┐ ┌──────────────┐ │ │ │ MD5 │ → │ mnsv2 │ → │ customBase64 │ → X-s │ │ └─────────┘ └──────┬──────┘ └──────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────┐ │ │ │ mns0301 │ │ │ │ (RC4) │ │ │ └────┬─────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │ RC4 (JS) │ │ │ │ 预置 S-box │ │ │ └──────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` mns0301 使用 **RC4 流密码 + 预置 S-box**,整体难度适中,核心在于定位 VM 入口和还原字节码逻辑。
登录后可查看完整内容
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
收藏
・
28
点赞
・
24
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
wx_晨梦
你的分享对大家帮助很大,非常感谢!
2026-7-16 08:15
程序原
这个讨论对我很有帮助,谢谢!
2026-5-7 08:09
Yangser
期待更多优质内容的分享,论坛有你更精彩!
2026-3-25 09:52
yzmm0
谢谢你的细致分析,受益匪浅!
2026-3-24 20:58
正在缓冲99%
期待更多优质内容的分享,论坛有你更精彩!
2026-3-6 02:01
mb_ygrvwqld
你的分享对大家帮助很大,非常感谢!
2026-2-28 16:11
sinker_
为你点赞!
2026-2-18 19:13
beimingyouyu
为你点赞!
2026-2-12 09:56
Ally Switch
为你点赞!
2026-2-5 14:22
Bonnie-Ran
为你点赞!
2026-2-2 18:41
mb_hdiusdyf
感谢你的积极参与,期待更多精彩内容!
2026-1-31 21:44
从黎明到衰落
感谢你的贡献,论坛因你而更加精彩!
2026-1-19 10:44
点中你的心
为你点赞!
2026-1-14 17:19
铁柱7131
感谢你的贡献,论坛因你而更加精彩!
2026-1-9 03:15
frankyxu
感谢你分享这么好的资源!
2026-1-5 15:53
上网鱼
你的帖子非常有用,感谢分享!
2026-1-4 17:43
lvcoffee
感谢你的贡献,论坛因你而更加精彩!
2026-1-4 17:34
岁月。
感谢你的积极参与,期待更多精彩内容!
2026-1-4 10:00
尐进
+1
感谢你的积极参与,期待更多精彩内容!
2026-1-4 00:34
lrhtony
这个讨论对我很有帮助,谢谢!
2026-1-3 20:14
逆天而行
为你点赞!
2026-1-3 16:37
我的小拇指啊
为你点赞!
2026-1-3 15:00
孤独的街
感谢你分享这么好的资源!
2026-1-3 11:04
东方玻璃
期待更多优质内容的分享,论坛有你更精彩!
2026-1-3 10:43
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
6
)
huangjw
雪 币:
6728
活跃值:
(11927)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
513
粉丝
2
关注
私信
huangjw
2
楼
看看先
2026-1-4 09:19
0
natpacket
雪 币:
32
活跃值:
(897)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
13
粉丝
1
关注
私信
natpacket
3
楼
666
2026-1-5 15:18
0
TheLuckyOne
雪 币:
250
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
23
粉丝
0
关注
私信
TheLuckyOne
4
楼
tql
2026-1-5 18:31
0
zylyy
雪 币:
144
活跃值:
(2738)
能力值:
( LV2,RANK:10 )
在线值:
发帖
11
回帖
491
粉丝
3
关注
私信
zylyy
5
楼
666
2026-1-6 13:29
0
kkte
雪 币:
309
活跃值:
(1361)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
28
粉丝
3
关注
私信
kkte
6
楼
听君一席话 胜似一席话
2026-1-8 12:22
1
git_94730eaydwshawke-gif
雪 币:
0
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
44
粉丝
0
关注
私信
git_94730eaydwshawke-gif
7
楼
6
2026-1-26 16:53
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
执着的猫
1
20
发帖
23
回帖
120
RANK
关注
私信
他的文章
[原创] 我们给 DeepSeek Harness 加了一个逆向模式:逆向 Agent 的未来,也许就是 Harness 插件
5242
[原创] Superpowers 这类“教 AI 怎么写代码”的插件,过时了吗?
455
[原创]Claude Code 客户端安全机制逆向分析(二):从上报机制看中转池账号风险
11254
[原创] 从一次拒绝服务说起——Claude Code 客户端安全机制逆向分析(一)
17532
从 Web 到小程序:用 AI-First 思维重构逆向调试工具
10546
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部