发表于

前端逆向工程:从 Webpack/Vite 打包到滑块验证码与公私钥加密的全面解析

前端逆向工程:从 Webpack/Vite 打包到滑块验证码与公私钥加密的全面解析 - 封面图片
作者

前端逆向工程:从 Webpack/Vite 打包到滑块验证码与公私钥加密的全面解析

Webpack 和 Vite 的详细对比

Webpack 和 Vite 是现代前端开发中两种主流的构建工具,它们在打包原理、架构设计、性能表现以及适用场景上有着显著差异。以下将从底层打包原理、架构、技术差异、Vite 在开发和生产环境的不同处理方式,以及两者的历史发展背景进行详细分析。

一、Webpack 和 Vite 的底层打包原理与架构

1. Webpack 的打包原理与架构

打包原理: Webpack 是一个静态模块打包工具,其核心理念是将项目中的各种资源(如 JavaScript、CSS、图片等)视为模块,通过分析模块之间的依赖关系,将它们打包成一个或多个静态资源文件(通常是 bundle.js)。Webpack 的打包过程可以概括为以下步骤:

  1. 识别入口文件:从配置的 entry 文件开始,Webpack 分析项目的入口点。
  2. 构建依赖图:通过解析代码中的 import、require 等语句,递归识别所有模块的依赖关系,形成一个依赖图(Dependency Graph)。
  3. 模块处理:利用 Loader 将非 JavaScript 文件(如 CSS、图片、TypeScript 等)转换为 JavaScript 模块,并通过 Plugin 扩展功能(如代码压缩、提取 CSS 等)。
  4. 代码转换与编译:对模块进行转换(例如使用 Babel 转换 ES6+ 代码)、优化(如 Tree Shaking、代码分割)等操作。
  5. 输出 bundle:将处理后的模块合并为一个或多个 bundle 文件,输出到指定的目录。

架构特点

  • 全量打包:Webpack 在开发和生产环境中都会对整个项目进行打包,生成完整的 bundle 文件。开发环境中通过 Webpack Dev Server 提供热更新(HMR),但仍需重新编译受影响的模块链。
  • 模块化支持:支持 CommonJS、AMD、ES Modules 等多种模块化规范,兼容性强。
  • Loader 和 Plugin:Webpack 的核心优势在于其强大的生态系统。Loader 用于处理非 JavaScript 文件,Plugin 用于扩展打包过程中的功能(如代码优化、文件注入等)。
  • Node.js 驱动:Webpack 基于 Node.js 运行,使用 JavaScript 实现,性能受限于 JavaScript 的执行效率。

技术栈

  • 核心:Node.js(JavaScript 运行时)
  • 模块解析:通过 enhanced-resolve 解析模块依赖。
  • 代码转换:依赖 Loader(如 babel-loader、css-loader)和 Plugin(如 terser-webpack-plugin)。
  • 开发服务器:Webpack Dev Server 提供热更新和静态文件服务。

缺点

  • 启动速度慢:大型项目需要分析整个依赖图并打包,启动开发服务器耗时较长。
  • 热更新延迟:修改代码后,Webpack 需要重新编译受影响的模块链,大型项目中 HMR 速度较慢。
  • 配置复杂:需要编写详细的 webpack.config.js 文件,学习曲线陡峭。

2. Vite 的打包原理与架构

打包原理: Vite 是一个现代化的前端构建工具,由 Vue.js 作者尤雨溪开发,旨在提供更快的开发体验和更简单的配置。Vite 的核心设计理念是利用浏览器原生的 ES Modules(ESM)支持,将模块分为 依赖 和 源码 两类,优化开发和生产环境的构建流程。

开发环境

  • 无打包启动:Vite 不像 Webpack 那样在启动时对整个项目进行打包,而是直接启动一个开发服务器,利用浏览器的原生 ESM 按需加载模块。
  • 按需编译:当浏览器请求某个模块时,Vite 才会实时编译该模块及其依赖(例如将 Vue 单文件组件或 TypeScript 转换为 JavaScript)。这极大减少了启动时间。
  • 依赖预构建:Vite 使用 esbuild(基于 Go 语言的超快构建工具)对第三方依赖(如 node_modules 中的库)进行预构建,将其转换为 ESM 格式,缓存到 node_modules/.vite 中,减少浏览器请求的模块数量。
  • 热模块替换(HMR):Vite 的 HMR 基于 ESM,只更新修改的模块及其直接依赖,更新速度极快。

