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,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool definitions for Reversecore_MCP.
|
|
3
|
+
|
|
4
|
+
This package contains tool modules that wrap reverse engineering CLI tools
|
|
5
|
+
and libraries, making them accessible to AI agents through the MCP protocol.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Analysis tools
|
|
9
|
+
from reversecore_mcp.tools.analysis import diff_tools, lief_tools, signature_tools, static_analysis
|
|
10
|
+
|
|
11
|
+
# Common tools
|
|
12
|
+
from reversecore_mcp.tools.common import file_operations, patch_explainer
|
|
13
|
+
|
|
14
|
+
# Ghidra tools
|
|
15
|
+
from reversecore_mcp.tools.ghidra import decompilation
|
|
16
|
+
|
|
17
|
+
# Malware tools
|
|
18
|
+
from reversecore_mcp.tools.malware import adaptive_vaccine, dormant_detector, vulnerability_hunter
|
|
19
|
+
|
|
20
|
+
# Radare2 tools
|
|
21
|
+
from reversecore_mcp.tools.radare2 import r2_analysis
|
|
22
|
+
|
|
23
|
+
# Report tools
|
|
24
|
+
from reversecore_mcp.tools.report import report_mcp_tools, report_tools
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# Analysis tools
|
|
28
|
+
"static_analysis",
|
|
29
|
+
"diff_tools",
|
|
30
|
+
"signature_tools",
|
|
31
|
+
"lief_tools",
|
|
32
|
+
# Common tools
|
|
33
|
+
"file_operations",
|
|
34
|
+
"patch_explainer",
|
|
35
|
+
# Radare2 tools
|
|
36
|
+
"r2_analysis",
|
|
37
|
+
# Ghidra tools
|
|
38
|
+
"decompilation",
|
|
39
|
+
# Malware tools
|
|
40
|
+
"dormant_detector",
|
|
41
|
+
"adaptive_vaccine",
|
|
42
|
+
"vulnerability_hunter",
|
|
43
|
+
# Report tools
|
|
44
|
+
"report_tools",
|
|
45
|
+
"report_mcp_tools",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
# NOTE: Legacy alias 'ghost_trace' was removed in v1.0.0
|
|
49
|
+
# Use 'dormant_detector' directly
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Static analysis tools package.
|
|
2
|
+
|
|
3
|
+
Provides a unified AnalysisToolsPlugin that registers all analysis-related tools.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from reversecore_mcp.core.logging_config import get_logger
|
|
9
|
+
from reversecore_mcp.core.plugin import Plugin
|
|
10
|
+
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AnalysisToolsPlugin(Plugin):
|
|
15
|
+
"""Unified plugin for all static analysis tools."""
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def name(self) -> str:
|
|
19
|
+
return "analysis_tools"
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def description(self) -> str:
|
|
23
|
+
return "Unified static analysis tools including diffing, LIEF parsing, signature generation, and string extraction."
|
|
24
|
+
|
|
25
|
+
def register(self, mcp_server: Any) -> None:
|
|
26
|
+
"""Register all analysis tools."""
|
|
27
|
+
# Import tool functions from submodules
|
|
28
|
+
from reversecore_mcp.tools.analysis.capa_tools import (
|
|
29
|
+
run_capa,
|
|
30
|
+
run_capa_quick,
|
|
31
|
+
)
|
|
32
|
+
from reversecore_mcp.tools.analysis.die_tools import (
|
|
33
|
+
detect_packer,
|
|
34
|
+
detect_packer_deep,
|
|
35
|
+
)
|
|
36
|
+
from reversecore_mcp.tools.analysis.diff_tools import (
|
|
37
|
+
analyze_variant_changes,
|
|
38
|
+
diff_binaries,
|
|
39
|
+
match_libraries,
|
|
40
|
+
)
|
|
41
|
+
from reversecore_mcp.tools.analysis.lief_tools import parse_binary_with_lief
|
|
42
|
+
from reversecore_mcp.tools.analysis.signature_tools import (
|
|
43
|
+
generate_signature,
|
|
44
|
+
generate_yara_rule,
|
|
45
|
+
)
|
|
46
|
+
from reversecore_mcp.tools.analysis.static_analysis import (
|
|
47
|
+
extract_rtti_info,
|
|
48
|
+
run_binwalk,
|
|
49
|
+
run_binwalk_extract,
|
|
50
|
+
run_strings,
|
|
51
|
+
scan_for_versions,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Register all tools
|
|
55
|
+
mcp_server.tool(diff_binaries)
|
|
56
|
+
mcp_server.tool(analyze_variant_changes)
|
|
57
|
+
mcp_server.tool(match_libraries)
|
|
58
|
+
mcp_server.tool(parse_binary_with_lief)
|
|
59
|
+
mcp_server.tool(generate_signature)
|
|
60
|
+
mcp_server.tool(generate_yara_rule)
|
|
61
|
+
mcp_server.tool(run_strings)
|
|
62
|
+
mcp_server.tool(run_binwalk)
|
|
63
|
+
mcp_server.tool(run_binwalk_extract)
|
|
64
|
+
mcp_server.tool(scan_for_versions)
|
|
65
|
+
mcp_server.tool(extract_rtti_info)
|
|
66
|
+
mcp_server.tool(detect_packer)
|
|
67
|
+
mcp_server.tool(detect_packer_deep)
|
|
68
|
+
mcp_server.tool(run_capa)
|
|
69
|
+
mcp_server.tool(run_capa_quick)
|
|
70
|
+
|
|
71
|
+
logger.info(f"Registered {self.name} plugin with 15 analysis tools (unified)")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
__all__ = ["AnalysisToolsPlugin"]
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAPA integration for capability detection.
|
|
3
|
+
|
|
4
|
+
CAPA (by Mandiant FLARE) identifies capabilities in executable files,
|
|
5
|
+
providing high-level behavioral information like encryption, file deletion, etc.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from reversecore_mcp.core.decorators import log_execution
|
|
9
|
+
from reversecore_mcp.core.logging_config import get_logger
|
|
10
|
+
from reversecore_mcp.core.result import success, failure, ToolSuccess
|
|
11
|
+
from reversecore_mcp.core.security import validate_file_path
|
|
12
|
+
|
|
13
|
+
logger = get_logger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _is_capa_available() -> bool:
|
|
17
|
+
"""Check if CAPA library is available."""
|
|
18
|
+
try:
|
|
19
|
+
import capa # noqa: F401
|
|
20
|
+
|
|
21
|
+
return True
|
|
22
|
+
except ImportError:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@log_execution()
|
|
27
|
+
async def run_capa(file_path: str, output_format: str = "summary"):
|
|
28
|
+
"""
|
|
29
|
+
Analyze binary capabilities using CAPA (Mandiant FLARE).
|
|
30
|
+
|
|
31
|
+
CAPA identifies capabilities in executable files and provides high-level
|
|
32
|
+
behavioral information such as:
|
|
33
|
+
- "encrypt data using AES"
|
|
34
|
+
- "delete files"
|
|
35
|
+
- "communicate via HTTP"
|
|
36
|
+
- "create persistence via registry"
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
file_path: Path to the binary file to analyze
|
|
40
|
+
output_format: Output format - "summary" (default), "detailed", or "json"
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
ToolResult with capability analysis including:
|
|
44
|
+
- capabilities: List of detected capabilities
|
|
45
|
+
- mitre_attack: MITRE ATT&CK technique mappings
|
|
46
|
+
- mbc: Malware Behavior Catalog mappings
|
|
47
|
+
"""
|
|
48
|
+
validated_path = validate_file_path(file_path)
|
|
49
|
+
|
|
50
|
+
if not _is_capa_available():
|
|
51
|
+
return failure(
|
|
52
|
+
error_code="CAPA_NOT_INSTALLED",
|
|
53
|
+
message="CAPA is not installed. Install with: pip install flare-capa",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
import capa.loader
|
|
58
|
+
import capa.main
|
|
59
|
+
import capa.rules
|
|
60
|
+
|
|
61
|
+
# Get default rules path
|
|
62
|
+
rules_path = capa.main.get_default_root()
|
|
63
|
+
|
|
64
|
+
# Load rules
|
|
65
|
+
try:
|
|
66
|
+
rules, _ = capa.rules.get_rules([rules_path])
|
|
67
|
+
except Exception as e:
|
|
68
|
+
# Falls back to no rules if default path fails
|
|
69
|
+
logger.warning(f"Failed to load CAPA rules: {e}")
|
|
70
|
+
return failure(
|
|
71
|
+
error_code="CAPA_RULES_LOAD_FAILED",
|
|
72
|
+
message=f"Failed to load CAPA rules. Run: capa --update-rules\nError: {e}",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Analyze the file
|
|
76
|
+
try:
|
|
77
|
+
extractor = capa.loader.get_extractor(
|
|
78
|
+
str(validated_path),
|
|
79
|
+
"auto", # Auto-detect format
|
|
80
|
+
capa.main.BACKEND_VIV, # Use vivisect backend
|
|
81
|
+
[], # No signatures
|
|
82
|
+
False, # Disable progress
|
|
83
|
+
)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logger.error(f"CAPA failed to load file: {e}")
|
|
86
|
+
return failure(
|
|
87
|
+
error_code="CAPA_LOAD_FILE_FAILED",
|
|
88
|
+
message=f"CAPA cannot analyze this file: {e}",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Get capabilities
|
|
92
|
+
capabilities, counts = capa.main.find_capabilities(rules, extractor)
|
|
93
|
+
|
|
94
|
+
# Format results
|
|
95
|
+
result = {
|
|
96
|
+
"capabilities": [],
|
|
97
|
+
"mitre_attack": [],
|
|
98
|
+
"mbc": [],
|
|
99
|
+
"summary": {
|
|
100
|
+
"total_capabilities": len(capabilities),
|
|
101
|
+
"namespaces": {},
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for rule_name, matches in capabilities.items():
|
|
106
|
+
rule = rules[rule_name]
|
|
107
|
+
|
|
108
|
+
capability = {
|
|
109
|
+
"name": rule_name,
|
|
110
|
+
"namespace": rule.meta.get("namespace", ""),
|
|
111
|
+
"description": rule.meta.get("description", ""),
|
|
112
|
+
"scope": rule.meta.get("scope", "function"),
|
|
113
|
+
"match_count": len(matches),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# Extract MITRE ATT&CK
|
|
117
|
+
if "att&ck" in rule.meta:
|
|
118
|
+
for attack in rule.meta["att&ck"]:
|
|
119
|
+
if attack not in result["mitre_attack"]:
|
|
120
|
+
result["mitre_attack"].append(attack)
|
|
121
|
+
|
|
122
|
+
# Extract MBC (Malware Behavior Catalog)
|
|
123
|
+
if "mbc" in rule.meta:
|
|
124
|
+
for mbc in rule.meta["mbc"]:
|
|
125
|
+
if mbc not in result["mbc"]:
|
|
126
|
+
result["mbc"].append(mbc)
|
|
127
|
+
|
|
128
|
+
result["capabilities"].append(capability)
|
|
129
|
+
|
|
130
|
+
# Count by namespace
|
|
131
|
+
ns = capability["namespace"]
|
|
132
|
+
result["summary"]["namespaces"][ns] = result["summary"]["namespaces"].get(ns, 0) + 1
|
|
133
|
+
|
|
134
|
+
# Create summary message
|
|
135
|
+
high_risk_namespaces = [
|
|
136
|
+
"anti-analysis",
|
|
137
|
+
"collection",
|
|
138
|
+
"command-and-control",
|
|
139
|
+
"defense-evasion",
|
|
140
|
+
"exfiltration",
|
|
141
|
+
"impact",
|
|
142
|
+
"persistence",
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
high_risk_count = sum(
|
|
146
|
+
result["summary"]["namespaces"].get(ns, 0) for ns in high_risk_namespaces
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
message = f"Detected {len(capabilities)} capabilities"
|
|
150
|
+
if high_risk_count > 0:
|
|
151
|
+
message += f" ({high_risk_count} high-risk)"
|
|
152
|
+
if result["mitre_attack"]:
|
|
153
|
+
message += f", {len(result['mitre_attack'])} MITRE ATT&CK techniques"
|
|
154
|
+
|
|
155
|
+
return success(
|
|
156
|
+
data=result,
|
|
157
|
+
message=message,
|
|
158
|
+
high_risk_count=high_risk_count,
|
|
159
|
+
mitre_count=len(result["mitre_attack"]),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
except Exception as e:
|
|
163
|
+
logger.error(f"CAPA analysis failed: {e}")
|
|
164
|
+
return failure(error_code="CAPA_ANALYSIS_FAILED", message=f"CAPA analysis failed: {e}")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@log_execution()
|
|
168
|
+
async def run_capa_quick(file_path: str):
|
|
169
|
+
"""
|
|
170
|
+
Quick CAPA scan returning only high-risk capabilities.
|
|
171
|
+
|
|
172
|
+
Faster than full run_capa, focuses on:
|
|
173
|
+
- Anti-analysis techniques
|
|
174
|
+
- Persistence mechanisms
|
|
175
|
+
- C2 communication
|
|
176
|
+
- Data exfiltration
|
|
177
|
+
- Impact capabilities
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
file_path: Path to the binary file
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
ToolResult with high-risk capabilities only
|
|
184
|
+
"""
|
|
185
|
+
result = await run_capa(file_path)
|
|
186
|
+
|
|
187
|
+
if not isinstance(result, ToolSuccess):
|
|
188
|
+
return result
|
|
189
|
+
|
|
190
|
+
# Filter to high-risk only
|
|
191
|
+
high_risk_namespaces = {
|
|
192
|
+
"anti-analysis",
|
|
193
|
+
"collection",
|
|
194
|
+
"command-and-control",
|
|
195
|
+
"defense-evasion",
|
|
196
|
+
"exfiltration",
|
|
197
|
+
"impact",
|
|
198
|
+
"persistence",
|
|
199
|
+
"execution",
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
filtered_caps = [
|
|
203
|
+
cap
|
|
204
|
+
for cap in result.data["capabilities"]
|
|
205
|
+
if any(ns in cap["namespace"] for ns in high_risk_namespaces)
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
return success(
|
|
209
|
+
data={
|
|
210
|
+
"high_risk_capabilities": filtered_caps,
|
|
211
|
+
"mitre_attack": result.data["mitre_attack"],
|
|
212
|
+
"total_filtered": len(filtered_caps),
|
|
213
|
+
},
|
|
214
|
+
message=f"{len(filtered_caps)} high-risk capabilities detected",
|
|
215
|
+
)
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Detect It Easy (DIE) integration for packer/compiler detection.
|
|
3
|
+
|
|
4
|
+
Provides tools to identify binary file characteristics using the DIE CLI (diec).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import shutil
|
|
8
|
+
|
|
9
|
+
from reversecore_mcp.core.decorators import log_execution
|
|
10
|
+
from reversecore_mcp.core.execution import execute_subprocess_async
|
|
11
|
+
from reversecore_mcp.core.logging_config import get_logger
|
|
12
|
+
from reversecore_mcp.core.result import success, failure
|
|
13
|
+
from reversecore_mcp.core.security import validate_file_path
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _is_die_available() -> bool:
|
|
19
|
+
"""Check if Detect It Easy CLI (diec) is available."""
|
|
20
|
+
return shutil.which("diec") is not None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _parse_die_output(output: str) -> dict:
|
|
24
|
+
"""
|
|
25
|
+
Parse DIE output into structured data.
|
|
26
|
+
|
|
27
|
+
DIE output format example:
|
|
28
|
+
PE32
|
|
29
|
+
Compiler: Microsoft Visual C/C++(2019 v.16.0-4)[-]
|
|
30
|
+
Linker: Microsoft Linker(14.26.28805)[EXE32,console]
|
|
31
|
+
Packer: UPX(3.96)[NRV,brute]
|
|
32
|
+
"""
|
|
33
|
+
result = {
|
|
34
|
+
"file_type": None,
|
|
35
|
+
"arch": None,
|
|
36
|
+
"compiler": None,
|
|
37
|
+
"linker": None,
|
|
38
|
+
"packer": None,
|
|
39
|
+
"protector": None,
|
|
40
|
+
"installer": None,
|
|
41
|
+
"sfx": None,
|
|
42
|
+
"overlay": False,
|
|
43
|
+
"raw_output": output,
|
|
44
|
+
"detections": [],
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
lines = output.strip().split("\n")
|
|
48
|
+
|
|
49
|
+
for line in lines:
|
|
50
|
+
line = line.strip()
|
|
51
|
+
if not line:
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
# First line is usually the file type
|
|
55
|
+
if result["file_type"] is None and ":" not in line:
|
|
56
|
+
result["file_type"] = line
|
|
57
|
+
# Extract architecture from file type
|
|
58
|
+
if "PE32+" in line or "PE64" in line or "ELF64" in line:
|
|
59
|
+
result["arch"] = "x64"
|
|
60
|
+
elif "PE32" in line or "ELF32" in line:
|
|
61
|
+
result["arch"] = "x86"
|
|
62
|
+
elif "Mach-O" in line:
|
|
63
|
+
result["arch"] = "arm64" if "arm64" in line.lower() else "x64"
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
# Parse key: value lines
|
|
67
|
+
if ":" in line:
|
|
68
|
+
key, _, value = line.partition(":")
|
|
69
|
+
key = key.strip().lower()
|
|
70
|
+
value = value.strip()
|
|
71
|
+
|
|
72
|
+
if key == "compiler":
|
|
73
|
+
result["compiler"] = value
|
|
74
|
+
elif key == "linker":
|
|
75
|
+
result["linker"] = value
|
|
76
|
+
elif key == "packer":
|
|
77
|
+
result["packer"] = value
|
|
78
|
+
elif key == "protector":
|
|
79
|
+
result["protector"] = value
|
|
80
|
+
elif key == "installer":
|
|
81
|
+
result["installer"] = value
|
|
82
|
+
elif key == "sfx":
|
|
83
|
+
result["sfx"] = value
|
|
84
|
+
elif "overlay" in key:
|
|
85
|
+
result["overlay"] = True
|
|
86
|
+
|
|
87
|
+
result["detections"].append({"type": key, "value": value})
|
|
88
|
+
|
|
89
|
+
return result
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@log_execution()
|
|
93
|
+
async def detect_packer(file_path: str):
|
|
94
|
+
"""
|
|
95
|
+
Detect packer, compiler, and protector using Detect It Easy (DIE).
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
file_path: Path to the binary file to analyze
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
ToolResult with detection information including:
|
|
102
|
+
- file_type: PE32, PE64, ELF, Mach-O, etc.
|
|
103
|
+
- compiler: Detected compiler and version
|
|
104
|
+
- linker: Detected linker information
|
|
105
|
+
- packer: Detected packer (UPX, ASPack, etc.)
|
|
106
|
+
- protector: Detected protector (Themida, VMProtect, etc.)
|
|
107
|
+
"""
|
|
108
|
+
# Validate file path
|
|
109
|
+
validated_path = validate_file_path(file_path)
|
|
110
|
+
|
|
111
|
+
# Check if DIE is available
|
|
112
|
+
if not _is_die_available():
|
|
113
|
+
return failure(
|
|
114
|
+
error_code="DIE_NOT_INSTALLED",
|
|
115
|
+
message="Detect It Easy (diec) is not installed. "
|
|
116
|
+
"Install with: apt install detect-it-easy (Linux) or brew install detect-it-easy (macOS)",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Run DIE
|
|
120
|
+
try:
|
|
121
|
+
output, _ = await execute_subprocess_async(
|
|
122
|
+
["diec", str(validated_path)],
|
|
123
|
+
timeout_seconds=30,
|
|
124
|
+
)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
return failure(error_code="DIE_EXECUTION_ERROR", message=f"DIE execution failed: {e}")
|
|
127
|
+
|
|
128
|
+
# Parse output
|
|
129
|
+
result = _parse_die_output(output)
|
|
130
|
+
|
|
131
|
+
# Determine if packed
|
|
132
|
+
is_packed = result["packer"] is not None or result["protector"] is not None
|
|
133
|
+
|
|
134
|
+
return success(
|
|
135
|
+
data=result,
|
|
136
|
+
message=f"Detected: {result['file_type'] or 'Unknown'}"
|
|
137
|
+
+ (f" | Packer: {result['packer']}" if result["packer"] else "")
|
|
138
|
+
+ (f" | Compiler: {result['compiler']}" if result["compiler"] else ""),
|
|
139
|
+
is_packed=is_packed,
|
|
140
|
+
detection_count=len(result["detections"]),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@log_execution()
|
|
145
|
+
async def detect_packer_deep(file_path: str):
|
|
146
|
+
"""
|
|
147
|
+
Deep scan with DIE for more thorough detection.
|
|
148
|
+
|
|
149
|
+
Uses additional DIE options for deeper analysis.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
file_path: Path to the binary file to analyze
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
ToolResult with detailed detection information
|
|
156
|
+
"""
|
|
157
|
+
validated_path = validate_file_path(file_path)
|
|
158
|
+
|
|
159
|
+
if not _is_die_available():
|
|
160
|
+
return failure(
|
|
161
|
+
error_code="DIE_NOT_INSTALLED",
|
|
162
|
+
message="Detect It Easy (diec) is not installed.",
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
# Use deep scan option
|
|
167
|
+
output, _ = await execute_subprocess_async(
|
|
168
|
+
["diec", "-d", str(validated_path)],
|
|
169
|
+
timeout_seconds=60,
|
|
170
|
+
)
|
|
171
|
+
except Exception as e:
|
|
172
|
+
return failure(error_code="DIE_DEEP_SCAN_FAILED", message=f"DIE deep scan failed: {e}")
|
|
173
|
+
|
|
174
|
+
result = _parse_die_output(output)
|
|
175
|
+
|
|
176
|
+
return success(
|
|
177
|
+
data=result,
|
|
178
|
+
message=f"Deep scan complete: {len(result['detections'])} detections",
|
|
179
|
+
scan_type="deep",
|
|
180
|
+
)
|