├── .gitignore ├── img.png ├── img_1.png ├── img_2.png ├── scripts ├── 签名 │ ├── img.png │ ├── README.md │ └── main.js ├── Gzip │ ├── img.png │ ├── img_1.png │ ├── README.md │ ├── decode.py │ └── encode.py ├── 文件代理 │ ├── img.png │ ├── README.md │ └── main.js ├── 加解密代理 │ ├── img.png │ ├── README.md │ ├── proxy.js │ ├── main.py │ └── frida.py └── 直接进行加解密 │ ├── img.png │ ├── README.md │ └── main.py ├── src └── main │ └── java │ └── burp │ ├── core │ ├── CommandType.java │ ├── Rule.java │ ├── Storage.java │ ├── Rules.java │ ├── Process.java │ └── HttpAgreement.java │ ├── MessageEditorTabFactory.java │ ├── BurpExtender.java │ ├── MessageEditorTab.java │ ├── BurpHttpListener.java │ ├── MainGUI.jfd │ └── MainGUI.java ├── tests └── Main.java ├── License ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.idea/ 3 | -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamncn/CustomCrypto/HEAD/img.png -------------------------------------------------------------------------------- /img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamncn/CustomCrypto/HEAD/img_1.png -------------------------------------------------------------------------------- /img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamncn/CustomCrypto/HEAD/img_2.png -------------------------------------------------------------------------------- /scripts/签名/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamncn/CustomCrypto/HEAD/scripts/签名/img.png -------------------------------------------------------------------------------- /scripts/Gzip/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamncn/CustomCrypto/HEAD/scripts/Gzip/img.png -------------------------------------------------------------------------------- /scripts/文件代理/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamncn/CustomCrypto/HEAD/scripts/文件代理/img.png -------------------------------------------------------------------------------- /scripts/Gzip/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamncn/CustomCrypto/HEAD/scripts/Gzip/img_1.png -------------------------------------------------------------------------------- /scripts/加解密代理/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamncn/CustomCrypto/HEAD/scripts/加解密代理/img.png -------------------------------------------------------------------------------- /scripts/直接进行加解密/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamncn/CustomCrypto/HEAD/scripts/直接进行加解密/img.png -------------------------------------------------------------------------------- /scripts/直接进行加解密/README.md: -------------------------------------------------------------------------------- 1 | ## 签名 2 | 部分参数修改后需要更新签名才能正常使用。 3 | 4 | ## Usage 5 | 需要手动提取加解密算法 6 | 7 | ## 脚本配置 8 | 9 | ![img.png](img.png) -------------------------------------------------------------------------------- /scripts/签名/README.md: -------------------------------------------------------------------------------- 1 | ## 签名 2 | 部分参数修改后需要更新签名才能正常使用。 3 | 4 | ## Usage 5 | 需要手动提取签名算法。 6 | 7 | ## 脚本配置 8 | 9 | ![img.png](img.png) 10 | -------------------------------------------------------------------------------- /scripts/Gzip/README.md: -------------------------------------------------------------------------------- 1 | ## GZip 2 | 3 | Burp无法自动进行Gzip加解密 4 | 5 | 6 | ## 脚本配置 7 | 8 | 解压脚本如图: 9 | ![img.png](img.png) 10 | 压缩脚本如图: 11 | ![img_1.png](img_1.png) -------------------------------------------------------------------------------- /scripts/文件代理/README.md: -------------------------------------------------------------------------------- 1 | ## 文件代理 2 | 将服务端文件替换成本地文件,可以解决部分水土不服的问题 3 | 4 | ## Usage 5 | 6 | 将所需替换的文件置于`replace`文件夹下,文件路径与Web路径保持一致 7 | 8 | ## 脚本配置 9 | 10 | ![img.png](img.png) 11 | -------------------------------------------------------------------------------- /src/main/java/burp/core/CommandType.java: -------------------------------------------------------------------------------- 1 | package burp.core; 2 | 3 | public enum CommandType { 4 | RequestFromClient, 5 | RequestToServer, 6 | ResponseFromServer, 7 | ResponseToClient 8 | } 9 | -------------------------------------------------------------------------------- /scripts/加解密代理/README.md: -------------------------------------------------------------------------------- 1 | ## 加解密代理 2 | 3 | 主要适用于无法直接利用脚本模拟出该App当前使用的加解密函数的情况。 4 | 5 | 6 | ## Usage 7 | 8 | 1. 手机端连接电脑并启动`frida-server`。 9 | 2. 电脑端执行`python3 frida.py`(记得安装依赖库),连接到手机端,启动Web服务。 10 | 3. 默认Web服务端口为`5555` 11 | 12 | ## 脚本配置 13 | 14 | 如图: 15 | ![img.png](img.png) -------------------------------------------------------------------------------- /tests/Main.java: -------------------------------------------------------------------------------- 1 | class Main{ 2 | static String RequestFromClient = "0" //日志/Interrupt收到请求(请求包解密) 3 | static String RequestToServer = "1" // Repeater/Interrupt发出请求(请求包加密) 4 | static String ResponseFromServer = "2" // 日志/Repeater/Interrupt收到响应(响应包解密) 5 | static String ResponseToClient = "3" // Repeater/Interrupt发出响应(响应包加密) 6 | public static void main(String[] args) { 7 | 8 | } 9 | } -------------------------------------------------------------------------------- /src/main/java/burp/MessageEditorTabFactory.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.util.HashMap; 4 | 5 | public class MessageEditorTabFactory implements IMessageEditorTabFactory { 6 | private static HashMap hashMap = new HashMap<>(); 7 | @Override 8 | public IMessageEditorTab createNewInstance(IMessageEditorController iMessageEditorController, boolean b) { 9 | return new MessageEditorTab(iMessageEditorController, b); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /scripts/加解密代理/proxy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 此处以Frida导出函数为例 3 | */ 4 | if(Java.available) { 5 | Java.perform(function(){ 6 | var cryptUtil=Java.use("xxx.DesUtil"); //获取 cryptUtil 加解密类 7 | var key = "123456";//加密密钥 8 | rpc.exports = { 9 | encrypt: function(plaintext) 10 | { 11 | //导出加密函数 12 | return cryptUtil.encrypt(plaintext,key) 13 | }, 14 | decrypt: function(plaintext) 15 | { 16 | //导出解密函数 17 | return cryptUtil.decrypt(plaintext,key) 18 | } 19 | } 20 | }); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /scripts/Gzip/decode.py: -------------------------------------------------------------------------------- 1 | import gzip 2 | RequestFromClient = "0" # 日志/Interrupt收到请求(请求包解密) 3 | RequestToServer = "1" # Repeater/Interrupt发出请求(请求包加密) 4 | ResponseFromServer = "2" # 日志/Repeater/Interrupt收到响应(响应包解密) 5 | ResponseToClient = "3" # Repeater/Interrupt发出响应(响应包加密) 6 | 7 | def decode(data): 8 | return gzip.decompress(data).decode('utf8') 9 | 10 | if len(sys.argv) != 3: 11 | raise RuntimeError('错误,至少需要两个参数') 12 | # 这里就不管请求类型了 13 | isRequest = sys.argv[1] == RequestFromClient or sys.argv[1] == RequestToServer 14 | # 构建文件名 15 | file = sys.argv[2] + "/" + 'body.txt' if isRequest else 'response_body.txt' 16 | 17 | with open(file,'r',encoding="utf-8") as f: 18 | data = f.read() 19 | with open(file, 'w', encoding='utf-8') as fe: 20 | fe.write(decode(data)) -------------------------------------------------------------------------------- /scripts/加解密代理/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | # use python 3 4 | import sys 5 | import requests 6 | RequestFromClient = "0" # 日志/Interrupt收到请求(请求包解密) 7 | RequestToServer = "1" # Repeater/Interrupt发出请求(请求包加密) 8 | ResponseFromServer = "2" # 日志/Repeater/Interrupt收到响应(响应包解密) 9 | ResponseToClient = "3" # Repeater/Interrupt发出响应(响应包加密) 10 | 11 | if len(sys.argv) != 3: 12 | raise RuntimeError('错误,至少需要两个参数') 13 | # 是否加密 14 | isEncrypt = (sys.argv[1] == RequestToServer or sys.argv[1] == ResponseToClient) 15 | 16 | isRequest = sys.argv[1] == RequestFromClient or sys.argv[1] == RequestToServer 17 | 18 | # 构建文件名 19 | file = sys.argv[2] + "/" + 'body.txt' if isRequest else 'response_body.txt' 20 | 21 | # 此处假设是body部分 22 | 23 | print(requests.post("http://127.0.0.1:5555", {"type": "encrypt" if isEncrypt else "decrypt",'file':file})) 24 | -------------------------------------------------------------------------------- /scripts/Gzip/encode.py: -------------------------------------------------------------------------------- 1 | import gzip 2 | RequestFromClient = "0" # 日志/Interrupt收到请求(请求包解密) 3 | RequestToServer = "1" # Repeater/Interrupt发出请求(请求包加密) 4 | ResponseFromServer = "2" # 日志/Repeater/Interrupt收到响应(响应包解密) 5 | ResponseToClient = "3" # Repeater/Interrupt发出响应(响应包加密) 6 | 7 | def encode(data): 8 | if type(data) == str: 9 | data = bytes(data, 'utf8') 10 | s_out = gzip.compress(data) 11 | return s_out 12 | 13 | if len(sys.argv) != 3: 14 | raise RuntimeError('错误,至少需要两个参数') 15 | # 这里就不管请求类型了 16 | isRequest = sys.argv[1] == RequestFromClient or sys.argv[1] == RequestToServer 17 | # 构建文件名 18 | file = sys.argv[2] + "/" + 'body.txt' if isRequest else 'response_body.txt' 19 | 20 | with open(file,'r',encoding="utf-8") as f: 21 | data = f.read() 22 | with open(file, 'w', encoding='utf-8') as fe: 23 | fe.write(encode(data)) 24 | 25 | -------------------------------------------------------------------------------- /scripts/文件代理/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 文件代理,目的是将服务端文件替换成本地文件 3 | * @type {string} 4 | */ 5 | const RequestFromClient = "0";// 日志/Interrupt收到请求(请求包解密) 6 | const RequestToServer = "1";// Repeater/Interrupt发出请求(请求包加密) 7 | const ResponseFromServer = "2";// 日志/Repeater/Interrupt收到响应(响应包解密) 8 | const ResponseToClient = "3";// Repeater/Interrupt发出响应(响应包加密) 9 | 10 | //从命令行获取参数 11 | var args = process.argv.slice(2); 12 | if(args.length!==2){ 13 | throw "错误,至少有两个参数!" 14 | } 15 | //数据 16 | var path = args[1]; 17 | const fs = require('fs'); 18 | const mime = require('mime'); 19 | if(args[2]===ResponseToClient){ 20 | //替换body 21 | 22 | var file = "./replace/"+fs.readFileSync(path[1]+"/path.txt"); 23 | if(fs.existsSync(file)){ 24 | fs.writeFileSync(path[1]+"/response_body.txt",fs.readFileSync(file)); 25 | //Content-type 26 | var header = fs.readFileSync(path[1]+"/response_header.txt"); 27 | //替换headers 28 | fs.writeFileSync(path[1]+"/response_header.txt",header.replace('/content-type:.*?/i',mime.getType(file))); 29 | } 30 | 31 | } 32 | console.log("success"); 33 | -------------------------------------------------------------------------------- /scripts/直接进行加解密/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | # use python 3 4 | import sys 5 | import requests 6 | RequestFromClient = "0" # 日志/Interrupt收到请求(请求包解密) 7 | RequestToServer = "1" # Repeater/Interrupt发出请求(请求包加密) 8 | ResponseFromServer = "2" # 日志/Repeater/Interrupt收到响应(响应包解密) 9 | ResponseToClient = "3" # Repeater/Interrupt发出响应(响应包加密) 10 | 11 | if len(sys.argv) != 3: 12 | raise RuntimeError('错误,至少需要两个参数') 13 | # 是否加密 14 | isEncrypt = (sys.argv[1] == RequestToServer or sys.argv[1] == ResponseToClient) 15 | 16 | isRequest = sys.argv[1] == RequestFromClient or sys.argv[1] == RequestToServer 17 | 18 | # 构建文件名 19 | file = sys.argv[2] + "/" + 'body.txt' if isRequest else 'response_body.txt' 20 | 21 | # 此处假设是body部分 22 | 23 | def encrypt(str): 24 | return '加密:'+str 25 | 26 | def decrypt(str): 27 | return '解密:'+str 28 | 29 | 30 | with open(file,'r',encoding='utf-8') as f: 31 | data = f.read() 32 | 33 | if isEncrypt : 34 | data = encrypt(data) 35 | else: 36 | data = decrypt(data) 37 | 38 | 39 | with open(file,'w',encoding='utf-8') as f: 40 | f.write() 41 | print("success") -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ankio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scripts/签名/main.js: -------------------------------------------------------------------------------- 1 | const RequestFromClient = "0";// 日志/Interrupt收到请求(请求包解密) 2 | const RequestToServer = "1";// Repeater/Interrupt发出请求(请求包加密) 3 | const ResponseFromServer = "2";// 日志/Repeater/Interrupt收到响应(响应包解密) 4 | const ResponseToClient = "3";// Repeater/Interrupt发出响应(响应包加密) 5 | 6 | //从命令行获取参数 7 | var args = process.argv.slice(2); 8 | if(args.length!==2){ 9 | throw "错误,至少有两个参数!" 10 | } 11 | 12 | //数据 13 | var path = args[1]; 14 | const fs = require('fs'); 15 | 16 | //获取 17 | function getPath() { 18 | return fs.readFileSync(path + '/path.txt').toString(); 19 | } 20 | //设置 21 | function setPath(data) { 22 | fs.writeFileSync(path + '/path.txt',data); 23 | } 24 | function getResponseBody() { 25 | return fs.readFileSync(path + '/response_body.txt').toString(); 26 | } 27 | //设置 28 | function setResponseBody(data) { 29 | fs.writeFileSync(path + '/response_body.txt',data); 30 | } 31 | 32 | if(args[0]===RequestToServer){ 33 | setPath(getPath()+"&sign=12345678") 34 | } 35 | 36 | if(args[0]===ResponseFromServer){ 37 | setResponseBody(getResponseBody()+"\r\n\r\n-----------\r\n changed by ankio 2023.") 38 | } 39 | 40 | //最后一定要输出 success 41 | console.log("success") 42 | -------------------------------------------------------------------------------- /src/main/java/burp/core/Rule.java: -------------------------------------------------------------------------------- 1 | package burp.core; 2 | 3 | import burp.BurpExtender; 4 | 5 | import java.io.Serializable; 6 | import java.util.ArrayList; 7 | 8 | public class Rule implements Serializable { 9 | public String name = ""; 10 | public String url = ""; 11 | public String header = ""; 12 | public String body = ""; 13 | public ArrayList method = new ArrayList<>(); 14 | public boolean regex = false; 15 | public String command = ""; 16 | 17 | 18 | public boolean inMethod(String m){ 19 | return method.size()==0||method.contains(m.toLowerCase()); 20 | } 21 | 22 | public boolean inUrl(String u){ 23 | return compare(u,url); 24 | } 25 | public boolean inBody(String b){ 26 | return compare(b,body); 27 | } 28 | public boolean inHeader(String h){ 29 | return compare(h,header); 30 | } 31 | 32 | private boolean compare(String a,String b){ 33 | if(b.equals(""))return true; 34 | if(regex){ 35 | return a.toLowerCase().matches(b); 36 | }else{ 37 | return a.toLowerCase().contains(b); 38 | } 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return name; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /scripts/加解密代理/frida.py: -------------------------------------------------------------------------------- 1 | import frida 2 | from urllib import parse 3 | from wsgiref.simple_server import make_server 4 | 5 | # 使用usb设备 6 | a = frida.get_usb_device(1) 7 | # 附加指定进程 8 | session = a.attach("xxx") 9 | # 加载js脚本 10 | f = open("./proxy.js", "r", encoding="utf-8") 11 | script = session.create_script(f.read()) 12 | f.close() 13 | 14 | 15 | # 映射加解密函数 16 | def encode(message): 17 | return script.exports.encrypt(message) 18 | 19 | 20 | def decode(message): 21 | return script.exports.decrypt(message) 22 | 23 | 24 | # 脚本执行 25 | script.load() 26 | 27 | 28 | # Web服务器入口 29 | def app(env, start_response): 30 | start_response("200 ok", [("Content-Type", "text/plain")]) 31 | return [cryptController(env['wsgi.input'].read())] # 获取post请求 32 | 33 | 34 | # Web服务器处理函数 35 | def cryptController(requestParameter): 36 | par = {} 37 | ParameterList = requestParameter.split("&") 38 | for parameter in ParameterList: 39 | par[parameter.split("=")[0]] = parameter.split("=")[1] 40 | if 'type' in par.keys() and 'file' in par.keys(): 41 | with open(par['file'], 'r', encoding='utf-8') as fr: 42 | data = fr.read() # 读取数据 43 | if par['type'] == 'encrypt': # 处理加密 44 | with open(par['file'], 'w', encoding='utf-8') as fe: 45 | fe.write(encode(data)) 46 | else: # 处理解密 47 | with open(par['file'], 'w', encoding='utf-8') as fd: 48 | fd.write(decode(data)) 49 | return b'success' 50 | 51 | 52 | # 启动Web服务器 53 | sever = make_server("", 5555, app) 54 | sever.serve_forever() 55 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | me.gv7.tools.burpextend 7 | CustomCrypto 8 | 1.0.1 9 | 10 | 11 | 12 | 13 | net.portswigger.burp.extender 14 | burp-extender-api 15 | 2.3 16 | 17 | 18 | com.google.guava 19 | guava 20 | 31.1-jre 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-assembly-plugin 30 | 31 | 32 | package 33 | 34 | single 35 | 36 | 37 | 38 | 39 | 40 | jar-with-dependencies 41 | 42 | 43 | 44 | 45 | org.apache.maven.plugins 46 | maven-compiler-plugin 47 | 48 | 1.8 49 | 1.8 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | import burp.core.Rules; 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.io.IOException; 6 | import java.io.PrintWriter; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | public class BurpExtender implements IBurpExtender,ITab { 10 | public final static String extensionName = "CustomCrypto"; 11 | public final static String version ="1.0.2"; 12 | public static IBurpExtenderCallbacks callbacks; 13 | public static IExtensionHelpers helpers; 14 | public static PrintWriter stdout; 15 | public static PrintWriter stderr; 16 | public static MainGUI gui; 17 | public static Rules rules; 18 | @Override 19 | public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { 20 | BurpExtender.callbacks = callbacks; 21 | helpers = callbacks.getHelpers(); 22 | stdout = new PrintWriter(callbacks.getStdout(),true); 23 | stderr = new PrintWriter(callbacks.getStderr(),true); 24 | callbacks.setExtensionName(extensionName+" "+version); 25 | rules = new Rules(); 26 | gui = new MainGUI(); 27 | 28 | try { 29 | callbacks.registerHttpListener(new BurpHttpListener()); 30 | callbacks.registerProxyListener(new BurpHttpListener()); 31 | callbacks.registerMessageEditorTabFactory(new MessageEditorTabFactory()); 32 | SwingUtilities.invokeLater(() -> { 33 | BurpExtender.callbacks.addSuiteTab(BurpExtender.this); 34 | stdout.print( "[+] " + BurpExtender.extensionName + " is loaded\n" 35 | + "[+] ^_^\n" 36 | + "[+]\n" 37 | + "[+] #####################################\n" 38 | + "[+] " + BurpExtender.extensionName + " v" + BurpExtender.version +"\n" 39 | + "[+] author: ankio\n" 40 | + "[+] email: admin@ankio.net\n" 41 | + "[+] github: https://github.com/dreamncn\n" 42 | + "[+] ####################################"); 43 | }); 44 | } catch (IOException e) { 45 | print("初始化异常:"+e.getMessage(),1); 46 | e.printStackTrace(); 47 | } 48 | } 49 | public static void print(String msg){ 50 | print(msg,0); 51 | } 52 | public static void print(String msg,int type){ 53 | SimpleDateFormat sdf = new SimpleDateFormat();// 格式化时间 54 | sdf.applyPattern("yyyy-MM-dd HH:mm:ss");// a为am/pm的标记 55 | Date date = new Date();// 获取当前时间 56 | msg = String.format("[ %s ] [%s] %s\n", sdf.format(date),type==0?"*":"-",msg); 57 | stdout.print(msg); 58 | System.out.println(msg); 59 | } 60 | @Override 61 | public String getTabCaption() { 62 | return extensionName; 63 | } 64 | @Override 65 | public Component getUiComponent() { 66 | return gui.getRoot(); 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/burp/core/Storage.java: -------------------------------------------------------------------------------- 1 | package burp.core; 2 | 3 | import burp.BurpExtender; 4 | 5 | import java.io.*; 6 | import java.util.ArrayList; 7 | import java.util.Base64; 8 | import java.util.Objects; 9 | 10 | /** 11 | * 存储类,控制插件数据存储部分 12 | */ 13 | public class Storage { 14 | /** 15 | * 读取rule数据 16 | * @param type String 17 | * @return JSONArray 18 | */ 19 | public static ArrayList read(String type){ 20 | String string = BurpExtender.callbacks.loadExtensionSetting(type); 21 | return decode(string); 22 | } 23 | public static boolean readBoolean(String type){ 24 | return Objects.equals(BurpExtender.callbacks.loadExtensionSetting(type), "true"); 25 | } 26 | 27 | public static void writeBoolean(String type,boolean value){ 28 | BurpExtender.callbacks.saveExtensionSetting(type,value?"true":"false"); 29 | } 30 | public static String readString(String type){ 31 | return BurpExtender.callbacks.loadExtensionSetting(type); 32 | } 33 | 34 | public static void writeString(String type,String value){ 35 | BurpExtender.callbacks.saveExtensionSetting(type,value); 36 | } 37 | /** 38 | * 将rule数据写入文件 39 | * @param type String 40 | * @param rules ArrayList 41 | */ 42 | public static void write(String type, ArrayList rules) { 43 | BurpExtender.callbacks.saveExtensionSetting(type, encode(rules)); 44 | } 45 | 46 | public static String encode(ArrayList rules){ 47 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 48 | ObjectOutputStream output = null; 49 | try { 50 | output = new ObjectOutputStream(buffer); 51 | output.writeObject(rules); 52 | output.close(); 53 | return Base64.getEncoder().encodeToString(buffer.toByteArray()); 54 | } catch (IOException e) { 55 | BurpExtender.print("写入数据失败:"+e.getMessage()); 56 | return ""; 57 | } 58 | 59 | } 60 | @SuppressWarnings("unchecked") 61 | public static ArrayList decode(String string){ 62 | try{ 63 | if(string==null|| string.equals("null")){ 64 | return new ArrayList<>(); 65 | } 66 | ByteArrayInputStream buffer = new ByteArrayInputStream(Base64.getDecoder().decode(string.getBytes())); 67 | ObjectInputStream input = new ObjectInputStream(buffer); 68 | ArrayList rule = (ArrayList) input.readObject(); 69 | input.close(); 70 | return rule; 71 | }catch (Exception e){ 72 | return new ArrayList<>(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 |

