BurpSuite插件开发指南之Java篇
0x00 Java 接口简介
知其然更要知其所以然。在真正动手编写 Burp 插件之前,有必要对Burp提供的各个接口有一定的了解,同时要有一定的编程经验和能力。那么,在此篇中读者则有必要了解 Java 的接口技术。
接口(英文:Interface)在 Java 编程语言中是一个比较抽象的东西。熟悉 OOP 的同学可以用“类”的思想来理解接口。但是,要明白的是,类与接口有相似的地方同时也有很多不同的地方。
接口的声明
接口的声明语法格式如下:
1 2 3 4 5
| #!java [可见度] interface 接口名称 [extends 其他的类名] { // 声明变量 // 抽象方法 }
|
例如,Burp 的 接口声明原型如下:
1 2 3 4 5 6 7
| #!java package burp; public interface IBurpExtender { void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks); }
|
接口的实现
一个接口可以被另外一个接口继承,也可以被一个类实现。当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。不熟悉Java编程的读者要牢记这几点。
实现一个接口的语法如下:
1 2
| #!java ... implements 接口名称[, 其他接口1, 其他接口2..., ...] ...
|
例如,编写 Burp 插件必须编写的 BurpExtender 类实现 IBurpExtender 和 IProxyListener 接口代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #!java package burp; public class BurpExtender implements IBurpExtender, IProxyListener{ // 实现 IBurpExtender 接口的 registerExtenderCallbacks 方法 @Override public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { // TODO here } // 实现 IProxyListener 接口的 processProxyMessage 方法 @Override public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message) { // TODO here } }
|
需要注意的是,在Burp提供的接口文档中,并不是所有的接口都可以用类实现,只有在接口的描述中说明了“该接口可以被实现”时,所对应的接口才可以被你所编写的类实现其方法。
0x01 Java Swing 和 AWT 包简介
Java Swing 是 Java Foundation Classes(JFC)的一部分。在 Swing 中,包含了很多强大灵活的,跨平台的 GUI 控件。Swing 组件遵循(MVC)模型 - 视图 - 控制器架构,并提供了三个通用的顶层容器类 JFrame,JDialog 和 JApplet。在开发带有 GUI 的 BurpSuite 插件时,一般不会直接用到这三个顶层容器类,具体要看个人的设计和需求。
下面是一些最常用的控件:
- JLabel 标签控件,JLabel 的对象是在容器中放置一个文本标签。
- JButton 按钮控件。
- JColorChooser 颜色选择控件,用于让用户操作和选择颜色。
- JCheckBox 选择框控件,支持分组。
- JRadioButton 单选框控件,支持分组。
- JList 列表控件。
- JComboBox 组合框控件。
- JTextField 文本框控件。
- JPasswordField 密码输入框控件。
- JTextArea 多行文本控件。
- ImageIcon 绘制图标的控件。
- JScrollbar 滚动条控件,支持水平和垂直滚动。
- JFileChooser 选择文件对话框。
- JProgressBar 进度条控件。
- JPanel 面板控件,此控件在开发插件时会经常用到。
Swing 是在 AWT 的基础上构建的一套新的图形界面系统,所以 AWT 是 Java 实现图形界面的基础,图形控件的事件监听和响应也是由 AWT 完成的。不过,编写 Burp 插件所用到的图形组件和事件并不多,很容易上手。
有关更多 GUI 组件的知识,请读者自行百度了解。在此不做过多阐述。
0x02 自定义 Burp UI 标签
编写 GUI 的 Burp 插件在实际使用时更加易于操作和表达信息。当然,编写起来也十分简单,只需遵循一定的“套路”,就可以了。
最终编写好的基本的样式如下图所示:
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| #!java /* * BurpSuite 插件开发指南之 Java 篇 * writend by Her0in */ package burp; import java.awt.Component; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.PrintWriter; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.SwingUtilities; public class BurpExtender implements IBurpExtender, ITab{ public PrintWriter stdout; public IExtensionHelpers hps; public IBurpExtenderCallbacks cbs; public JPanel jPanelMain; @Override public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { callbacks.setExtensionName("BurpExtender"); this.hps = callbacks.getHelpers(); this.cbs = callbacks; this.stdout = new PrintWriter(callbacks.getStdout(), true); this.stdout.println("hello burp!"); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { jPanelMain = new JPanel(); JButton jButton = new JButton("老司机,快点我!"); jButton.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e){ stdout.println("哔..."); } }); // 将按钮添加到 主面板 jPanelMain 中. jPanelMain.add(jButton); // 设置自定义组件并添加标签 cbs.customizeUiComponent(jPanelMain); cbs.addSuiteTab(BurpExtender.this); } }); } // 实现 ITab 接口的 getTabCaption 方法 @Override public String getTabCaption() { return "Burp 标签测试"; } // 实现 ITab 接口的 getUiComponent 方法 @Override public Component getUiComponent() { return jPanelMain; } }
|
从上述代码中,读者也能够看到,Java 的 Swing 图形编程有点蛋疼,需要一层层的编写。首先需要添加一个 JPanel 上去,然后在这个 JPanel 中再添加图形组件,如果还有上层的图形组件,则需要再添加一个 Panel 类型的控件。
在这里有一个比较快速编写 Burp GUI插件的方法,先利用 NetBeans IDE 将图形界面拖拽式的写好,然后将上述显示控件的代码替换为显示这个 JFrame 的代码。之后编写相关的事件响应代码。
0x03 BurpSuite插件开发实例之 JSON 水坑 检测插件
本小节,笔者将会使用一个实例来“抛砖引玉”式的描述编写带有 GUI 的 Burp 插件。读者可以把关注点放在图形控件的放置顺序和事件处理上,可以无视 JSON 水坑检测的逻辑是否严谨以及误报率,准确率是否科学等问题。
最终编写好的插件如下图:
顶部的控件是一个表格列表控件,会放置检测到的结果。下面两个 ITextEditor 分别显示当前 HTTP 数据包的请求信息和响应信息。
代码就直接贴出来吧,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
| #!java /* * BurpSuite 插件开发指南之 Java 篇 * writend by Her0in */ package burp; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Rectangle; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.io.PrintWriter; import java.util.Vector; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; public class BurpExtender implements IBurpExtender, ITab, IHttpListener{ public PrintWriter stdout; public IExtensionHelpers hps; public IBurpExtenderCallbacks cbs; public IRequestInfo iRequestInfo; public IResponseInfo iResponseInfo; public JPanel jPanel_top; public JTabbedPane jTabbedPane; public JScrollPane jScrollPane; public JSplitPane jSplitPaneV; // 自己封装一个 Table 控件 private Her0inTable jsonTable; //请求,响应信息显示 public JPanel jPanel_reqInfo_left; public JPanel jPanel_respInfo_right; public JSplitPane jSplitPaneInfo; public ITextEditor iRequestTextEditor; public ITextEditor iResponseTextEditor; Boolean bFind = false; String strTags = ""; @Override public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { callbacks.setExtensionName("JSON 水坑检测"); this.hps = callbacks.getHelpers(); this.cbs = callbacks; this.stdout = new PrintWriter(callbacks.getStdout(), true); this.stdout.println("hello burp!"); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // 初始化垂直分隔面板 jSplitPaneV = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true); jSplitPaneV.setDividerLocation(0.5); jSplitPaneV.setOneTouchExpandable(true); // 垂直分隔面板的顶部 jPanel_top = new JPanel(); // 设置垂直分隔面板顶部的子控件 // 放置表格控件 jTabbedPane = new JTabbedPane(); // 初始化 Burp 提供的 ITextEditor 编辑器接口 iRequestTextEditor = cbs.createTextEditor(); iRequestTextEditor.setEditable(false); iResponseTextEditor = cbs.createTextEditor(); iResponseTextEditor.setEditable(false); // 初始化 jsonTable jsonTable = new Her0inTable(iRequestTextEditor, iResponseTextEditor, stdout); // 最好放置一个 JScrollPane JScrollPane jScrollPane1 = new JScrollPane(jsonTable.getTab()); jScrollPane1.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); jScrollPane1.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); jTabbedPane.scrollRectToVisible(new Rectangle(500, 70)); jTabbedPane.addTab("JSON 水坑检测", jScrollPane1); jScrollPane = new JScrollPane(jTabbedPane); jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); jScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); jPanel_top.add(jScrollPane, BorderLayout.CENTER); jPanel_top.setLayout(null); // 添加componentResized事件 否则在改变Burp 主窗口大小时会错位 jPanel_top.addComponentListener(new ComponentListener() { @Override public void componentShown(ComponentEvent e) { } @Override public void componentResized(ComponentEvent e) { if(e.getSource() == jPanel_top){ jScrollPane.setSize(jPanel_top.getSize().width - 5, jPanel_top.getSize().height - 5); jScrollPane.setSize(jPanel_top.getSize().width - 10, jPanel_top.getSize().height - 10); } } @Override public void componentMoved(ComponentEvent e) { // TODO Auto-generated method stub } @Override public void componentHidden(ComponentEvent e) { // TODO Auto-generated method stub } }); // 设置垂直分隔面板底部的子控件 // 显示请求/响应 信息的水平分隔面板初始化 jSplitPaneInfo = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); jSplitPaneInfo.setDividerLocation(0.5); jSplitPaneInfo.setOneTouchExpandable(true); // 初始化 请求,响应信息显示 面板 jPanel_reqInfo_left = new JPanel(); jPanel_respInfo_right = new JPanel(); jPanel_reqInfo_left.setLayout(new BorderLayout()); jPanel_respInfo_right.setLayout(new BorderLayout()); // 将 Burp 提供的 ITextEditor 编辑器 添加到请求,响应信息显示 面板中 jPanel_reqInfo_left.add(iRequestTextEditor.getComponent(), BorderLayout.CENTER); jPanel_respInfo_right.add(iResponseTextEditor.getComponent(), BorderLayout.CENTER); // 分别添加 请求,响应信息显示 面板 到 垂直分隔面板底部 jSplitPaneInfo.add(jPanel_reqInfo_left, JSplitPane.LEFT); jSplitPaneInfo.add(jPanel_respInfo_right, JSplitPane.RIGHT); // 最后,为垂直分隔面板添加顶部面板和水平分隔面板 jSplitPaneV.add(jPanel_top, JSplitPane.TOP); jSplitPaneV.add(jSplitPaneInfo, JSplitPane.BOTTOM); // 设置自定义组件并添加标签 cbs.customizeUiComponent(jSplitPaneV); cbs.addSuiteTab(BurpExtender.this); } }); callbacks.registerHttpListener(this); } // 实现 ITab 接口的 getTabCaption 方法 @Override public String getTabCaption() { return "JSON 水坑检测"; } // 实现 ITab 接口的 getUiComponent 方法 @Override public Component getUiComponent() { return jSplitPaneV; } public void CheckJson(IHttpRequestResponse messageInfo) { try { this.iRequestInfo = this.hps.analyzeRequest(messageInfo); this.iResponseInfo = this.hps.analyzeResponse(messageInfo.getResponse()); } catch (Exception e) { return ; } // stdout.println(messageInfo.getHttpService().getHost()); this.bFind = false; java.util.List<IParameter> listIParameters = iRequestInfo.getParameters(); strTags = ""; for (IParameter param : listIParameters) { String strName = param.getName().toLowerCase(); if(strName.indexOf("callback") != -1 || strName.indexOf("_callback") !=-1 || strName.indexOf("cb") !=-1 || strName.indexOf("_cb") != -1 || strName.indexOf("huidiao") !=-1 ){ strTags += "# find => " + strName; this.bFind = true; } } if(this.bFind){ Vector<String> vectorRow = new Vector<String>(); vectorRow.addElement(new String(Integer.toString(jsonTable.defaultTableModel.getRowCount()))); vectorRow.addElement(new String(this.iRequestInfo.getUrl().getHost())); vectorRow.addElement(new String(this.iRequestInfo.getMethod())); if(this.iRequestInfo.getUrl().getQuery() != null){ vectorRow.addElement(new String(this.iRequestInfo.getUrl().getPath() + "?" + this.iRequestInfo.getUrl().getQuery())); }else{ vectorRow.addElement(new String(this.iRequestInfo.getUrl().getPath())); } vectorRow.addElement(new String(strTags)); jsonTable.defaultTableModel.addRow(vectorRow); jsonTable.iHttpList.add(messageInfo); } } @Override public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { if (!messageIsRequest) { //JSON 检测 this.CheckJson(messageInfo); } } }
|
转自:https://www.tuicool.com/articles/aaaa6fA