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

Python 爬虫逆向实战 4:JS 混淆 AST 解混淆 + webpack 打包代码拆包还原

前言

大量加密接口站点使用 JS 混淆、webpack 打包压缩、eval 加密、JJSC/obfuscator 变量乱码,前端源码全是随机变量名、控制流平坦化、字符串加密,常规抠 JS 加密代码无法直接复用。本章从 AST 抽象语法树原理、webpack 拆包、JS 反混淆落地,分手动格式化、简易 AST 解混淆、在线工具原理复刻,配套 Python 脚本还原混淆 JS,无缝衔接前文 execjs 加密逆向工程。

本文所需依赖官方文档超链接:

  1. esprima-python
  2. Requests 官方文档
  3. estraverse

一、JS 混淆常见类型与逆向难点

1.1 主流混淆手段

  1. 变量名混淆:变量 / 函数名变为_0x12ab_0x3f5d无意义十六进制字符;
  2. 字符串数组加密:所有明文统一存入数组,运行时下标取值解密;
  3. 控制流平坦化:大量 switch+while 打乱代码执行顺序;
  4. webpack 打包:代码被打包成!function(e,t,n){...}闭包模块,多接口代码揉在同一个 JS;
  5. eval/atob 加密:代码 base64 编码后 eval 动态执行,源码肉眼不可读。

1.2 逆向阻碍

无法定位加密函数、密钥、AES/RSA 逻辑,无法直接复制 JS 用于 execjs 调用。

二、环境依赖安装

bash

运行

pip install esprima==4.0.1 estraverse==5.3.0 requests==2.31.0

三、模块 1:JS 基础预处理(去 eval、base64 解码外层加密)

python

运行

import base64 import re def decode_eval_b64(js_raw:str): """解析eval(atob('xxx'))外层base64加密代码""" reg_b64 = re.compile(r'atob\(["\']([A-Za-z0-9+/=]+)["\']\)') res_list = reg_b64.findall(js_raw) for b64_str in res_list: try: decode_code = base64.b64decode(b64_str).decode("utf-8") js_raw = js_raw.replace(f"atob('{b64_str}')",f'"{decode_code}"') except: continue return js_raw

四、模块 2:AST 结构说明 + 字符串数组解密(高频混淆核心)

4.1 AST 解混淆逻辑

  1. esprima 将 JS 转为 AST 抽象语法树;
  2. 遍历 AST 提取加密字符串数组;
  3. 替换代码中数组下标取值为原始明文;
  4. 生成还原后可读 JS 源码。

python

运行

import esprima import estraverse def str_array_deobfuscate(js_code): # 解析生成AST树 ast = esprima.parseScript(js_code) str_dict = {} # 第一步:遍历提取全局字符串数组 def collect_str(node,parent): nonlocal str_dict # 匹配var _0xabc=["xxx","yyy"]数组定义 if node.type == "VariableDeclaration": for dec in node.declarations: if dec.init and dec.init.type == "ArrayExpression": arr_val = [] for elem in dec.init.elements: if elem.type == "Literal" and isinstance(elem.value,str): arr_val.append(elem.value) if arr_val: var_name = dec.id.name str_dict[var_name] = arr_val estraverse.traverse(ast,{"enter":collect_str}) # 第二步:替换下标取值 _0xabc[0] → "xxx" def replace_index(node,parent): if node.type == "MemberExpression": if node.object.type=="Identifier" and node.property.type=="Literal": var_n = node.object.name idx = node.property.value if var_n in str_dict and idx < len(str_dict[var_n]): node.type = "Literal" node.value = str_dict[var_n][idx] del node.object del node.property estraverse.traverse(ast,{"enter":replace_index}) # AST转回JS字符串 from escodegen import generate clean_js = generate(ast) return clean_js

五、模块 3:webpack 打包 JS 拆包提取指定模块

webpack 打包代码采用模块 ID 映射,通过 ID 分离单个接口加密模块:

python

运行

def split_webpack_js(js_all:str,target_module_id:int): """拆分webpack,根据模块ID截取单独JS代码""" # 简易正则匹配模块包裹结构 reg_mod = re.compile(r',(\d+):\[(function.*?)\],',re.S) mod_map = {} for mid,code in reg_mod.findall(js_all): mod_map[int(mid)] = code if target_module_id in mod_map: return f"var module={{}};var exports={{}};{mod_map[target_module_id]}" return ""

六、完整一键解混淆调用链路

python

运行

def full_deobfuscate(raw_js): # 1.解码外层base64 eval step1 = decode_eval_b64(raw_js) # 2.AST字符串数组解密 step2 = str_array_deobfuscate(step1) return step2 # 使用示例 if __name__ == "__main__": with open("obf_code.js","r",encoding="utf-8") as f: obf_js = f.read() clear_code = full_deobfuscate(obf_js) with open("clear_code.js","w",encoding="utf-8") as fw: fw.write(clear_code) print("解混淆完成,已输出clear_code.js")

七、配合前文 AES 逆向实战:解混淆后提取加密函数

python

运行