生产环境

  • Rollup 打包:Vite 在生产环境中使用 Rollup 进行打包。Rollup 专注于 ESM,擅长 Tree Shaking 和代码优化,生成更小的 bundle 文件。
  • 按需加载:Vite 利用 ESM 的动态导入(import())和代码分割,优化生产环境的加载性能。

架构特点

  • 双引擎设计:
    • 开发环境:基于 esbuild 和浏览器原生 ESM,提供快速冷启动和 HMR。
    • 生产环境:基于 Rollup,提供高效的 Tree Shaking 和代码压缩。
  • 原生 ESM:Vite 充分利用现代浏览器的 ESM 支持,将模块加载交给浏览器,减少构建工具的工作量。
  • 简单配置:Vite 的配置文件 vite.config.js 简单直观,默认提供现代化开发环境配置,开箱即用。
  • 插件系统:Vite 兼容 Rollup 的插件 API,同时支持专为 Vite 设计的插件,但生态系统较 Webpack 小。

技术栈

  • 核心:esbuild(Go 语言,预构建依赖)、Rollup(生产环境打包)。
  • 模块解析:利用浏览器原生 ESM,结合 esbuild 的高效解析。
  • 开发服务器:基于 Koa(Node.js 框架),提供快速的静态文件服务和 HMR。
  • 文件转换:支持 Vue、React、Svelte 等框架的单文件组件,内置 CSS 和 TypeScript 处理。

缺点

  • 生态不成熟:相比 Webpack,Vite 的插件和 Loader 生态较小,某些复杂场景可能需要手动配置。
  • 浏览器兼容性:依赖 ESM,要求目标浏览器支持原生 ES Modules(如 Chrome 61+、Firefox 60+),对旧浏览器支持需额外配置(如 @vitejs/plugin-legacy)。
  • CommonJS 限制:Vite 不直接支持 CommonJS 模块,需通过 esbuild 转换为 ESM。

二、Webpack 和 Vite 的技术与设计差异

以下从多个维度对比 Webpack 和 Vite 的区别:

维度WebpackVite
开发模式全量打包,启动开发服务器前需分析依赖图并生成 bundle无打包,利用浏览器 ESM,按需编译模块,启动几乎瞬时
打包速度较慢,需处理整个项目依赖图,Node.js 驱动开发环境极快(esbuild 预构建),生产环境用 Rollup 优化
热更新(HMR)全量更新,需重新编译受影响的模块链,速度随项目规模变慢增量更新,仅更新修改模块,速度快且稳定
插件生态成熟丰富,Loader 和 Plugin 支持几乎所有场景基于 Rollup 插件,生态较小,但快速增长
配置复杂度配置复杂,需详细设置 webpack.config.js配置简单,vite.config.js 开箱即用,适合快速开发
模块化支持支持 CommonJS、AMD、ESM 等多种格式,兼容性强专注于 ESM,需转换 CommonJS,适合现代浏览器
生产环境全程使用 Webpack 打包,生成 bundle,支持复杂优化使用 Rollup 打包,注重 Tree Shaking 和代码分割,生成更小 bundle
适用场景大型、复杂项目,需要高度定制化中小型项目、快速原型开发,注重开发体验

技术差异

  • 底层语言:
    • Webpack 使用 JavaScript(Node.js)实现,性能受限于 JavaScript 的执行效率。
    • Vite 的依赖预构建使用 esbuild(Go 语言,速度比 JavaScript 快 10-100 倍),生产环境使用 Rollup(JavaScript,但专注于 ESM 优化)。
  • 模块加载:
    • Webpack 通过打包生成 bundle,浏览器加载整个 bundle 文件。
    • Vite 利用浏览器原生 ESM,动态加载模块,减少打包开销。
  • HMR 实现:
    • Webpack 的 HMR 需要重新编译受影响的模块链,效率较低。
    • Vite 的 HMR 基于 ESM,只更新修改模块,效率极高。
  • 缓存机制:
    • Webpack 依赖文件系统缓存(如 cache 配置),但仍需全量打包。
    • Vite 使用 HTTP 缓存(304 Not Modified 协商缓存、Cache-Control: max-age=31536000,immutable 强缓存)优化依赖加载。

