实战指南:基于Burpsuite Montoya API的加解密插件开发全流程

从零到一:用BurpSuite插件打造你的第一个HTTP请求“中间人” (基于Montoya API最新版) 本文详细介绍了如何使用BurpSuite的Montoya API开发插件,实现HTTP请求拦截和修改。从开发环境搭建到核心拦截逻辑实战,再到高级调试技巧和生产级插件开发要点,帮助开发者快速掌握BurpSuite插件开发技术,打造高效的HTTP请求“中间人”工具。 阅读详情

1. 为什么你需要一个自己的Burpsuite加解密插件?

如果你经常做安全测试,尤其是渗透测试或者API接口测试,最近几年肯定被各种加密请求搞得头大。我自己的项目里,十个应用有八个都在请求体和响应体里玩起了AES、RSA、自定义签名这些花样。早期还能找到一些现成的插件凑合用,但要么年久失修,要么配置复杂得让人想放弃,最关键的是,它们往往跟不上你手头项目那“独特”的加解密逻辑。

这时候,自己动手开发一个定制化的Burpsuite加解密插件,就成了最高效的解决方案。它就像给你的Burpsuite装上了一双“透视眼”,能让加密的流量在你面前变成清晰的明文,拦截、修改、重放这些操作变得和测试普通HTTP请求一样简单。新版Burpsuite推出的Montoya API,更是让插件开发的门槛降低了不少,它设计得更现代、更清晰,哪怕你Java基础不是特别扎实,跟着流程走也能搞出个能用的插件来。

这篇文章,我就以一个实战过来人的身份,带你走一遍完整的开发流程。我们不谈空泛的理论,就从环境搭建、算法集成,一直讲到如何把加解密逻辑精准地“注入”到Burpsuite的请求/响应处理链中。我会分享我踩过的坑、调试的技巧,以及如何让插件既实用又稳定。目标很简单:让你看完就能动手,做出一个解决自己实际问题的插件。

2. 开发前的环境与思想准备

工欲善其事,必先利其器。在敲代码之前,把环境和思路理清楚,能省掉后面一大堆莫名其妙的报错时间。

2.1 软硬件环境清单

我的开发环境组合经过实践检验,比较稳定,你可以直接照搬:

  • 操作系统:Windows 10 或 11, macOS 和 Linux 同样可行,但本文以 Windows 为例,路径分隔符需要注意。
  • Burpsuite:必须使用支持 Montoya API 的版本,我使用的是 Burp Suite Community Edition 2024.5.3。你可以在 PortSwigger 官网下载到最新版。确保你的 Burp 是 2023.6 或更高版本,旧版的 Extender API 不兼容。
  • Java 开发环境:JDK 17 或 21。Montoya API 对 Java 版本有要求,推荐使用 JDK 17 LTS 版本。安装后记得配置好 JAVA_HOME 环境变量。
  • 集成开发环境 (IDE):IntelliJ IDEA Community Edition。它对 Maven 和 Java 的支持非常友好,社区版完全免费且功能强大。版本我用的是 2023.2.3,新版也一样。
  • 构建工具:Apache Maven。我们用它来管理项目依赖,特别是引入核心的 Montoya API 库。
  • 辅助工具:Visual Studio Code 和 Node.js。这两个不是开发 Burp 插件的必需品,但如果你需要参考或调试目标网站的前端加密 JS 逻辑(这是常事),用它们来运行和分析 JS 加密代码会非常方便。

2.2 理解 Montoya API 的处理流程(核心思想)

在写第一行代码前,我们必须搞清楚 Burpsuite 处理流量的几个关键节点,以及我们的插件代码应该在哪个节点介入。这是整个插件开发的“地图”,理解错了,代码写得再对也没用。

新版 Montoya API 主要通过几个 Handler(处理器)来挂钩流量。对于我们加解密插件,最关键的是三个:

  1. ProxyRequestHandler:处理经过 Burp Proxy 的请求。它有两个关键方法:

    • handleRequestReceived:当浏览器发出的请求刚刚到达Burp Proxy 时触发。此时你看到的是浏览器发送的原始数据(可能是加密的)。这个点通常用于读取和存储原始请求,或者提取一些初始信息(比如从请求头里获取密钥),但一般不对请求体做解密修改。
    • handleRequestToBeSent:当请求即将从Burp Proxy 发往目标服务器时触发。这是请求解密的黄金位置!因为用户在 Proxy 的拦截界面或 Repeater 里修改了数据后,会经过这里。我们需要在这里将用户输入的明文,重新加密成服务端期望的格式发出去。
  2. HttpHandler:处理 Burp 工具(如 Repeater, Intruder)直接发出的 HTTP 请求,以及从服务器返回的响应。它也有两个关键方法:

    • handleHttpRequestToBeSent:当从 Repeater 等工具发出的请求即将发送到服务器时触发。这里是另一个重要的请求加密点,必须确保从这里出去的请求是加密后的,否则服务端无法识别。
    • handleHttpResponseReceived:当服务器的响应刚刚到达Burp 时触发。这是响应解密的黄金位置!我们在这里将服务端返回的加密响应解密成明文,供 Burp 界面(如 Proxy 历史、Repeater 响应面板)显示。
  3. ProxyResponseHandler:处理从服务器返回、经过 Burp Proxy 再流向浏览器的响应。

    • handleResponseReceived:服务器响应到达 Proxy 时触发。类似请求侧,这里一般用于记录。
    • handleResponseToBeSent:响应即将发送回浏览器时触发。如果我们在 HttpHandler 里修改了响应(解密了),就需要在这里确保返回给浏览器的是解密后的明文,否则浏览器看不懂。