def get_encrypt_func_from_deob(clear_js): """解混淆完成后正则提取AES加密函数""" reg_encrypt = re.compile(r'function\s+aesEncrypt\(.*?\{.*?\}',re.S) func_list = reg_encrypt.findall(clear_js) return func_list

八、进阶:控制流平坦化简易优化思路

控制流混淆依靠 while+switch 调度代码,人工优化方案:

  1. 解混淆后格式化 JS,标注 case 对应执行逻辑;
  2. 删除无用 switch 调度代码,按执行顺序重写原生代码;
  3. 精简后代码直接用于 execjs 加密。

九、Playwright 动态运行提取明文(懒人免 AST 方案)

python

运行

from playwright.sync_api import sync_playwright def auto_get_decrypt_str(ob_js): with sync_playwright() as pw: browser = pw.chromium.launch(headless=True) page = browser.new_page() # 在页面注入混淆JS,运行后导出解密后的全局变量 page.evaluate(ob_js) # 打印全局解密后的加密函数 res = page.evaluate("JSON.stringify(aesEncrypt)") browser.close() return res

十、常见故障与优化表

表格

异常现象解决方案
AST 解析报错语法错误剔除注释、补全缺失分号,预处理脏字符
部分下标无法替换数组为动态运行生成,改用浏览器运行提取
webpack 多依赖报错拆分依赖模块,补齐 require 模拟环境
多层嵌套混淆循环多次执行解混淆脚本逐层解密

十一、本章总结

JS 混淆逆向标准流程:外层 Base64/Eval 解码 → AST 字符串解密 → webpack 拆包 → 格式化精简源码 → 提取加密逻辑;小规模调试用 Playwright 动态运行取值,大批量逆向用 Python+AST 自动化解混淆,解混淆后的干净 JS 可无缝对接 execjs 实现参数加密爬虫。后续拓展:JS 虚拟机还原、obfuscator 全量反混淆、RPC 方式调用浏览器 V8 引擎解密。

http://www.zskr.cn/news/1469328.html

相关文章:

  • 12MHz晶振51单片机实现精准9600波特率串口通信方案
  • VSCode写C++竞赛代码总报错?可能是你的‘万能头’bits/stdc++.h没放对地方
  • 2026年 黑豆淘平台/电商零售/网店推荐榜单:高转化率与新店扶持政策深度解析及优质服务商盘点 - 品牌企业推荐师(官方)
  • 成都角钢经销商推荐|型钢厂家|四川盛世钢联青白江最新现货批发 - 四川盛世钢联营销中心
  • 基于hal库的ETH外设完整指南
  • 全球首次WEB4 KYC活体核验已正式开启
  • 2026苏州姑苏平江沧浪三区卫生间阳台飘窗屋顶漏水怎么修?泰源防水免砸砖上榜.doc - 资讯焦点
  • linux下一步学习内容
  • 基于BQ2057的USB锂电池充电电路设计:从原理到实践
  • 零基础入行 IT 运维 / 网络,华为、思科、红帽先考哪个?
  • STM32C8T6 硬件设计完全指南:元器件选型、EMI 屏蔽与防护从入门到精通
  • 2026年职称评审机构如何选择 重庆正规申报机构口碑推荐指南 - 资讯焦点
  • RTKLIB四种模糊度固定方式的含义和适用性
  • 字节跳动・火山引擎・火山方舟:模型开通与接入教程
  • 【天河区】珠江新城玻璃幕墙后的无尘哲学——2026天河CBD单位保洁与开荒三强纪事 - 广州搬家老班长
  • 2026年清晖教育初级、中级、高级、副高及正高职称评审全层级指南 - 资讯焦点
  • AI订阅费用黑洞排查清单,含12类隐性计费陷阱与对应法律条款援引(附ISO/IEC 27001合规对照表)
  • 告别混乱低效!autoAGC云端协同,升级电商团队办公模式
  • 全英文行为面试(BQ):海外留学生如何通过去中式客套展现个人主导权「蒸汽求职分享」
  • 腾讯游戏卡顿终结者:ACE-Guard资源限制器终极指南
  • 长视频和播客怎么变成结构化读书笔记?一套 AI 时代的知识管理方法
  • 2026年本地职称评审机构推荐 重庆三级申报人分级优选指南 - 资讯焦点
  • 小米智能家居如何一键接入HomeAssistant?Hass-Xiaomi-Miot全攻略
  • 2026年工程类职称评审机构怎么选 五类申报者画像精准匹配指南 - 资讯焦点
  • 19. 大数据- BI 入门-数据集成全维度详解
  • 2026年砂磨机厂家推荐排行榜:立式/卧式/纳米/节能/实验室砂磨机与研磨设备源头工厂优选 - 品牌企业推荐师(官方)
  • 一书一码常见问题解答——出版人关注的20个问题 - 资讯焦点
  • 终极指南:3步用Happy Island Designer打造你的梦想岛屿
  • 3个秘密武器:让你的M1 Mac流畅运行Android模拟器
  • 沉浸式文旅新标杆,大体量黑暗乘骑重塑场馆核心价值