三、Vite 在开发环境与生产环境的打包差异

Vite 在开发环境和生产环境的打包策略完全不同,这是其设计的核心优势之一。

1. 开发环境

  • 无打包:Vite 不进行全量打包,而是启动一个基于 Koa 的开发服务器,直接服务源代码。
  • 按需编译:利用浏览器原生 ESM,当浏览器请求某个模块(如 .vue、.tsx)时,Vite 实时编译该模块及其依赖。
  • 依赖预构建:第三方依赖(如 React、Vue)由 esbuild 预构建为 ESM 格式,缓存到 node_modules/.vite,避免重复解析。
  • HMR:基于 ESM 的 HMR 只更新修改模块,速度极快,通常在毫秒级别。
  • 优势:启动快、HMR 高效、开发体验流畅,特别适合大型项目。

2. 生产环境

  • Rollup 打包:Vite 使用 Rollup 进行打包,生成优化的 bundle 文件。
  • Tree Shaking:Rollup 的静态分析能力优于 Webpack,移除未使用的代码更高效。
  • 代码分割:Vite 自动支持动态导入(import())和 CSS 代码分割,优化加载性能。
  • 压缩优化:Rollup 配合 terser 或 esbuild 进行代码压缩,生成更小的 bundle。
  • 优势:生成的文件体积小,加载速度快,适合生产环境部署。

差异总结

  • 开发环境:无打包,按需编译,依赖 esbuild 和浏览器 ESM,注重速度和开发体验。
  • 生产环境:全量打包,依赖 Rollup,注重代码优化和性能。
  • 一致性:Vite 通过预构建依赖和统一的插件 API(基于 Rollup)确保开发和生产环境的一致性,但打包流程完全不同。

四、Webpack 和 Vite 的历史发展

1. Webpack 的历史

  • 2012 年:Webpack 由 Tobias Koppers 创建,最初是为了解决 Browserify 的局限性(如缺乏对 CSS、图片等非 JS 资源的处理能力)。
  • 2014 年:Webpack 1.0 发布,引入模块打包、Loader 和 Plugin 机制,成为 Grunt 和 Gulp 的替代品。当时 JavaScript 生态以 CommonJS 为主,Webpack 提供了强大的模块化支持。
  • 2016-2017 年:Webpack 2 引入 Tree Shaking 和动态导入,支持 ES Modules,适应了前端框架(如 React、Vue)的快速发展。
  • 2018 年:Webpack 4 优化性能,引入 mode(开发/生产模式),默认支持零配置。
  • 2020 年:Webpack 5 发布,改进持久化缓存、模块联邦(Module Federation),并优化长期存在的性能问题。
  • 现状:Webpack 拥有成熟的生态系统,广泛应用于大型项目,但其复杂配置和高性能开销推动了更轻量工具的出现。

2. Vite 的历史

  • 2020 年:Vite 由尤雨溪在 Vue 3.0 开发期间推出,初衷是为 Vue 项目提供更快的构建工具。当时 Webpack 的启动和 HMR 速度在大型项目中成为瓶颈。
  • 2021 年:Vite 2.0 发布,支持多框架(Vue、React、Svelte 等),引入 esbuild 预构建依赖,确立了"无打包开发、Rollup 生产打包"的模式。Vue 3 的默认脚手架从 vue-cli(基于 Webpack)切换到 create-vite。
  • 2022-2023 年:Vite 生态快速发展,插件数量增加,支持更多框架和场景。ViteConf 2023 上,尤雨溪宣布 Rolldown(基于 Rust 的 Rollup 替代品)计划,旨在进一步提升构建性能。
  • 现状:Vite 已成为现代前端开发的首选工具,尤其在 Vue 和 React 社区中流行。生态系统虽不如 Webpack 成熟,但快速增长。

历史对比

  • Webpack:起步早(2012 年),经历了前端模块化从 CommonJS 到 ESM 的演变,生态成熟但复杂。
  • Vite:起步晚(2020 年),针对现代浏览器和 ESM 设计,强调速度和简单性,代表了下一代构建工具的方向。