简单来说,一个完整的加解密闭环是:浏览器加密请求 -> ProxyRequestHandler (记录) -> 用户可能在Proxy界面修改 -> ProxyRequestHandler (解密为明文供用户看) -> HttpHandler (将明文重新加密后发出) -> 服务器处理 -> HttpHandler (将加密响应解密为明文) -> ProxyResponseHandler (将明文响应返回给浏览器)。

脑子里有了这张流程图,我们写代码就知道每一段逻辑该放在哪儿了。

3. 从零开始:创建你的第一个 Montoya 插件项目

接下来我们动手创建一个最基础的、能成功加载的插件项目。这是验证环境是否正确的关键一步。

3.1 在 IDEA 中创建 Maven 项目

打开 IntelliJ IDEA,选择 “New Project”。在左侧选择 “Maven”,确保 JDK 选择了你安装的 17 或 21 版本。GroupId 和 ArtifactId 可以按你喜欢的来,比如 com.security 和 mycrypto-plugin。点击创建,IDEA 会自动生成一个包含 pom.xml 文件的项目。

3.2 配置核心依赖:pom.xml

项目创建后,打开根目录下的 pom.xml 文件。这是 Maven 项目的“心脏”,所有依赖库都在这里声明。我们需要添加两个最重要的依赖:

  1. Montoya API:这是 Burpsuite 官方提供的插件开发接口库。
  2. 一个 JSON 处理库:比如 Fastjson2 或 Jackson,用于方便地解析和修改 JSON 格式的请求/响应体。这里我用 Fastjson2,因为它速度快且 API 简洁。

将 pom.xml 中的 <dependencies> 部分修改或补充为如下内容:

<dependencies>
    <!-- Burpsuite Montoya API 核心依赖 -->
    <dependency>
        <groupId>net.portswigger.burp.extensions</groupId>
        <artifactId>montoya-api</artifactId>
        <version>2024.5</version> <!-- 请务必使用与你Burp版本匹配或稍旧的版本 -->
        <scope>provided</scope> <!-- 关键!因为Burp运行时自带此库 -->
    </dependency>

    <!-- Fastjson2 用于JSON处理 -->
    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>2.0.51</version>
    </dependency>

    <!-- 日志框架,调试时非常有用 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>2.0.13</version>
    </dependency>
</dependencies>

特别注意:montoya-api 的 scope 必须设置为 provided。这意思是“编译和测试时需要这个库,但打包时不要把它打进去”,因为 Burpsuite 启动时自己就带着这个 API 了。如果打包进去,反而可能导致类冲突,插件加载失败。

3.3 编写插件入口类:BurpExtender

Montoya API 规定,插件的入口类必须命名为 BurpExtender,并且实现 BurpExtension 接口。这个类只有一个 initialize 方法,Burpsuite 在加载插件时会调用它。

在 src/main/java 下创建你的包路径(如 com.security.plugin),然后新建一个名为 BurpExtender 的 Java 类。

package com.security.plugin;

import burp.api.montoya.BurpExtension;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.logging.Logging;

public class BurpExtender implements BurpExtension {
    private MontoyaApi montoyaApi;
    private Logging logging;

    @Override
    public void initialize(MontoyaApi api) {
        // 保存API实例,后续到处都要用
        this.montoyaApi = api;
        this.logging = api.logging();

        // 设置插件名称,这会在Burp的Extender面板显示
        api.extension().setName("My Crypto Plugin");

        // 输出日志,证明插件加载成功!
        logging.logToOutput("=== 我的加解密插件初始化成功! ===");

        // 在这里注册我们后续要写的各种 Handler
        // 例如:api.proxy().registerRequestHandler(new MyProxyRequestHandler(api));
        // 我们先注释掉,等Handler写完再取消注释
    }
}

现在,一个最基础的插件框架就完成了。你可以点击 IDEA 的 Maven 面板中的 package 命令(或运行 mvn clean compile package),它会在 target 目录下生成一个 .jar 文件。

3.4 在 Burpsuite 中加载并测试

打开你的 Burpsuite,转到 Extender 标签页,再切换到 Extensions 子标签。点击 Add 按钮,在弹窗里选择 Java 类型,然后找到并选中你刚刚打包生成的 .jar 文件。点击 Next,如果一切顺利,你会在下方的输出面板看到 “Extension loaded successfully” 以及我们代码里打印的 “=== 我的加解密插件初始化成功! ===” 日志。

恭喜!至此,你的开发环境、项目框架和 Burpsuite 的联动已经完全跑通。这是万里长征第一步,也是最容易出问题的一步。如果这里成功了,后面就都是“添砖加瓦”的活了。

4. 集成加解密算法:让插件拥有“核心能力”

插件架子搭好了,现在要给它注入灵魂——加解密算法。通常我们需要从目标应用中逆向出它的加密逻辑。这里我以最常见的 AES-ECB 和 RSA 为例,演示如何将算法代码集成到插件项目中。

4.1 逆向与分析加密逻辑

在开发前,你需要弄清楚目标应用用了什么算法、密钥是什么、数据格式如何。通常有两种方法:

  1. 静态分析:如果能有前端代码,直接看 JavaScript 加密函数。用 VS Code 打开 JS 文件,搜索 CryptoJS、AES、encrypt、RSA 等关键字。
  2. 动态调试:在浏览器开发者工具的 Sources 或 Debugger 面板中,给加密函数打上断点,观察输入输出。

