一次 OpenClaw 驱动的"提效爆改",把 2 小时的 JS 逆向苦力活,压缩成 10 分钟的自动化脚本
文章目录
- 开篇------为何要请"龙虾"出山?
- 第一次实战:DES加密逆向
- [OpenClaw 生成的登录加密函数](#OpenClaw 生成的登录加密函数)
- 第二次实战:AES加密
- 难度升级:挑战公开靶场-典型的Webpack模块化混淆
- 运行测试
- 总结:这次"邪修"的收获
- 写在最后
开篇------为何要请"龙虾"出山?
前端加密算法的进化速度,总比我的头发脱落得快。RSA、AES、MD5 加盐、动态 Token...每次遇到新的 JS 登录逻辑,我的标准流程就自动触发:打开 DevTools、下断点、扣代码、补环境。两个小时起步,运气不好就得耗半天,还可能因为某个隐藏参数搞错导致前功尽弃。
我也试过用传统 AI 调用 MCP 工具来自动化,但配置环境、调试参数这套流程下来,感觉就像"为了喝水先挖口井",前置工作量一点没少。
直到我遇见了 OpenClaw。
这东西不一样。不用装复杂环境,不用写配置文件,甚至不用精确描述问题。我只需要像跟人说话一样告诉它:"这个网站的登录加密搞不定,帮我看看",剩下的就是等待。它真的能自己打开浏览器、访问目标网页、抓取 JS 源码、分析加密逻辑、自动验证算法,最后把加密逻辑和可运行代码完整地吐出来。
这就是我今天要分享的"龙虾邪修"------用 OpenClaw 将前端 JS 逆向从"手工活"变成"自动化流水线"的实战。
第一次实战:DES加密逆向
请出今天的"受害者"目标网址(已打码):aHR0cHM6Ly9zaHgubHd2Yy5lZHUuY24vU1hHTFhUL2xvZ2luL2dvdG9XZWxjb20=

今天的目标是一个常见的电商后台登录页。表面平平无奇,但一看网络请求就露出了"獠牙":

cpp
POST /SXGLXT/login/checkLogin
参数:
code: 'UJivNfa7nEWNiQX09k1JkH2gCsCj132rndMkmF44y2Ny3Vc86LPsyiQHeZVwbYnz3bIHwEnhQh5C2N9AxubhCM0v1YJ5Mg=='
传统做法,我得:
- 在 Sources 里搜 code、md5、encrypt,或者是找到断点
- 找到加密函数,看它怎么拼接参数
- 手动扣出这个函数,测试是否正确
- 转换成python代码
有了龙虾,我只需要:
向 OpenClaw 输入指令

我的指令非常简单:"分析这个网页的登录加密逻辑,并生成可用的 Python 代码。"
然后见证奇迹:
OpenClaw 自动化执行流程截图



- 自动打开浏览器访问目标网址
- 自动触发登录流程,监控网络请求
- 智能分析加密点,定位关键 JS 函数
- 提取并优化代码,生成干净的加密逻辑
- 输出完整代码,包含详细注释
不到 5 分钟,龙虾"吐"出了一段可以直接运行的 Python 代码:
OpenClaw 生成的登录加密函数

cpp
"""
加密方式: DES-ECB / PKCS7填充 / 8位随机密钥 / Base64编码
"""
import base64
import json
import random
import string
from Crypto.Cipher import DES
from urllib.parse import quote
def pkcs7_pad(data: bytes, block_size: int = 8) -> bytes:
"""PKCS7 填充,DES 块大小为 8 字节"""
pad_len = block_size - (len(data) % block_size)
return data + bytes([pad_len] * pad_len)
def generate_random_key(length: int = 8) -> str:
"""生成 8 位随机 DES 密钥"""
return "".join(random.choices(string.ascii_letters + string.digits, k=length))
def encrypt_by_des(plaintext: str, key: str) -> str:
"""
前端 encryptByDES(str) 的 Python 实现
返回: key + base64(DES_ECB_PKCS7(plaintext))
"""
# DES-ECB 模式,key 作为密钥
cipher = DES.new(key.encode("utf-8"), DES.MODE_ECB)
# PKCS7 填充
padded = pkcs7_pad(plaintext.encode("utf-8"))
encrypted = cipher.encrypt(padded)
# Base64 编码
ciphertext_b64 = base64.b64encode(encrypted).decode("ascii")
return key + ciphertext_b64
def convert_json(data: dict) -> str:
"""模拟前端的 ConvertJson(obj, false)"""
return json.dumps(data, separators=(",", ":"), ensure_ascii=False)
def generate_login_code(username: str, password: str) -> str:
"""
完整的加密流程
1. 构造 JSON
2. 生成随机密钥
3. DES 加密
4. URL 编码
"""
# 1. 构造明文 JSON
payload = {
"openid": "",
"username": username,
"password": password,
"roleType": None,
}
json_str = convert_json(payload)
# 2. 生成 8 位随机密钥
des_key = generate_random_key(8)
# 3. DES-ECB + PKCS7 加密
encrypted = encrypt_by_des(json_str, des_key)
# 4. URL 编码
return quote(encrypted, safe="")
# 使用示例
if __name__ == "__main__":
username = "test"
password = "123456"
code = generate_login_code(username, password)
print(f"生成的加密 code 参数:\n{code}")
# 验证加密流程
print(f"\nDES 密钥长度: 8")
print(f"加密模式: ECB")
print(f"填充方式: PKCS7")
print(f"输出格式: 密钥 + base64(密文)")
运行生成的代码,成功登录

我简单测试了一下,用龙虾生成的代码直接就能登录成功。原本需要 1-2 小时的手工逆向,这次只用了 7 分钟。
第二次实战:AES加密
简单分析,发现这个网站登陆成功后是一个302状态码,需要一层层的授权重定向,通常对应权限分级或单点登录(SSO)场景:
cpp
第一跳(身份认证):/login→ 验证账号密码,生成会话。
第二跳(权限路由):根据用户角色(如普通用户、管理员)重定向到不同的首页或授权网关。
第三跳(资源获取):最终跳转到实际要访问的页面(如 /dashboard)。
之前手动分析过是一个aes加密,每次得到的加密结果都不一样但是可以用,看看之前我手动写的加密代码
cpp
def encrypt_password_hyzy(password, salt):
# 生成 64 字节的随机字符串
random_prefix = random_string_hyzy(64)
# 拼接随机字符串和密码
data_to_encrypt = random_prefix + password
# 将 salt 和随机生成的 IV 转换为字节
key = salt.encode('utf-8')
iv = random_string_hyzy(16).encode('utf-8')
# 加密
encrypted_data = encrypt_aes_hyzy(data_to_encrypt, key, iv)
return encrypted_data
# 提取加密盐值
salt_match = re.search(r'var pwdDefaultEncryptSalt = "(.*?)";', response.text)
if not salt_match:
print("未找到加密盐值")
push(user['phone'], '加密参数获取失败', user['openid'], user['push_id'], '9', '2')
return False
salt_value = salt_match.group(1)
print(f"pwdDefaultEncryptSalt: {salt_value}")
# 加密密码
encrypted_password = encrypt_password_hyzy(password, salt_value)
print("加密后的密码:", encrypted_password)
通过抓包,发现除了密码其他的都可以从网页页面拿到参数,我们重点让龙虾分析password即可

向龙虾提出问题

思考后得到算法

可以看到和我写的算法是一致的,例如
data_to_encrypt = random_prefix + password和_rds(64) + password
难度升级:挑战公开靶场-典型的Webpack模块化混淆
前几个案例太简单?好,我们加点难度。
靶场:http://39.98.108.20:8085/#/login

部分代码:
cpp
login: function() {
var e = this;
this.$refs["form"].validate((function(t) {
if (t) {
if (!e.form.validCode)
return void p["a"].error("请填写验证码");
if (e.form.validCode.toLowerCase() !== e.validCode.toLowerCase())
return void p["a"].error("验证码错误");
l["a"].post("user/login", e.form).then((function(t) {
0 == t.code ? (p["a"].success("登录成功"),
h.a.set("token", t.data.token, {
expires: .25
}),
sessionStorage.setItem("user", JSON.stringify(t.data)),
e.$router.push("/dashboard")) : p["a"].error(t.msg)
}
))
}
}
))
}
这是典型的 Webpack 模块化混淆。让我详细拆解人工逆向它的难点:
1、难点一:模块化迷宫(模块编号 + 依赖注入)
函数被拆成几十个像 a55b、b775这样的哈希模块
cpp
// 原始代码(清晰)
import axios from 'axios';
import CryptoJS from 'crypto-js';
function login(username, password) {
const encrypted = CryptoJS.AES.encrypt(password, 'key');
return axios.post('/login', { username, encrypted });
}
// Webpack混淆后(你的代码)
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["chunk-4dcb0d3f"], {
"a55b": function(e, t, r) { // 模块被编号为 a55b
var l = r("b775") // 依赖注入,l 是什么?需要找模块 b775
var p = r("3ef4") // 又是什么模块?
l["a"].post("user/login", e.form) // 完全不知道这是什么
}
}]);
2、难点二:变量天书(压缩 + 重命名)
所有变量被压缩成单字母:e、t、r、n、o、a、c,逻辑被压缩到一行,可读性为零
cpp
// 原始代码(可读)
const username = document.getElementById('username').value;
const password = encryptData(formData.password);
const timestamp = Date.now();
// 混淆后(你的代码片段)
var e = this; // e 是什么?可能是 this,可能是 event
var t = r; // t 是什么?可能是参数,可能是模块
var n = o; // 完全失去语义
向龙虾提出需求

