news 2026/6/22 5:43:01

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 引擎解密。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/22 5:40:08

运筹学面试必考:单纯形法最优解判定的3种情况和1个经典易错点

运筹学面试必考&#xff1a;单纯形法最优解判定的3种情况和1个经典易错点在运筹优化岗位的面试中&#xff0c;单纯形法几乎是必考的核心知识点。许多候选人在笔试和面试环节能够完成基础计算&#xff0c;却在最优解判定这一关键环节频频失分。本文将深入剖析单纯形法最优解判定…

作者头像 李华
网站建设 2026/6/5 21:45:37

2026 前沿:Claude+Codex 跨进程质控,实现论文可追溯化写作方案

技术点目录目录一、阶段⓪范式入门科研新范式&#xff1a;传统写法vsAI时代科研写法三、阶段①数据生产数据获取与自动化分析四、阶段①数据生产科研绘图&#xff1a;图表作为数据结构五、阶段②初稿生成论文初稿自动生成六、阶段②初稿生成AI合规&#xff1a;贯穿式质控钩子七…

作者头像 李华
网站建设 2026/6/7 13:02:42

手动复制看似简单,真正难的是保持数据一致

之前做过一次招聘市场分析&#xff0c;需要整理多个公开招聘页面上的岗位名称、城市、薪资范围、经验要求、学历要求、发布时间和公司信息。刚开始我以为这个任务并不复杂&#xff1a;打开页面&#xff0c;复制内容&#xff0c;粘贴到表格里&#xff0c;再做汇总分析就可以了。…

作者头像 李华