一、背景:从明文到加密的逆向困境
在逆向某 TTS 接口时,请求体原本为明文传输;近期网站升级后,请求/响应均被加密。首次接触加密接口逆向时毫无头绪,最终借助调试工具与逻辑分析,理清了基于 WebAssembly 的加密解密流程。
二、逆向核心:定位 encrypt_req 为请求体加密函数
要破解加密逻辑,首先需确定哪个函数负责请求体加密。通过「函数语义 + 参数特征 + 代码逻辑 + Wasm 桥接规则 + 抓包验证」5 个维度,最终锁定 encrypt_req 是核心加密函数。
1. 从函数名语义锁定方向
encrypt_req 由 encrypt(加密)+ req(request,请求)组成,字面含义就是'对请求进行加密',结合'请求体是 HTTP 加密核心'的常识,初步判断其作用是加密请求体。
2. 从参数特征确认加密对象
函数定义为 export function encrypt_req(body) { ... }:
- Web 开发中,
body是'请求体'的标准命名(如fetch/XMLHttpRequest的请求体参数); - 函数唯一入参是
body,说明加密对象仅为请求体,与请求头/URL 无关。
3. 代码逻辑:JS→Wasm 的加密桥接
逐行解析 encrypt_req 代码,验证其'加密请求体'的核心行为:
| 代码行 | 代码内容 | 解析依据 |
|---|---|---|
| 1 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); | 调用 Wasm 栈管理函数,预留 16 字节空间存储加密结果,准备执行 Wasm 逻辑 |
| 2 | const ptr0 = passStringToWasm0(body, ...); | 通过 wasm-bindgen 工具,将 JS 请求体字符串转为 Wasm 可读取的内存格式(Wasm 无法直接读取 JS 字符串) |
| 3 | const len0 = WASM_VECTOR_LEN; | 获取请求体转内存后的字节长度,Wasm 通过'指针 + 长度'访问内存数据 |
| 4 | wasm.encrypt_req(retptr, ptr0, len0); | 调用 Wasm 层的 encrypt_req(JS 仅为桥接层,真正加密逻辑在 Wasm 中) |
| 5 | var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true); | 从预留栈空间读取 Wasm 返回结果:r0 是加密后的数据(含密文 + 密钥) |
| 6 | return takeObject(r0); | 将 Wasm 内存中的结果转回 JS 对象,最终返回加密后的请求体密文 + 密钥 |
4. Wasm 桥接规则:返回复合加密结果
wasm-bindgen 不支持 Wasm 直接返回多值,但可返回复合对象:
- 调试发现
takeObject(r0)先返回'堆索引(数字)',最终解析出包含 (请求体密文)和 (解密密钥)的对象;