分析结束

python代码:
cpp
# -*- coding: utf-8 -*-
import base64
import hashlib
import json
import os
import time
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
KEY = b"1234567891234567" # 硬编码!
IV = b"1234567891234567" # 与Key相同!
def build_login_request(username: str, password: str, validCode: str):
"""
构造登录请求参数(Headers + Body)
"""
# Step 1: JSON body (注意:属性之间无空格)
body = json.dumps(
{"username": username, "password": password, "validCode": validCode},
separators=(',', ':') # ← 关键:无空格
)
# Step 2: requestId - 32位随机hex
request_id = os.urandom(16).hex()
# Step 3: timestamp - 毫秒时间戳
timestamp = int(time.time() * 1000)
# Step 4: sign = MD5(body + requestId + timestamp)
sign = hashlib.md5(
(body + request_id + str(timestamp)).encode()
).hexdigest()
# Step 5: AES-128-CBC 加密 body
cipher = AES.new(KEY, AES.MODE_CBC, IV)
encrypted_b64 = base64.b64encode(
cipher.encrypt(pad(body.encode(), 16))
).decode()
return {
"headers": {
"Content-Type": "application/json;charset=UTF-8",
"X-Requested-With": "XMLHttpRequest",
"timestamp": str(timestamp),
"requestId": request_id,
"sign": sign,
},
"body": encrypted_b64, # POST body
}
# 使用示例
req = build_login_request("admin", "admin123", "1234")
print("Headers:", req["headers"])
print("Body:", req["body"])
运行测试
得到响应体
cpp
Headers: {'Content-Type': 'application/json;charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest', 'timestamp': '1775890395747', 'requestId': 'fc4387d3a3af99f6e1b438a201fbfd28', 'sign': 'a9bba7fff43d1568b7e534e73a5e9c8e'}
Body: cvdiXCkLIoxi/ChIz3/gh/6mx7LovCWwNnw7zMHx8ULNHJVa4ex8myTv9G2zRV43HrnMbDIDbwZq/fROCdNvvg==
运行代码后我很好奇为什么他除了给我加密参数以外,还要给我Headers?
我重新对登录接口抓包看了下
cpp
headers = {
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Content-Type': 'application/json;charset=UTF-8',
# 'Cookie': 'token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxOCIsImV4cCI6MTc3NTk3NjY5NX0.ZhLxiCK1zewiM5QMtkYwpntmBVetKrat1AZCweRcRJM',
'Origin': 'http://39.98.108.20:8085',
'Pragma': 'no-cache',
'Referer': 'http://39.98.108.20:8085/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest',
'requestId': 'd4c317ce891af84eba88b628ca43529a12',
'sign': '611123355b2d466a2d4b96e84ed6b3b8',
'timestamp': '1775890306000',
'token': 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxOCIsImV4cCI6MTc3NTk3NjY5NX0.ZhLxiCK1zewiM5QMtkYwpntmBVetKrat1AZCweRcRJM',
}
data = 'viaGravie+m/oW4EuPkeHqG/7yj8+jEAwRr/3pr+0d0r9HZKX4mENVMjpdw/XNXxOZvjFb7vSRVC7cV0hdYhZw=='
response = requests.post('http://39.98.108.20:8085/api/user/login', cookies=cookies, headers=headers, data=data, verify=False)
发现有请求体加密, 增加签名, 时间戳, RequestId等防止进行数据包的抓包改包 我让龙虾分析的时候没想那么多,以为只有请求体加密,但是龙虾依然全部处理了,刚刚测试了下的确是有这些防护的,我大概测了几个
{"msg":"RequestId 非法","code":400}
{"msg":"签名过期","code":400}
换上龙虾给我的请求头和加密数据测试看看
Headers: {'Content-Type': 'application/json;charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest', 'timestamp': '1775890832575', 'requestId': '1724772bb674a04f69bfd8479af746c6', 'sign': '4b4d6186349e421cb9301679608748a9'}
Body: cvdiXCkLIoxi/ChIz3/gh/6mx7LovCWwNnw7zMHx8ULNHJVa4ex8myTv9G2zRV43HrnMbDIDbwZq/fROCdNvvg==
替换后请求可以得到响应体

