当前位置: 首页 > 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.jsqmd.com/news/957710/

相关文章:

  • 【海珠区】琶洲会展之光后的纤尘不染——2026海珠企业保洁与开荒三强纪事 - 广州搬家老班长
  • 【增城区】新塘热土上的窗明几净——2026增城工厂单位保洁开荒三强纪事 - 广州搬家老班长
  • 2026国际EMBA排名榜单解析|优质国际化EMBA项目实力盘点
  • 保姆级教程:手把手教你搞定Gurobi 9.1在PyCharm和Anaconda环境下的完整部署(附DLL缺失解决方案)
  • Recaf:Java字节码编辑的终极免费解决方案
  • 大语言模型自动化生成前端脚手架:高质量测试用例的效能探索
  • 蓝桥杯CT107D开发板即用型外设驱动合集:IIC、DS1302时钟、单总线温度全支持
  • 基于高性能云原生 CNI 插件优化 K8s 调度器与节点间延迟
  • 【白云区】民企厂房与新城公馆的双向洁净——2026白云区单位保洁开荒三强纪事 - 广州搬家老班长
  • VSCode写C++竞赛代码总报错?可能是你的‘万能头’bits/stdc++.h没放对地方
  • TinyPinyin:高性能轻量级Java汉字转拼音库架构设计与实现
  • 2026年职称评审靠谱机构推荐 - 资讯焦点
  • Windows11 Enterprise/IoT LTSC2024 系统介绍与完整安装技术教程
  • 2026年 黑豆淘平台/电商零售/网店推荐榜单:高转化率与新店扶持政策深度解析及优质服务商盘点 - 品牌企业推荐师(官方)
  • LinkSwift网盘直链下载助手:高效获取九大网盘下载地址的完整指南
  • 佛山靠谱猫犬舍哪家好?佛山买纯种猫狗不踩坑实体店推荐【2026实测】 - 萌宠俱乐部
  • linux下一步学习内容
  • Pygame版AI贪吃蛇:自动寻路、实时吃食、碰撞即停的可运行Python项目
  • SillyTavern深度解析:构建沉浸式AI角色扮演体验的实践指南
  • 从‘内表行数’到‘数据库计数’:ABAP里SELECT COUNT(*)的5个实战避坑点
  • 广州家庭教育指导师报名机构哪家好?正规授权机构推荐:中山优才教育 - 最新教育培训热点
  • 2026年职称评审机构如何选择 重庆正规申报机构口碑推荐指南 - 资讯焦点
  • 零基础入行 IT 运维 / 网络,华为、思科、红帽先考哪个?
  • 基于BQ2057的USB锂电池充电电路设计:从原理到实践
  • STM32C8T6 硬件设计完全指南:元器件选型、EMI 屏蔽与防护从入门到精通
  • 手动复制看似简单,真正难的是保持数据一致
  • 19项提名领跑,伊利第七次亮相世界乳品创新奖 - 资讯焦点
  • 3步掌握COM3D2.MaidFiddler:游戏角色实时编辑器新手指南
  • RTKLIB四种模糊度固定方式的含义和适用性
  • [t.9.11] Scrum Meeting 11