当前位置: 首页 > news >正文

JS逆向实战:破解前端加密参数payload与sig的完整流程

1. 项目概述:从抓包到逆向,破解前端JS加密参数

最近在分析一个数据接口时,遇到了典型的JS加密参数问题。目标网站的数据加载方式很有意思,它不是传统的分页,而是通过鼠标下滑动态加载。用Selenium这类自动化工具去模拟滚动,效率低不说,还容易被反爬机制识别。更关键的是,直接请求其API,返回的数据是加密的,而请求必须携带两个由前端JavaScript动态生成的加密参数:payloadsig。去掉或者传错这两个参数,服务器直接返回错误。这显然是一个学习JS逆向和Python模拟加密的绝佳案例。通过逆向分析,我们不仅能拿到数据,这套思路同样适用于分析那些对登录密码进行前端加密的站点,进行安全审计或授权测试。今天,我就把完整的分析思路、逆向过程和Python实现代码拆解清楚,让你不仅能复现,更能理解背后的“为什么”。

2. 核心思路与逆向分析前的准备

2.1 目标分析与工具选型

首先明确目标:我们需要绕过前端JS加密,直接使用Python构造合法的HTTP请求来获取数据。核心在于逆向出payloadsig这两个参数的生成算法。

为什么不直接用Selenium?原文提到了,网站是动态加载,没有分页。这意味着你需要用Selenium控制浏览器不断模拟滚动,等待新内容加载,再提取。这个过程极其耗时,对网络稳定性要求高,并且大量、频繁的自动化操作很容易触发网站的反爬策略(如IP限制、验证码)。对于需要稳定、高效获取数据的场景,直接逆向接口才是更优解。

工欲善其事,必先利其器。逆向分析离不开浏览器开发者工具:

  1. Chrome/Edge DevTools:核心工具。主要用到Network(网络)面板抓包,和Sources(源代码)面板进行JavaScript调试。
  2. Overrides(本地覆盖):Chrome DevTools的一个神级功能。允许你将在线JS文件映射到本地修改后的版本,实现断点持久化、代码修改即时生效,无需每次刷新都重新寻找断点。
  3. Python环境:需要requests库发起网络请求,以及hashlib等标准库用于实现加密算法。

注意:所有分析与操作均应在法律允许和网站授权范围内进行,仅用于学习交流与技术研究,切勿用于非法爬取、侵犯他人隐私或破坏网站正常运行。

2.2 抓包与参数定位

打开目标网页,清空Network记录,然后进行下滑操作,触发数据加载。很快就能在Network面板中找到一个XHR或Fetch请求,其响应内容是一串看似乱码的加密数据。

点击这个请求,查看Headers部分,重点是PayloadQuery String Parameters。在这个案例中,我们发现在请求的负载(Payload)里,有两个关键的参数:

  • payload: 一长串Base64编码样式的字符串。
  • sig: 一个32位的十六进制大写字符串,很像MD5。

尝试在Python中用requests库模拟这个请求,如果不带这两个参数,或者任意修改其中一个字符,服务器返回的都是错误信息(如{"code": 400, "msg": "invalid signature"})。这证实了这两个参数是服务端进行请求合法性校验的关键。

初步观察,payload参数很可能包含了我们真正的查询条件(如页码、每页条数),并被加密了。而sig看起来像是一个签名,很可能由payload加上某个密钥(_P)后,再经过MD5之类哈希算法生成,用于防止参数被篡改。

3. JS逆向实战:深入加密函数腹地

逆向的核心是在庞大的JS代码中找到生成这两个参数的函数。这需要耐心和技巧。

