前言

最近使用了下这个工具感觉确实很好用在想着自己能不能进行扩展,所以就对其的源码进行了分析,分享一下

 
关于此插件的下载地址是:https://github.com/boy-hack/wooyun-payload

使用详情如下

乌云漏洞库payload-Burp插件源码分析

乌云漏洞库payload-Burp插件源码分析

下面就直接从其的源码入手,BurpExtender.java的代码分析如下所示,此代码主要是实现burpsuite当中要定义插件的要求
package burp;

import wooyun.GUI;import wooyun.Menu;

import javax.swing.*;import java.io.PrintWriter;import java.util.ArrayList;import java.util.List;

//所有的插件必须实现此接口。实现的类名必须为“BurpExtender”。在 burp包中,必须申明为 public ,并且必须提供一个默认的构造器。//对于IContextMenuFactory接口提供了下面的方法//#!java java.util.List<javax.swing.JMenuItem>   createMenuItems(IContextMenuInvocation invocation)public class BurpExtender implements IBurpExtender, IContextMenuFactory {    public PrintWriter stdout;    public PrintWriter stderr;    //这个接口包含许多帮助器方法,这些扩展可以用来帮助处理Burp扩展中出现的各种常见任务。扩展可以调用BurpExtenderCallbacks.getHelpers获取该接口的实例。    public IExtensionHelpers helpers;    //使用这个接口burpsuite通过扩展一组扩展使用的回调方法    public IBurpExtenderCallbacks cbs;    //当Burp调用扩展提供的带有上下文菜单调用细节的IContextMenuFactory时,将使用此接口。    //自定义上下文菜单工厂可以查询此接口来获取调用事件的详细信息,以便确定应该显示哪些菜单项。    public IContextMenuInvocation context;
    @Override    //// 实现 IBurpExtender 接口的 registerExtenderCallbacks 方法    public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
        String pluginName = "From wooyun search";        //设置扩展插件名        callbacks.setExtensionName(pluginName);        //burp扩展的帮助类,有一些字符串转换的功能,例如helpers.base64Encode(parameter.getValue())        this.helpers = callbacks.getHelpers();        this.cbs = callbacks;        //输出流,在burp输出内容,可用来调试代码        this.stdout = new PrintWriter(callbacks.getStdout(), true);        //错误流,在burp输出内容,可用来调试代码        this.stderr = new PrintWriter(callbacks.getStderr(), true);        this.stdout.println("hello burp!");        //Burp 的作者在设计上下文菜单功能中采用了工厂模式的设计模式,扩展可以实现此接口,然后调用 IBurpExtenderCallbacks.registerContextMenuFactory() 注册自定义上下文菜单项的工厂。        callbacks.registerContextMenuFactory(this);// for menus    }

    @Override    //当用户在 Burp 中的任何地方调用一个上下文菜单时,Burp 则会调用这个工厂方法。    //此方法会根据菜单调用的细节,提供应该被显示在上下文菜单中的任何自定义上下文菜单项。    //invocation - 一个实现 IMessageEditorTabFactory 接口的对象, 通过此对象可以获取上下文菜单调用的细节。    public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {        ArrayList<JMenuItem> menu_list = new ArrayList<JMenuItem>();        this.context = invocation;        menu_list.add(new Menu(this));        //此工厂方法将会返回需要被显示的自定义菜单项的一个列表(包含子菜单,checkbox 菜单项等等),         //若无菜单项显示,此工厂方法会返回 null 。        return menu_list;    }}

关于TableStruct.java的代码,其实就是要显示的一个table表的结构