假设我们分析出以下逻辑(这是一个非常典型的场景):

  • 请求体加密:POST 的 JSON 数据整体先经过 AES-ECB 模式加密,密钥是固定的字符串(如 this_is_a_secret_key)。
  • 密钥传递:这个 AES 密钥本身,会被当前时间戳拼接后,再用 RSA 公钥加密,结果放在 HTTP 请求头的 X-Encrypt-Key 字段里。
  • 响应体加密:服务器返回的 JSON 数据中,data 字段是经过相同 AES 算法加密的密文。

4.2 实现 AES-ECB 加解密工具类

在项目中创建一个工具类,比如 AesUtil.java。这里给出一个健壮性更好的实现:

package com.security.plugin.utils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class AesUtil {
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";

    /**
     * AES-ECB 加密
     * @param plainText 明文
     * @param secretKey 密钥 (必须是16, 24或32字节)
     * @return Base64编码的密文
     */
    public static String encrypt(String plainText, String secretKey) {
        try {
            // 确保密钥长度符合AES要求,这里简单处理,实际可能需要补位
            byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, ALGORITHM);

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);

            byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            throw new RuntimeException("AES加密失败", e);
        }
    }

    /**
     * AES-ECB 解密
     * @param cipherText Base64编码的密文
     * @param secretKey 密钥
     * @return 明文
     */
    public static String decrypt(String cipherText, String secretKey) {
        try {
            byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, ALGORITHM);

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, keySpec);

            byte[] decodedBytes = Base64.getDecoder().decode(cipherText);
            byte[] decryptedBytes = cipher.doFinal(decodedBytes);
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("AES解密失败", e);
        }
    }

    // 一个简单的测试方法
    public static void main(String[] args) {
        String key = "1234567890123456"; // 16位密钥
        String text = "{\"user\":\"admin\",\"pass\":\"123456\"}";

        String encrypted = encrypt(text, key);
        System.out.println("加密后: " + encrypted);

        String decrypted = decrypt(encrypted, key);
        System.out.println("解密后: " + decrypted);
        System.out.println("解密是否成功: " + text.equals(decrypted));
    }
}

4.3 实现 RSA 加解密工具类

再创建一个 RsaUtil.java。注意,在实际插件中,RSA 可能只用于加密密钥,解密可能在服务端。我们这里实现公钥加密和私钥解密(如果需要的话)。

package com.security.plugin.utils;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RsaUtil {
    private static final String ALGORITHM = "RSA";

    /**
     * 从字符串加载RSA公钥
     * @param publicKeyStr Base64编码的公钥字符串(通常去头尾)
     * @return PublicKey 对象
     */
    public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKeyStr);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        return keyFactory.generatePublic(spec);
    }

    /**
     * 用公钥加密数据
     * @param data 待加密数据
     * @param publicKey 公钥字符串
     * @return Base64编码的密文
     */
    public static String encryptWithPublicKey(String data, String publicKey) throws Exception {
        PublicKey key = loadPublicKey(publicKey);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] encryptedBytes = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    // 如果需要私钥解密,可以类似地实现 loadPrivateKey 和 decryptWithPrivateKey 方法
    // ...

    public static void main(String[] args) throws Exception {
        // 这里需要替换成你从目标应用逆向出来的真实公钥
        String samplePublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."; // 省略
        String dataToEncrypt = "this_is_a_secret_key_" + System.currentTimeMillis();

        String encrypted = encryptWithPublicKey(dataToEncrypt, samplePublicKey);
        System.out.println("RSA加密结果: " + encrypted);
    }
}

把这两个工具类写好,并在 main 方法里用你知道的密钥和密文测试通过,确保算法逻辑和你的目标应用完全一致。这是插件能否正常工作的基石。

5. 构建插件处理链:连接算法与 Burpsuite

现在我们有了解密算法,也知道 Burpsuite 的流量节点,接下来就是最核心的一步:编写各个 Handler,在正确的时机调用我们的算法。

5.1 设计配置界面(Swing UI)

为了让插件更实用,我们通常需要一个设置界面,让用户能动态输入 AES 密钥、RSA 公钥、配置主机白名单等。这里我们创建一个简单的 SettingsTab 类,实现 UserInterface 相关的接口。

package com.security.plugin.ui;

import burp.api.montoya.userinterface.UserInterface;

import javax.swing.*;
import java.awt.*;

public class SettingsTab implements burp.api.montoya.ui.UserInterface {
    private final JPanel mainPanel;
    private JTextField aesKeyField;
    private JTextField rsaPubKeyField;
    private JTextArea hostWhiteListArea;

    public SettingsTab() {
        mainPanel = new JPanel(new BorderLayout());

        // 创建设置面板
        JPanel configPanel = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(5, 5, 5, 5);

        // AES密钥输入
        gbc.gridx = 0; gbc.gridy = 0;
        configPanel.add(new JLabel("AES密钥:"), gbc);
        gbc.gridx = 1;
        aesKeyField = new JTextField(30);
        aesKeyField.setText("your_aes_key_here"); // 默认值
        configPanel.add(aesKeyField, gbc);

        // RSA公钥输入
        gbc.gridx = 0; gbc.gridy = 1;
        configPanel.add(new JLabel("RSA公钥:"), gbc);
        gbc.gridx = 1;
        rsaPubKeyField = new JTextField(30);
        rsaPubKeyField.setText("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...");
        configPanel.add(rsaPubKeyField, gbc);

        // 主机白名单
        gbc.gridx = 0; gbc.gridy = 2;
        configPanel.add(new JLabel("主机白名单 (每行一个):"), gbc);
        gbc.gridx = 1; gbc.gridy = 2;
        gbc.gridheight = 3;
        hostWhiteListArea = new JTextArea(10, 30);
        hostWhiteListArea.setText("api.target.com\napp.victim.com");
        configPanel.add(new JScrollPane(hostWhiteListArea), gbc);

        mainPanel.add(configPanel, BorderLayout.NORTH);
        // 可以添加一个保存按钮,将配置保存到持久化存储中
    }