3.1 定位加密入口:搜索与断点

  1. 全局搜索:在Sources面板,按Ctrl+Shift+F进行全局搜索。可以尝试搜索关键词如payloadsigencryptmd5_P等。在这个案例中,搜索sig可能直接定位到类似sig: md5(e + _p).toUpperCase()的代码行,这就是突破口。
  2. XHR/Fetch断点:如果搜索无果,可以在Network面板找到那个请求的Initiator(发起者),点击跳转到发起请求的JS代码行。更通用的方法是,在Sources面板的XHR/Fetch Breakpoints里,添加一个包含部分请求URL的断点。当浏览器发起匹配的请求时,代码执行会自动暂停。
  3. 事件监听断点:在Event Listener Breakpoints中勾选Script->Script First Statement,然后触发页面动作(如下滑),代码会在第一时间暂停,然后你可以一步步(F10)执行,观察网络请求何时被发出。

通过上述方法,我们成功在代码中找到了疑似生成sig的地方,并在此处打上断点。

3.2 逆向payload加密流程

刷新页面或再次触发数据加载,代码会在断点处暂停。我们顺着调用栈(Call Stack)向上追溯,寻找payload被加密的地方。

从原文描述和调试过程,我们梳理出payload的加密路径:

  1. 明文payload对象:最初是一个简单的JavaScript对象,例如{sort: 1, start: 40, limit: 20}。其中start是起始位置,limit是每页条数,通过改变start来实现“翻页”。
  2. 进入函数e2(e):参数e是明文对象。内部调用了_u_e(e),这个函数看起来只是将对象JSON序列化成字符串'{"sort":1,"start":40,"limit":20}',此时值未变。
  3. e2(e)的后续操作:在_u_e返回后,e2函数内部有一个for循环,对字符串进行了处理。经过这一步,字符串变成了一个包含不可见字符和乱码的中间状态:",x177WB:d\ym{1L$'=x10nx02x04x15p8[ '&olwx022"`。这是一个关键转变,说明加密的核心可能发生在这里,可能是某种自定义的混淆或编码。
  4. 进入函数e1(e):上一步的结果作为参数e传入e1。这个函数执行后,返回了最终的payload加密值:LBc3V0I6ZGB5bXsxTCQnPRBuBwYJfnZeJCM7OXR/AH8q

逆向心得payload的加密并非标准算法(如AES),而是一个网站自定义的、由e2e1两个函数组成的流程。其中e2可能负责混淆,e1看起来像是进行了Base64编码(因为输出字符集符合Base64特征)。但注意,直接对中间状态的乱码做Base64编码,是得不到这个结果的。这说明e1内部可能还包含了字符替换或二次加密。我们需要把e2e1这两个函数的完整JS代码抠出来。

3.3 逆向sig签名生成流程

sig的生成相对清晰。在断点处继续执行,发现sig的值是通过md5(e + _p).toUpperCase()计算得出的。

  • e:就是上一步得到的加密后的payload字符串(LBc3V0I6ZGB5bXsxTCQnPRBuBwYJfnZeJCM7OXR/AH8q)。
  • _p:一个常量字符串,在JS代码中可以找到,例如可能是"W5D80NFZHAYB8EUI2T649RT2MNRMVE2O"
  • 将两者拼接,然后进行MD5哈希,最后将结果转为大写。

通过跟踪md5函数(通常是一个名为md5的函数或CryptoJS.MD5),确认其内部实现是标准的MD5算法,没有额外的魔改。这意味着在Python中,我们可以直接用hashlib库的标准MD5来实现,无需调用JS环境。

踩坑记录:在抠md5函数时,注意检查它是否依赖了其他全局变量或函数。有些站点会修改MD5的初始常量(IV)来定制算法。务必在调试器中,用相同的输入(payload+_p)运行JS的md5函数,与Pythonhashlib.md5().hexdigest().upper()的结果进行比对,确保完全一致。

4. Python实现:还原加密与数据获取

分析清楚后,我们就可以用Python来模拟整个流程了。这里分为两个部分:实现加密函数,以及发起请求并解密响应。

4.1 环境准备与JS代码移植

首先,我们需要将关键的JS函数移植到Python中。对于sig的MD5,直接用Python标准库。

import hashlib def generate_sig(encrypted_payload: str, p_constant: str) -> str: """ 生成 sig 参数 :param encrypted_payload: 加密后的 payload 字符串 :param p_constant: 从JS中提取的常量 _P :return: 大写格式的MD5 hexdigest """ data = encrypted_payload + p_constant return hashlib.md5(data.encode('utf-8')).hexdigest().upper()

对于payload的加密,我们需要将e2e1函数用Python重写。这需要仔细分析原JS代码。

假设我们通过“Overrides”功能将JS文件保存到本地,并仔细分析后,发现e2函数是一个简单的字符替换/位移混淆,e1是一个变种的Base64编码(可能更换了码表)。

import json import base64 # 假设我们分析出的 e2 函数是一个简单的异或混淆 def e2_js_logic(plain_dict: dict) -> str: """模拟JS中的 e2 函数""" json_str = json.dumps(plain_dict, separators=(',', ':')) # 模拟 _u_e,紧凑格式 # 假设JS中的for循环是每个字符码点与一个固定值进行异或 key = 0x17 # 这个key需要从JS代码中分析得出 confused_chars = [] for char in json_str: confused_chars.append(chr(ord(char) ^ key)) confused_str = ''.join(confused_chars) return confused_str # 假设我们分析出的 e1 函数是更换了码表的Base64编码 # 标准码表: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ # 自定义码表: 从JS代码中提取,例如可能是倒序或替换的 CUSTOM_B64_TABLE = "W5D80NFZHAYB8EUI2T649RT2MNRMVE2OabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/" # 示例,需替换真实码表 STANDARD_B64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" def e1_js_logic(confused_str: str) -> str: """模拟JS中的 e1 函数:自定义Base64编码""" # 先进行标准base64编码 standard_b64 = base64.b64encode(confused_str.encode('utf-8')).decode('ascii') # 然后根据自定义码表进行字符替换 translation_table = str.maketrans(STANDARD_B64_TABLE, CUSTOM_B64_TABLE) custom_b64 = standard_b64.translate(translation_table) return custom_b64 def generate_payload(plain_dict: dict) -> str: """生成加密的 payload 参数""" confused = e2_js_logic(plain_dict) encrypted = e1_js_logic(confused) return encrypted

重要提示:上面的e2_js_logice1_js_logic是示例逻辑,绝非真实算法。你必须根据自己逆向分析出的真实JS代码来编写对应的Python函数。可能需要使用execjs库直接执行抠出来的JS代码片段,这对于复杂混淆是最稳妥的办法。

import execjs # 将抠出来的完整e2和e1函数代码,放在一个字符串里 js_code = """ function _u_e(obj) { return JSON.stringify(obj); } function e2(e) { // ... 完整的e2函数JS代码 ... } function e1(e) { // ... 完整的e1函数JS代码 ... } function generatePayload(obj) { return e1(e2(obj)); } """ ctx = execjs.compile(js_code) encrypted_payload = ctx.call("generatePayload", {"sort": 1, "start": 40, "limit": 20}) print(encrypted_payload) # 输出应与浏览器生成的一致

4.2 构建请求与解密响应

有了生成参数的函数,就可以组装请求了。

import requests def get_data(page: int, page_size: int = 20): """ 获取某一页数据 :param page: 页码(从0开始) :param page_size: 每页条数 """ # 1. 构造明文参数 plain_params = { "sort": 1, "start": page * page_size, # 计算start "limit": page_size } # 2. 生成加密payload (使用execjs或Python还原函数) encrypted_payload = generate_payload(plain_params) # 使用上文定义的方法 # 3. 生成sig (假设已知常量_P) _P = "W5D80NFZHAYB8EUI2T649RT2MNRMVE2O" # 从JS中提取的真实常量 sig = generate_sig(encrypted_payload, _P) # 4. 构造请求 url = "https://目标网站/api/data" # 替换为真实API地址 headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Content-Type": "application/x-www-form-urlencoded", # 根据抓包确定 # 可能还需要其他Headers,如Referer, Authorization等 } data = { "payload": encrypted_payload, "sig": sig } # 5. 发送请求 resp = requests.post(url, data=data, headers=headers) resp.raise_for_status() # 检查HTTP错误 # 6. 处理加密响应 encrypted_response = resp.json().get('d') # 假设响应JSON中数据在'd'字段 if not encrypted_response: print("响应中未找到加密数据字段") return None # 响应数据也是加密的,需要找到对应的JS解密函数并移植 # 假设解密函数叫 decryptData decrypted_data = decrypt_response_data(encrypted_response) # 需要实现此函数 return decrypted_data # 使用示例 data_page_2 = get_data(page=2) # 获取第三页数据(start=40) if data_page_2: print(json.dumps(data_page_2, indent=2, ensure_ascii=False))

4.3 响应数据解密

原文提到返回的d字段也是加密的。我们需要用同样的逆向思路,找到解密d的JS函数。通常,解密函数会在同一个JS文件里,可能在加密函数附近。找到后,用同样的方式(分析算法用Python重写,或直接用execjs调用)实现解密。

def decrypt_response_data(encrypted_data: str) -> dict: """ 解密响应中的d字段 :param encrypted_data: 加密的字符串 :return: 解密后的Python字典 """ # 方法A:如果解密算法简单,用Python重写 # ... 分析JS解密函数并实现 ... # 方法B:使用execjs调用抠出来的JS解密函数(推荐,更可靠) js_decrypt_code = """ function decryptData(encryptedStr) { // ... 抠出来的完整JS解密函数 ... } """ ctx = execjs.compile(js_decrypt_code) decrypted_str = ctx.call("decryptData", encrypted_data) return json.loads(decrypted_str)

5. 常见问题、调试技巧与进阶思考

5.1 逆向与调试中的常见问题

  1. 断点被反调试:有些网站会检测开发者工具,并在调试时触发无限debugger或跳转。解决方法:

    • 在Sources面板,找到干扰的代码行,右键选择Never pause here
    • 使用Overrides功能,将包含反调试代码的JS文件替换为清理后的版本。
    • 使用条件断点(右键行号 ->Add conditional breakpoint),设置一个永远为假的条件。
  2. 代码被混淆压缩:变量名变成a,b,c,难以阅读。解决方法:

    • 利用浏览器的Pretty Print功能({}图标)格式化代码。
    • 关注字符串常量、API接口URL,它们通常不会被混淆,是重要的定位锚点。
    • 逐步执行,观察变量值的变化来推断函数功能。
  3. 加密函数依赖浏览器环境:某些函数可能依赖windowdocument或其他浏览器特有对象。在Node.js或execjs环境中执行时会报错。解决方法:

    • 使用jsdompyppeteer等库模拟一个简易浏览器环境。
    • 更简单的方法是,分析其依赖,在JS代码执行前,手动在全局注入这些缺失的对象或函数(window = {}; document = {};),但只注入必要的空对象或模拟函数。
  4. Python生成的签名与JS不一致:这是最常遇到的问题。

    • 编码问题:确保字符串拼接和编码完全一致。JS和Python的字符串编码要统一,通常使用UTF-8。在MD5前,确认拼接后的字符串完全一致,包括不可见字符。
    • 常量错误:双重检查从JS中提取的常量_P是否正确,是否有隐藏的换行符或空格。
    • 算法差异:确认JS中的MD5是否是标准实现。可以用一个已知的字符串(如"test")分别在JS控制台和Python中计算MD5,进行比对。

5.2 效率优化与工程化建议

  1. 缓存与复用:对于固定的_P常量和不变化的加密逻辑部分,只需初始化一次(如execjs编译环境),避免每次请求都重新编译JS,极大提升效率。
  2. 错误重试与日志:网络请求不稳定,应添加重试机制和详细的日志记录,记录每次请求的参数和响应,便于排查问题。
  3. 参数化与配置:将URL、请求头、常量_P、加解密函数代码等提取到配置文件或单独模块中,使代码更易维护。
  4. 应对算法更新:网站可能会更新加密算法。最好能监控请求是否突然开始失败,并准备快速响应,重新进行逆向分析。

5.3 进阶:更复杂的加密与RPC调用

本例的加密相对直接。你可能会遇到更复杂的情况:

  • WebAssembly加密:核心算法用Wasm实现,逆向难度大。可以考虑直接调用Wasm模块,或者用工具将Wasm反编译为C/Go再分析。
  • Obfuscator等高级混淆:代码被严重混淆,控制流扁平化。需要耐心和强大的静态分析工具辅助。
  • 动态密钥_P这类常量可能不是固定的,而是每次页面加载时从服务器动态获取。这就需要先请求一个初始化接口,获取本次会话的密钥。

这套JS逆向的思路,其价值远不止于爬虫。在Web安全测试中,常用于分析登录接口的密码加密方式,进行安全的密码爆破测试(在授权范围内)。在前端开发中,理解加密流程有助于设计更安全的API交互方案。整个过程锻炼的是对网络协议、浏览器运行机制和代码调试的深入理解能力,这才是最重要的收获。当你再遇到加密参数时,不会再感到无从下手,而是会系统地抓包、搜索、下断点、跟栈、抠代码,最终解决问题。

http://www.jsqmd.com/news/873138/

相关文章:

  • C++引用的详细解释
  • Linux终端字体终极指南:10款精选字体与安装优化全解析
  • 【流体】二维稳态不可压缩层流通道流利用FVM和SIMPLE 解平行板间层流的速度、压力和温度【含Matlab源码 15558期】
  • 为开源项目OpenClaw配置Taotoken作为AI能力供应商的步骤
  • RK3568 SPI驱动实战:MCP2515 CAN控制器寄存器读写原理与优化
  • 18分钟攻破GitHub:TeamPCP供应链攻击全技术解析与防御新范式
  • 如何快速解决Windows 11区域模拟问题:完整API钩子技术指南
  • 为OpenClaw智能体工作流配置Taotoken后端模型
  • S-Video端口ESD防护方案:TVS阵列选型与PCB布局实战指南
  • 芯片设计后期DFT友好ECO:原理、实践与工具选型
  • 全志T113-S3开发板XR829 WiFi蓝牙驱动加载、固件配置与稳定性测试全攻略
  • 西恩士液冷板清洁度萃取设备/清洗机:从源头守护液冷系统“血液”洁净 - 工业设备研究社
  • CVE-2026-9082深度解析:Drupal十年最致命SQL注入,补丁发布3小时即遭全球轰炸
  • 基于RK3399核心板的智能PCR仪开发:从嵌入式系统到高精度温控
  • 为内部培训系统集成Taotoken提供个性化学习内容生成与答疑
  • Photoshop 2026(PSv27.x)详细安装教程与下载地址
  • 【学习笔记】探讨大模型应用安全建设系列8——成果汇报与持续运营
  • 为什么92%的健身APP AI聊天功能被弃用?(行为日志分析+3周A/B测试结论)
  • RK3588蓝牙功能完整测试指南:从驱动到应用实战
  • 嵌入式开发硬件生态构建:MIPI屏、UVC摄像头与4G模块的选型与集成实战
  • S-Video端口ESD防护方案解析:低电容TVS阵列选型与PCB布局实战
  • RK3588开发板蓝牙功能快速测试与配置指南
  • 汽车12V电源保护:TVS二极管选型、应用与EMC测试实战
  • 隐私至上:2026加密不存库PDF转Word工具推荐 - 时讯资讯
  • 2026年企业流量增长视角下档案托管行业GEO优化三家服务商专业分析与选型参考 - 产业观察网
  • 推理 → 行动 → 观察:用 LangChain + Python 实现一个智能体循环
  • 实测SpringBoot集成Taotoken后API调用的延迟与稳定性表现
  • AI智能体Skills设计:从API工具到核心能力的工程实践
  • 汽车12V电源防护:P6KE TVS二极管选型、设计与实战指南
  • Taotoken API Key管理与访问控制功能实际使用反馈