iflow-mcp_developermode-korea_reversecore-mcp 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/METADATA +543 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/RECORD +79 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/WHEEL +5 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/licenses/LICENSE +21 -0
- iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/top_level.txt +1 -0
- reversecore_mcp/__init__.py +9 -0
- reversecore_mcp/core/__init__.py +78 -0
- reversecore_mcp/core/audit.py +101 -0
- reversecore_mcp/core/binary_cache.py +138 -0
- reversecore_mcp/core/command_spec.py +357 -0
- reversecore_mcp/core/config.py +432 -0
- reversecore_mcp/core/container.py +288 -0
- reversecore_mcp/core/decorators.py +152 -0
- reversecore_mcp/core/error_formatting.py +93 -0
- reversecore_mcp/core/error_handling.py +142 -0
- reversecore_mcp/core/evidence.py +229 -0
- reversecore_mcp/core/exceptions.py +296 -0
- reversecore_mcp/core/execution.py +240 -0
- reversecore_mcp/core/ghidra.py +642 -0
- reversecore_mcp/core/ghidra_helper.py +481 -0
- reversecore_mcp/core/ghidra_manager.py +234 -0
- reversecore_mcp/core/json_utils.py +131 -0
- reversecore_mcp/core/loader.py +73 -0
- reversecore_mcp/core/logging_config.py +206 -0
- reversecore_mcp/core/memory.py +721 -0
- reversecore_mcp/core/metrics.py +198 -0
- reversecore_mcp/core/mitre_mapper.py +365 -0
- reversecore_mcp/core/plugin.py +45 -0
- reversecore_mcp/core/r2_helpers.py +404 -0
- reversecore_mcp/core/r2_pool.py +403 -0
- reversecore_mcp/core/report_generator.py +268 -0
- reversecore_mcp/core/resilience.py +252 -0
- reversecore_mcp/core/resource_manager.py +169 -0
- reversecore_mcp/core/result.py +132 -0
- reversecore_mcp/core/security.py +213 -0
- reversecore_mcp/core/validators.py +238 -0
- reversecore_mcp/dashboard/__init__.py +221 -0
- reversecore_mcp/prompts/__init__.py +56 -0
- reversecore_mcp/prompts/common.py +24 -0
- reversecore_mcp/prompts/game.py +280 -0
- reversecore_mcp/prompts/malware.py +1219 -0
- reversecore_mcp/prompts/report.py +150 -0
- reversecore_mcp/prompts/security.py +136 -0
- reversecore_mcp/resources.py +329 -0
- reversecore_mcp/server.py +727 -0
- reversecore_mcp/tools/__init__.py +49 -0
- reversecore_mcp/tools/analysis/__init__.py +74 -0
- reversecore_mcp/tools/analysis/capa_tools.py +215 -0
- reversecore_mcp/tools/analysis/die_tools.py +180 -0
- reversecore_mcp/tools/analysis/diff_tools.py +643 -0
- reversecore_mcp/tools/analysis/lief_tools.py +272 -0
- reversecore_mcp/tools/analysis/signature_tools.py +591 -0
- reversecore_mcp/tools/analysis/static_analysis.py +479 -0
- reversecore_mcp/tools/common/__init__.py +58 -0
- reversecore_mcp/tools/common/file_operations.py +352 -0
- reversecore_mcp/tools/common/memory_tools.py +516 -0
- reversecore_mcp/tools/common/patch_explainer.py +230 -0
- reversecore_mcp/tools/common/server_tools.py +115 -0
- reversecore_mcp/tools/ghidra/__init__.py +19 -0
- reversecore_mcp/tools/ghidra/decompilation.py +975 -0
- reversecore_mcp/tools/ghidra/ghidra_tools.py +1052 -0
- reversecore_mcp/tools/malware/__init__.py +61 -0
- reversecore_mcp/tools/malware/adaptive_vaccine.py +579 -0
- reversecore_mcp/tools/malware/dormant_detector.py +756 -0
- reversecore_mcp/tools/malware/ioc_tools.py +228 -0
- reversecore_mcp/tools/malware/vulnerability_hunter.py +519 -0
- reversecore_mcp/tools/malware/yara_tools.py +214 -0
- reversecore_mcp/tools/patch_explainer.py +19 -0
- reversecore_mcp/tools/radare2/__init__.py +13 -0
- reversecore_mcp/tools/radare2/r2_analysis.py +972 -0
- reversecore_mcp/tools/radare2/r2_session.py +376 -0
- reversecore_mcp/tools/radare2/radare2_mcp_tools.py +1183 -0
- reversecore_mcp/tools/report/__init__.py +4 -0
- reversecore_mcp/tools/report/email.py +82 -0
- reversecore_mcp/tools/report/report_mcp_tools.py +344 -0
- reversecore_mcp/tools/report/report_tools.py +1076 -0
- reversecore_mcp/tools/report/session.py +194 -0
- reversecore_mcp/tools/report_tools.py +11 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""IOC (Indicators of Compromise) extraction tools using regex patterns."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
# Use high-performance JSON implementation (3-5x faster)
|
|
7
|
+
from reversecore_mcp.core import json_utils as json
|
|
8
|
+
from reversecore_mcp.core.decorators import log_execution
|
|
9
|
+
from reversecore_mcp.core.error_handling import handle_tool_errors
|
|
10
|
+
from reversecore_mcp.core.metrics import track_metrics
|
|
11
|
+
from reversecore_mcp.core.result import ToolResult, failure, success
|
|
12
|
+
|
|
13
|
+
# Pre-compile IOC extraction patterns for better performance
|
|
14
|
+
_IOC_IPV4_PATTERN = re.compile(
|
|
15
|
+
r"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"
|
|
16
|
+
)
|
|
17
|
+
_IOC_URL_PATTERN = re.compile(
|
|
18
|
+
r"https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)"
|
|
19
|
+
)
|
|
20
|
+
_IOC_EMAIL_PATTERN = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
|
|
21
|
+
_IOC_BITCOIN_PATTERN = re.compile(r"\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\b")
|
|
22
|
+
_IOC_MD5_PATTERN = re.compile(r"\b[0-9a-fA-F]{32}\b")
|
|
23
|
+
_IOC_SHA1_PATTERN = re.compile(r"\b[0-9a-fA-F]{40}\b")
|
|
24
|
+
_IOC_SHA256_PATTERN = re.compile(r"\b[0-9a-fA-F]{64}\b")
|
|
25
|
+
_IOC_CVE_PATTERN = re.compile(r"\bCVE-\d{4}-\d{4,7}\b")
|
|
26
|
+
_IOC_MAC_PATTERN = re.compile(r"\b(?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2})\b")
|
|
27
|
+
# Regex for common Registry hives (HKEY_...)
|
|
28
|
+
_IOC_REGISTRY_PATTERN = re.compile(
|
|
29
|
+
r"\b(?:HKEY_LOCAL_MACHINE|HKEY_CURRENT_USER|HKEY_CLASSES_ROOT|HKEY_USERS|HKEY_CURRENT_CONFIG|HKLM|HKCU|HKCR|HKU|HKCC)\\[\w\\_-]+\b",
|
|
30
|
+
re.IGNORECASE,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@log_execution(tool_name="extract_iocs")
|
|
35
|
+
@track_metrics("extract_iocs")
|
|
36
|
+
@handle_tool_errors
|
|
37
|
+
def extract_iocs(
|
|
38
|
+
text: str = "",
|
|
39
|
+
file_path: str = "", # Alternative: provide file path directly
|
|
40
|
+
extract_ips: bool = True,
|
|
41
|
+
extract_urls: bool = True,
|
|
42
|
+
extract_emails: bool = True,
|
|
43
|
+
extract_bitcoin: bool = True,
|
|
44
|
+
extract_hashes: bool = True,
|
|
45
|
+
extract_others: bool = True, # CVE, Registry, MAC
|
|
46
|
+
limit: int = 100,
|
|
47
|
+
) -> ToolResult:
|
|
48
|
+
"""
|
|
49
|
+
Extract Indicators of Compromise (IOCs) from text or file using regex.
|
|
50
|
+
|
|
51
|
+
This tool automatically finds and extracts potential IOCs like IP addresses,
|
|
52
|
+
URLs, and email addresses from any text input (e.g., strings output,
|
|
53
|
+
decompiled code, logs).
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
text: The text to analyze for IOCs (can also be a file path)
|
|
57
|
+
file_path: Alternative: path to a file to extract IOCs from
|
|
58
|
+
extract_ips: Whether to extract IPv4 addresses (default: True)
|
|
59
|
+
extract_urls: Whether to extract URLs (default: True)
|
|
60
|
+
extract_emails: Whether to extract email addresses (default: True)
|
|
61
|
+
extract_bitcoin: Whether to extract Bitcoin addresses (default: True)
|
|
62
|
+
extract_hashes: Whether to extract MD5/SHA1/SHA256 hashes (default: True)
|
|
63
|
+
extract_others: Whether to extract CVEs, Registry keys, MAC addresses (default: True)
|
|
64
|
+
limit: Maximum number of IOCs to return per category (default: 100)
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
ToolResult with extracted IOCs in structured format
|
|
68
|
+
"""
|
|
69
|
+
# If file_path is provided but text is empty, use file_path as text
|
|
70
|
+
if file_path and not text:
|
|
71
|
+
text = file_path
|
|
72
|
+
|
|
73
|
+
if not text:
|
|
74
|
+
return failure(
|
|
75
|
+
"MISSING_INPUT",
|
|
76
|
+
"Either 'text' or 'file_path' parameter is required.",
|
|
77
|
+
hint="Provide text content or a file path to extract IOCs from."
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
iocs = {}
|
|
81
|
+
total_count = 0
|
|
82
|
+
|
|
83
|
+
# Handle JSON input (e.g. ToolResult from another tool)
|
|
84
|
+
if text.strip().startswith("{") and text.strip().endswith("}"):
|
|
85
|
+
try:
|
|
86
|
+
data = json.loads(text)
|
|
87
|
+
# If it's a ToolResult structure, extract the 'data' or 'content'
|
|
88
|
+
if isinstance(data, dict):
|
|
89
|
+
if "data" in data:
|
|
90
|
+
# data can be string or dict
|
|
91
|
+
if isinstance(data["data"], str):
|
|
92
|
+
text = data["data"]
|
|
93
|
+
elif isinstance(data["data"], dict):
|
|
94
|
+
text = json.dumps(data["data"]) # Convert back to string for regex
|
|
95
|
+
elif "content" in data: # Legacy or other format
|
|
96
|
+
if isinstance(data["content"], list):
|
|
97
|
+
text = "\n".join(
|
|
98
|
+
[c.get("text", "") for c in data["content"] if isinstance(c, dict)]
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
text = str(data["content"])
|
|
102
|
+
except json.JSONDecodeError:
|
|
103
|
+
pass # Not valid JSON, treat as raw text
|
|
104
|
+
|
|
105
|
+
# Handle file paths: if text is a valid file path, read its content
|
|
106
|
+
# This handles cases where users pass a file path instead of content
|
|
107
|
+
if len(text) < 260 and os.path.exists(text) and os.path.isfile(text):
|
|
108
|
+
try:
|
|
109
|
+
# Read file content, but limit size to avoid memory issues
|
|
110
|
+
# 10MB limit for text analysis
|
|
111
|
+
if os.path.getsize(text) > 10 * 1024 * 1024:
|
|
112
|
+
return failure(
|
|
113
|
+
"FILE_TOO_LARGE",
|
|
114
|
+
f"File {text} is too large for regex analysis (>10MB).",
|
|
115
|
+
hint="Use 'run_strings' or 'grep' to filter content first.",
|
|
116
|
+
)
|
|
117
|
+
# Use buffered reading for better I/O performance on large files
|
|
118
|
+
# This reduces system calls and improves throughput
|
|
119
|
+
with open(text, encoding="utf-8", errors="ignore", buffering=8192) as f:
|
|
120
|
+
text = f.read()
|
|
121
|
+
except Exception as e:
|
|
122
|
+
return failure("FILE_READ_ERROR", f"Failed to read file: {str(e)}")
|
|
123
|
+
|
|
124
|
+
# Optimization: If text is very large (>100KB), pre-filter to avoid regex performance issues
|
|
125
|
+
# and ReDoS with large single-line files (e.g. minified JS, packed malware)
|
|
126
|
+
if len(text) > 100 * 1024:
|
|
127
|
+
# Check if we have very long lines (indication of minified/packed content)
|
|
128
|
+
# Using find() is much faster than split() for checking first newline
|
|
129
|
+
first_newline = text.find("\n")
|
|
130
|
+
if first_newline == -1 or first_newline > 2000:
|
|
131
|
+
# WARNING: Single HUGE line detected. split('\n') would double memory
|
|
132
|
+
# and regex will hang. Truncate arbitrarily for safety.
|
|
133
|
+
text = text[: 100 * 1024]
|
|
134
|
+
|
|
135
|
+
lines = text.split("\n")
|
|
136
|
+
# Keep lines that look like they might contain IOCs (dots, @, http)
|
|
137
|
+
# This is a rough heuristic to reduce data size before heavy regex
|
|
138
|
+
filtered_lines = [
|
|
139
|
+
line
|
|
140
|
+
for line in lines
|
|
141
|
+
if len(line) < 2000 and ("." in line or "@" in line or ":" in line)
|
|
142
|
+
]
|
|
143
|
+
# Limit to top 2000 suspicious lines to prevent memory issues
|
|
144
|
+
text = "\n".join(filtered_lines[:2000])
|
|
145
|
+
|
|
146
|
+
# IPv4 Regex - use pre-compiled pattern
|
|
147
|
+
if extract_ips:
|
|
148
|
+
ips = list(set(_IOC_IPV4_PATTERN.findall(text)))
|
|
149
|
+
if len(ips) > limit:
|
|
150
|
+
ips = ips[:limit]
|
|
151
|
+
iocs["ipv4"] = ips
|
|
152
|
+
total_count += len(ips)
|
|
153
|
+
|
|
154
|
+
# URL Regex - use pre-compiled pattern
|
|
155
|
+
if extract_urls:
|
|
156
|
+
raw_urls = _IOC_URL_PATTERN.findall(text)
|
|
157
|
+
# Use set comprehension for better performance
|
|
158
|
+
urls = list({url.rstrip(".,:;?!") for url in raw_urls})
|
|
159
|
+
if len(urls) > limit:
|
|
160
|
+
urls = urls[:limit]
|
|
161
|
+
iocs["urls"] = urls
|
|
162
|
+
total_count += len(urls)
|
|
163
|
+
|
|
164
|
+
# Email Regex - use pre-compiled pattern
|
|
165
|
+
if extract_emails:
|
|
166
|
+
emails = list(set(_IOC_EMAIL_PATTERN.findall(text)))
|
|
167
|
+
if len(emails) > limit:
|
|
168
|
+
emails = emails[:limit]
|
|
169
|
+
iocs["emails"] = emails
|
|
170
|
+
total_count += len(emails)
|
|
171
|
+
|
|
172
|
+
# Bitcoin Regex
|
|
173
|
+
if extract_bitcoin:
|
|
174
|
+
bitcoin_addresses = list(set(_IOC_BITCOIN_PATTERN.findall(text)))
|
|
175
|
+
if len(bitcoin_addresses) > limit:
|
|
176
|
+
bitcoin_addresses = bitcoin_addresses[:limit]
|
|
177
|
+
iocs["bitcoin_addresses"] = bitcoin_addresses
|
|
178
|
+
total_count += len(bitcoin_addresses)
|
|
179
|
+
|
|
180
|
+
# Hashes (MD5, SHA1, SHA256)
|
|
181
|
+
if extract_hashes:
|
|
182
|
+
# MD5
|
|
183
|
+
md5s = list(set(_IOC_MD5_PATTERN.findall(text)))
|
|
184
|
+
if len(md5s) > limit:
|
|
185
|
+
md5s = md5s[:limit]
|
|
186
|
+
iocs["md5"] = md5s
|
|
187
|
+
|
|
188
|
+
# SHA1
|
|
189
|
+
sha1s = list(set(_IOC_SHA1_PATTERN.findall(text)))
|
|
190
|
+
if len(sha1s) > limit:
|
|
191
|
+
sha1s = sha1s[:limit]
|
|
192
|
+
iocs["sha1"] = sha1s
|
|
193
|
+
|
|
194
|
+
# SHA256
|
|
195
|
+
sha256s = list(set(_IOC_SHA256_PATTERN.findall(text)))
|
|
196
|
+
if len(sha256s) > limit:
|
|
197
|
+
sha256s = sha256s[:limit]
|
|
198
|
+
iocs["sha256"] = sha256s
|
|
199
|
+
|
|
200
|
+
total_count += len(md5s) + len(sha1s) + len(sha256s)
|
|
201
|
+
|
|
202
|
+
# Other IOCs (CVE, Registry, MAC)
|
|
203
|
+
if extract_others:
|
|
204
|
+
# CVE
|
|
205
|
+
cves = list(set(_IOC_CVE_PATTERN.findall(text)))
|
|
206
|
+
if len(cves) > limit:
|
|
207
|
+
cves = cves[:limit]
|
|
208
|
+
iocs["cves"] = cves
|
|
209
|
+
|
|
210
|
+
# Registry Keys
|
|
211
|
+
registry_keys = list(set(_IOC_REGISTRY_PATTERN.findall(text)))
|
|
212
|
+
if len(registry_keys) > limit:
|
|
213
|
+
registry_keys = registry_keys[:limit]
|
|
214
|
+
iocs["registry_keys"] = registry_keys
|
|
215
|
+
|
|
216
|
+
# MAC Addresses
|
|
217
|
+
macs = list(set(_IOC_MAC_PATTERN.findall(text)))
|
|
218
|
+
if len(macs) > limit:
|
|
219
|
+
macs = macs[:limit]
|
|
220
|
+
iocs["mac_addresses"] = macs
|
|
221
|
+
|
|
222
|
+
total_count += len(cves) + len(registry_keys) + len(macs)
|
|
223
|
+
|
|
224
|
+
return success(
|
|
225
|
+
iocs,
|
|
226
|
+
ioc_count=total_count,
|
|
227
|
+
description=f"Extracted {total_count} IOCs from text (limit: {limit} per category)",
|
|
228
|
+
)
|