    @Override
    public Component getUiComponent() {
        return mainPanel;
    }

    // 提供获取配置值的方法,供其他Handler调用
    public String getAesKey() {
        return aesKeyField.getText().trim();
    }
    public String getRsaPublicKey() {
        return rsaPubKeyField.getText().trim();
    }
    public String[] getHostWhiteList() {
        return hostWhiteListArea.getText().split("\\n");
    }
}

然后在 BurpExtender 的 initialize 方法中注册这个标签页:api.userInterface().registerSuiteTab("加解密设置", settingsTab.getUiComponent());。

5.2 实现 MyProxyRequestHandler(请求解密)

这个 Handler 负责处理来自浏览器的请求。我们主要工作在 handleRequestToBeSent 方法中。

package com.security.plugin.handler;

import burp.api.montoya.MontoyaApi;
import burp.api.montoya.proxy.ProxyRequestHandler;
import burp.api.montoya.proxy.ProxyRequestReceivedAction;
import burp.api.montoya.proxy.ProxyRequestToBeSentAction;
import burp.api.montoya.core.ByteArray;
import burp.api.montoya.proxy.http.ProxyRequestReceived;
import burp.api.montoya.proxy.http.ProxyRequestToBeSent;
import com.security.plugin.ui.SettingsTab;
import com.security.plugin.utils.AesUtil;
import static burp.api.montoya.proxy.ProxyRequestReceivedAction.continueWith;
import static burp.api.montoya.proxy.ProxyRequestToBeSentAction.continueWith;

public class MyProxyRequestHandler implements ProxyRequestHandler {
    private final MontoyaApi api;
    private final SettingsTab settings;

    public MyProxyRequestHandler(MontoyaApi api, SettingsTab settings) {
        this.api = api;
        this.settings = settings;
    }

    @Override
    public ProxyRequestReceivedAction handleRequestReceived(ProxyRequestReceived requestReceived) {
        // 1. 检查主机是否在白名单内,如果不是,直接放行,不处理
        String host = requestReceived.request().host();
        if (!isHostInWhiteList(host)) {
            return continueWith(requestReceived);
        }

        // 2. 可以在这里记录原始请求的ID或提取一些信息,存入上下文
        // api.logging().logToOutput("收到请求: " + requestReceived.request().url());

        return continueWith(requestReceived);
    }

    @Override
    public ProxyRequestToBeSentAction handleRequestToBeSent(ProxyRequestToBeSent requestToBeSent) {
        String host = requestToBeSent.request().host();
        if (!isHostInWhiteList(host)) {
            return continueWith(requestToBeSent);
        }

        // 获取当前配置的AES密钥
        String aesKey = settings.getAesKey();
        if (aesKey.isEmpty()) {
            api.logging().logToError("AES密钥未配置,跳过解密。");
            return continueWith(requestToBeSent);
        }

        try {
            // 获取请求体(假设是加密的)
            ByteArray requestBody = requestToBeSent.request().body();
            String bodyStr = requestBody.toString();

            // 尝试解密请求体
            String decryptedBody = AesUtil.decrypt(bodyStr, aesKey);

            // 构建新的请求,用解密后的明文替换原请求体
            ProxyRequestToBeSent newRequest = requestToBeSent.withBody(ByteArray.byteArray(decryptedBody));

            // 记录日志,方便调试
            api.logging().logToOutput(String.format("[ProxyReqOut] 主机: %s, 解密成功。明文: %s", host, decryptedBody));

            // 继续发送修改后的请求
            return continueWith(newRequest);

        } catch (Exception e) {
            // 如果解密失败(可能不是加密请求,或者密钥不对),则原样发送
            api.logging().logToError(String.format("[ProxyReqOut] 主机: %s, 解密失败: %s", host, e.getMessage()));
            return continueWith(requestToBeSent);
        }
    }

    private boolean isHostInWhiteList(String host) {
        for (String whiteHost : settings.getHostWhiteList()) {
            if (host.contains(whiteHost.trim())) {
                return true;
            }
        }
        return false;
    }
}

5.3 实现 MyHttpHandler(请求加密与响应解密)

这个 Handler 对于 Repeater 等工具发出的请求至关重要。它需要把明文重新加密发出去,并把加密响应解密回来。

package com.security.plugin.handler;

import burp.api.montoya.MontoyaApi;
import burp.api.montoya.http.handler.*;
import burp.api.montoya.core.ByteArray;
import com.security.plugin.ui.SettingsTab;
import com.security.plugin.utils.AesUtil;
import com.security.plugin.utils.RsaUtil;
import com.alibaba.fastjson2.JSONObject;
import java.util.Optional;
import static burp.api.montoya.http.handler.HttpRequestToBeSentAction.continueWith;
import static burp.api.montoya.http.handler.HttpResponseReceivedAction.continueWith;

public class MyHttpHandler implements HttpHandler {
    private final MontoyaApi api;
    private final SettingsTab settings;

    public MyHttpHandler(MontoyaApi api, SettingsTab settings) {
        this.api = api;
        this.settings = settings;
    }