5 | 6 | # 重要 7 | 8 | 本项目关闭,合并至iCrypto:https://github.com/dreamncn/iCrypto 9 | 10 | ## 项目简介 11 | 12 | 做一些app测试经常会遇到加密、签名的问题,这个插件可以帮助你进行重新签名、数据包解密、偷天换日... 13 | 14 | 15 | ## 使用 16 | 17 | - 启用插件 18 | - [编写你的脚本](#脚本编写指南) 19 | - 在`Proxy`的`History`中,可以看到被修改后的请求,Repeater部分也可以自动进行修改 20 | ![image-20220926231527312](https://pic.dreamn.cn/uPic/2022_09_26_23_15_28_1664205328_1664205328301_RN4xqs.png) 21 | 22 | ### 自动加解密配置 23 | ![img.png](img.png) 24 | 25 | ### 手动加解密 26 | > 本来这里想用`createMessageEditor`实现的,但是测试过程中发现在渲染`MessageEditor`的过程中也会渲染`Select extension`导致出现递归异常。 27 | 28 | ![img_1.png](img_1.png) 29 | ## 脚本编写指南 30 | 31 | 插件调用脚本为: 32 | 33 | ```shell 34 | 执行命令 请求类型 临时文件夹 35 | ``` 36 | 37 | 其中,第一个参数为 `请求类型`,一共有四种类型: 38 | 39 | ```js 40 | const RequestFromClient = "0";// 日志/Interrupt收到请求(请求包解密) 41 | const RequestToServer = "1";// Repeater/Interrupt发出请求(请求包加密) 42 | const ResponseFromServer = "2";// 日志/Repeater/Interrupt收到响应(响应包解密) 43 | const ResponseToClient = "3";// Repeater/Interrupt发出响应(响应包加密 44 | ``` 45 | 46 | 可以根据burp的生命周期来理解这四种类型: 47 | 48 | ![7338E56B-BB55-4C6B-B1B6-BD486C4BCEA4](https://pic.dreamn.cn/uPic/2022_09_26_22_57_11_1664204231_1664204231936_3uIT8e.png) 49 | 50 | 第二个参数为临时文件夹,数据如下: 51 | ![img_2.png](img_2.png) 52 | 53 | 脚本在收到请求后,去修改对应临时文件夹的数据,处理成功,必须输出`success`字样 54 | 55 | ### 文件释义 56 | 57 | | 名称 | 解释 | 举例 | 在哪种请求下存在 | 58 | | :------------------- | ------------------------ | --------------------- | ---------------- | 59 | | body.txt | 请求包的body部分 | id=1 | Request/Response | 60 | | headers.txt | 请求包的headers部分 | Host: 127.0.0.1 等 | Request/Response | 61 | | method.txt | 请求包的请求方法 | GET | Request/Response | 62 | | path.txt | 请求包的请求路径 | /index.php | Request/Response | 63 | | version.txt | 请求包使用的Http协议版本 | HTTP/2 | Request/Response | 64 | | response_body.txt | 响应包的body部分 | {"body":"sssss"} | Response | 65 | | response_headers.txt | 响应包的headers部分 | Set-cookle: www=12333 | Response | 66 | | response_version.txt | 响应包的Http协议版本 | HTTP/2 | Response | 67 | | state.txt | 响应包的响应代码 | 404 | Response | 68 | | state_msg.txt | 响应包的响应消息 | Not Found |Response| 69 | 70 | ## 脚本调试指南 71 | 72 | - 多关注`Extender`中`CustomCrypto`的`output`内容,如果脚本生效,则会在这里输出处理的请求包响应包等。 73 | - 如果你发现脚本未生效,也可以复制`CustomCrypto`输出的命令内容,直接在命令行进行测试,以便于调试脚本。 74 | 75 | ## 案例&模板 76 | 77 | - [Gzip](./scripts/Gzip) 78 | - [加解密代理](./scripts/加解密代理) 79 | - [文件代理](./scripts/文件代理) 80 | - [直接进行加解密](./scripts/直接进行加解密) 81 | - [签名](./scripts/签名) 82 | 83 | ## 协议 84 | 85 | MIT 86 | 87 | -------------------------------------------------------------------------------- /src/main/java/burp/core/Rules.java: -------------------------------------------------------------------------------- 1 | package burp.core; 2 | 3 | import burp.BurpExtender; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.InputStream; 6 | import java.util.ArrayList; 7 | public class Rules { 8 | private ArrayList rule = null; 9 | private boolean auto = false; 10 | public Rules(){ 11 | readRule(); 12 | } 13 | public void readRule(){ 14 | rule = Storage.read("rules"); 15 | auto = Storage.readBoolean("auto"); 16 | } 17 | public boolean getAuto(){ 18 | return auto; 19 | } 20 | public void setAuto(boolean isAuto){ 21 | auto = isAuto; 22 | Storage.writeBoolean("auto",isAuto); 23 | } 24 | public ArrayList getAll(){ 25 | readRule(); 26 | return rule; 27 | } 28 | /** 29 | * 根据条件查找规则 30 | */ 31 | public Rule findRule(String method,String url,String headers){ 32 | for (Rule r : getAll()) { 33 | if (r.inMethod(method) && r.inHeader(headers) && r.inUrl(url)) { 34 | if(r.command.isEmpty())continue; 35 | return r; 36 | } 37 | } 38 | return null; 39 | } 40 | /** 41 | * 根据指定id获取执行的命令 42 | * @param id int 43 | */ 44 | public Rule getRule(int id){ 45 | readRule(); 46 | if(id=0){ 47 | return rule.get(id); 48 | } 49 | return null; 50 | } 51 | public void update(int id,Rule rule){ 52 | readRule(); 53 | if(id=0){ 54 | this.rule.set(id,rule); 55 | } 56 | saveRule(); 57 | } 58 | public void del(int id){ 59 | readRule(); 60 | if(id=0){ 61 | this.rule.remove(id); 62 | } 63 | saveRule(); 64 | } 65 | public void add(Rule rule){ 66 | readRule(); 67 | this.rule.add(rule); 68 | saveRule(); 69 | } 70 | public boolean run(String command,CommandType type,String file){ 71 | StringBuilder stringBuilder = new StringBuilder(); 72 | String root = Storage.readString("root"); 73 | if(root!=null) command = command.replace("${root}",root); 74 | stringBuilder.append(command).append(" "); 75 | switch (type){ 76 | case RequestFromClient:stringBuilder.append(0);break; 77 | case RequestToServer:stringBuilder.append(1);break; 78 | case ResponseToClient:stringBuilder.append(3);break; 79 | default:stringBuilder.append(2);break; 80 | } 81 | stringBuilder.append(" ").append(file); 82 | String result = exec(stringBuilder.toString()).trim(); 83 | if("success".equals(result)){ 84 | return true; 85 | }else{ 86 | BurpExtender.print("命令执行异常:"+result,1); 87 | return false; 88 | } 89 | } 90 | private String exec(String commands) { 91 | BurpExtender.print("执行命令:" + commands); 92 | try{ 93 | InputStream in = Runtime.getRuntime().exec(commands).getInputStream(); 94 | byte[] bcache = new byte[1024]; 95 | int readSize = 0; //每次读取的字节长度 96 | ByteArrayOutputStream infoStream = new ByteArrayOutputStream(); 97 | while ((readSize = in.read(bcache)) > 0) { 98 | infoStream.write(bcache, 0, readSize); 99 | } 100 | return infoStream.toString(); 101 | }catch (Exception e){ 102 | 103 | return e.getMessage(); 104 | } 105 | 106 | } 107 | 108 | private void saveRule(){ 109 | Storage.write("rules",rule); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/burp/core/Process.java: -------------------------------------------------------------------------------- 1 | package burp.core; 2 | 3 | import burp.BurpExtender; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.*; 7 | import java.nio.file.attribute.BasicFileAttributes; 8 | 9 | /** 10 | * 交互 11 | */ 12 | public class Process { 13 | private String temp = ""; 14 | public Process() throws IOException { 15 | //创建临时文件存储数据 16 | Path tmpCustomPrefix = Files.createTempDirectory("CustomCrypto"); 17 | temp = tmpCustomPrefix.toString(); 18 | BurpExtender.print(String.format("临时会话文件夹[ %s ]创建成功",temp)); 19 | } 20 | 21 | public String getTemp() { 22 | return temp; 23 | } 24 | 25 | /** 26 | * 设置值 27 | * @param key String 28 | * @param value String 29 | */ 30 | public void set(String key,String value){ 31 | try { 32 | Files.write(Paths.get(temp+"/"+key+".txt"), value.getBytes()); 33 | BurpExtender.print(String.format("写入文件(%s)数据:%s",key,value)); 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | BurpExtender.print(String.format("错误:%s",e.getMessage()),1); 37 | BurpExtender.print(String.format("写入文件失败:%s",temp+"/"+key+".txt")); 38 | } 39 | } 40 | 41 | /** 42 | * 获取值 43 | * @param key String 44 | */ 45 | public String get(String key){ 46 | try { 47 | BurpExtender.print("读取:"+temp + "/" + key + ".txt"); 48 | return new String(Files.readAllBytes(Paths.get(temp + "/" + key + ".txt"))).trim(); 49 | } catch (IOException e) { 50 | e.printStackTrace(); 51 | BurpExtender.print(String.format("错误:%s",e.getMessage()),1); 52 | BurpExtender.print(String.format("读取文件失败:%s",temp+"/"+key+".txt")); 53 | } 54 | return ""; 55 | } 56 | 57 | public byte[] getRaw(String key){ 58 | try { 59 | BurpExtender.print("读取:"+temp + "/" + key + ".txt"); 60 | return Files.readAllBytes(Paths.get(temp + "/" + key + ".txt")); 61 | } catch (IOException e) { 62 | e.printStackTrace(); 63 | BurpExtender.print(String.format("错误:%s",e.getMessage()),1); 64 | BurpExtender.print(String.format("读取文件失败:%s",temp+"/"+key+".txt")); 65 | } 66 | return null; 67 | } 68 | public void setRaw(String key,byte[] value){ 69 | try { 70 | Files.write(Paths.get(temp+"/"+key+".txt"), value); 71 | } catch (IOException e) { 72 | e.printStackTrace(); 73 | BurpExtender.print(String.format("错误:%s",e.getMessage()),1); 74 | BurpExtender.print(String.format("写入文件失败:%s",temp+"/"+key+".txt")); 75 | } 76 | } 77 | 78 | 79 | public void destroy(){ 80 | try { 81 | Path path = Paths.get(temp); 82 | Files.walkFileTree(path, 83 | new SimpleFileVisitor() { 84 | // 先去遍历删除文件 85 | @Override 86 | public FileVisitResult visitFile(Path file, 87 | BasicFileAttributes attrs) throws IOException { 88 | Files.delete(file); 89 | System.out.printf("文件被删除 : %s%n", file); 90 | return FileVisitResult.CONTINUE; 91 | } 92 | // 再去遍历删除目录 93 | @Override 94 | public FileVisitResult postVisitDirectory(Path dir, 95 | IOException exc) throws IOException { 96 | Files.delete(dir); 97 | System.out.printf("文件夹被删除: %s%n", dir); 98 | return FileVisitResult.CONTINUE; 99 | } 100 | 101 | } 102 | ); 103 | BurpExtender.print(String.format("临时文件夹[ %s ]已删除",temp)); 104 | } catch (IOException ignored) { 105 | 106 | } 107 | } 108 | 109 | @Override 110 | protected void finalize() throws Throwable { 111 | this.destroy(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/burp/MessageEditorTab.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import burp.core.*; 4 | import burp.core.Process; 5 | 6 | import javax.swing.*; 7 | import javax.swing.event.ListDataListener; 8 | import java.awt.*; 9 | import java.awt.event.ActionEvent; 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.Objects; 13 | 14 | public class MessageEditorTab implements IMessageEditorTab { 15 | private ITextEditor messageEditor = null; 16 | private boolean isRequest = false; 17 | private IMessageEditorController iMessageEditorController = null; 18 | private void selectItem(ActionEvent e) { 19 | Rule rule = (Rule)selectBox.getSelectedItem(); 20 | assert rule != null; 21 | try { 22 | Process process = new Process(); 23 | if(Objects.equals(rule.command, "")){ 24 | process.destroy(); 25 | return; 26 | } 27 | BurpExtender.print(String.format("脚本: %s 执行",rule.name)); 28 | if(isRequest){ 29 | HttpAgreement httpAgreement = new HttpAgreement(iMessageEditorController.getRequest(),null,process); 30 | if(BurpExtender.rules.run(rule.command, CommandType.RequestFromClient,process.getTemp())){ 31 | setMessage(httpAgreement.toRequest(process),isRequest); 32 | } 33 | }else{ 34 | HttpAgreement httpAgreement = new HttpAgreement(iMessageEditorController.getRequest(),iMessageEditorController.getResponse(),process); 35 | if(BurpExtender.rules.run(rule.command, CommandType.ResponseFromServer,process.getTemp())){ 36 | setMessage(httpAgreement.toResponse(process),isRequest); 37 | } 38 | } 39 | }catch (IOException exception){ 40 | exception.printStackTrace(); 41 | BurpExtender.print("错误信息2:"+exception.getMessage(),1); 42 | } 43 | BurpExtender.print("规则:"+rule.toString()); 44 | } 45 | private final JSplitPane rootPanel; 46 | private final JComboBox selectBox; 47 | MessageEditorTab(IMessageEditorController iMessageEditorController, boolean b){ 48 | this.iMessageEditorController =iMessageEditorController; 49 | messageEditor = BurpExtender.callbacks.createTextEditor(); 50 | messageEditor.setEditable(b); 51 | Component EditorComponent = messageEditor.getComponent(); 52 | rootPanel = new JSplitPane(); 53 | JPanel panel1 = new JPanel(); 54 | JLabel label1 = new JLabel(); 55 | selectBox = new JComboBox<>(); 56 | 57 | //======== splitPane1 ======== 58 | rootPanel.setOrientation(JSplitPane.VERTICAL_SPLIT); 59 | //======== panel1 ======== 60 | panel1.setPreferredSize(new Dimension(0, 30)); 61 | panel1.setMaximumSize(new Dimension(2147483647, 30)); 62 | panel1.setLayout(new BorderLayout()); 63 | //---- label1 ---- 64 | label1.setText("\u8bf7\u9009\u62e9\u811a\u672c "); 65 | panel1.add(label1, BorderLayout.WEST); 66 | 67 | //---- comboBox1 ---- 68 | selectBox.addActionListener(this::selectItem); 69 | selectBox.setPreferredSize(new Dimension(0, 30)); 70 | panel1.add(selectBox, BorderLayout.CENTER); 71 | 72 | class Model implements ComboBoxModel{ 73 | private final ArrayList arrayList; 74 | private Rule rule = null; 75 | Model(){ 76 | arrayList = BurpExtender.rules.getAll(); 77 | } 78 | @Override 79 | public void setSelectedItem(Object anItem) { 80 | rule = (Rule) anItem; 81 | } 82 | @Override 83 | public Object getSelectedItem() { 84 | return rule; 85 | } 86 | @Override 87 | public int getSize() { 88 | return arrayList.size(); 89 | } 90 | 91 | @Override 92 | public Rule getElementAt(int index) { 93 | return arrayList.get(index); 94 | } 95 | 96 | @Override 97 | public void addListDataListener(ListDataListener l) { 98 | 99 | } 100 | 101 | @Override 102 | public void removeListDataListener(ListDataListener l) { 103 | 104 | } 105 | } 106 | selectBox.setModel(new Model()); 107 | rootPanel.setTopComponent(panel1); 108 | 109 | rootPanel.setBottomComponent(EditorComponent); 110 | } 111 | @Override 112 | public String getTabCaption() { 113 | return "CustomCrypto"; 114 | } 115 | 116 | @Override 117 | public Component getUiComponent() { 118 | return rootPanel; 119 | } 120 | 121 | @Override 122 | public boolean isEnabled(byte[] bytes, boolean b) { 123 | return true; 124 | } 125 | 126 | @Override 127 | public void setMessage(byte[] content, boolean isRequest) { 128 | this.isRequest = isRequest; 129 | messageEditor.setText(content); 130 | } 131 | 132 | @Override 133 | public byte[] getMessage() { 134 | return messageEditor.getText(); 135 | } 136 | 137 | @Override 138 | public boolean isModified() { 139 | return messageEditor.isTextModified(); 140 | } 141 | 142 | @Override 143 | public byte[] getSelectedData() { 144 | return messageEditor.getSelectedText(); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/burp/BurpHttpListener.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | 4 | import burp.core.Process; 5 | import burp.core.*; 6 | 7 | import java.io.IOException; 8 | import java.util.Objects; 9 | 10 | public class BurpHttpListener implements IHttpListener, IProxyListener { 11 | 12 | HttpAgreement httpAgreement; 13 | BurpHttpListener() throws IOException { 14 | 15 | } 16 | /** 17 | * 检查是否需要拦截 18 | * 19 | */ 20 | private boolean analyze(IHttpRequestResponse request, boolean messageIsRequest,String[] cmd,Process process) throws IOException { 21 | BurpExtender.print("分析请求中"); 22 | if(!BurpExtender.rules.getAuto()){ 23 | BurpExtender.print("根据设置不自动解密"); 24 | return false; 25 | } 26 | String url = request.getHttpService().getProtocol() + "://" + request.getHttpService().getHost(); 27 | if(!messageIsRequest){ 28 | httpAgreement = new HttpAgreement(request.getRequest(),request.getResponse(),process); 29 | }else{ 30 | httpAgreement = new HttpAgreement(request.getRequest(),null,process); 31 | } 32 | url += httpAgreement.path; 33 | Rule rule = BurpExtender.rules.findRule(httpAgreement.method, url, httpAgreement.headers); 34 | if(rule==null) { 35 | BurpExtender.print("没有合适的脚本允许解密"); 36 | return false; 37 | } 38 | BurpExtender.print(String.format("脚本: %s 执行",rule.name)); 39 | if(Objects.equals(rule.command, ""))return false; 40 | cmd[0] = rule.command; 41 | return true; 42 | } 43 | 44 | /** 45 | * 当即将发出HTTP请求以及收到HTTP响应时,会调用此方法。 46 | * 47 | * @param toolFlag 指示发出请求的Burp工具的flag。 48 | * Burp工具 flags 定义在 IBurpExtenderCallbacks 接口. 49 | * @param messageIsRequest 是否为请求。 50 | * @param messageInfo 要处理的请求/回复的详细信息。 扩展可以调用此对象上的setter方法来更新当前消息,从而修改Burp的行为。 51 | */ 52 | @Override 53 | public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { 54 | String[] cmd = new String[1]; 55 | BurpExtender.print("================================================="); 56 | //使用引用传递获取需要执行的命令 57 | //返回值标识是否需要拦截 58 | try { 59 | Process process = new Process(); 60 | //不需要处理直接返回 61 | if (!analyze(messageInfo, messageIsRequest,cmd, process)){ 62 | process.destroy(); 63 | return; 64 | } 65 | if (messageIsRequest) { 66 | BurpExtender.print("======> 发送给服务端"); 67 | requestOut(messageInfo, process, cmd[0]);//发送请求 68 | } else { 69 | BurpExtender.print("======> 收到服务端"); 70 | responseIn(messageInfo, process, cmd[0]);//收到响应 71 | } 72 | 73 | } catch (IOException e) { 74 | e.printStackTrace(); 75 | BurpExtender.print("错误信息:"+e.getMessage(),1); 76 | } 77 | BurpExtender.print("================================================="); 78 | } 79 | 80 | /** 81 | * 当代理处理HTTP消息时,会调用此方法。 82 | * 83 | * @param messageIsRequest 是否为请求。 84 | * @param message 扩展可用于查询和更新消息的详细信息,并控制消息是否应拦截并显示给用户进行手动审查或修改。 85 | */ 86 | @Override 87 | public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message) { 88 | String[] cmd = new String[1]; 89 | //使用引用传递获取需要执行的命令 90 | //返回值标识是否需要拦截 91 | BurpExtender.print("================================================="); 92 | try { 93 | Process process = new Process(); 94 | if (!analyze(message.getMessageInfo(), messageIsRequest, cmd,process)) { 95 | process.destroy(); 96 | return; 97 | } 98 | 99 | if (messageIsRequest) { 100 | BurpExtender.print("======> 发送给客户端"); 101 | requestIn(message, process, cmd[0]);//收到请求 102 | } else { 103 | BurpExtender.print("======> 收到客户端"); 104 | responseOut(message,process, cmd[0]);//发送响应 105 | } 106 | 107 | } catch (IOException e) { 108 | e.printStackTrace(); 109 | BurpExtender.print("错误信息:"+e.getMessage(),1); 110 | } 111 | BurpExtender.print("================================================="); 112 | } 113 | 114 | /** 115 | * requestIn阶段,收到客户端发送的加密request,进行解密并替换requestBody,使得BurpSuite中显示明文request; 116 | * 117 | */ 118 | private void requestIn(IInterceptedProxyMessage message,Process process, String cmd) { 119 | if(BurpExtender.rules.run(cmd, CommandType.RequestFromClient,process.getTemp())){ 120 | message.getMessageInfo().setRequest(httpAgreement.toRequest(process)); 121 | } 122 | } 123 | //requestOut阶段,即将发送request到服务端,读取明文的request,重新进行加密(包括签名、编码、更新时间戳等),使得服务端正常解析; 124 | private void requestOut(IHttpRequestResponse messageInfo, Process process,String cmd) { 125 | if(BurpExtender.rules.run(cmd, CommandType.RequestToServer,process.getTemp())){ 126 | messageInfo.setRequest(httpAgreement.toRequest(process)); 127 | } 128 | } 129 | 130 | //responseIn阶段,收到加密response,进行解密并替换responseBody,使得BurpSuite中显示明文response; 131 | private void responseIn(IHttpRequestResponse messageInfo, Process process, String cmd) { 132 | if(BurpExtender.rules.run(cmd, CommandType.ResponseFromServer,process.getTemp())){ 133 | messageInfo.setResponse(httpAgreement.toResponse(process)); 134 | } 135 | } 136 | 137 | //responseOut阶段,即将发送response到客户端,读取明文的response,重新进行加密,使得客户端正常解析。 138 | private void responseOut(IInterceptedProxyMessage message, Process process,String cmd) { 139 | if(BurpExtender.rules.run(cmd, CommandType.ResponseToClient,process.getTemp())){ 140 | message.getMessageInfo().setResponse(httpAgreement.toResponse(process)); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/burp/core/HttpAgreement.java: -------------------------------------------------------------------------------- 1 | package burp.core; 2 | 3 | import burp.BurpExtender; 4 | import burp.IRequestInfo; 5 | import com.google.common.base.Splitter; 6 | import com.google.common.collect.Lists; 7 | 8 | import java.io.IOException; 9 | import java.nio.charset.StandardCharsets; 10 | import java.util.ArrayList; 11 | 12 | // http协议封装 13 | public class HttpAgreement { 14 | public String method = ""; 15 | public String path = ""; 16 | public String version = ""; 17 | public String headers = ""; 18 | public byte[] body; 19 | 20 | public String response_version = ""; 21 | public String state = ""; 22 | public String state_msg = ""; 23 | public String response_headers = ""; 24 | public byte[] response_body; 25 | public HttpAgreement() throws IOException { 26 | new HttpAgreement(null); 27 | } 28 | public HttpAgreement(byte[] requestData) throws IOException { 29 | new HttpAgreement(requestData,null); 30 | } 31 | public HttpAgreement(byte[] requestData,byte[] responseData) throws IOException { 32 | new HttpAgreement(requestData,responseData,null); 33 | } 34 | public byte[] subByte(byte[] b,int off,int length){ 35 | byte[] b1 = new byte[length]; 36 | System.arraycopy(b, off, b1, 0, length); 37 | return b1; 38 | } 39 | public HttpAgreement(byte[] requestData,byte[] responseData,Process process) throws IOException { 40 | //从请求解析http协议 41 | if(requestData!=null){ 42 | IRequestInfo requestInfo = BurpExtender.helpers.analyzeRequest(requestData); 43 | Iterable split = Splitter.on("\r\n").split(new String(requestData)); 44 | ArrayList strings = Lists.newArrayList(split); 45 | String[] path = strings.get(0).split(" "); //首行是请求头 46 | int i; 47 | method = path[0]; 48 | this.path = path[1]; 49 | version = path[2]; 50 | headers = ""; 51 | StringBuilder s = new StringBuilder(); 52 | for ( i = 1; i < strings.size()-2; i++) { 53 | s.append(strings.get(i)).append("\r\n"); 54 | } 55 | headers = s.toString().trim(); 56 | 57 | body = subByte(requestData,requestInfo.getBodyOffset(), requestData.length - requestInfo.getBodyOffset()); 58 | if(process!=null){ 59 | process.set("method",method); 60 | process.set("path",this.path); 61 | process.set("version",version); 62 | process.set("headers",headers); 63 | process.setRaw("body",body); 64 | } 65 | } 66 | if(responseData!=null){ 67 | Iterable split = Splitter.on("\r\n").split(new String(responseData)); 68 | ArrayList strings = Lists.newArrayList(split); 69 | String[] path = strings.get(0).split(" "); //首行是请求头 70 | int i; 71 | state = path[1]; 72 | state_msg = path[2]; 73 | response_version = path[0]; 74 | response_headers = ""; 75 | StringBuilder s = new StringBuilder(); 76 | for ( i = 1; i < strings.size()-2; i++) { 77 | s.append(strings.get(i)).append("\r\n"); 78 | } 79 | response_headers = s.toString().trim(); 80 | IRequestInfo requestInfo = BurpExtender.helpers.analyzeRequest(responseData); 81 | response_body = subByte(requestData,requestInfo.getBodyOffset(), responseData.length - requestInfo.getBodyOffset()); 82 | if(process!=null){ 83 | process.set("state",state); 84 | process.set("state_msg",this.state_msg); 85 | process.set("response_version",response_version); 86 | process.set("response_headers",response_headers); 87 | process.setRaw("response_body",response_body); 88 | } 89 | } 90 | } 91 | byte[] toRequest() { 92 | return toRequest(null); 93 | } 94 | public static byte[] byteMerger(byte[] bt1, byte[] bt2){ 95 | byte[] bt3 = new byte[bt1.length+bt2.length]; 96 | System.arraycopy(bt1, 0, bt3, 0, bt1.length); 97 | System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length); 98 | return bt3; 99 | } 100 | public byte[] toRequest(Process process) { 101 | StringBuilder stringBuilder = new StringBuilder(); 102 | if(process!=null){ 103 | method = process.get("method"); 104 | path = process.get("path"); 105 | version = process.get("version"); 106 | headers = process.get("headers"); 107 | body = process.getRaw("body"); 108 | 109 | } 110 | int length = body.length; 111 | headers = headers.replaceAll("Content-Length: \\d+","Content-Length: "+length); 112 | //更新headers 113 | stringBuilder.append(method).append(" ").append(path).append(" ").append(version).append("\r\n"); 114 | stringBuilder.append(headers).append("\r\n"); 115 | stringBuilder.append("\r\n"); 116 | byte[] bytes = stringBuilder.toString().getBytes(); 117 | 118 | return byteMerger(bytes,body); 119 | } 120 | byte[] toResponse() { 121 | return toResponse(null); 122 | } 123 | public byte[] toResponse(Process process) { 124 | StringBuilder stringBuilder = new StringBuilder(); 125 | if(process!=null){ 126 | state = process.get("state"); 127 | state_msg = process.get("state_msg"); 128 | response_version = process.get("response_version"); 129 | response_headers = process.get("response_headers"); 130 | response_body = process.getRaw("response_body"); 131 | } 132 | stringBuilder.append(response_version).append(" ").append(state).append(" ").append(state_msg).append("\r\n"); 133 | stringBuilder.append(response_headers).append("\r\n"); 134 | stringBuilder.append("\r\n"); 135 | byte[] bytes = stringBuilder.toString().getBytes(); 136 | 137 | return byteMerger(bytes,response_body); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/burp/MainGUI.jfd: -------------------------------------------------------------------------------- 1 | JFDML JFormDesigner: "8.0.0.0.194" Java: "11.0.11" encoding: "UTF-8" 2 | 3 | new FormModel { 4 | contentType: "form/swing" 5 | root: new FormRoot { 6 | add( new FormContainer( "javax.swing.JTabbedPane", new FormLayoutManager( class javax.swing.JTabbedPane ) ) { 7 | name: "tabbedPane1" 8 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { 9 | name: "panel1" 10 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { 11 | name: "panel6" 12 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { 13 | "$horizontalGroup": "par l {seq {space :::p, par l {comp autoRun::l:p:800:p, seq l {par l {comp label9:::p::p, comp watchExport::l:p::p}, space :p:12:p, par l {comp watchRoot:::p:442:p, comp watchImport:::p::p}}}, space :p:0:p}}" 14 | "$verticalGroup": "par l {seq l {comp autoRun:::p::p, space :p:6:p, par b {comp label9::b:p::p, comp watchRoot::b:p::p}, space :::p, par b {comp watchImport::b:p::p, comp watchExport::b:p::p}}}" 15 | } ) { 16 | name: "panel7" 17 | "border": new javax.swing.border.TitledBorder( "插件配置" ) 18 | add( new FormComponent( "javax.swing.JCheckBox" ) { 19 | name: "autoRun" 20 | "text": "自动执行脚本" 21 | addEvent( new FormEvent( "javax.swing.event.ChangeListener", "stateChanged", "autoRunStateChanged", true ) ) 22 | } ) 23 | add( new FormComponent( "javax.swing.JLabel" ) { 24 | name: "label9" 25 | "text": "脚本的存放目录(可选)" 26 | } ) 27 | add( new FormComponent( "javax.swing.JTextField" ) { 28 | name: "watchRoot" 29 | addEvent( new FormEvent( "java.awt.event.InputMethodListener", "inputMethodTextChanged", "watchRootInputMethodTextChanged", true ) ) 30 | } ) 31 | add( new FormComponent( "javax.swing.JButton" ) { 32 | name: "watchImport" 33 | "text": "导入配置" 34 | addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "watchImport", true ) ) 35 | } ) 36 | add( new FormComponent( "javax.swing.JButton" ) { 37 | name: "watchExport" 38 | "text": "导出配置" 39 | addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "watchExport", true ) ) 40 | } ) 41 | }, new FormLayoutConstraints( class java.lang.String ) { 42 | "value": "North" 43 | } ) 44 | }, new FormLayoutConstraints( class java.lang.String ) { 45 | "value": "North" 46 | } ) 47 | add( new FormContainer( "javax.swing.JSplitPane", new FormLayoutManager( class javax.swing.JSplitPane ) ) { 48 | name: "splitPane1" 49 | "dividerLocation": 200 50 | add( new FormComponent( "javax.swing.JList" ) { 51 | name: "watchList" 52 | "maximumSize": new java.awt.Dimension( 200, 62 ) 53 | "fixedCellWidth": 200 54 | "border": new javax.swing.border.LineBorder( sfield java.awt.Color black, 1, false ) 55 | "selectionMode": 0 56 | auxiliary() { 57 | "JavaCodeGenerator.typeParameters": "String" 58 | } 59 | addEvent( new FormEvent( "javax.swing.event.ListSelectionListener", "valueChanged", "watchListValueChanged", true ) ) 60 | }, new FormLayoutConstraints( class java.lang.String ) { 61 | "value": "left" 62 | } ) 63 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { 64 | "$horizontalGroup": "par l {seq {space :::p, par l {comp panel4::l:::x, comp panel3::t:::x, seq l {comp watchSave:::p::p, space s:::p, comp watchDel:::p::p, space :0:0:x}}, space :::p}}" 65 | "$verticalGroup": "par l {seq t {comp panel4:::p::p, space :p:18:p, comp panel3:::p:254:p, space u:::p, par b {comp watchSave::b:p::p, comp watchDel::b:p::p}, space ::26:x}}" 66 | } ) { 67 | name: "panel2" 68 | "border": new javax.swing.border.EmptyBorder( 20, 20, 20, 20 ) 69 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { 70 | "$horizontalGroup": "par l {seq {space :::p, par l {comp watchUseRegex::t::513:x, seq {space :6:6:p, comp label2:::p::p, space u:::p, comp watchUrlInclude:::::x}, seq l {par l {seq {space :p:6:p, comp label3:::p:77:p, space :p:11:p}, seq t {comp label4:::p:77:p, space u:::p}}, par l {comp watchReqHeadInclude::l:::x, comp watchReqBodyInclude::l:::x}}, comp panel5::l:::x}, space :::p}}" 71 | "$verticalGroup": "par l {seq t {space :::p, comp panel5:::p::p, space :::x, par b {comp label2::b:p::p, comp watchUrlInclude::b:p::p}, space :::p, par b {comp watchReqHeadInclude::b:p::p, comp label3::b:p::p}, space :::p, par b {comp watchReqBodyInclude::b:p::p, comp label4::b:p::p}, space s:::p, comp watchUseRegex:::p::p, space :p:57:p}}" 72 | } ) { 73 | name: "panel3" 74 | "border": new javax.swing.border.TitledBorder( "监控参数(自动执行脚本需要配置)" ) 75 | add( new FormComponent( "javax.swing.JTextField" ) { 76 | name: "watchUrlInclude" 77 | } ) 78 | add( new FormComponent( "javax.swing.JTextField" ) { 79 | name: "watchReqHeadInclude" 80 | } ) 81 | add( new FormComponent( "javax.swing.JTextField" ) { 82 | name: "watchReqBodyInclude" 83 | } ) 84 | add( new FormComponent( "javax.swing.JCheckBox" ) { 85 | name: "watchUseRegex" 86 | "text": "所有匹配规则采用正则模式" 87 | } ) 88 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { 89 | "$horizontalGroup": "par l {seq l {space :::p, comp watchGET:::p::p, space s:::p, comp watchPOST:::p::p, space s:::p, comp watchPATCH:::p::p, space s:::p, comp watchPUT:::p::p, space s:::p, comp watchDELETE:::p::p, space :::x}}" 90 | "$verticalGroup": "par l {seq l {space :::p, par b {comp watchGET::b:p::p, comp watchPOST::b:p::p, comp watchPATCH::b:p::p, comp watchPUT::b:p::p, comp watchDELETE::b:p::p}, space :::x}}" 91 | } ) { 92 | name: "panel5" 93 | "border": new javax.swing.border.TitledBorder( "请求类型" ) 94 | add( new FormComponent( "javax.swing.JCheckBox" ) { 95 | name: "watchGET" 96 | "text": "GET" 97 | } ) 98 | add( new FormComponent( "javax.swing.JCheckBox" ) { 99 | name: "watchPOST" 100 | "text": "POST" 101 | } ) 102 | add( new FormComponent( "javax.swing.JCheckBox" ) { 103 | name: "watchPATCH" 104 | "text": "PATCH" 105 | } ) 106 | add( new FormComponent( "javax.swing.JCheckBox" ) { 107 | name: "watchPUT" 108 | "text": "PUT" 109 | } ) 110 | add( new FormComponent( "javax.swing.JCheckBox" ) { 111 | name: "watchDELETE" 112 | "text": "DELETE" 113 | } ) 114 | } ) 115 | add( new FormComponent( "javax.swing.JLabel" ) { 116 | name: "label2" 117 | "text": "URL包含" 118 | } ) 119 | add( new FormComponent( "javax.swing.JLabel" ) { 120 | name: "label3" 121 | "text": "请求头包含" 122 | } ) 123 | add( new FormComponent( "javax.swing.JLabel" ) { 124 | name: "label4" 125 | "text": "请求体包含" 126 | } ) 127 | } ) 128 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class org.jdesktop.layout.GroupLayout ) { 129 | "$horizontalGroup": "par l {seq l {space :::p, par l {seq l {comp label8:::p::p, space :::p, comp watchCustom::::442:x}, seq l {comp label7:::p::p, space :::p, comp watchName::::442:x}}, space :::p}}" 130 | "$verticalGroup": "par l {seq {space :p:8:p, par b {comp label7::b:p::p, comp watchName::b:p::p}, space u:::p, par b {comp label8::b:p::p, comp watchCustom::b:p::p}, space :0:3:x}}" 131 | } ) { 132 | name: "panel4" 133 | "border": new javax.swing.border.TitledBorder( "脚本配置" ) 134 | add( new FormComponent( "javax.swing.JTextField" ) { 135 | name: "watchCustom" 136 | } ) 137 | add( new FormComponent( "javax.swing.JLabel" ) { 138 | name: "label8" 139 | "text": "执行命令:" 140 | } ) 141 | add( new FormComponent( "javax.swing.JLabel" ) { 142 | name: "label7" 143 | "text": "配置名称:" 144 | } ) 145 | add( new FormComponent( "javax.swing.JTextField" ) { 146 | name: "watchName" 147 | } ) 148 | } ) 149 | add( new FormComponent( "javax.swing.JButton" ) { 150 | name: "watchSave" 151 | "text": "保存" 152 | addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "watchSave", true ) ) 153 | } ) 154 | add( new FormComponent( "javax.swing.JButton" ) { 155 | name: "watchDel" 156 | "text": "删除" 157 | addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "watchDel", true ) ) 158 | } ) 159 | }, new FormLayoutConstraints( class java.lang.String ) { 160 | "value": "right" 161 | } ) 162 | }, new FormLayoutConstraints( class java.lang.String ) { 163 | "value": "Center" 164 | } ) 165 | }, new FormLayoutConstraints( null ) { 166 | "title": "脚本配置" 167 | } ) 168 | add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class java.awt.BorderLayout ) ) { 169 | name: "Setting" 170 | "border": new javax.swing.border.EmptyBorder( 20, 20, 20, 20 ) 171 | add( new FormComponent( "javax.swing.JLabel" ) { 172 | name: "label1" 173 | "text": "

