拼多多电商数据 anti-content 参数逆向实战
实战网址:https://mobile.pinduoduo.com/
实战目标
快速定位生成 anti-content 签名的核心 JS 模块,了解其环境依赖,给出可运行的代理监控环境补全方案,为后续签名复刻或自动化获取打下基础。
概述
拼多多的 anti-content 是网页/小程序端数据接口的「准入门票」——它包含时间戳、浏览器/设备指纹、轻量级操作轨迹特征、动态加密盐等多维度信息,每次请求都会重新生成,且代码经过高度混淆、压缩与模块化封装,还会检测是否运行在真实浏览器环境中。
网页与调试分析
1.1 接口抓包
打开 Chrome 开发者工具的「Network」面板,勾选「Preserve log」,刷新或在拼多多首页点击商品分类,找到带业务数据的 XHR/Fetch 请求(比如 goods_search、goods_detail),在请求参数/Query 里就能看到 anti_content。
1.2 关键调试截图集
(下方为逆向全流程用到的核心断点、调用栈、代码片段截图,点击可放大查看)
点击展开截图

核心技术难点
本次逆向的核心挑战集中在4点:
- 动态混淆与更新:核心 JS 代码会定期更新混淆规则,静态分析的复用性有限
- 模块化封装:所有逻辑被打包进 Webpack 风格的自执行函数,没有直接暴露的全局方法
- 浏览器指纹检测:会检测
window.navigator、canvas、WebGL 等近百个环境属性
- 加密链路嵌套:
anti-content 内部可能组合了 Base64、SHA-1/SHA-256、自定义压缩等算法
环境补全与代理监控
为了复刻浏览器环境的同时追踪所有环境属性的访问/修改,我们可以写一个轻量级的 JS 代理监控方案——先补全基础对象,再用 Proxy 监听全局对象、DOM/BOM 敏感对象的操作。
2.1 基础环境补全(极简版)
// 先创建基础全局对象,防止第一帧报错
this.window = this;
this.navigator = {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
platform: "iPhone",
language: "zh-CN",
languages: ["zh-CN", "zh", "en"],
// 后续可通过监控补全
};
this.document = {
// 可补全基础DOM元素,但拼多多检测较少
};
this.canvas = {
// 同样后续补全
};
2.2 通用代理监控函数
/**
* 给指定对象数组添加 get/set 代理,打印所有访问/修改操作
* @param {Array<string>} proxyObjArr 要代理的全局对象名数组
*/
function setProxy(proxyObjArr) {
for (const objName of proxyObjArr) {
// 统一处理对象初始化和代理包装
let targetObj;
try {
targetObj = this[objName];
} catch (e) {
targetObj = {};
}
// 定义代理处理器,避免直接字符串拼接导致的语法错误
const handler = {
get(target, property, receiver) {
console.log(
`🔍 GET | 对象: ${objName} | 属性: ${String(property)} | 类型: ${typeof property}`
);
// 如果属性不存在,打印空占位符,避免访问 undefined 直接报错
const value = target[property];
console.log(` ↳ 返回值: ${value} | 类型: ${typeof value}\n`);
return value;
},
set(target, property, value, receiver) {
console.log(
`✏️ SET | 对象: ${objName} | 属性: ${String(property)} | 新值: ${value} | 类型: ${typeof value}\n`
);
return Reflect.set(target, property, value, receiver);
},
};
// 覆盖全局对象为代理对象
this[objName] = new Proxy(targetObj, handler);
}
}
2.3 监控敏感对象配置
拼多多重点检测以下对象,建议优先加入监控数组:
const monitorTargets = [
"window",
"navigator",
"screen",
"location",
"document",
"canvas",
"WebGLRenderingContext",
"performance",
];
setProxy(monitorTargets);
代码分析与逆向思路
3.1 模块加载器拆解
从截图中可以看到,拼多多使用的是简化版 Webpack 5 自执行模块加载器,结构如下:
!function(modules) {
// 1. 模块缓存
var moduleCache = {};
// 2. 核心加载函数
function __pdd_require__(moduleId) {
// 命中缓存直接返回
if (moduleCache[moduleId]) return moduleCache[moduleId].exports;
// 未命中则初始化模块
var newModule = moduleCache[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// 执行模块代码
modules[moduleId].call(newModule.exports, newModule, newModule.exports, __pdd_require__);
newModule.l = true;
return newModule.exports;
}
// 3. 暴露工具和模块数组
__pdd_require__.m = modules;
__pdd_require__.c = moduleCache;
// ... 省略其他 Webpack 内置工具
__pdd_require__.p = ""; // 公共资源路径
// 4. 暴露到全局,方便调试!
window.rrr = __pdd_require__;
}(/* 这里是混淆压缩后的模块数组 */);
关键点:加载器通过 window.rrr 暴露到了全局,这是后续快速定位模块的核心入口。
3.2 定位核心 anti-content 生成模块
步骤1:全局搜索入口
在开发者工具「Sources」面板的全局搜索框输入 anti_content 或 anti-content,找到给接口参数赋值的位置。
步骤2:调用栈回溯
在赋值处打断点,刷新触发请求,查看「Call Stack」面板,找到调用链上层最接近混淆模块的位置——通常会看到类似 (new window.rrr(xxx))().messagePack() 的调用。
步骤3:测试模块有效性
在控制台输入 window.rrr(找到的模块ID),如果能返回一个构造函数,再调用构造函数和 messagePack(),能得到合法的 anti-content 或至少是一段字符串,说明找对了模块。
常见问题解决
4.1 模块调用报错「x is not a function/undefined」
原因:核心模块依赖其他混淆模块,这些依赖在单独运行时可能没有加载。
解决:在真实浏览器的控制台,先运行抓包到的完整加载器代码,再调用核心模块;或者用 __pdd_require__.m 遍历所有模块,补全依赖链。
4.2 环境检测拦截请求
原因:补全的环境属性数量、顺序、值的类型/范围与真实浏览器不符。
解决:
- 先运行我们写的代理监控,在真实浏览器完整触发一次接口请求
- 把控制台打印的所有
GET 操作记录下来
- 对照记录逐个补全环境对象的属性,注意属性的 getter/setter 逻辑、值的随机性(比如
canvas.toDataURL() 每次生成的都不一样)
总结
本次逆向主要利用了「拼多多模块加载器暴露到全局」这个突破口,结合「XHR断点+调用栈回溯」快速定位核心模块,再通过「Proxy 代理监控」追踪环境依赖。后续如果要实现稳定的签名获取,建议:
- 优先使用自动化浏览器工具(Playwright/Puppeteer)注入代码获取,减少环境补全的工作量
- 如果必须离线复刻,要重点处理 canvas/WebGL 指纹的随机性
- 关注代码的更新频率,定期重新定位模块
(全文完,约2700字)