当龙虾接管浏览器:龙虾邪修——自动化破解网页JS登录的“魔法”

一次 OpenClaw 驱动的"提效爆改",把 2 小时的 JS 逆向苦力活,压缩成 10 分钟的自动化脚本

文章目录

开篇------为何要请"龙虾"出山?

前端加密算法的进化速度,总比我的头发脱落得快。RSA、AES、MD5 加盐、动态 Token...每次遇到新的 JS 登录逻辑,我的标准流程就自动触发:打开 DevTools、下断点、扣代码、补环境。两个小时起步,运气不好就得耗半天,还可能因为某个隐藏参数搞错导致前功尽弃。

我也试过用传统 AI 调用 MCP 工具来自动化,但配置环境、调试参数这套流程下来,感觉就像"为了喝水先挖口井",前置工作量一点没少。

直到我遇见了 OpenClaw。

这东西不一样。不用装复杂环境,不用写配置文件,甚至不用精确描述问题。我只需要像跟人说话一样告诉它:"这个网站的登录加密搞不定,帮我看看",剩下的就是等待。它真的能自己打开浏览器、访问目标网页、抓取 JS 源码、分析加密逻辑、自动验证算法,最后把加密逻辑和可运行代码完整地吐出来。

这就是我今天要分享的"龙虾邪修"------用 OpenClaw 将前端 JS 逆向从"手工活"变成"自动化流水线"的实战。

第一次实战:DES加密逆向

请出今天的"受害者"目标网址(已打码):aHR0cHM6Ly9zaHgubHd2Yy5lZHUuY24vU1hHTFhUL2xvZ2luL2dvdG9XZWxjb20=

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

cpp 复制代码
POST /SXGLXT/login/checkLogin
参数:
code: 'UJivNfa7nEWNiQX09k1JkH2gCsCj132rndMkmF44y2Ny3Vc86LPsyiQHeZVwbYnz3bIHwEnhQh5C2N9AxubhCM0v1YJ5Mg=='

传统做法,我得:

  1. 在 Sources 里搜 code、md5、encrypt,或者是找到断点
  2. 找到加密函数,看它怎么拼接参数
  3. 手动扣出这个函数,测试是否正确
  4. 转换成python代码

有了龙虾,我只需要:

向 OpenClaw 输入指令

我的指令非常简单:"分析这个网页的登录加密逻辑,并生成可用的 Python 代码。"

然后见证奇迹:
OpenClaw 自动化执行流程截图

  1. 自动打开浏览器访问目标网址
  2. 自动触发登录流程,监控网络请求
  3. 智能分析加密点,定位关键 JS 函数
  4. 提取并优化代码,生成干净的加密逻辑
  5. 输出完整代码,包含详细注释

不到 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 登录逆向的体验,有几个关键收获:

  1. 效率的指数级提升

• 简单加密:2小时 → 5-7分钟

• 复杂混淆:3-4小时 → 10-15分钟

• 效率提升:10-20 倍

  1. 门槛的大幅降低

以前做 JS 逆向,需要:

• 熟悉浏览器调试工具

• 懂 JavaScript 运行原理

• 会基本的密码学知识

• 有耐心和头发

现在只需要:1、 会描述问题2、能复制粘贴3、有 10 分钟等待时间

写在最后

这次"龙虾邪修"的实践,让我看到了 AI 在工程效率上的真实价值。OpenClaw 不是万能魔法,但它确实把"前端 JS 逆向"这个原本需要专业技能和大量时间的任务,变成了普通开发者也能快速解决的问题。

技术本身是中性的,关键在于如何使用。 我用这个流程来做:

• 公司内部系统的自动化测试

• 合规的数据采集研究

• 学习前端加密的实现方式

• 验证自家产品的安全性

如果你也在被各种前端加密困扰,或者在寻找提效的方法,不妨试试 OpenClaw 这条"邪修"路线。它可能不会让你一夜成为逆向大神,但一定能帮你省下大把的头发和时间。

本文聚焦于技术研究和个人效率提升,所有技术仅用于学习交流和授权测试场景,请遵守相关法律法规和网站协议。

相关推荐
SpikeKing2 小时前
VibeCoding - OpenClaw 公网访问配置指南 (自动化)
运维·自动化·vibecoding·openclaw
林恒smileZAZ7 小时前
Vue<前端页面版本检测>
前端·javascript·vue.js
ulias2129 小时前
Linux系统中的权限问题
linux·运维·服务器
青花瓷10 小时前
Ubuntu下OpenClaw的安装(豆包火山API版)
运维·服务器·ubuntu
问简11 小时前
docker 镜像相关
运维·docker·容器
我是Superman丶11 小时前
Element UI 表格某行突出悬浮效果
前端·javascript·vue.js
Dream of maid12 小时前
Linux(下)
linux·运维·服务器
齐鲁大虾12 小时前
统信系统UOS常用命令集
linux·运维·服务器
Huanzhi_Lin12 小时前
关于V8/MajorGC/MinorGC——性能优化
javascript·性能优化·ts·js·v8·新生代·老生代