    @Override
    public HttpRequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) {
        String host = requestToBeSent.host();
        if (!isHostInWhiteList(host)) {
            return continueWith(requestToBeSent);
        }

        String aesKey = settings.getAesKey();
        String rsaPubKey = settings.getRsaPublicKey();

        try {
            // 1. 获取请求体(此时应该是用户在Repeater里输入的明文)
            ByteArray requestBody = requestToBeSent.body();
            String plainBody = requestBody.toString();

            // 2. 用AES加密请求体
            String encryptedBody = AesUtil.encrypt(plainBody, aesKey);

            // 3. 构造新的RSA加密的密钥头(模拟前端行为)
            String timestamp = String.valueOf(System.currentTimeMillis());
            String keyToEncrypt = aesKey + "_" + timestamp;
            String encryptedKeyHeader = RsaUtil.encryptWithPublicKey(keyToEncrypt, rsaPubKey);

            // 4. 构建新的请求:更新请求体和请求头
            HttpRequestToBeSent newRequest = requestToBeSent
                    .withBody(ByteArray.byteArray(encryptedBody))
                    .withUpdatedHeader("X-Encrypt-Key", encryptedKeyHeader);

            api.logging().logToOutput(String.format("[HttpReqOut] 主机: %s, 加密完成并添加签名头。", host));
            return continueWith(newRequest);

        } catch (Exception e) {
            api.logging().logToError(String.format("[HttpReqOut] 主机: %s, 请求加密失败: %s", host, e.getMessage()));
            return continueWith(requestToBeSent);
        }
    }

    @Override
    public HttpResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived) {
        String host = responseReceived.initiatingRequest().host();
        if (!isHostInWhiteList(host)) {
            return continueWith(responseReceived);
        }

        String aesKey = settings.getAesKey();

        try {
            ByteArray responseBody = responseReceived.body();
            String bodyStr = responseBody.toString();

            // 假设响应是JSON,且加密数据在 `data` 字段
            JSONObject jsonResponse = JSONObject.parseObject(bodyStr);
            Optional<String> encryptedDataOpt = Optional.ofNullable(jsonResponse.getString("data"));

            if (encryptedDataOpt.isPresent() && !encryptedDataOpt.get().isEmpty()) {
                // 解密 data 字段
                String decryptedData = AesUtil.decrypt(encryptedDataOpt.get(), aesKey);
                // 将解密后的数据替换回JSON
                jsonResponse.put("data", decryptedData);

                // 构建新的响应
                HttpResponseReceived newResponse = responseReceived.withBody(ByteArray.byteArray(jsonResponse.toJSONString()));
                api.logging().logToOutput(String.format("[HttpRespIn] 主机: %s, 响应解密成功。", host));
                return continueWith(newResponse);
            } else {
                // 如果没有加密的data字段,可能是其他响应,原样返回
                return continueWith(responseReceived);
            }

        } catch (Exception e) {
            api.logging().logToError(String.format("[HttpRespIn] 主机: %s, 响应解密失败: %s", host, e.getMessage()));
            return continueWith(responseReceived);
        }
    }

    private boolean isHostInWhiteList(String host) {
        // ... 同 MyProxyRequestHandler 中的实现
        return true; // 简化示例
    }
}

5.4 组装并注册 Handler

最后,回到 BurpExtender 类,在 initialize 方法中创建各个 Handler 实例并进行注册。

@Override
public void initialize(MontoyaApi api) {
    this.montoyaApi = api;
    this.logging = api.logging();
    api.extension().setName("My Crypto Plugin V1.0");
    logging.logToOutput("=== 插件初始化开始 ===");

    // 1. 创建UI配置标签页
    SettingsTab settingsTab = new SettingsTab();
    api.userInterface().registerSuiteTab("加解密配置", settingsTab.getUiComponent());

    // 2. 创建各个Handler,并传入api和配置页引用
    MyProxyRequestHandler proxyReqHandler = new MyProxyRequestHandler(api, settingsTab);
    MyHttpHandler httpHandler = new HttpHandler(api, settingsTab);
    // 如果需要处理返回给浏览器的响应,还可以创建 MyProxyResponseHandler

    // 3. 向Burpsuite注册Handler
    api.proxy().registerRequestHandler(proxyReqHandler);
    api.http().registerHttpHandler(httpHandler);
    // api.proxy().registerResponseHandler(proxyRespHandler);

    logging.logToOutput("=== 所有处理器注册完成,插件已就绪! ===");
}

6. 打包、调试与实战技巧

代码写完了,但离一个稳定可用的插件还有一段距离。打包和调试过程中有很多细节需要注意。

6.1 使用 Maven 生成可加载的 JAR

我们的 pom.xml 需要配置 maven-shade-plugin 或 maven-assembly-plugin 来打包所有依赖(除了 scope 为 provided 的 Montoya API)。这里推荐使用 maven-shade-plugin,它可以处理依赖冲突和重命名。