package wooyun;
import java.util.HashMap;import java.util.Map;
public class TableStruct {    private Map<String, Object[]> ColumnNames;    private Map<String, Object[][]> RowDatas;    private Map<String, String> ExtraInfo;
    TableStruct() {        //列名        ColumnNames = new HashMap<String, Object[]>();        //行数据        RowDatas = new HashMap<String, Object[][]>();        //提取的信息        ExtraInfo = new HashMap<String, String>();    }
    public void SetExtraInfo(String name, String info) {        this.ExtraInfo.put(name, info);    }
    public String GetExtraInfo(String name) {        return this.ExtraInfo.get(name);    }

    public String[] GetNameSets() {        //带参数的toArray方法,则是根据参数数组的类型,构造了一个对应类型的,长度跟ArrayList的size一致的空数组,        //虽然方法本身还是以 Object数组的形式返回结果,不过由于构造数组使用的ComponentType跟需要转型的ComponentType一致,就不会产生转型异常。        //java.lang.reflect.Array类提供静态方法来动态创建和访问Java数组        //在Java的反射机制中,通过 数组的 class 对象的getComponentType()方法可以取得一个数组的Class对象,        a = (Object[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);          return RowDatas.keySet().toArray(new String[0]);    }

    public void AddColumnNames(String name, Object[] obj) {        this.ColumnNames.put(name, obj);    }

    public void AddRowDatas(String name, Object[][] obj) {        this.RowDatas.put(name, obj);    }

    public Object[] GetColumnNamesFromTabName(String name) {        return this.ColumnNames.get(name);    }

    public Object[][] GetRowDatasFromTabName(String name) {        return this.RowDatas.get(name);    }

}

关于GUI.java的代码

package wooyun;
import javax.swing.*;import javax.swing.event.ChangeEvent;import javax.swing.event.ChangeListener;import javax.swing.table.JTableHeader;import javax.swing.table.TableColumnModel;import java.awt.*;
public class GUI {    public GUI(TableStruct t) {        TabbedPaneFrame jf = new TabbedPaneFrame(t);    //创建一个JFrame对象        //设置Title        jf.setTitle("WooYun 搜索结果");        //设置大小        jf.setSize(650, 480);        //设置窗口相对于指定组件的位置。如果组件当前未显示或者 c 为 null,则此窗口将置于屏幕的中央。        jf.setLocationRelativeTo(null);//        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        jf.setVisible(true);    //设置窗口可见    }}
//java的GUI程序的基本思路是以JFrame为基础,它是屏幕上window的对象,能够最大化、最小化、关闭class TabbedPaneFrame extends JFrame {
    //JTabbedPane是一种可以放多个面板的并且可以方便切换的容器,选项卡面板。它允许用户通过点击给定标题或图标的选项卡,在一组组件之间进行切换显示    private JTabbedPane tabbedPane;    private int count = 0;    //定义结构体化的    private TableStruct struct;
    public TabbedPaneFrame(TableStruct t) {        this.struct = t;        // 添加选项卡,进行初始化        tabbedPane = new JTabbedPane();        //获取key的集合        for (String s : struct.GetNameSets()) {            //例如可以以下面的方式进行创建            //创建第 1 个选项卡(选项卡只包含 标题)            //tabbedPane.addTab("Tab01", createTextPanel("TAB 01"));            // 创建第 2 个选项卡(选项卡包含 标题 和 图标)            //tabbedPane.addTab("Tab02", new ImageIcon("bb.jpg"), createTextPanel("TAB 02"));            // 创建第 3 个选项卡(选项卡包含 标题、图标 和 tip提示)            //tabbedPane.addTab("Tab03", new ImageIcon("bb.jpg"), createTextPanel("TAB 03"), "This is a tab.");            tabbedPane.addTab(s, null);        }        // 添加选项卡面板的控件        add(tabbedPane, "Center");        // 添加监听器,添加选项卡选中状态改变的监听器        tabbedPane.addChangeListener(new ChangeListener() {            @Override
            public void stateChanged(ChangeEvent e) {
                // TODO Auto-generated method stub                //获取当前被选中的选项卡                int n = tabbedPane.getSelectedIndex();                loadTab(n);
            }
        });        //设置默认进行加载的方式        loadTab(0);

    }
    private static String convertToMultiline(String orig) {        //replaceAll(String regex, String replacement),        //用replacement替换所有的regex匹配项,regex很明显是个正则表达式,replacement是字符串。        return "<html>" + orig.replaceAll("\n", "");    }
    private void loadTab(int n) {        //获取选定的title        String title = tabbedPane.getTitleAt(n);        //根据title获取行的数据        Object[][] tableDate = struct.GetRowDatasFromTabName(title);        //根据title获取列数据        Object[] name = struct.GetColumnNamesFromTabName(title);        //创建表格控件        //JTable table = new JTable(cellData, columnNames);        JTable table = new JTable(tableDate, name);
        //JPanel:面板组件,非顶层容器        JPanel jp = new JPanel();    //创建一个JPanel对象

        JTableHeader head = table.getTableHeader(); // 创建表格标题对象        //dimension是Java的一个类,封装了一个构件的高度和宽度        //setSize是设定的固定大小,而setPreferredSize仅仅是设置最好的大小,这个不一定与实际显示出来的控件大小一致(根据界面整体的变化而变化)        head.setPreferredSize(new Dimension(head.getWidth(), 20));// 设置表头大小        //设置自动调整的模式        //AUTO_RESIZE_SUBSEQUENT_COLUMNS  在 UI 调整中,更改后续列以保持总宽度不变,这是默认的行为        table.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);        //利用JTable中的getColumnModel()方法取得TableColumnModel对象;再利用TableColumnModel界面         //所定义的getColumn()方法取TableColumn对象,利用此对象的setPreferredWidth()方法就可以控制字段的宽度.        TableColumnModel tc = table.getColumnModel();        tc.getColumn(0).setPreferredWidth(10);        table.setRowHeight(22);        //获取额外的信息        String ret = struct.GetExtraInfo(title);        JLabel jl = new JLabel(convertToMultiline(ret));    //创建一个标签

        //BorderLayout表示的就是边界布局        jp.setLayout(new BorderLayout());        //        jp.add(jl, "North");        //在JScrollPane里放入JPanel        jp.add(new JScrollPane(table), "Center");
        //将组件设置为index至component,如果该索引中没有选项卡,则会引发内部异常        tabbedPane.setComponentAt(n, jp);    }}

关于Menu.java

package wooyun;

import burp.*;

import javax.swing.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.io.PrintWriter;import java.net.URL;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;

public class Menu extends JMenuItem {//JMenuItem vs. JMenu

   //初始化方法    public Menu(BurpExtender burp) {        this.setText(" 从乌云搜索类似漏洞");        // 添加动作监听器        this.addActionListener(new SearchFromWooyun(burp, burp.context));    }}

class SearchFromWooyun implements ActionListener {    public IContextMenuInvocation invocation;    public IExtensionHelpers helpers;    public PrintWriter stdout;    public PrintWriter stderr;    public IBurpExtenderCallbacks callbacks;

    //下面就是其的初始化方法    public SearchFromWooyun(BurpExtender burp, IContextMenuInvocation context) {        //通过burpExtender当中的内容给context        this.invocation = context;        this.helpers = burp.helpers;        this.callbacks = burp.cbs;        this.stdout = burp.stdout;        this.stderr = burp.stderr;    }

    @Override    //在ActionListener中的actionPerformed中定义相应方法处理事件    public void actionPerformed(ActionEvent e) {

        //IContextMenuInvocation的getSelectedMessages方法        //此方法可用于检索用户在调用上下文菜单时显示或选择的HTTP请求响应的详细信息。注意:出于性能原因,        //从这个方法返回的对象被绑定到Burp UI中消息的原始上下文。例如,如果在代理拦截面板上调用了上下文菜单,        //那么由该方法返回的IHttpRequestResponse将反映拦截面板的当前内容,并且当当前消息已被转发或删除时,        //这将发生变化。如果您的扩展需要存储的细节信息的上下文菜单中被调用,那么你应该从IHttpRequestResponse查询这些细节的时候调用,        //或者你应该使用IBurpExtenderCallbacks.saveBuffersToTempFiles()来创建一个持久的只读副本        //IHttpRequestResponse @return IHttpRequestResponse对象数组代表显示的物品或由用户选择上下文菜单时调用。        //如果没有适用于调用的消息,此方法将返回null。        IHttpRequestResponse[] selectedItems = this.invocation.getSelectedMessages();

        //此方法用于检索请求消息        //byte[] getRequest();        byte[] selectedRequest = selectedItems[0].getRequest();        //此方法用于接收此请求响应的HTTP服务。        IHttpService httpService = selectedItems[0].getHttpService();



        //**************获取参数 通过IRequestInfo对象*************************//        //此方法可用于分析HTTP请求,并获取有关它的各种关键细节。        // IRequestInfo analyzeRequest(byte[] request);        IRequestInfo analyzedRequest = this.helpers.analyzeRequest(selectedRequest);//only get the first//        String url = analyzedRequest.getUrl().toString();        String pattern = "(GET|POST) ([^ ]*) HTTP/";        //List<String> getHeaders();此方法用于获取请求中包含的HTTP标头。        //获取请求当中的第一个行        String line0 = analyzedRequest.getHeaders().get(0);        //利用正则去匹配        Pattern r = Pattern.compile(pattern);        Matcher m = r.matcher(line0);        String url = "";        if (m.find()) {            //获取协议和host,然后获取匹配到的url            url = httpService.getProtocol() + "://" + httpService.getHost() + m.group(2);        } else {            stderr.println("Host:" + httpService.getHost() + " url匹配失败");            return;        }        List<String> gets = new ArrayList<String>();        List<String> posts = new ArrayList<String>();        List<String> cookies = new ArrayList<String>();
        //下面就是获取请求参数        List<IParameter> paraList = analyzedRequest.getParameters();        for (IParameter para : paraList) {            byte type = para.getType(); //获取参数的类型            String key = para.getName(); //获取参数的名称            String value = para.getValue(); //获取参数的值            //根据不同的type放到不同的list当中            switch (type) {                case 0:                    gets.add(key);                    break;                case 1:                    posts.add(key);                    break;                case 2:                    cookies.add(key);                    break;            }        }       //参数共有7种格式,0是URL参数,1是body参数,2是cookie参数,6是json格式参数        Wooyun w = null;        try {            w = new Wooyun();        } catch (Exception e1) {            for (StackTraceElement                    elem : e1.getStackTrace()) {                this.stderr.println(elem);            }            //showMessageDialog():消息对话框            //JOptionPane.showMessageDialog有三种参数设置            // JOptionPane.showMessageDialog(parentComponent, message);            // JOptionPane.showMessageDialog(parentComponent, message, title, messageType);            // JOptionPane.showMessageDialog(parentComponent, message, title, messageType, icon);            //type为1表示messagetType值为1,含义为提示信息            JOptionPane.showMessageDialog(null, "初始化数据库失败", "提示", 1);            return;        }        try {            //初始化TableStruct结构,调用Search方法,然后将gets、posts、cookies进行转换为数组            TableStruct ret = w.Search(new URL(url), gets.toArray(new String[gets.size()]), cookies.toArray(new String[cookies.size()]), posts.toArray(new String[posts.size()]));            //获取RowDatas当中的所有keySet            if (ret.GetNameSets().length == 0) {                JOptionPane.showMessageDialog(null, "未发现可用参数", "提示", 1);                return;            }            new GUI(ret);        } catch (Exception e1) {            for (StackTraceElement                    elem : e1.getStackTrace()) {                this.stderr.println(elem);            }            this.stderr.println(e1);

        }

    }}
关于此项目的Wooyun.java其实就是搜索类,根据源码当中的wooyun.json进行搜索
package wooyun;

import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.MalformedURLException;import java.net.URL;import java.nio.charset.StandardCharsets;import java.util.*;

import static java.lang.String.join;import static java.lang.String.valueOf;

public class Wooyun {    public StringBuilder sb;    private String shuimugan;    private TableStruct struct;

    public Wooyun() throws IOException {        //Class.getClassLoader.getResourceAsStream(String path) :默认则是从ClassPath根下获取,path不能以’/'开头,最终是由ClassLoader获取资源。        InputStream is = this.getClass().getClassLoader().getResourceAsStream("wooyun.json");        if (is==null) {            throw new NullPointerException();        }//        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("wooyun.json");        sb = new StringBuilder();        //public InputStreamReader(InputStream in,Charset cs)        //创建使用给定字符集的 InputStreamReader,下面就是使用的是StandardCharsets.UTF_8字符集        //BufferedReader :提供通用的缓冲方式文本读取        BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));        int ch;        while ((ch = br.read()) != -1) {            sb.append((char) ch);        }        shuimugan = "https://shuimugan.com/bug/view?bug_no=";        struct = new TableStruct();    }    //自己定义了一个数组的连接函数    private static Object[] concat(Object[] a, Object[] b) {        Object[] c = new Object[a.length + b.length];        System.arraycopy(a, 0, c, 0, a.length);        System.arraycopy(b, 0, c, a.length, b.length);        return c;    }



    /*        输入:一段url,host,get参数,cookie参数,post参数        返回: path统计,host统计,各种参数统计               漏洞类型占比(path,host)     */     //相应的搜索函数    public TableStruct Search(URL url, String[] GetParams, String[] CookieParams, String[] PostParams) {        JSONArray jobj;        //参数是Stringbuffer的toString方法        //转换为jsonobject对象        jobj = JSONArray.parseArray(sb.toString());        //如果用“.”作为分隔的话,必须是如下写法,String.split("\\."),这样才能正确的分隔开,不能用String.split(".");        //获取路径        String[] path_array = url.getPath().split("/");        String path = path_array[path_array.length - 1];        //从url当中获取host        String host = url.getHost();

        Map<String, Integer> pathTJMap = new TreeMap<String, Integer>();        List<Object[]> pathList = new ArrayList<Object[]>();

        Map<String, Integer> hostTJMap = new TreeMap<String, Integer>();        List<Object[]> hostList = new ArrayList<Object[]>();

        Map<String, Integer> getMap = new TreeMap<String, Integer>();        Map<String, Integer> cookieMap = new TreeMap<String, Integer>();        Map<String, Integer> postMap = new TreeMap<String, Integer>();

        for (Object object : jobj) {            JSONObject jsonObject = (JSONObject) object;            //获取漏洞的类型            String BugType = jsonObject.getString("type");            if (BugType.equals("")) {                BugType = "不知名的漏洞类型";            }            String BugID = jsonObject.getString("bugid");            String Title = jsonObject.getString("title");

            JSONArray urls = jsonObject.getJSONArray("url");            JSONArray targets = jsonObject.getJSONArray("target");            JSONArray get_params = jsonObject.getJSONArray("params");            JSONArray cookie_params = jsonObject.getJSONArray("cookie_params");            JSONArray post_params = jsonObject.getJSONArray("post_params");            for (Object obj_url : urls)                try {                    URL _url = new URL((String) obj_url);                    String[] path_array2 = _url.getPath().split("/");                    if (path_array2.length > 0) {                        String _path = path_array2[path_array2.length - 1];                        //此处就是判断url是否是一样的                        if (!_path.equals("") && _path.equalsIgnoreCase(path)) {                            int count = 1;                            if (pathTJMap.containsKey(BugType)) {                                count = pathTJMap.get(BugType) + 1;                            }                            pathTJMap.put(BugType, count);                            pathList.add(new Object[]{BugType, Title, shuimugan + BugID, _url.toString()});                            break;                        }                    }                } catch (MalformedURLException | ArrayIndexOutOfBoundsException ignored) {                }            for (Object _target : targets) {                String target = (String) _target;                //判断host是否是一样的                if (target.equalsIgnoreCase(host)) {                    int count = 1;                    if (hostTJMap.containsKey(BugType)) {                        count = hostTJMap.get(BugType) + 1;                    }                    hostTJMap.put(BugType, count);//                    hostList.add("[" + BugType + "] " + Title + " 漏洞链接:" + shuimugan + BugID);                    hostList.add(new Object[]{BugType, Title, shuimugan + BugID});                }            }

            String flag = "!!flag{aabbc}!!";            if (GetParams.length > 0) {                Set<String> param = new HashSet<String>();                //根据get的参数进行判断                for (Object _get : get_params) {                    String target = (String) _get;                    for (String get2 : GetParams) {                        if (target.equalsIgnoreCase(get2)) {                            param.add(get2);                        }                    }                }                if (param.size() > 0) {                    String[] ret = {BugType, Title, join(",", param), shuimugan + BugID};

                    getMap.put(join(flag, ret), param.size());                }

            }             //根据cookie参数进行判断            if (CookieParams.length > 0) {                Set<String> param = new HashSet<String>();                for (Object _get : cookie_params) {                    String target = (String) _get;                    for (String get2 : CookieParams) {                        if (target.equalsIgnoreCase(get2)) {                            param.add(get2);                        }                    }                }                if (param.size() > 0) {                    String[] ret = {BugType, Title, join(",", param), shuimugan + BugID};                    cookieMap.put(join(flag, ret), param.size());                }

            }            //根据post参数进行判断            if (PostParams.length > 0) {                Set<String> param = new HashSet<String>();                for (Object _get : post_params) {                    String target = (String) _get;                    for (String get2 : PostParams) {                        if (target.equalsIgnoreCase(get2)) {                            param.add(get2);                        }                    }                }                if (param.size() > 0) {                    String[] ret = {BugType, Title, join(",", param), shuimugan + BugID};                    postMap.put(join(flag, ret), param.size());                }

            }

        }



        List<Map.Entry<String, Integer>> patTJlist = new ArrayList<Map.Entry<String, Integer>>(pathTJMap.entrySet());

        Collections.sort(patTJlist, new Comparator<Map.Entry<String, Integer>>() {            @Override            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {                return o2.getValue() - o1.getValue();            }        });

        List<Map.Entry<String, Integer>> hostTJlist = new ArrayList<Map.Entry<String, Integer>>(hostTJMap.entrySet());

       //调用函数进行排序       //Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的        Collections.sort(hostTJlist, new Comparator<Map.Entry<String, Integer>>() {            @Override            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {                //// 返回值为int类型,大于0表示正序,小于0表示逆序                return o2.getValue() - o1.getValue();            }        });

        List<Map.Entry<String, Integer>> getList = new ArrayList<Map.Entry<String, Integer>>(getMap.entrySet());

        getList.sort(new Comparator<Map.Entry<String, Integer>>() {            @Override            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {                return o2.getValue() - o1.getValue();            }        });

        List<Map.Entry<String, Integer>> cookieList = new ArrayList<Map.Entry<String, Integer>>(cookieMap.entrySet());

        cookieList.sort(new Comparator<Map.Entry<String, Integer>>() {            @Override            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {                return o2.getValue() - o1.getValue();            }        });

        List<Map.Entry<String, Integer>> postList = new ArrayList<Map.Entry<String, Integer>>(postMap.entrySet());

        postList.sort(new Comparator<Map.Entry<String, Integer>>() {            @Override            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {                return o2.getValue() - o1.getValue();            }        });



        // 输出 path 部分        if (pathList.size() > 0) {            StringBuilder ret = new StringBuilder(String.format("相同路径:%s 下历史漏洞及漏洞类型:\n", path));            int index = 0;            for (Map.Entry<String, Integer> entry : patTJlist) {                ret.append(entry.getKey()).append(":").append(entry.getValue()).append("    ");                index++;                if (index % 3 == 0) {                    ret.append("\n");                }                if (index >= 6) {                    break;                }            }            struct.SetExtraInfo("Path", ret.toString());            struct.AddColumnNames("Path", new Object[]{"ID", "漏洞类型", "漏洞标题", "乌云镜像地址", "漏洞页面"});//            struct.AddRowDatas();            Object[][] rows = new Object[pathList.size()][];            for (int i = 0; i < pathList.size(); i++) {                Object[] obj = concat(new Object[]{valueOf(i)}, pathList.get(i));                rows[i] = obj;            }            struct.AddRowDatas("Path", rows);        }

        // 输出host部分        if (hostList.size() > 0) {            StringBuilder ret = new StringBuilder(String.format("相同Host:%s 下历史漏洞及漏洞类型:\n", host));            int index = 0;            for (Map.Entry<String, Integer> entry : hostTJlist) {                ret.append(entry.getKey()).append(":").append(entry.getValue()).append("    ");                index++;                if (index % 3 == 0) {                    ret.append("\n");                }                if (index >= 6) {                    break;                }            }            struct.SetExtraInfo("Host", ret.toString());            struct.AddColumnNames("Host", new Object[]{"ID", "漏洞类型", "漏洞标题", "乌云镜像地址"});            Object[][] rows = new Object[hostList.size()][];            for (int i = 0; i < hostList.size(); i++) {                Object[] obj = concat(new Object[]{valueOf(i)}, hostList.get(i));                rows[i] = obj;            }            struct.AddRowDatas("Host", rows);

        }

        // 输出Get参数部分        String flag_split = "!!flag\\{aabbc\\}!!";        if (getList.size() > 0) {            struct.SetExtraInfo("Get Params", "Get参数历史漏洞:");            struct.AddColumnNames("Get Params", new Object[]{"ID", "漏洞类型", "漏洞标题", "影响参数", "漏洞链接"});            Object[][] data = new Object[getList.size()][5];            for (int i = 0; i < getList.size(); i++) {                Map.Entry<String, Integer> entry = getList.get(i);                data[i] = concat(new Object[]{String.valueOf(i)}, (Object[]) entry.getKey().split(flag_split));            }            struct.AddRowDatas("Get Params", data);        }

        if (cookieList.size() > 0) {            struct.SetExtraInfo("Cookie Params", "Cookie参数历史漏洞:");            struct.AddColumnNames("Cookie Params", new Object[]{"ID", "漏洞类型", "漏洞标题", "影响参数", "漏洞链接"});            Object[][] data = new Object[cookieList.size()][5];            for (int i = 0; i < cookieList.size(); i++) {                Map.Entry<String, Integer> entry = cookieList.get(i);                data[i] = concat(new Object[]{String.valueOf(i)}, (Object[]) entry.getKey().split(flag_split));            }            struct.AddRowDatas("Cookie Params", data);

        }        if (postList.size() > 0) {            struct.SetExtraInfo("Post Params", "Post参数历史漏洞:");            struct.AddColumnNames("Post Params", new Object[]{"ID", "漏洞类型", "漏洞标题", "影响参数", "漏洞链接"});            Object[][] data = new Object[postList.size()][5];            for (int i = 0; i < postList.size(); i++) {                Map.Entry<String, Integer> entry = postList.get(i);                data[i] = concat(new Object[]{String.valueOf(i)}, (Object[]) entry.getKey().split(flag_split));            }            struct.AddRowDatas("Post Params", data);

        }        return struct;    }

}

总结


 
了解了此插件的工作原理,我们其实也可以自己进行构造自己的json结构,根据某些特定的参数进行搜索扩展