五、详细分析与选择建议

1. 性能分析

  • 启动速度:Vite 的无打包启动和 esbuild 预构建使其在开发环境中启动速度远超 Webpack(毫秒级 vs 秒级)。
  • HMR 速度:Vite 的增量更新机制使其 HMR 速度几乎不受项目规模影响,而 Webpack 的全量更新在大项目中明显变慢。
  • 生产打包:Vite 的 Rollup 打包生成更小的 bundle,且 Tree Shaking 更高效;Webpack 的打包更全面但体积可能较大。

2. 生态与扩展性

  • Webpack:拥有庞大的 Loader 和 Plugin 生态,几乎支持所有场景,但需要复杂的配置和维护。
  • Vite:插件生态基于 Rollup,数量较少,但足以覆盖常见需求,配置简单,适合快速开发。

3. 适用场景

  • Webpack:
    • 适合大型、复杂项目,需要高度定制化(如微前端、模块联邦)。
    • 适合需要支持旧浏览器的项目(通过 polyfill 和 Loader)。
    • 适合已有 Webpack 配置的遗留项目。
  • Vite:
    • 适合中小型项目、快速原型开发或现代化前端框架(如 Vue 3、React)。
    • 适合追求开发体验和性能的团队。
    • 不适合需要复杂自定义构建逻辑或支持旧浏览器的项目(除非使用插件)。

4. 未来趋势

  • Webpack:仍在优化性能(如缓存、模块联邦),但受限于 Node.js 和全量打包模式,难以达到 Vite 的速度。
  • Vite:随着 Rolldown 的开发(Rust 实现的 Rollup),Vite 的性能和生态将进一步提升,有望取代 Webpack 在更多场景中的地位。

六、总结

Webpack 是一个功能强大、生态成熟的模块打包工具,适合复杂的大型项目,但其启动和 HMR 速度较慢,配置复杂。Vite 利用浏览器原生 ESM 和 esbuild,提供了极快的开发体验和简单的配置,生产环境中通过 Rollup 优化输出,适合现代前端开发。两者的核心区别在于设计理念:Webpack 强调全面性和兼容性,Vite 追求速度和现代化。

选择建议

  • 如果你的项目需要高度定制、支持复杂场景或兼容旧浏览器,选择 Webpack。
  • 如果你追求开发效率、快速迭代或使用现代框架(如 Vue 3、React),Vite 是更好的选择。
  • 对于新项目,推荐优先尝试 Vite,尤其在中小型项目中;对于遗留项目,逐步迁移到 Vite 需评估生态兼容性。

历史趋势: Webpack 代表了前端构建工具的上一代,解决了模块化问题但性能逐渐成为瓶颈;Vite 代表了下一代工具,利用现代浏览器特性和高效语言(如 Go、Rust)推动了构建速度的革命。随着浏览器对 ESM 的支持增强和 Vite 生态的成熟,Vite 有望在未来占据更大市场份额。

参考资料

以下是对 npm run build 和 npm run dev 在 Webpack 和 Vite 环境下的详细内部操作流程分析,涵盖每个步骤的技术细节、工具链行为、文件处理流程,以及最终打包输出的过程。由于问题中提到 Webpack 和 Vite,我将分别分析两者的流程,并以 Vite 的开发与生产环境差异为重点,同时结合 Webpack 的对比,全面解析从命令执行到最终输出的全过程。

1.1 逆向目标

  • 某花顺登录滑块逆向:提取登录过程中的加密参数(如 unamepasswdSalt)和滑块验证码验证逻辑,实现自动化登录。
  • Webpack 代码抠取:将 Webpack 打包的浏览器端代码移植到 Node.js 环境,调用关键函数(如 MD5、RSA 加密)。
  • 公私钥加密:分析 RSA 加密实现,探讨公钥指数固定的安全性。
  • 滑块验证码:理解为何验证码可用数据表示,并实现自动化识别。