在 pom.xml 的 <build><plugins> 部分添加:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.5.1</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <!-- 合并META-INF下的文件,避免冲突 -->
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <!-- 指定主类,虽然不是必须,但规范 -->
                        <mainClass>com.security.plugin.BurpExtender</mainClass>
                    </transformer>
                </transformers>
                <filters>
                    <filter>
                        <!-- 排除签名文件,避免安全警告 -->
                        <artifact>*:*</artifact>
                        <excludes>
                            <exclude>META-INF/*.SF</exclude>
                            <exclude>META-INF/*.DSA</exclude>
                            <exclude>META-INF/*.RSA</exclude>
                        </excludes>
                    </filter>
                </filters>
            </configuration>
        </execution>
    </executions>
</plugin>

然后在 IDEA 中运行 mvn clean package,会在 target 目录下生成一个 -shaded.jar 文件,这个就是最终可以加载的插件包。

6.2 调试插件:日志与断点

调试是开发过程中最耗时的部分。有几个高效的方法:

  1. 充分利用日志:在所有关键判断、成功失败处都加上 api.logging().logToOutput() 和 logToError()。Burpsuite 的 Extender -> Output 和 Errors 标签页会实时显示这些信息,是定位问题的第一现场。
  2. 在 IDEA 中远程调试:这是最强大的手段。首先,在 IDEA 中为你的插件项目配置一个 “Remote JVM Debug” 运行配置,使用默认端口(如 5005)。然后,在启动 Burpsuite 时,修改启动脚本,加上 JVM 调试参数,例如:java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar burpsuite_community.jar。这样启动 Burpsuite 后,在 IDEA 里点击 Debug,就能连接到 Burpsuite 的 JVM,在你插件的代码里打上断点,进行单步调试,观察变量状态。
  3. 使用 Repeater 作为测试工具:Repeater 是你最好的测试场。发送一个明文请求,观察插件是否将其加密;发送一个加密请求,观察 Proxy 历史或 Repeater 本身是否显示为明文。通过对比原始请求和最终发送的请求,可以快速定位问题出在哪个 Handler。

6.3 处理常见的坑与边界情况

在实际开发中,我遇到过不少坑,这里分享几个:

  • 字符编码问题:确保加解密和字符串转换时始终使用 UTF-8。ByteArray 的 toString() 默认也是 UTF-8,但显式指定更安全。
  • Base64 格式:注意 URL Safe 与标准 Base64 的区别。有些前端可能使用 - 和 _ 替换 + 和 /,并去掉填充 =。你的工具类可能需要兼容处理。
  • JSON 格式容错:解析请求/响应 JSON 时,不要假设结构永远固定。使用 Optional 或判空来避免 NullPointerException。
  • 性能考虑:加解密是 CPU 密集型操作。在白名单判断时尽早返回,避免对非目标流量进行不必要的加解密尝试。
  • 多线程安全:Burpsuite 的 Handler 可能在多线程环境下被调用。如果你的插件有共享状态(比如一个缓存),需要考虑同步问题。不过,像从 UI 读取配置这种操作,通常是线程安全的。

开发这样一个插件,从分析到最终稳定运行,可能需要反复迭代好几次。但一旦成功,它对你测试效率的提升是巨大的。你不再需要手动复制密文到外部工具解密,也不再需要担心重放攻击时忘记加密。一切都在 Burpsuite 内部自动化完成,让你可以完全专注于业务逻辑的安全测试本身。

新版Burpsuite加解密插件开发基础 如果修改对应的请求数据,则会改变burpsuite-proxy-history对应的原始请求数据,一般不需要在这边对请求数据进行修改,但是可以在handleRequestReceived做一些初始化操作,如获取key数据,获取请求的ID值等操作,Proxy_Req_In。目前项目测试中总会遇到很多的加解密相关的请求和响应,由于早期的加解密插件不太好用,且目前加解密请求响应越来越普遍,因此就尝试自己写一个简单的burp加解密插件。 阅读详情

相关推荐

实战指南:基于Burpsuite Montoya API开发高效加解密插件

本文是一份详细的实战指南,指导安全测试人员如何基于Burpsuite Montoya API开发定制化的加解密插件。文章从开发环境搭建、项目初始化、UI设计,到核心加解密算法实现和流量处理器编写,逐步解析了插件开发的全过程,旨在帮助读者高效解决Web安全测试中遇到的请求/响应加解密难题,从而显著提升测试效率。

weixin_28836507的博客 237

IntelliJ IDEA Community 之使用debug调试burpsuite插件

双击对应的ts.bat文件,使用相同版本的JDK打开对应的burpsuite.jar工具(burpsuite24.5.3)之后就可以在指定的位置下断点,就可以在idea中查看了,例如本次在插件加载时下断点,就会被断住了。就会在\out\artifacts\burp_test_jar文件夹下生成对应的jar包了。新建burp插件开发项,Java语言,Maven组件,使用openjdk21.0.3。修改name为自己满意的信息,修改端口为不冲突的端口,然后复制对应的命令。修改对应的端口为8888,可以打开。

u014531320的专栏 922

基于Montoya API的BurpSuite加解密插件开发实战指南

在Web应用安全测试中,数据加密传输是常见的安全机制,它通过密码学算法对通信内容进行混淆,以保障数据的机密性和完整性。理解加解密原理对于安全测试人员至关重要,它涉及对称加密(如AES)、非对称加密、哈希算法等核心概念。掌握这些技术能帮助测试人员穿透加密层,直接分析应用逻辑漏洞,提升渗透测试效率。BurpSuite作为主流的Web安全测试平台,其插件扩展机制允许测试人员自定义数据处理流程。通过Montoya API这一现代扩展框架,开发者可以构建透明加解密插件,在Proxy、Repeater等模块中自动实现请

weixin_28840143的博客 283

Burpsuite插件开发实战:手把手教你打造个性化加解密工具(含AES/RSA完整代码)

本文提供了一份详细的Burpsuite插件开发实战指南,手把手教你打造个性化的加解密工具。文章基于最新的Montoya API,从环境搭建、项目初始化开始,详细讲解了AES与RSA核心算法模块的实现、图形用户界面(GUI)的构建,并重点解决了与Repeater等工具的兼容性问题。通过完整的代码示例,帮助安全测试人员高效应对渗透测试中常见的加密数据流,提升审计效率。

gaochao的博客 414

Burpsuite插件开发实战:手把手教你打造个性化加解密工具(附完整代码)

本文详细介绍了如何从零开发一个Burpsuite加解密插件,涵盖环境搭建、架构设计、AES/RSA算法实现、请求响应处理及UI开发等核心环节。通过实战代码示例,帮助安全测试人员高效处理加密接口测试,提升渗透测试效率。

weixin_28839549的博客 186

逆向Galaxy插件:Burpsuite加解密核心原理与改造指南(2024新版)

本文深入剖析了Burpsuite Galaxy加解密插件的核心架构与设计原理,重点解析其基于Montoya API的双通道流量处理模型与规则引擎。文章提供了将插件改造为支持国密SM4算法、处理动态密钥协商及实现NodeJS混合调用的实战指南,旨在帮助安全测试人员构建高度定制化、可扩展的Burpsuite加解密插件,以应对复杂的Web应用安全测试场景。

weixin_28835551的博客 220

告别手动加解密!用Burpsuite插件自动化处理加密请求的完整指南

本文详细介绍了如何开发一款Burpsuite插件,实现自动化处理加密请求,提升安全测试效率。通过Montoya API拦截流量、设计算法适配层及实战案例,帮助开发者快速掌握加解密插件的核心原理与实现方法,告别手动操作。

weixin_30892037的博客 266

国央企创新负责人如何通过平台提升技术需求挖掘与资源配置的精准性?.docx

国央企创新负责人如何通过平台提升技术需求挖掘与资源配置的精准性?

虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型(simulink仿真实现)

内容概要:本文提供了“虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型”的Simulink仿真实现方法,系统阐述了如何通过仿真手段研究VSG在电力系统中的动态响应特性,重点聚焦于其惯量与阻尼的自适应控制策略。文中详细说明了模型构建流程,涵盖控制算法设计、参数调节、根轨迹分析等内容,并通过对比不同调节系数下的系统响应,深入探讨了控制参数对系统稳定性与动态性能的影响。该仿真模型有效复现了VSG模拟传统同步发电机惯性与阻尼特性的能力,对于提升新能源并网系统的频率稳定性和抗扰动能力具有重要研究价值。; 适合人群:具备电力系统、自动化或相关领域基础知识,从事新能源并网、微电网控制、电力电子与电力系统仿真研究的研究生、科研人员及工程师。; 使用场景及目标:①用于教学与科研中深入理解虚拟同步发电机的工作原理及其在现代电力系统中的应用;②为新能源发电系统接入电网时的稳定性分析与控制策略设计提供仿真验证平台;③支持对VSG控制参数进行优化,提升系统应对负荷波动和扰动的能力。; 阅读建议:建议读者结合Matlab/Simulink软件动手实践,按照文档结构逐步搭建仿真模型,重点关注控制环路设计与参数整定部分,同时参考提供的代码与仿真结果进行对比分析,以加深对VSG自适应控制机制的理解。

科技中介服务机构在协助企业对接科研成果时,如何提升服务响应速度与专业化水平.docx

科技中介服务机构在协助企业对接科研成果时,如何提升服务响应速度与专业化水平

国央企创新负责人在推动内部技术成果转化与外部协同创新时,如何实现资源的高效整合与协同联动?.docx

国央企创新负责人在推动内部技术成果转化与外部协同创新时,如何实现资源的高效整合与协同联动?

两相交错并联Boost电路仿真模型(Simulink仿真实现)

内容概要:本文介绍了“两相交错并联Boost电路仿真模型(Simulink仿真实现)”的技术资源,属于电力电子变换器领域的仿真研究内容。该模型基于Simulink平台构建,旨在深入分析两相交错并联Boost电路的工作原理、动态响应特性及控制策略。交错并联结构通过错开各相开关管的导通时刻,有效降低输入电流纹波、提升系统效率与功率密度,适用于中高功率DC-DC变换应用场景。资源包含完整的主电路建模、PWM驱动信号生成、闭环控制器设计(如电压/电流双闭环控制)等关键模块,并可能涵盖软启动、均流控制等关键技术问题。整个仿真模型强调理论分析与工程实践的紧密结合,不仅有助于理解电路基本行为,也为优化控制算法提供验证平台,服务于科研与教学需求。; 适合人群:电气工程、自动化、电力电子及相关专业的高校本科生、研究生,以及从事电源系统研发的工程技术人员,具备一定的电路理论、自动控制原理和Simulink仿真基础者更佳。; 使用场景及目标:① 学习和掌握两相交错并联Boost电路的拓扑结构、工作机理及性能优势;② 利用Simulink进行电力电子电路的建模与仿真,深入理解PWM控制、闭环调节、均流控制等核心技术的应用;③ 为科研项目、课程设计或毕业设计提供可复用的仿真案例和技术支持,辅助开展中高功率电源系统的创新设计与优化研究。; 阅读建议:建议结合Simulink软件动手搭建并调试模型,通过调整占空比、开关频率、电感参数等变量,观察其对输入电流纹波、输出电压稳定性及动态响应的影响,以深化对电路特性的理解;同时可参考文中列出的其他相关资源(如LLC、PFC等仿真模型)进行横向对比学习,拓宽电力电子系统设计视野。

两路半桥LLC谐振变换器软开关Simulink仿真(模拟谐振参数不同、分裂电容)

内容概要:本文介绍了“两路半桥LLC谐振变换器软开关Simulink仿真”资源包,重点在于基于Simulink平台对两路半桥LLC谐振变换器进行高精度建模与动态仿真,特别考虑了谐振参数不一致及分裂电容等非理想工况对系统性能的影响。该仿真模型不仅涵盖了主电路拓扑、软开关实现机制与谐振网络设计,还深入分析了不同参数配置下系统的启动特性、稳态效率、动态响应及软开关条件的满足情况。通过参数扫描与对比仿真,帮助用户掌握LLC变换器的关键设计要素与优化方法,具备较强的工程指导价值和科研教学意义。; 适合人群:电力电子、电气工程及其自动化等相关专业的高校师生,以及从事高频开关电源、新能源变换器、车载充电机等领域的研发工程师和技术人员。; 使用场景及目标:① 掌握两路并联半桥LLC谐振变换器的建模与仿真方法;② 理解软开关技术在提升变换器效率与降低EMI方面的关键作用;③ 分析谐振参数偏差与分裂电容结构对系统均流特性、增益曲线及软开关范围的影响;④ 支持科研项目中的拓扑验证、控制策略开发与故障工况分析; 阅读建议:建议结合Simulink仿真模型与配套说明文档进行学习,重点关注谐振腔参数设置、驱动时序设计及软开关波形观测;可通过调整谐振电感、电容值或引入参数失配,观察系统行为变化,深入理解LLC变换器的稳定边界与优化设计原则。

区域电网含风光火压缩空气储能抽水蓄能以及电化学储能的联合调度,加入IEEE39系统,进行潮流计算,分析电能质量研究(Matlab代码实现)

内容概要:本文研究了包含风能、太阳能、火电以及压缩空气储能、抽水蓄能和电化学储能等多种能源形式的区域电网联合调度问题,通过将系统接入IEEE 39节点标准测试系统,利用Matlab平台开展潮流计算与电能质量分析,旨在提升高比例可再生能源接入背景下电网运行的稳定性与供电品质。研究重点涵盖多类型电源与多种储能系统的协调优化调度策略设计,深入探讨储能系统在削峰填谷、平滑新能源出力波动方面的关键作用,并对复杂运行工况下电压偏差、频率波动、谐波污染等电能质量问题进行评估与改善。通过构建完整的仿真模型,系统性地分析了联合调度对电网电能质量的影响机制,为现代电力系统中多能互补与协同控制提供了理论依据和技术参考。; 适合人群:具备电力系统分析、优化调度及电能质量相关基础知识,熟悉Matlab/Simulink仿真环境的研究生、科研人员及电力系统相关领域的工程师。; 使用场景及目标:①为含多种储能的区域电网联合调度研究提供完整的Matlab仿真案例与开源代码参考;②分析大规模新能源接入对IEEE 39系统潮流分布及电能质量的综合影响,并定量评估不同类型储能系统的调控效果;③为电力系统优化调度、电能质量综合治理、多能协同控制等学术研究与工程项目提供可复现的技术支撑与解决方案。; 阅读建议:本资源以Matlab代码实现为核心,读者应在掌握电力系统基本理论的基础上,重点关注联合调度的数学建模过程、储能系统充放电策略的制定逻辑以及电能质量各项评价指标的计算方法。建议在学习过程中动手调试仿真代码,尝试调整系统参数、优化目标权重或改变新能源渗透率,以深入理解系统动态行为与控制策略的有效性。

双向全桥CLLC谐振变换器仿真模型(Simulink仿真实现)

内容概要:本文系统介绍了双向全桥CLLC谐振变换器的Simulink仿真模型,重点阐述了变频控制、闭环仿真及其与多种电力电子拓扑(如维也纳整流器、DAB变换器、图腾柱PFC等)的联合仿真设计。资源包涵盖2.5kW双向全桥LLC、V2G电动汽车充放电系统、三相PFC、多相交错LLC等多种变换器的仿真案例,并深入展示了三电平VIENNA整流器在双闭环PI控制与SVPWM调制策略下,结合上下电容电压差值补偿实现中点电位平衡的先进控制方法。通过仿真验证,该策略不仅有效维持了800V直流母线电压的稳定,还在动态工况下显著提升了电容电压的均衡度,同时保持了较低的输入电流THD,充分体现了SVPWM与中点平衡控制协同优化的技术优势。; 适合人群:具备电力电子与自动控制理论基础,从事新能源发电、电动汽车、工业电源、微电网等领域的研究生、工程师及科研人员。; 使用场景及目标:① 学习并构建高频谐振变换器(如LLC、CLLC)的Simulink仿真模型,掌握其变频与混合控制策略;② 深入理解并应用中点电位平衡、双闭环PI控制、SVPWM调制等核心技术于VIENNA等三电平变换器中;③ 复现高水平期刊论文中的控制算法,服务于科研项目申报、学术论文撰写或工程产品开发。; 阅读建议:该资源为综合性仿真资料库,建议使用者结合自身研究方向(如电动汽车充电、新能源并网)选择对应案例进行精学,重点关注控制策略的设计逻辑、仿真参数的整定方法以及SVPWM与中点平衡的协同机制,并充分利用附带的计算书和参考文献深化理解,以实现从仿真模型到实际工程应用的顺利转化。

全桥LLC谐振(变频+移相混合控制)变换器闭环仿真模型(Simulink仿真实现)

全桥LLC谐振(变频+移相混合控制)变换器闭环仿真模型(Simulink仿真实现)

上一篇: ClickHouse 3节点集群部署与分布式表实战
下一篇: CLIP模型下载太慢?3种国内镜像加速方案实测对比(附完整代码)
vim8coder
博客等级 码龄1天 0粉丝 1693原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值