← 资料库索引 ← 个人博客 原始链接 ↗ 🔍
个人博客

拼多多 anti-content 参数逆向实战:Webpack 模块加载器与代理监控 原文标题:拼多多anti-content参数逆向实战:Webpack模块加载器与代理监控 - 道满PythonAI

发表时间:(页面未标注)采集时间:2026-10-09 10:58:20来源:daomanpy.com原文语言:zh状态:完整

内容概要总结

一篇以拼多多移动端网页(mobile.pinduoduo.com)为例的 anti-content 签名逆向实战笔记。anti-content 是拼多多网页/小程序端数据接口的“准入门票”,融合时间戳、浏览器/设备指纹、操作轨迹特征、动态加密盐,每次请求重新生成,且经过高度混淆与 Webpack 模块化封装并检测真实浏览器环境。文章指出四大难点(动态混淆更新、模块化封装、浏览器指纹检测、加密链路嵌套),给出完整方法论:先用 Chrome 抓包定位携带 anti_content 的 XHR/Fetch 请求;再通过全局搜索 anti_content、断点调用栈回溯锁定模块;关键突破口是加载器把 __pdd_require__ 暴露到全局 window.rrr。文中提供一套 JS Proxy 代理监控方案,为 window/navigator/screen/location/document/canvas/WebGLRenderingContext/performance 等敏感对象加 get/set 监听以发现缺失的环境属性,并附基础环境补全代码、模块加载器结构与常见报错的排查方法。结论建议:优先用 Playwright/Puppeteer 注入补丁,离线复刻需处理 canvas/WebGL 随机性,并持续跟踪核心 JS 更新。

原文内容(原文即中文)

实战网址: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 关键调试截图集

下面汇总了逆向全流程中使用的核心断点、调用栈及代码片段截图,点击可放大查看。

接口抓包全局搜索关键词XHR 断点触发调用栈回溯模块加载器入口全局暴露的加载器核心模块调用环境检测属性访问补环境报错排查监控代理生效插件安装思路插件配置
接口抓包

#二、核心技术难点

动态混淆与更新
核心 JS 代码会定期更换混淆规则,静态分析的复用性极为有限。

模块化封装
所有逻辑被打包进 Webpack 风格的自执行函数,没有直接暴露的全局方法,需要先突破模块加载器。

浏览器指纹检测
脚本会检测 window.navigator、canvas、WebGL 等近百个环境属性,任何一个异常都可能触发拦截。

加密链路嵌套
anti-content 内部可能组合了 Base64、SHA-1/SHA-256、自定义压缩等多种算法,追踪难度大。

#三、环境补全与代理监控

为了在补全浏览器环境的同时追踪所有环境属性的访问/修改,我们设计了一套轻量级的 JS 代理监控方案:先补全基础对象,再用 Proxy 监听对全局对象及 DOM/BOM 敏感对象的操作。

#3.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 = {
 // 同样后续补全
};

#3.2 通用代理监控函数

下面这个函数可以为任意全局对象加上“监控层”,在所有 get / set 操作时输出详细日志,帮助我们快速发现缺失的环境属性。

/**
 * 给指定对象数组添加 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}`
 );
 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);
 }
}

#3.3 监控敏感对象配置

const monitorTargets = [
 "window",
 "navigator",
 "screen",
 "location",
 "document",
 "canvas",
 "WebGLRenderingContext",
 "performance",
];
setProxy(monitorTargets);

提示:运行时先注入监控代码,再加载业务 JS,控制台会打印所有被访问和修改的环境属性。依据这些日志,就能有条不紊地完成“补环境”。

#四、代码分析与逆向思路

#4.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 暴露到了全局,这是后续快速定位模块的核心入口。

#4.2 定位核心 anti-content 生成模块

#步骤1:全局搜索入口

在开发者工具 Sources 面板的全局搜索框输入 anti_content 或 anti-content,找到给接口参数赋值的位置。

#步骤2:调用栈回溯

在赋值处打断点,刷新页面触发请求,查看 Call Stack 面板,找到调用链上层最接近混淆模块的位置。通常会看到类似 (new window.rrr(xxx))().messagePack() 的调用。

#步骤3:测试模块有效性

在控制台输入 window.rrr(找到的模块ID),如果能返回一个构造函数,再依次调用构造函数和 messagePack(),并得到合法的 anti-content 或一段加密字符串,就说明找对了模块。

#五、常见问题解决

#5.1 模块调用报错「x is not a function/undefined」

原因:核心模块依赖其他混淆模块,单独运行时这些依赖未被加载。
解决方法:

  • 在真实浏览器的控制台,先运行抓包到的完整加载器代码,再调用核心模块;
  • 或者通过 pdd_require.m 遍历所有模块,手动补全依赖链。

#5.2 环境检测拦截请求

原因:补全的环境属性数量、顺序、值的类型/范围与真实浏览器不符。
解决方法:

  • 先运行我们写好的代理监控代码,在真实浏览器完整触发一次接口请求;
  • 对照记录逐个补全环境对象的属性。特别要注意属性的 getter/setter 逻辑以及值的随机性(例如 canvas.toDataURL() 每次生成的 base64 数据都不相同)。

#六、总结与建议

本次逆向充分利用了 拼多多模块加载器暴露到全局 这一突破口,结合 XHR 断点 + 调用栈回溯 快速锁定了核心模块,并借助 Proxy 代理监控 完整追踪了环境依赖。后续如果希望稳定获取 anti-content 签名,建议从以下方向继续深入:

  • 优先使用自动化浏览器工具(Playwright / Puppeteer)注入补丁代码,降低环境模拟的复杂度;
  • 若必须离线复刻算法,一定要重点处理 canvas、WebGL 等指纹的随机性问题;
  • 关注核心 JS 文件的更新频率,定期重新定位模块 ID 与加密逻辑;
  • 利用本文提供的代理监控方案,持续完善环境属性,直至完全消除环境检测报错。

📚 推荐阅读

放大预览