Files
HaE/src/main/java/hae/instances/http/utils/RegularMatcher.java

271 lines
11 KiB
Java
Raw Normal View History

2024-05-06 12:56:56 +08:00
package hae.instances.http.utils;
import burp.api.montoya.MontoyaApi;
2023-10-12 21:38:27 +08:00
import dk.brics.automaton.Automaton;
import dk.brics.automaton.AutomatonMatcher;
import dk.brics.automaton.RegExp;
import dk.brics.automaton.RunAutomaton;
2024-05-06 12:56:56 +08:00
import hae.Config;
import hae.cache.CachePool;
import hae.utils.string.HashCalculator;
import hae.utils.string.StringProcessor;
2024-02-02 19:07:03 +08:00
import java.text.MessageFormat;
2023-10-18 00:51:01 +08:00
import java.util.*;
2023-11-27 14:55:28 +08:00
import java.util.concurrent.ConcurrentHashMap;
2024-05-23 12:00:13 +08:00
import java.util.regex.Matcher;
import java.util.regex.Pattern;
2023-10-12 21:38:27 +08:00
2024-05-06 12:56:56 +08:00
public class RegularMatcher {
private final MontoyaApi api;
public RegularMatcher(MontoyaApi api) {
this.api = api;
2023-10-12 21:38:27 +08:00
}
2024-05-06 12:56:56 +08:00
public Map<String, Map<String, Object>> match(String host, String type, String message, String header, String body) {
2023-10-23 21:51:12 +08:00
// 先从缓存池里判断是否有已经匹配好的结果
2024-05-06 12:56:56 +08:00
String messageIndex = HashCalculator.calculateHash(message.getBytes());
2024-05-23 12:00:13 +08:00
Map<String, Map<String, Object>> map = CachePool.get(messageIndex);
2023-10-12 21:38:27 +08:00
if (map != null) {
return map;
} else {
// 最终返回的结果
Map<String, Map<String, Object>> finalMap = new HashMap<>();
2024-05-06 12:56:56 +08:00
Config.globalRules.keySet().parallelStream().forEach(i -> {
for (Object[] objects : Config.globalRules.get(i)) {
2023-10-12 21:38:27 +08:00
// 多线程执行,一定程度上减少阻塞现象
2023-11-07 11:15:20 +08:00
String matchContent = "";
// 遍历获取规则
List<String> result = new ArrayList<>();
Map<String, Object> tmpMap = new HashMap<>();
boolean loaded = (Boolean) objects[0];
2024-02-02 19:07:03 +08:00
String name = objects[1].toString();
String f_regex = objects[2].toString();
String s_regex = objects[3].toString();
String format = objects[4].toString();
String color = objects[5].toString();
String scope = objects[6].toString();
String engine = objects[7].toString();
boolean sensitive = (Boolean) objects[8];
2023-11-07 11:15:20 +08:00
// 判断规则是否开启与作用域
2024-05-06 12:56:56 +08:00
if (loaded && (scope.contains(type) || scope.contains("any") || type.equals("any"))) {
2023-11-07 11:15:20 +08:00
switch (scope) {
case "any":
case "request":
case "response":
2024-05-06 12:56:56 +08:00
matchContent = message;
2023-11-07 11:15:20 +08:00
break;
case "any header":
case "request header":
case "response header":
2024-05-06 12:56:56 +08:00
matchContent = header;
2023-11-07 11:15:20 +08:00
break;
case "any body":
case "request body":
case "response body":
2024-05-06 12:56:56 +08:00
matchContent = body;
2023-11-07 11:15:20 +08:00
break;
2024-05-09 13:32:22 +08:00
case "request line":
case "response line":
matchContent = message.split("\\r?\\n", 2)[0];
break;
2023-11-07 11:15:20 +08:00
default:
break;
}
2023-10-12 21:38:27 +08:00
2023-11-27 14:55:28 +08:00
try {
2024-02-02 19:07:03 +08:00
result.addAll(matchByRegex(f_regex, s_regex, matchContent, format, engine, sensitive));
2023-11-27 14:55:28 +08:00
} catch (Exception e) {
2024-05-06 12:56:56 +08:00
api.logging().logToError(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, f_regex));
2024-05-23 12:00:13 +08:00
api.logging().logToError(e.getMessage());
2023-11-27 14:55:28 +08:00
continue;
2023-11-07 11:15:20 +08:00
}
// 去除重复内容
HashSet tmpList = new HashSet(result);
result.clear();
result.addAll(tmpList);
String nameAndSize = String.format("%s (%s)", name, result.size());
if (!result.isEmpty()) {
tmpMap.put("color", color);
String dataStr = String.join("\n", result);
tmpMap.put("data", dataStr);
finalMap.put(nameAndSize, tmpMap);
// 添加到全局变量中便于Databoard检索
2023-11-16 19:33:38 +08:00
if (!Objects.equals(host, "") && host != null) {
2023-11-07 11:15:20 +08:00
List<String> dataList = Arrays.asList(dataStr.split("\n"));
2024-05-23 12:00:13 +08:00
Config.globalDataMap.compute(host, (existingHost, existingMap) -> {
Map<String, List<String>> gRuleMap = Optional.ofNullable(existingMap).orElse(new ConcurrentHashMap<>());
gRuleMap.merge(name, new ArrayList<>(dataList), (existingList, newList) -> {
Set<String> combinedSet = new LinkedHashSet<>(existingList);
combinedSet.addAll(newList);
return new ArrayList<>(combinedSet);
});
return gRuleMap;
});
2023-10-12 21:38:27 +08:00
2023-11-07 11:15:20 +08:00
String[] splitHost = host.split("\\.");
2024-05-06 12:56:56 +08:00
String onlyHost = host.split(":")[0];
2023-10-12 21:38:27 +08:00
2024-05-06 12:56:56 +08:00
String anyHost = (splitHost.length > 2 && !onlyHost.matches("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b")) ? StringProcessor.replaceFirstOccurrence(onlyHost, splitHost[0], "*") : "";
2023-10-12 21:38:27 +08:00
2024-05-06 12:56:56 +08:00
if (!Config.globalDataMap.containsKey(anyHost) && anyHost.length() > 0) {
2023-11-07 11:15:20 +08:00
// 添加通配符Host实际数据从查询哪里将所有数据提取
2024-05-06 12:56:56 +08:00
Config.globalDataMap.put(anyHost, new HashMap<>());
2023-11-07 11:15:20 +08:00
}
2023-10-12 21:38:27 +08:00
2024-05-06 12:56:56 +08:00
if (!Config.globalDataMap.containsKey("*")) {
2023-11-07 11:15:20 +08:00
// 添加通配符全匹配,同上
2024-05-06 12:56:56 +08:00
Config.globalDataMap.put("*", new HashMap<>());
2023-10-12 21:38:27 +08:00
}
}
}
}
}
});
2024-05-23 12:00:13 +08:00
CachePool.put(messageIndex, finalMap);
2023-10-12 21:38:27 +08:00
return finalMap;
}
2024-02-02 19:07:03 +08:00
}
private List<String> matchByRegex(String f_regex, String s_regex, String content, String format, String engine, boolean sensitive) {
List<String> retList = new ArrayList<>();
if ("nfa".equals(engine)) {
Matcher matcher = createPatternMatcher(f_regex, content, sensitive);
retList.addAll(extractMatches(s_regex, format, sensitive, matcher));
} else {
2024-03-22 15:34:14 +08:00
// DFA不支持格式化输出因此不关注format
2024-02-02 19:07:03 +08:00
String newContent = content;
String newFirstRegex = f_regex;
if (!sensitive) {
newContent = content.toLowerCase();
newFirstRegex = f_regex.toLowerCase();
}
AutomatonMatcher autoMatcher = createAutomatonMatcher(newFirstRegex, newContent);
2024-03-22 15:34:14 +08:00
retList.addAll(extractMatches(s_regex, autoMatcher, content));
2024-02-02 19:07:03 +08:00
}
return retList;
}
private List<String> extractMatches(String s_regex, String format, boolean sensitive, Matcher matcher) {
List<String> matches = new ArrayList<>();
if (s_regex.isEmpty()) {
matches.addAll(getFormatString(matcher, format));
} else {
while (matcher.find()) {
2024-03-22 15:34:14 +08:00
String matchContent = matcher.group(1);
if (!matchContent.isEmpty()) {
matcher = createPatternMatcher(s_regex, matchContent, sensitive);
matches.addAll(getFormatString(matcher, format));
}
2024-02-02 19:07:03 +08:00
}
}
return matches;
}
2024-03-22 15:34:14 +08:00
private List<String> extractMatches(String s_regex, AutomatonMatcher autoMatcher, String content) {
2024-02-02 19:07:03 +08:00
List<String> matches = new ArrayList<>();
if (s_regex.isEmpty()) {
2024-03-22 15:34:14 +08:00
matches.addAll(getFormatString(autoMatcher, content));
2024-02-02 19:07:03 +08:00
} else {
while (autoMatcher.find()) {
2024-03-22 15:34:14 +08:00
String s = autoMatcher.group();
if (!s.isEmpty()) {
autoMatcher = createAutomatonMatcher(s_regex, getSubString(content, s));
matches.addAll(getFormatString(autoMatcher, content));
}
2024-02-02 19:07:03 +08:00
}
}
return matches;
}
2024-05-06 12:56:56 +08:00
private List<String> getFormatString(Matcher matcher, String format) {
2024-02-02 19:07:03 +08:00
List<Integer> indexList = parseIndexesFromString(format);
List<String> stringList = new ArrayList<>();
while (matcher.find()) {
2024-03-22 15:34:14 +08:00
if (!matcher.group(1).isEmpty()) {
Object[] params = indexList.stream().map(i -> {
2024-05-12 19:02:38 +08:00
if (!matcher.group(i + 1).isEmpty()) {
return matcher.group(i + 1);
2024-03-22 15:34:14 +08:00
}
return "";
}).toArray();
stringList.add(MessageFormat.format(reorderIndex(format), params));
}
2024-02-02 19:07:03 +08:00
}
return stringList;
}
2024-05-06 12:56:56 +08:00
private List<String> getFormatString(AutomatonMatcher matcher, String content) {
2024-02-02 19:07:03 +08:00
List<String> stringList = new ArrayList<>();
while (matcher.find()) {
2024-03-22 15:34:14 +08:00
String s = matcher.group(0);
if (!s.isEmpty()) {
stringList.add(getSubString(content, s));
}
2024-02-02 19:07:03 +08:00
}
return stringList;
}
private Matcher createPatternMatcher(String regex, String content, boolean sensitive) {
2024-05-23 12:00:13 +08:00
Pattern pattern = sensitive ? Pattern.compile(regex) : Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
2024-02-02 19:07:03 +08:00
return pattern.matcher(content);
}
2023-10-12 21:38:27 +08:00
2024-02-02 19:07:03 +08:00
private AutomatonMatcher createAutomatonMatcher(String regex, String content) {
RegExp regexp = new RegExp(regex);
Automaton auto = regexp.toAutomaton();
RunAutomaton runAuto = new RunAutomaton(auto, true);
return runAuto.newMatcher(content);
2023-10-12 21:38:27 +08:00
}
2024-02-02 19:07:03 +08:00
private LinkedList<Integer> parseIndexesFromString(String input) {
LinkedList<Integer> indexes = new LinkedList<>();
2024-05-23 12:00:13 +08:00
Pattern pattern = Pattern.compile("\\{(\\d+)}");
2024-02-02 19:07:03 +08:00
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
2024-03-22 15:34:14 +08:00
String index = matcher.group(1);
if (!index.isEmpty()) {
indexes.add(Integer.valueOf(index));
}
2024-02-02 19:07:03 +08:00
}
return indexes;
}
private String getSubString(String content, String s) {
2024-05-06 12:56:56 +08:00
byte[] contentByte = api.utilities().byteUtils().convertFromString(content);
byte[] sByte = api.utilities().byteUtils().convertFromString(s);
int startIndex = api.utilities().byteUtils().indexOf(contentByte, sByte, false, 1, contentByte.length);
2024-02-02 19:07:03 +08:00
int endIndex = startIndex + s.length();
return content.substring(startIndex, endIndex);
}
private String reorderIndex(String format) {
2024-05-23 12:00:13 +08:00
Pattern pattern = Pattern.compile("\\{(\\d+)}");
2024-02-02 19:07:03 +08:00
Matcher matcher = pattern.matcher(format);
int count = 0;
while (matcher.find()) {
String newStr = String.format("{%s}", count);
String matchStr = matcher.group(0);
format = format.replace(matchStr, newStr);
count++;
}
return format;
}
}