CustomCrypto

burp自定义加解密插件

Github: https://github.com/dreamncn/CustomCrypto

Site: https://ankio.net

Powered by Ankio 2023.

" 174 | }, new FormLayoutConstraints( class java.lang.String ) { 175 | "value": "North" 176 | } ) 177 | }, new FormLayoutConstraints( null ) { 178 | "title": "关于" 179 | } ) 180 | }, new FormLayoutConstraints( null ) { 181 | "location": new java.awt.Point( 0, 0 ) 182 | "size": new java.awt.Dimension( 800, 640 ) 183 | } ) 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/burp/MainGUI.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by JFormDesigner on Mon Sep 19 12:24:48 CST 2022 3 | */ 4 | 5 | package burp; 6 | 7 | import burp.core.Rule; 8 | import burp.core.Storage; 9 | 10 | import javax.swing.*; 11 | import javax.swing.border.EmptyBorder; 12 | import javax.swing.border.LineBorder; 13 | import javax.swing.border.TitledBorder; 14 | import javax.swing.event.ChangeEvent; 15 | import javax.swing.event.ListSelectionEvent; 16 | import java.awt.*; 17 | import java.awt.datatransfer.StringSelection; 18 | import java.awt.event.ActionEvent; 19 | import java.awt.event.InputMethodEvent; 20 | import java.awt.event.InputMethodListener; 21 | import java.util.ArrayList; 22 | 23 | 24 | /** 25 | * 插件的GUI实现 26 | * @author ankio 27 | */ 28 | public class MainGUI { 29 | 30 | /** 31 | * 构造函数 32 | */ MainGUI() { 33 | //初始化UI 34 | initComponents(); 35 | //初始化数据 36 | initData(); 37 | 38 | } 39 | 40 | 41 | 42 | /** 43 | * 初始化数据 44 | */ 45 | private void initData(){ 46 | autoRun.setSelected(BurpExtender.rules.getAuto()); 47 | ArrayList arrayList = new ArrayList<>(); 48 | for (Rule rule:BurpExtender.rules.getAll()) { 49 | arrayList.add(rule.name); 50 | } 51 | watchList.setListData(arrayList.toArray(new String[0])); 52 | initTable(); 53 | watchRoot.setText(Storage.readString("root")); 54 | 55 | } 56 | 57 | 58 | 59 | /** 60 | * 获取根View 61 | */ 62 | public JTabbedPane getRoot(){ 63 | return tabbedPane1; 64 | } 65 | 66 | private void watchListValueChanged(ListSelectionEvent e) { 67 | select = watchList.getSelectedIndex(); 68 | BurpExtender.print("选择:"+select); 69 | if (select == -1) return; 70 | 71 | Rule rule = BurpExtender.rules.getRule(select); 72 | if(rule==null)return; 73 | initTable(); 74 | watchName.setText(rule.name); 75 | watchCustom.setText(rule.command); 76 | for (String s:rule.method) { 77 | switch (s){ 78 | case "patch":watchPATCH.setSelected(true);break; 79 | case "delete":watchDELETE.setSelected(true);break; 80 | case "get":watchGET.setSelected(true);break; 81 | case "post":watchPOST.setSelected(true);break; 82 | case "put":watchPUT.setSelected(true);break; 83 | } 84 | } 85 | watchReqBodyInclude.setText(rule.body); 86 | watchReqHeadInclude.setText(rule.header); 87 | watchUrlInclude.setText(rule.url); 88 | watchUseRegex.setSelected(rule.regex); 89 | } 90 | 91 | private void autoRunStateChanged(ChangeEvent e) { 92 | BurpExtender.rules.setAuto(autoRun.isSelected()); 93 | } 94 | 95 | private void watchRootInputMethodTextChanged(InputMethodEvent e) { 96 | Storage.writeString("root",watchRoot.getText()); 97 | } 98 | 99 | private void watchExport(ActionEvent e) { 100 | StringSelection ss = new StringSelection(Storage.encode(BurpExtender.rules.getAll()));//创建能传输指定 String 的 Transferable 101 | Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null); //跟上面三行代码效果相同 102 | Storage.encode(BurpExtender.rules.getAll()); 103 | showMsg("已复制到剪切板!"); 104 | } 105 | 106 | private void watchImport(ActionEvent e) { 107 | String input = JOptionPane.showInputDialog( "请输入数据" ); 108 | ArrayList rules = Storage.decode(input); 109 | for (Rule rule:rules ) { 110 | BurpExtender.rules.add(rule); 111 | } 112 | showMsg("导入成功!"); 113 | } 114 | 115 | 116 | 117 | private void initComponents() { 118 | // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents 119 | // Generated using JFormDesigner Evaluation license - unknown 120 | tabbedPane1 = new JTabbedPane(); 121 | panel1 = new JPanel(); 122 | panel6 = new JPanel(); 123 | panel7 = new JPanel(); 124 | autoRun = new JCheckBox(); 125 | label9 = new JLabel(); 126 | watchRoot = new JTextField(); 127 | watchImport = new JButton(); 128 | watchExport = new JButton(); 129 | splitPane1 = new JSplitPane(); 130 | watchList = new JList<>(); 131 | panel2 = new JPanel(); 132 | panel3 = new JPanel(); 133 | watchUrlInclude = new JTextField(); 134 | watchReqHeadInclude = new JTextField(); 135 | watchReqBodyInclude = new JTextField(); 136 | watchUseRegex = new JCheckBox(); 137 | panel5 = new JPanel(); 138 | watchGET = new JCheckBox(); 139 | watchPOST = new JCheckBox(); 140 | watchPATCH = new JCheckBox(); 141 | watchPUT = new JCheckBox(); 142 | watchDELETE = new JCheckBox(); 143 | label2 = new JLabel(); 144 | label3 = new JLabel(); 145 | label4 = new JLabel(); 146 | panel4 = new JPanel(); 147 | watchCustom = new JTextField(); 148 | label8 = new JLabel(); 149 | label7 = new JLabel(); 150 | watchName = new JTextField(); 151 | watchSave = new JButton(); 152 | watchDel = new JButton(); 153 | Setting = new JPanel(); 154 | label1 = new JLabel(); 155 | 156 | //======== tabbedPane1 ======== 157 | { 158 | 159 | //======== panel1 ======== 160 | { 161 | panel1.setBorder (new javax. swing. border. CompoundBorder( new javax .swing .border .TitledBorder (new javax. swing. border. 162 | EmptyBorder( 0, 0, 0, 0) , "JFor\u006dDesi\u0067ner \u0045valu\u0061tion", javax. swing. border. TitledBorder. CENTER, javax. swing 163 | . border. TitledBorder. BOTTOM, new java .awt .Font ("Dia\u006cog" ,java .awt .Font .BOLD ,12 ), 164 | java. awt. Color. red) ,panel1. getBorder( )) ); panel1. addPropertyChangeListener (new java. beans. PropertyChangeListener( ) 165 | { @Override public void propertyChange (java .beans .PropertyChangeEvent e) {if ("bord\u0065r" .equals (e .getPropertyName () )) 166 | throw new RuntimeException( ); }} ); 167 | panel1.setLayout(new BorderLayout()); 168 | 169 | //======== panel6 ======== 170 | { 171 | panel6.setLayout(new BorderLayout()); 172 | 173 | //======== panel7 ======== 174 | { 175 | panel7.setBorder(new TitledBorder("\u63d2\u4ef6\u914d\u7f6e")); 176 | 177 | //---- autoRun ---- 178 | autoRun.setText("\u81ea\u52a8\u6267\u884c\u811a\u672c"); 179 | autoRun.addChangeListener(e -> autoRunStateChanged(e)); 180 | 181 | //---- label9 ---- 182 | label9.setText("\u811a\u672c\u7684\u5b58\u653e\u76ee\u5f55\uff08\u53ef\u9009\uff09"); 183 | 184 | //---- watchRoot ---- 185 | watchRoot.addInputMethodListener(new InputMethodListener() { 186 | @Override 187 | public void caretPositionChanged(InputMethodEvent e) {} 188 | @Override 189 | public void inputMethodTextChanged(InputMethodEvent e) { 190 | watchRootInputMethodTextChanged(e); 191 | } 192 | }); 193 | 194 | //---- watchImport ---- 195 | watchImport.setText("\u5bfc\u5165\u914d\u7f6e"); 196 | watchImport.addActionListener(e -> watchImport(e)); 197 | 198 | //---- watchExport ---- 199 | watchExport.setText("\u5bfc\u51fa\u914d\u7f6e"); 200 | watchExport.addActionListener(e -> watchExport(e)); 201 | 202 | GroupLayout panel7Layout = new GroupLayout(panel7); 203 | panel7.setLayout(panel7Layout); 204 | panel7Layout.setHorizontalGroup( 205 | panel7Layout.createParallelGroup() 206 | .addGroup(panel7Layout.createSequentialGroup() 207 | .addContainerGap() 208 | .addGroup(panel7Layout.createParallelGroup() 209 | .addComponent(autoRun, GroupLayout.PREFERRED_SIZE, 800, GroupLayout.PREFERRED_SIZE) 210 | .addGroup(panel7Layout.createSequentialGroup() 211 | .addGroup(panel7Layout.createParallelGroup() 212 | .addComponent(label9) 213 | .addComponent(watchExport)) 214 | .addGap(12, 12, 12) 215 | .addGroup(panel7Layout.createParallelGroup() 216 | .addComponent(watchRoot, GroupLayout.PREFERRED_SIZE, 442, GroupLayout.PREFERRED_SIZE) 217 | .addComponent(watchImport)))) 218 | .addGap(0, 0, 0)) 219 | ); 220 | panel7Layout.setVerticalGroup( 221 | panel7Layout.createParallelGroup() 222 | .addGroup(panel7Layout.createSequentialGroup() 223 | .addComponent(autoRun) 224 | .addGap(6, 6, 6) 225 | .addGroup(panel7Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 226 | .addComponent(label9) 227 | .addComponent(watchRoot, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) 228 | .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 229 | .addGroup(panel7Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 230 | .addComponent(watchImport) 231 | .addComponent(watchExport))) 232 | ); 233 | } 234 | panel6.add(panel7, BorderLayout.NORTH); 235 | } 236 | panel1.add(panel6, BorderLayout.NORTH); 237 | 238 | //======== splitPane1 ======== 239 | { 240 | splitPane1.setDividerLocation(200); 241 | 242 | //---- watchList ---- 243 | watchList.setMaximumSize(new Dimension(200, 62)); 244 | watchList.setFixedCellWidth(200); 245 | watchList.setBorder(LineBorder.createBlackLineBorder()); 246 | watchList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 247 | watchList.addListSelectionListener(e -> watchListValueChanged(e)); 248 | splitPane1.setLeftComponent(watchList); 249 | 250 | //======== panel2 ======== 251 | { 252 | panel2.setBorder(new EmptyBorder(20, 20, 20, 20)); 253 | 254 | //======== panel3 ======== 255 | { 256 | panel3.setBorder(new TitledBorder("\u76d1\u63a7\u53c2\u6570\uff08\u81ea\u52a8\u6267\u884c\u811a\u672c\u9700\u8981\u914d\u7f6e\uff09")); 257 | 258 | //---- watchUseRegex ---- 259 | watchUseRegex.setText("\u6240\u6709\u5339\u914d\u89c4\u5219\u91c7\u7528\u6b63\u5219\u6a21\u5f0f"); 260 | 261 | //======== panel5 ======== 262 | { 263 | panel5.setBorder(new TitledBorder("\u8bf7\u6c42\u7c7b\u578b")); 264 | 265 | //---- watchGET ---- 266 | watchGET.setText("GET"); 267 | 268 | //---- watchPOST ---- 269 | watchPOST.setText("POST"); 270 | 271 | //---- watchPATCH ---- 272 | watchPATCH.setText("PATCH"); 273 | 274 | //---- watchPUT ---- 275 | watchPUT.setText("PUT"); 276 | 277 | //---- watchDELETE ---- 278 | watchDELETE.setText("DELETE"); 279 | 280 | GroupLayout panel5Layout = new GroupLayout(panel5); 281 | panel5.setLayout(panel5Layout); 282 | panel5Layout.setHorizontalGroup( 283 | panel5Layout.createParallelGroup() 284 | .addGroup(panel5Layout.createSequentialGroup() 285 | .addContainerGap() 286 | .addComponent(watchGET) 287 | .addGap(18, 18, 18) 288 | .addComponent(watchPOST) 289 | .addGap(18, 18, 18) 290 | .addComponent(watchPATCH) 291 | .addGap(18, 18, 18) 292 | .addComponent(watchPUT) 293 | .addGap(18, 18, 18) 294 | .addComponent(watchDELETE) 295 | .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 296 | ); 297 | panel5Layout.setVerticalGroup( 298 | panel5Layout.createParallelGroup() 299 | .addGroup(panel5Layout.createSequentialGroup() 300 | .addContainerGap() 301 | .addGroup(panel5Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 302 | .addComponent(watchGET) 303 | .addComponent(watchPOST) 304 | .addComponent(watchPATCH) 305 | .addComponent(watchPUT) 306 | .addComponent(watchDELETE)) 307 | .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 308 | ); 309 | } 310 | 311 | //---- label2 ---- 312 | label2.setText("URL\u5305\u542b"); 313 | 314 | //---- label3 ---- 315 | label3.setText("\u8bf7\u6c42\u5934\u5305\u542b"); 316 | 317 | //---- label4 ---- 318 | label4.setText("\u8bf7\u6c42\u4f53\u5305\u542b"); 319 | 320 | GroupLayout panel3Layout = new GroupLayout(panel3); 321 | panel3.setLayout(panel3Layout); 322 | panel3Layout.setHorizontalGroup( 323 | panel3Layout.createParallelGroup() 324 | .addGroup(panel3Layout.createSequentialGroup() 325 | .addContainerGap() 326 | .addGroup(panel3Layout.createParallelGroup() 327 | .addComponent(watchUseRegex, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 513, Short.MAX_VALUE) 328 | .addGroup(panel3Layout.createSequentialGroup() 329 | .addGap(6, 6, 6) 330 | .addComponent(label2) 331 | .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) 332 | .addComponent(watchUrlInclude)) 333 | .addGroup(panel3Layout.createSequentialGroup() 334 | .addGroup(panel3Layout.createParallelGroup() 335 | .addGroup(panel3Layout.createSequentialGroup() 336 | .addGap(6, 6, 6) 337 | .addComponent(label3, GroupLayout.PREFERRED_SIZE, 77, GroupLayout.PREFERRED_SIZE) 338 | .addGap(11, 11, 11)) 339 | .addGroup(GroupLayout.Alignment.TRAILING, panel3Layout.createSequentialGroup() 340 | .addComponent(label4, GroupLayout.PREFERRED_SIZE, 77, GroupLayout.PREFERRED_SIZE) 341 | .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED))) 342 | .addGroup(panel3Layout.createParallelGroup() 343 | .addComponent(watchReqHeadInclude) 344 | .addComponent(watchReqBodyInclude))) 345 | .addComponent(panel5, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 346 | .addContainerGap()) 347 | ); 348 | panel3Layout.setVerticalGroup( 349 | panel3Layout.createParallelGroup() 350 | .addGroup(GroupLayout.Alignment.TRAILING, panel3Layout.createSequentialGroup() 351 | .addContainerGap() 352 | .addComponent(panel5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 353 | .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 354 | .addGroup(panel3Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 355 | .addComponent(label2) 356 | .addComponent(watchUrlInclude, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) 357 | .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 358 | .addGroup(panel3Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 359 | .addComponent(watchReqHeadInclude, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 360 | .addComponent(label3)) 361 | .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 362 | .addGroup(panel3Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 363 | .addComponent(watchReqBodyInclude, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 364 | .addComponent(label4)) 365 | .addGap(18, 18, 18) 366 | .addComponent(watchUseRegex) 367 | .addGap(57, 57, 57)) 368 | ); 369 | } 370 | 371 | //======== panel4 ======== 372 | { 373 | panel4.setBorder(new TitledBorder("\u811a\u672c\u914d\u7f6e")); 374 | 375 | //---- label8 ---- 376 | label8.setText("\u6267\u884c\u547d\u4ee4\uff1a"); 377 | 378 | //---- label7 ---- 379 | label7.setText("\u914d\u7f6e\u540d\u79f0\uff1a"); 380 | 381 | GroupLayout panel4Layout = new GroupLayout(panel4); 382 | panel4.setLayout(panel4Layout); 383 | panel4Layout.setHorizontalGroup( 384 | panel4Layout.createParallelGroup() 385 | .addGroup(panel4Layout.createSequentialGroup() 386 | .addContainerGap() 387 | .addGroup(panel4Layout.createParallelGroup() 388 | .addGroup(panel4Layout.createSequentialGroup() 389 | .addComponent(label8) 390 | .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 391 | .addComponent(watchCustom, GroupLayout.DEFAULT_SIZE, 442, Short.MAX_VALUE)) 392 | .addGroup(panel4Layout.createSequentialGroup() 393 | .addComponent(label7) 394 | .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) 395 | .addComponent(watchName, GroupLayout.DEFAULT_SIZE, 442, Short.MAX_VALUE))) 396 | .addContainerGap()) 397 | ); 398 | panel4Layout.setVerticalGroup( 399 | panel4Layout.createParallelGroup() 400 | .addGroup(panel4Layout.createSequentialGroup() 401 | .addGap(8, 8, 8) 402 | .addGroup(panel4Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 403 | .addComponent(label7) 404 | .addComponent(watchName, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) 405 | .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) 406 | .addGroup(panel4Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 407 | .addComponent(label8) 408 | .addComponent(watchCustom, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) 409 | .addGap(0, 3, Short.MAX_VALUE)) 410 | ); 411 | } 412 | 413 | //---- watchSave ---- 414 | watchSave.setText("\u4fdd\u5b58"); 415 | watchSave.addActionListener(e -> watchSave(e)); 416 | 417 | //---- watchDel ---- 418 | watchDel.setText("\u5220\u9664"); 419 | watchDel.addActionListener(e -> watchDel(e)); 420 | 421 | GroupLayout panel2Layout = new GroupLayout(panel2); 422 | panel2.setLayout(panel2Layout); 423 | panel2Layout.setHorizontalGroup( 424 | panel2Layout.createParallelGroup() 425 | .addGroup(panel2Layout.createSequentialGroup() 426 | .addContainerGap() 427 | .addGroup(panel2Layout.createParallelGroup() 428 | .addComponent(panel4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 429 | .addComponent(panel3, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 430 | .addGroup(panel2Layout.createSequentialGroup() 431 | .addComponent(watchSave) 432 | .addGap(18, 18, 18) 433 | .addComponent(watchDel) 434 | .addGap(0, 0, Short.MAX_VALUE))) 435 | .addContainerGap()) 436 | ); 437 | panel2Layout.setVerticalGroup( 438 | panel2Layout.createParallelGroup() 439 | .addGroup(GroupLayout.Alignment.TRAILING, panel2Layout.createSequentialGroup() 440 | .addComponent(panel4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) 441 | .addGap(18, 18, 18) 442 | .addComponent(panel3, GroupLayout.PREFERRED_SIZE, 254, GroupLayout.PREFERRED_SIZE) 443 | .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) 444 | .addGroup(panel2Layout.createParallelGroup(GroupLayout.Alignment.BASELINE) 445 | .addComponent(watchSave) 446 | .addComponent(watchDel)) 447 | .addContainerGap(26, Short.MAX_VALUE)) 448 | ); 449 | } 450 | splitPane1.setRightComponent(panel2); 451 | } 452 | panel1.add(splitPane1, BorderLayout.CENTER); 453 | } 454 | tabbedPane1.addTab("\u811a\u672c\u914d\u7f6e", panel1); 455 | 456 | //======== Setting ======== 457 | { 458 | Setting.setBorder(new EmptyBorder(20, 20, 20, 20)); 459 | Setting.setLayout(new BorderLayout()); 460 | 461 | //---- label1 ---- 462 | label1.setText("

