文章目录
前言
我们已经学习了什么是burp扩展、怎么开发一个简单的burp扩展,现在是时候深入学习Montoya API 来帮助我们开发出一个实用的burp扩展。本文基于Montoya API 2025.4.x(对应burpsuite2025.4.x版本),介绍其内部结构及常用方法。
1. API 结构
1.1 概述
Montoya API 是 Burp Suite 的扩展接口,通过模块化设计提供丰富的功能。模块化扩展点:每个功能(如Proxy拦截、Scanner检查)以独立模块形式暴露,开发者按需调用。其核心结构分为以下模块:
- 基础模块
- BurpExtension(扩展入口,包含初始化方法
initialize(MontoyaApi api)
,用于注入 API 实例,即实现initialize方法,其参数MontoyaApi由burp传入,通过MontoyaApi访问burp提供的服务) - MontoyaApi,提供访问 Burp 各模块的入口,例如:
- burpSuite():访问 Burp 应用级功能(如导出配置、关闭 Burp)。
- collaborator():操作 Collaborator 模块(生成反连 payload)。
- comparer():数据比对功能。
- http():HTTP 请求/响应处理。
- extension():插件管理功能(如注册事件监听器)。
- BurpExtension(扩展入口,包含初始化方法
- 功能模块
1.2 API接口文件解析
montoya-api-2025.4.jar
jar包里主要是一大堆接口,这些接口定义了burp提供的服务,也就是通过调用接口里的方法,控制brup可以做什么。
去到官方文档的首页,是MontoyaApi
接 口,罗列了一大堆接口规定的方法(函数)。
我们再打开SDK,会发现文件夹几乎就对应上面的方法。实际上,调用上面的方法,都是返回一个相应的对象,这些对象的结构声明就在对应的文件夹下的文件里。
我们从扩展的入口BurpExtension接口
开始,进入MontoyaApi接口
,接着到 MontoyaApi 对象方法里可实例化的各个接口,逐步剖析整套 MontoyaAPI SDK。
2. BurpExtension 接口
API doc
相应的翻译也在图片里注明了,这个接口很简单,写一个类implements
这个接口,并实现其中的initialize(Montoya api)
方法即可。
需要注意的是,我们常说要运行程序必须要有一个main
方法,一个程序只能有一个main
方法,扩展的main
方法在哪?主方法在BurpSuite里,扩展由burp调用,MontoyaApi
对象则由burp的主程序传进来。
public class HelloBurp implements BurpExtension {
// 实现实现该方法
@override
void initialize(MontoyaApi api) {
/* 加载插件时调用此方法,我们可以在这里写逻辑代码,我们可以写点什么?
依靠主程序传入的 MontoyaApi 类型对象,做点什么那么就需要看 MontoyaApi 这个类型提供了什么
*/
3. MontoyaApi接口
前面我们说了,burp中每个功能(如Proxy拦截、Scanner检查)以独立模块形式暴露,开发者按需调用。具体的实现方式就是通过MontoyaApi中的方法获取对应的模块对象。
那么,接下来的任务就是学习各个模块接口。那么多接口,该怎么学习呢?哪些是主要的接口,有没有一条线索将主要的接口串联起来?
我们回归本质:Burp的核心是能够在模块之间传递 HTTP 请求以执行特定任务的能力。那我们以Http数据包为线索,主要学习的方向就是:
- Http数据包的来源与发送
- Http数据包对象
- 模块之间数据包的传递
4. package burp.api.montoya.proxy
一般情况下,我们都是通过代理浏览器的HTTP流量获得HTTP数据包进行测试。可以确定,,代理模块Proxy中必然会产生HTTP请求的流量,我们通过跟踪一个HTTP请求,学习Montoya API 提供给我们的接服务能力。
包结构如下,这个包定义了我们在 Proxy UI 界面看到的、可控制的大部分功能。
4.1 Proxy 接口
burp中proxy模块的抽象。回顾proxy中的主要功能:
- 捕获流量(intercept on | off、forward、drop)
- Http history
对应的,Proxy接口的主要方法如下:
public interface Proxy {
void enableIntercept(); // 设置 intercept on
void disableIntercept(); // 设置 intercept off
boolean isInterceptEnabled(); // 获取 intercept 状态
List<ProxyHttpRequestResponse> history();
Registration registerRequestHandler(ProxyRequestHandler handler);
Registration registerResponseHandler(ProxyResponseHandler handler);
}
List<ProxyHttpRequestResponse> history();
,对应图形界面中的HTTP history
tab。该方法返回一个List<ProxyHttpRequestResponse>
,代表http的历史记录,List<ProxyHttpRequestResponse>
中的一个ProxyHttpRequestResponse
对象,即表示tab中的一行记录。
Registration registerRequestHandler(ProxyRequestHandler handler);
,学过GUI(图形用户接口)编程的师傅应该对这个方法感到熟悉,handler、注册handler,这是事件驱动编程中常用的术语,这里我们不关心什么是事件驱动编程(请问deepseek),只需要知道构造一个ProxyRequestHandler
类型的对象,并将其传给registerRequestHandler()
,当proxy中捕获到流量时,就会通知handler,调用handler里面的方法。
4.2 ProxyRequestHandler接口
我们来看一下ProxyRequestHandler.class
public interface ProxyRequestHandler {
ProxyRequestReceivedAction handleRequestReceived(InterceptedRequest interceptedRequest);
ProxyRequestToBeSentAction handleRequestToBeSent(InterceptedRequest interceptedRequest);
}
1. ProxyRequestHandler接口中又涉及的接口角色与职责:
接口 | 作用 | 触发阶段 |
---|---|---|
InterceptedRequest | 表示被代理拦截的 原始请求,包含未经修改的请求数据(如 URL、头、体)。 | 代理首次捕获到客户端请求时生成。 |
ProxyRequestReceivedAction | 定义代理对拦截请求的 处理动作(如放行、拦截、丢弃)。 | 在请求被代理接收后、发送到服务器前触发。 |
ProxyRequestToBeSentAction | 定义代理对即将发送的请求的 最终处理动作(如修改后发送、取消发送)。 | 在请求经过处理后、即将发送到服务器前触发。 |
终于看到Http流量的来源了,其被封装为一个InterceptedRequest
类型的对象,并传给了两个方法,方法会在不同阶段被自动调用(事件驱动编程)。跟踪Http请求的流转,不就是查看这些方法之间的调用顺序,也是在这些方法里实现我们对Http请求的处理逻辑(增删改)。
2. 处理流程与协作关系
阶段 1:请求拦截(InterceptedRequest
)
- 触发条件:客户端(如浏览器)通过 Burp 代理发送请求,代理捕获到该请求。
- 数据载体:
InterceptedRequest
对象包含原始请求的完整信息。 - 扩展操作:通过实现
ProxyHttpRequestHandler
的handleRequestReceived()
方法处理该对象:public ProxyRequestReceivedAction handleRequestReceived(InterceptedRequest interceptedRequest) { // 分析或修改请求 if (interceptedRequest.url().contains("admin")) { // 拦截敏感请求并标记为待处理 return ProxyRequestReceivedAction.intercept(interceptedRequest); } // 直接放行非敏感请求 return ProxyRequestReceivedAction.continueWith(interceptedRequest); }
阶段 2:处理动作决策(ProxyRequestReceivedAction
)
- 动作类型:
continueWith(request)
:放行请求(可选传入修改后的请求)。intercept(request)
:拦截请求并等待用户操作(通过 Burp UI)。drop()
:直接丢弃请求。
- 示例:
// 修改请求头后放行 InterceptedRequest modifiedRequest = interceptedRequest.withHeader("X-Token", "123"); return ProxyRequestReceivedAction.continueWith(modifiedRequest);
阶段 3:发送前最终处理(ProxyRequestToBeSentAction
)
- 触发条件:请求经过
ProxyRequestReceivedAction
处理后决定放行。 - 数据载体:
InterceptedRequest
对象(包含可能已被修改的请求)。 - 扩展操作:通过实现
ProxyHttpRequestHandler
的handleRequestToBeSent()
方法进行最终调整:public ProxyRequestToBeSentAction handleRequestToBeSent(InterceptedRequest interceptedRequest) { // 添加请求头 HttpRequest modifiedRequest = interceptedRequest.withHeader("X-Author", "MingSec"); // 继续发送修改后的请求 return ProxyRequestToBeSentAction.continueWith(modifiedRequest); }
3. 核心交互流程
4. 关键设计意图
-
职责分离
InterceptedRequest
专注原始请求数据,保持不可变性(需通过withXxx()
生成新对象)。ProxyRequestReceivedAction
控制请求的初步流向(拦截/放行/丢弃)。ProxyRequestToBeSentAction
提供最终修改机会,确保所有插件修改生效。
-
扩展灵活性
- 允许插件在 不同阶段 介入:
- 接收阶段:快速决策是否拦截(如基于 URL 黑名单)。
- 发送阶段:精细化修改(如添加签名头、加密敏感数据)。
- 允许插件在 不同阶段 介入:
-
状态一致性
- 通过分阶段处理,确保修改操作有序(例如先由插件 A 修改头,再由插件 B 加密体)。
值得注意一点,InterceptedRequest
的不可变性。InterceptedRequest接口中提供的对其自身的更改,都是产生一个新的HttpRequest类型的对象,将新的对象作为返回值,实际更改才会生效。
Http history tab 中展示的哪一个阶段的HttpRequest对象呢?
- 传进来的原始请求对象
InterceptedRequest
handleRequestReceived
执行后返回的HttpRequest
handleRequestToBeSent
执行后返回的HttpRequest
这个其实是可选的。在Settings -> Tools -> Proxy
中,如下
5. 总结
InterceptedRequest
是代理拦截的原始请求入口。ProxyRequestReceivedAction
决定请求的初步流向(放行/拦截/丢弃)。ProxyRequestToBeSentAction
控制请求的最终形态,确保修改生效。- 协作关系:三者形成代理处理流水线,实现从拦截到发送的全流程控制。
详细流程(类时序图):
4.3 Demo
通过一个例子,验证前面的知识。不要忘记这张时序图
package com.mingsec;
import burp.api.montoya.BurpExtension;
import burp.api.montoya.EnhancedCapability;
import burp.api.montoya.MontoyaApi;
import burp.api.montoya.core.HighlightColor;
import burp.api.montoya.http.message.requests.HttpRequest;
import burp.api.montoya.proxy.Proxy;
import burp.api.montoya.proxy.ProxyHttpRequestResponse;
import burp.api.montoya.proxy.http.InterceptedRequest;
import burp.api.montoya.proxy.http.ProxyRequestHandler;
import burp.api.montoya.proxy.http.ProxyRequestReceivedAction;
import burp.api.montoya.proxy.http.ProxyRequestToBeSentAction;
import java.util.List;
import java.util.Set;
public class Ext2025_4 implements BurpExtension {
//将MontoyaApi作为静态属性,方便后续传给其他类
static MontoyaApi montoya;
@Override
public void initialize(MontoyaApi api) {
//赋值给静态属性
Ext2025_4.montoya = api;
api.extension().setName("ext2025_4");
api.logging().logToOutput("Ext2025_4 by MingSec");
//获取Proxy模块的抽象 => Proxy对象
Proxy proxy = api.proxy();
//判断此时intercept状态
boolean interceptEnabled = proxy.isInterceptEnabled();
if (interceptEnabled) { //intercept 为 on
proxy.disableIntercept(); //设置 intercept 为 off
try {
Thread.sleep(7000); //睡眠 7 秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//创建一个实现了ProxyRequestHandler接口的类(在下面)的对象
MyProxyRequestHandler myProxyRequestHandler = new MyProxyRequestHandler();
proxy.registerRequestHandler(myProxyRequestHandler); //注册该handler
api.logging().logToOutput("7秒之期已到,启动!");
proxy.enableIntercept();
proxy.disableIntercept();
//? 获取当前状态下的history,并将requestBody中含password字符串的proxyHttpRequestResponse标注
List<ProxyHttpRequestResponse> history = proxy.history();
for (ProxyHttpRequestResponse proxyHttpRequestResponse : history) {
if (proxyHttpRequestResponse.request().bodyToString().contains("password")) {
proxyHttpRequestResponse.annotations().setHighlightColor(HighlightColor.RED);
proxyHttpRequestResponse.annotations().setNotes("mingsec");
}
}
}
@Override
public Set<EnhancedCapability> enhancedCapabilities() {
return BurpExtension.super.enhancedCapabilities();
}
}
//实现 ProxyRequestHandler接口
class MyProxyRequestHandler implements ProxyRequestHandler {
MontoyaApi api = Ext2025_4.montoya;
String keyword = "password";
boolean flag = false;
private boolean contains(InterceptedRequest interceptedRequest) {
return interceptedRequest.bodyToString().contains(keyword);
}
@Override
/**Proxy代理到流量时调用,返回值ProxyRequestReceivedAction
* ProxyRequestReceivedAction 接口中有3个静态方法用于返回一个该类型
* - ProxyRequestReceivedAction.intercept(HttpRequest httpRequest) 拦截该请求,等待用户在UI中处理
* - ProxyRequestReceivedAction.doNotIntercept(HttpRequest httpRequest) 直接放行
* - ProxyRequestReceivedAction.continueWith(HttpRequest httpRequest) //根据当前规则处理
* - ProxyRequestReceivedAction.drop(HttpRequest httpRequest) 丢弃该请求相当于在UI中手动drop
*/
public ProxyRequestReceivedAction handleRequestReceived(InterceptedRequest interceptedRequest) {
this.flag = false;
if (interceptedRequest.bodyToString().contains("password")) { //如果代理到的请求体中包含password
this.flag = true;
//给请求设置高亮和注释
interceptedRequest.annotations().setHighlightColor(HighlightColor.BLUE);
interceptedRequest.annotations().setNotes("mingsec");
//修改请求,返回一个修改后新的请求
HttpRequest httpRequest = interceptedRequest.withAddedHeader("author", "mingsec");
return ProxyRequestReceivedAction.intercept(httpRequest); //拦截请求,在UI中展示
}
return ProxyRequestReceivedAction.doNotIntercept(interceptedRequest); //将代理到的请求直接放行
}
@Override
public ProxyRequestToBeSentAction handleRequestToBeSent(InterceptedRequest interceptedRequest) {
api.logging().logToOutput(hashCode());
HttpRequest httpRequest = null;
if (flag) {
interceptedRequest.annotations().setNotes("mingsec-modified");
interceptedRequest.annotations().setHighlightColor(HighlightColor.GREEN);
httpRequest = interceptedRequest.withHeader("author", "mingsec by handler");
}
return ProxyRequestToBeSentAction.continueWith(httpRequest);
}
}
学习了关于处理Http消息的许多接口,接下来看看一些关于扩展其他功能的接口稍作休息。
5. burp.api.montoya.extension
此包下的接口定义了扩展本身的设置和卸载前的的处理机制,Extension
对象即代表扩展本身。
5.1 Extension接口
返回一个Extension类型的对象,我们去Extension,如下
看来看去,好像就这个setName(String extensionName)
方法所见即所得,设置扩展名称的代码就如下:
public class HelloBurp implements BurpExtension {
// 实现实现该方法
@override
void initialize(MontoyaApi api) {
/* 加载插件时调用此方法,我们可以在这里写逻辑代码,我们可以写点什么?
依靠主程序传入的 MontoyaApi 类型对象,做点什么那么就需要看 MontoyaApi 这个类型提供了什么
*/
Extension extension = api.extension();
extension.setName("bp扩展");
//或者直接写为
api.extension().setName("bp扩展");
}
注意到Registration registerUnloadingHandler(ExtensionUnloadingHandler handler);
就像上面Proxy中的例子,实现一个ExtensionUnloadingHandler接口的类,并其传给registerUnloadingHandler,则扩展在卸载时将调用该类中的方法。
5.2 ExtensionUnloadingHandler接口
public interface ExtensionUnloadingHandler
{
/**
* This method is invoked when the extension is unloaded.
*/
void extensionUnloaded();
}
实现该类,重写其中方法,并通过Extension接口中registerUnloadingHandler注册,扩展在卸载时将调用该类中的方法。
6. Http http()
6.1 Proxy 和 Http
有了Proxy模块中对http包的处理能力,为什么还需要http包呢?
在 Burp 的 Montoya API 中,proxy
包和 http
包分别定义了与 HTTP 数据包相关的接口,这种设计看似重复,但实际上是基于 模块化职责分离 和 功能分层 的考量。以下是具体原因分析:
-
职责分离:不同模块的职责划分
proxy
包:
聚焦于 代理交互流程,例如拦截请求/响应、修改流量、控制代理行为等。它定义的接口(如InterceptedRequest
、InterceptedResponse
)是代理流程中的核心对象,通常包含与拦截逻辑直接相关的功能(如sendAndReceive()
)。http
包:
提供 HTTP 消息的通用构建和解析能力,例如创建HttpRequest
、HttpResponse
对象,处理 HTTP 头、体、状态码等底层细节。它不绑定代理流程,而是为整个框架(如扫描器、扩展开发)提供通用的 HTTP 消息处理能力。
示例:
- 在代理拦截流程中,
proxy
包的InterceptedRequest
会封装一个http.HttpRequest
对象(通过getRequest()
方法),但前者额外提供了拦截控制方法(如setRequest()
修改请求)。 http.HttpRequest
是一个独立的 HTTP 请求对象,可在任何需要构建/解析 HTTP 消息的场景中使用(如自定义工具模块)。
- 功能分层:接口的抽象层级不同
http
包 是 基础层接口,定义了 HTTP 消息的通用结构(如HttpRequest
、HttpResponse
),不涉及具体的交互逻辑(如代理拦截、网络发送)。proxy
包 是 应用层接口,基于http
包的抽象进行扩展,实现了与代理交互相关的功能(如拦截、转发、修改)。
- 接口复用与扩展性
http
包的接口是通用的,可被多个模块共享。例如:- 代理模块(
proxy
)需要处理拦截的 HTTP 请求/响应。 - 扫描器模块需要分析 HTTP 消息以检测漏洞。
- 自定义扩展可能需要构建或解析 HTTP 消息。
- 代理模块(
- 如果所有功能都集中在
proxy
包中,会导致接口臃肿、职责混乱。通过http
包的抽象,可以避免重复定义相同的数据结构。
-
代理流程与 HTTP 消息处理的解耦
- 代理流程(
proxy
包):
关注的是 请求/响应的生命周期管理,例如:- 拦截请求时调用
interceptedRequest.getRequest()
获取原始请求。 - 修改请求后调用
interceptedRequest.setRequest(...)
。 - 通过
sendAndReceive()
发送请求并获取响应。
- 拦截请求时调用
- HTTP 消息处理(
http
包):
关注的是 消息的结构和内容操作,例如:- 使用
http.HttpRequest
解析请求头、参数、体。 - 使用
http.HttpResponse
构造响应内容。
- 使用
解耦优势:
- 代理模块可以专注于流程控制(如拦截规则、转发逻辑),而 HTTP 消息的解析/构建由
http
包统一处理。 - 修改 HTTP 消息的逻辑(如添加头、修改参数)可以在任意地方通过
http
包接口实现,无需依赖代理上下文。
- 代理流程(
总结
模块 | 职责 | 典型接口 | 特点 |
---|---|---|---|
proxy | 代理交互流程控制 | InterceptedRequest , InterceptedResponse | 聚焦拦截、修改、转发逻辑 |
http | HTTP 消息构建与解析 | HttpRequest , HttpResponse | 提供通用的 HTTP 消息处理能力 |
这种分层设计使得 Burp 的 API 更加模块化、可维护性更强,同时也为扩展开发提供了灵活的接口选择。
6.2 Http
注意这个方法
处理即将发送的请求,这是burp提供给我们对【全局Http消息】处理的能力,即无论Http的请求/响应来源于Proxy、Repeater、Intruder等任何可以产生Http消息的地方,都可以通过调用registerHttpHandler()
是注册一个HttpHandler
,在这个HttpHandler
里面实现我们最想用的功能——改包。我们点进HttpHandler
。
这个接口里有两个方法,分别是对发包/请求
和和回包/响应
进行操作,具体的逻辑与之前的ProxyHttpHandler
一致,都是事件驱动编程模型。
6.3 关键协作关系
-
代理模块与 HTTP 模块的协作
- 数据模型共享:
http 包中的 HttpRequest/HttpResponse 是代理模块的基础数据模型。
例如:InterceptedRequest 是 HttpRequest 的子类,添加了代理特有的元数据。
-流程优先级:
代理模块的处理器(ProxyRequestHandler)优先于 HTTP 模块的处理器(HttpHandler)执行。代理拦截 → 代理处理 → 全局 HTTP 处理 → 流量发送
- 数据模型共享:
7. 总结
主要的逻辑就是获取http消息,并在适当的时间点对http消息进行处理。http消息对象没什么好说的就是对该对象进行字符串的增删改查。http消息处理则是基于事件驱动编程模型,利用Montoya API 提供的几个关键类/方法
,实现对数据包的处理。处理的类需要注册到burp引擎进行统一管理,所以我们可以关注注册
这个核心方法,确定burp提供给我们的能力
- registerRequestHandler
- registerResponseHandler
- registerHttpHandler
8. 参考
[1] MontoyaApi
[2] 新版Burpsuite加解密插件开发基础
[3] Burp Extensions: Singletons and Scope
[4] deepseek