1.2 工具准备

  • 浏览器开发者工具

    • Chrome/Firefox DevTools,分析网络请求和 JavaScript 代码
    • 使用 Network 面板监控 API 请求
    • 使用 Sources 面板进行代码调试
    • 使用 Console 面板执行测试代码
  • 反混淆工具

  • Source Map 工具

    • unwebpack-sourcemap:还原 Webpack Source Map
    • source-map-explorer:分析 bundle 结构
    • webpack-bundle-analyzer:可视化依赖关系
  • 抓包工具

    • Fiddler:Windows 平台抓包
    • Burp Suite:跨平台抓包,支持 HTTPS
    • Charles:Mac 平台抓包
  • 图像处理

    • Python 的 ddddocr:OCR 识别
    • Pillow:图像处理
    • OpenCV:计算机视觉
  • Node.js 环境

    • 运行逆向代码
    • 复用 Webpack 模块
    • 模拟浏览器环境

2. Webpack 与 Vite 打包代码的逆向

2.1 Webpack 打包特点

Webpack 是前端常用的模块打包工具,将 JavaScript、CSS 等资源打包为 bundle 文件(如 main.js)。其特点包括:

  • 模块化

    • 使用 __webpack_require__ 加载模块
    • 模块以数字 ID(如 1337)组织
    • 存储在 window.webpackChunk__webpack_modules__
  • 压缩与混淆

    • 生产模式下使用 Terser 压缩代码
    • 变量名缩短
    • 函数名重命名
    • 移除注释和空白
  • Source Map

    • 开发模式生成 .map 文件
    • 包含原始代码映射
    • 支持调试和错误追踪
  • 动态导入

    • 支持代码分割
    • 生成 chunk 文件(如 chunk-xxx.js
    • 按需加载优化性能

2.2 Vite 打包特点

Vite 基于 ES 模块(ESM)和 Rollup,特点如下:

  • ESM 驱动

    • 开发环境直接使用浏览器原生模块
    • 生产环境由 Rollup 打包
    • 更快的开发服务器启动
  • 简洁 bundle

    • 相比 Webpack,代码结构更清晰
    • 混淆程度较低
    • 更好的可读性
  • 依赖预打包

    • node_modules 依赖打包到 dist/assets
    • 使用 esbuild 进行预构建
    • 提高开发环境性能

2.3 逆向步骤

2.3.1 获取前端资源

  • 打开 DevTools

    // 禁用 F12 检测
    document.addEventListener('keydown', function(e) {
      if (e.key === 'F12') {
        e.preventDefault();
      }
    });
    
  • 提取 Source Map

    # 安装工具
    npm install -g unwebpack-sourcemap
    
    # 还原代码
    unwebpack-sourcemap main.js.map
    
    # 分析 bundle
    npx source-map-explorer main.js.map
    

2.3.2 反混淆代码

  • 使用 de4js
    // 压缩代码
    function a(b,c){return b+c}
    
    // 美化后
    function add(num1, num2) {
      return num1 + num2;
    }
    

2.3.3 定位关键逻辑

  • 搜索关键字

    // 在某花顺案例中搜索
    thsencrypt
    passwdsalt
    
  • 断点调试

    // 在 DevTools 中设置条件断点
    if (plaintext === 'user@example.com') {
      debugger;
    }
    
  • 模块结构

    // Webpack 模块注册
    (window.webpackChunk = window.webpackChunk || []).push([[245], {
      245: (module, exports, __webpack_require__) => {
        module.exports = { 
          encode: function(plaintext) {
            // 加密逻辑
          } 
        };
      }
    }]);
    

2.3.4 提取 Webpack 运行时

  • 定位 webpack_require

    function __webpack_require__(moduleId) {
      var cachedModule = __webpack_module_cache__[moduleId];
      if (cachedModule !== undefined) {
        return cachedModule.exports;
      }
      var module = __webpack_module_cache__[moduleId] = { exports: {} };
      __webpack_modules__[moduleId].call(
        module.exports, 
        module, 
        module.exports, 
        __webpack_require__
      );
      return module.exports;
    }
    
  • 全局导出

    // 添加全局访问
    globalThis.__exposedWebpackRequire = __webpack_require__;
    

2.3.5 收集依赖模块

  • 设置日志断点
    // 在 __webpack_require__ 中添加日志
    window.module_array += moduleId + ':' + __webpack_modules__[moduleId] + ',\n';
    
    // 清空缓存强制加载
    __webpack_module_cache__ = {};
    

2.3.6 补环境

  • 模拟浏览器环境
    // Node.js 环境补丁
    global.self = global;
    global.window = global;
    global.navigator = { 
      userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' 
    };
    global.document = {
      createEvent: () => ({ 
        timeStamp: Number(process.hrtime.bigint() / 1_000_000n) 
      })
    };
    

2.3.7 在 Node.js 中运行

  • 编写运行脚本
    // run_encrypt.js
    global.self = global;
    require('./main.bundle.js');
    require('./chunk-xxx.js');
    
    let __webpack_require__ = globalThis.__exposedWebpackRequire;
    let module_xxx = __webpack_require__('xxx');
    console.log(module_xxx.encode('user@example.com'));
    

3. 某花顺登录滑块逆向

3.1 背景

某花顺登录涉及账号密码加密和滑块验证码验证,核心目标是: 加密 uname 和 passwdSalt 参数,发送到 getGS 和 dologinreturnjson2 接口。 自动识别滑块验证码,生成验证参数(如 phrase 和 signature)。

3.2 加密逻辑

3.2.1 RSA 加密

前端实现:使用 thsencrypt.encode 函数,基于 RSA 算法加密账号和密码:

var thsencrypt = {
  encode: function(plaintext, modulus_hex, exponent_hex) {
    var rsa = new JSEncrypt();
    rsa.setPublicKey(modulus_hex, exponent_hex);
    return rsa.encrypt(plaintext);
  }
};
var uname = thsencrypt.encode('user@example.com', 'YOUR_MODULUS', '10001');

Python 还原:

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from base64 import b64encode

def encrypt_encode(plaintext: str) -> str:
    modulus_hex = "YOUR_MODULUS_HERE"  # 从 getGS 获取
    exponent_hex = "10001"
    modulus = int(modulus_hex, 16)
    exponent = int(exponent_hex, 16)
    key = RSA.construct((modulus, exponent))
    cipher = PKCS1_v1_5.new(key)
    plaintext_bytes = plaintext.encode('utf-8')
    encrypted_bytes = cipher.encrypt(plaintext_bytes)
    return b64encode(encrypted_bytes).decode('utf-8')

密码加密:密码先经过 MD5 加密:

import hashlib
def hex_md5(text: str) -> str:
    return hashlib.md5(text.encode('utf-8')).hexdigest().lower()
passwd_md5 = hex_md5('mypassword')
passwd_encrypted = encrypt_encode(passwd_md5)

3.2.2 passwdSalt 生成

逻辑:涉及 MD5、HmacSHA256 和 XOR 操作:

import hmac
import hashlib
from base64 import b64decode, b64encode

def get_str_xor(e: str, t: str) -> str:
    s = len(e)
    r = len(t)
    o = []
    for d in range(s):
        n = d % r
        xor_char = chr(ord(e[d]) ^ ord(t[n]))
        o.append(xor_char)
    return ''.join(o)

def encode_data_salt_once(passwd: str, uname: str, crnd: str, dsk: str, ssv: str, dsv: str) -> str:
    n = hashlib.sha256((crnd + dsk).encode('utf-8')).hexdigest()
    ssv_decoded = b64decode(ssv).decode('utf-8')
    n = get_str_xor(n, ssv_decoded)
    passwd_md5 = hex_md5(passwd)
    n = hmac.new(n.encode('utf-8'), passwd_md5.encode('utf-8'), hashlib.sha256).hexdigest()
    dsv_sha256 = hashlib.sha256(dsv.encode('utf-8')).hexdigest()
    n = get_str_xor(n, dsv_sha256)
    n = b64encode(n.encode('utf-8')).decode('utf-8')
    return encrypt_encode(n)

3.3 滑块验证码实现

3.3.1 验证码分析

滑块验证码的核心是验证用户滑动轨迹是否合法。主要验证点包括:

  1. 轨迹数据

    • 滑动距离
    • 滑动时间
    • 加速度变化
    • 轨迹点分布
  2. 特征提取

    def extract_track_features(track_points):
        features = {
            'distance': calculate_distance(track_points),
            'duration': track_points[-1]['timestamp'] - track_points[0]['timestamp'],
            'acceleration': calculate_acceleration(track_points),
            'point_count': len(track_points)
        }
        return features
    
  3. 轨迹生成

    def generate_human_like_track(distance: int) -> List[Dict]:
        track = []
        current_x = 0
        current_time = int(time.time() * 1000)
        
        # 初始加速
        for i in range(5):
            current_x += random.randint(2, 4)
            current_time += random.randint(10, 20)
            track.append({'x': current_x, 'y': 0, 'timestamp': current_time})
            
        # 匀速滑动
        while current_x < distance - 10:
            current_x += random.randint(1, 3)
            current_time += random.randint(15, 25)
            track.append({'x': current_x, 'y': 0, 'timestamp': current_time})
            
        # 减速
        while current_x < distance:
            current_x += random.randint(0, 2)
            current_time += random.randint(20, 30)
            track.append({'x': current_x, 'y': 0, 'timestamp': current_time})
            
        return track
    

3.3.2 验证码绕过

  1. 图像识别

    import ddddocr
    
    def get_slider_distance(bg_image: bytes, slider_image: bytes) -> int:
        ocr = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
        res = ocr.slide_match(bg_image, slider_image, simple_target=True)
        return res['target'][0]
    
  2. 轨迹优化

    def optimize_track(track: List[Dict], target_distance: int) -> List[Dict]:
        # 调整轨迹点分布
        optimized = []
        for point in track:
            # 添加随机偏移
            point['y'] = random.randint(-2, 2)
            # 调整时间间隔
            point['timestamp'] += random.randint(-5, 5)
            optimized.append(point)
        return optimized
    
  3. 请求构造

    def construct_verify_request(track: List[Dict], distance: int) -> Dict:
        return {
            'phrase': base64.b64encode(json.dumps(track).encode()).decode(),
            'signature': calculate_signature(track, distance),
            'distance': distance
        }
    

3.4 完整登录流程

async def login(username: str, password: str) -> Dict:
    # 1. 获取加密参数
    gs_response = await get_gs()
    modulus = gs_response['modulus']
    crnd = gs_response['crnd']
    
    # 2. 加密用户名和密码
    encrypted_username = encrypt_encode(username, modulus)
    encrypted_password = encrypt_encode(hex_md5(password), modulus)
    
    # 3. 生成 passwdSalt
    passwd_salt = encode_data_salt_once(
        password, username, crnd,
        gs_response['dsk'],
        gs_response['ssv'],
        gs_response['dsv']
    )
    
    # 4. 处理滑块验证码
    slider_data = await get_slider()
    distance = get_slider_distance(slider_data['bg'], slider_data['slider'])
    track = generate_human_like_track(distance)
    verify_data = construct_verify_request(track, distance)
    
    # 5. 发送登录请求
    login_data = {
        'uname': encrypted_username,
        'passwd': encrypted_password,
        'passwdSalt': passwd_salt,
        **verify_data
    }
    
    return await send_login_request(login_data)

4. 安全建议

4.1 前端加密

  1. 避免固定公钥

    • 定期更换 RSA 密钥对
    • 使用动态生成的密钥
    • 考虑使用非对称加密的其他方案
  2. 增加验证复杂度

    • 添加时间戳验证
    • 使用动态盐值
    • 实现请求签名机制
  3. 混淆保护

    • 使用 JavaScript 混淆工具
    • 实现代码自修改
    • 添加反调试机制

4.2 验证码优化

  1. 轨迹验证

    • 增加轨迹点验证
    • 添加加速度检测
    • 实现轨迹特征分析
  2. 图像处理

    • 增加图像干扰
    • 使用动态背景
    • 实现多图验证
  3. 行为分析

    • 记录用户行为特征
    • 实现风险评分
    • 添加二次验证

5. 总结

前端逆向工程是一个复杂而有趣的技术领域,需要深入理解前端框架、加密算法和验证码机制。通过本文的案例,我们展示了:

  1. Webpack/Vite 打包代码的逆向方法
  2. RSA 加密算法的实现和安全性分析
  3. 滑块验证码的自动化解决方案
  4. 完整登录流程的实现

在实际应用中,我们需要在安全性和用户体验之间找到平衡,既要保护系统安全,又要确保良好的用户体验。同时,也要注意逆向工程的法律和道德边界,确保技术的合理使用。


注:本文仅用于技术研究和学习目的,请勿用于非法用途。在实际项目中,建议采用更安全的加密方案和验证机制。