CustomCrypto

burp\u81ea\u5b9a\u4e49\u52a0\u89e3\u5bc6\u63d2\u4ef6

Github: https://github.com/dreamncn/CustomCrypto

Site: https://ankio.net

Powered by Ankio 2023.

"); 463 | Setting.add(label1, BorderLayout.NORTH); 464 | } 465 | tabbedPane1.addTab("\u5173\u4e8e", Setting); 466 | } 467 | // JFormDesigner - End of component initialization //GEN-END:initComponents 468 | } 469 | 470 | private void initTable(){ 471 | 472 | watchPATCH.setSelected(false); 473 | watchDELETE.setSelected(false); 474 | watchGET.setSelected(false); 475 | watchPOST.setSelected(false); 476 | watchPUT.setSelected(false); 477 | watchName.setText(""); 478 | watchCustom.setText(""); 479 | watchReqBodyInclude.setText(""); 480 | watchReqHeadInclude.setText(""); 481 | watchUrlInclude.setText(""); 482 | watchUseRegex.setSelected(false); 483 | } 484 | private int select = -1; 485 | 486 | /** 487 | * 显示错误信息 488 | */ 489 | private void showMsg(String msg){ 490 | JOptionPane.showMessageDialog(null, msg, "错误", 491 | JOptionPane.ERROR_MESSAGE); 492 | } 493 | private void watchSave(ActionEvent e) { 494 | Rule rule = new Rule(); 495 | rule.command = watchCustom.getText(); 496 | rule.name = watchName.getText(); 497 | if(rule.name==null || rule.name.isEmpty()){ 498 | showMsg("必须填写脚本名称"); 499 | return; 500 | } 501 | rule.body = watchReqBodyInclude.getText(); 502 | rule.header = watchReqHeadInclude.getText(); 503 | rule.url = watchUrlInclude.getText(); 504 | rule.regex = watchUseRegex.isSelected(); 505 | ArrayList strings = new ArrayList<>(); 506 | if(watchPATCH.isSelected())strings.add("patch"); 507 | if(watchDELETE.isSelected())strings.add("delete"); 508 | if(watchGET.isSelected())strings.add("get"); 509 | if(watchPOST.isSelected())strings.add("post"); 510 | if(watchPUT.isSelected())strings.add("put"); 511 | rule.method = strings; 512 | if(select!=-1){ 513 | BurpExtender.rules.update(select,rule); 514 | }else{ 515 | BurpExtender.rules.add(rule); 516 | } 517 | select = -1; 518 | initData(); 519 | 520 | } 521 | 522 | private void watchDel(ActionEvent e) { 523 | if(select!=-1){ 524 | BurpExtender.rules.del(select); 525 | select = -1; 526 | initData(); 527 | } 528 | } 529 | 530 | 531 | // JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables 532 | // Generated using JFormDesigner Evaluation license - unknown 533 | private JTabbedPane tabbedPane1; 534 | private JPanel panel1; 535 | private JPanel panel6; 536 | private JPanel panel7; 537 | private JCheckBox autoRun; 538 | private JLabel label9; 539 | private JTextField watchRoot; 540 | private JButton watchImport; 541 | private JButton watchExport; 542 | private JSplitPane splitPane1; 543 | private JList watchList; 544 | private JPanel panel2; 545 | private JPanel panel3; 546 | private JTextField watchUrlInclude; 547 | private JTextField watchReqHeadInclude; 548 | private JTextField watchReqBodyInclude; 549 | private JCheckBox watchUseRegex; 550 | private JPanel panel5; 551 | private JCheckBox watchGET; 552 | private JCheckBox watchPOST; 553 | private JCheckBox watchPATCH; 554 | private JCheckBox watchPUT; 555 | private JCheckBox watchDELETE; 556 | private JLabel label2; 557 | private JLabel label3; 558 | private JLabel label4; 559 | private JPanel panel4; 560 | private JTextField watchCustom; 561 | private JLabel label8; 562 | private JLabel label7; 563 | private JTextField watchName; 564 | private JButton watchSave; 565 | private JButton watchDel; 566 | private JPanel Setting; 567 | private JLabel label1; 568 | // JFormDesigner - End of variables declaration //GEN-END:variables 569 | } 570 | --------------------------------------------------------------------------------