总结:这次"邪修"的收获
这次用 OpenClaw 做 JS 登录逆向的体验,有几个关键收获:
- 效率的指数级提升
• 简单加密:2小时 → 5-7分钟
• 复杂混淆:3-4小时 → 10-15分钟
• 效率提升:10-20 倍
- 门槛的大幅降低
以前做 JS 逆向,需要:
• 熟悉浏览器调试工具
• 懂 JavaScript 运行原理
• 会基本的密码学知识
• 有耐心和头发
现在只需要:1、 会描述问题2、能复制粘贴3、有 10 分钟等待时间
写在最后
这次"龙虾邪修"的实践,让我看到了 AI 在工程效率上的真实价值。OpenClaw 不是万能魔法,但它确实把"前端 JS 逆向"这个原本需要专业技能和大量时间的任务,变成了普通开发者也能快速解决的问题。
技术本身是中性的,关键在于如何使用。 我用这个流程来做:
• 公司内部系统的自动化测试
• 合规的数据采集研究
• 学习前端加密的实现方式
• 验证自家产品的安全性
如果你也在被各种前端加密困扰,或者在寻找提效的方法,不妨试试 OpenClaw 这条"邪修"路线。它可能不会让你一夜成为逆向大神,但一定能帮你省下大把的头发和时间。
本文聚焦于技术研究和个人效率提升,所有技术仅用于学习交流和授权测试场景,请遵守相关法律法规和网站协议。