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,229 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Evidence-based analysis types and confidence levels.
|
|
3
|
+
|
|
4
|
+
This module provides data structures for tracking the evidence level
|
|
5
|
+
and confidence of analysis findings, preventing hallucination and
|
|
6
|
+
over-inference in automated reports.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from typing import Any, Optional
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EvidenceLevel(str, Enum):
|
|
16
|
+
"""Evidence level classification for analysis findings.
|
|
17
|
+
|
|
18
|
+
Based on professional SOC/IR standards:
|
|
19
|
+
- OBSERVED: Directly observed through dynamic analysis or tracing
|
|
20
|
+
- INFERRED: Logically inferred from static analysis (high confidence)
|
|
21
|
+
- POSSIBLE: Hypothesized based on patterns (requires verification)
|
|
22
|
+
"""
|
|
23
|
+
OBSERVED = "observed" # Directly observed (dynamic analysis, logs, traces)
|
|
24
|
+
INFERRED = "inferred" # Logically inferred from static analysis
|
|
25
|
+
POSSIBLE = "possible" # Possible but needs verification
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def symbol(self) -> str:
|
|
29
|
+
"""Return a symbol for display."""
|
|
30
|
+
return {
|
|
31
|
+
"observed": "🔍",
|
|
32
|
+
"inferred": "🔎",
|
|
33
|
+
"possible": "❓",
|
|
34
|
+
}[self.value]
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def confidence_score(self) -> float:
|
|
38
|
+
"""Return a confidence score (0.0-1.0)."""
|
|
39
|
+
return {
|
|
40
|
+
"observed": 1.0,
|
|
41
|
+
"inferred": 0.7,
|
|
42
|
+
"possible": 0.4,
|
|
43
|
+
}[self.value]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class MITREConfidence(str, Enum):
|
|
47
|
+
"""MITRE ATT&CK mapping confidence levels."""
|
|
48
|
+
CONFIRMED = "confirmed" # Multiple evidence sources
|
|
49
|
+
HIGH = "high" # Strong single evidence
|
|
50
|
+
MEDIUM = "medium" # Inferred from API/patterns
|
|
51
|
+
LOW = "low" # Possible based on behavior
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class Evidence:
|
|
56
|
+
"""Evidence record for a finding."""
|
|
57
|
+
source: str # e.g., "strings", "imports", "decompile", "sandbox"
|
|
58
|
+
location: str # e.g., "0x401000", "section:.rsrc", "log:procmon"
|
|
59
|
+
description: str # What was found
|
|
60
|
+
raw_data: Optional[str] = None # Raw evidence (hex, string, etc.)
|
|
61
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
62
|
+
|
|
63
|
+
def to_dict(self) -> dict[str, Any]:
|
|
64
|
+
return {
|
|
65
|
+
"source": self.source,
|
|
66
|
+
"location": self.location,
|
|
67
|
+
"description": self.description,
|
|
68
|
+
"raw_data": self.raw_data,
|
|
69
|
+
"timestamp": self.timestamp.isoformat(),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class Finding:
|
|
75
|
+
"""An analysis finding with evidence tracking."""
|
|
76
|
+
title: str
|
|
77
|
+
description: str
|
|
78
|
+
level: EvidenceLevel
|
|
79
|
+
category: str # e.g., "persistence", "encryption", "network"
|
|
80
|
+
evidence: list[Evidence] = field(default_factory=list)
|
|
81
|
+
mitre_techniques: list[str] = field(default_factory=list)
|
|
82
|
+
|
|
83
|
+
def add_evidence(self, source: str, location: str, description: str,
|
|
84
|
+
raw_data: Optional[str] = None) -> None:
|
|
85
|
+
"""Add evidence to this finding."""
|
|
86
|
+
self.evidence.append(Evidence(
|
|
87
|
+
source=source,
|
|
88
|
+
location=location,
|
|
89
|
+
description=description,
|
|
90
|
+
raw_data=raw_data,
|
|
91
|
+
))
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def confidence(self) -> float:
|
|
95
|
+
"""Calculate overall confidence based on evidence level and count."""
|
|
96
|
+
base = self.level.confidence_score
|
|
97
|
+
# More evidence = higher confidence (up to 20% boost)
|
|
98
|
+
evidence_boost = min(len(self.evidence) * 0.05, 0.2)
|
|
99
|
+
return min(base + evidence_boost, 1.0)
|
|
100
|
+
|
|
101
|
+
def to_dict(self) -> dict[str, Any]:
|
|
102
|
+
return {
|
|
103
|
+
"title": self.title,
|
|
104
|
+
"description": self.description,
|
|
105
|
+
"level": self.level.value,
|
|
106
|
+
"level_symbol": self.level.symbol,
|
|
107
|
+
"category": self.category,
|
|
108
|
+
"confidence": round(self.confidence, 2),
|
|
109
|
+
"evidence_count": len(self.evidence),
|
|
110
|
+
"evidence": [e.to_dict() for e in self.evidence],
|
|
111
|
+
"mitre_techniques": self.mitre_techniques,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
def format_markdown(self) -> str:
|
|
115
|
+
"""Format finding as markdown with evidence."""
|
|
116
|
+
lines = [
|
|
117
|
+
f"### {self.level.symbol} [{self.level.value.upper()}] {self.title}",
|
|
118
|
+
f"",
|
|
119
|
+
f"**Confidence**: {self.confidence:.0%}",
|
|
120
|
+
f"**Category**: {self.category}",
|
|
121
|
+
f"",
|
|
122
|
+
self.description,
|
|
123
|
+
f"",
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
if self.evidence:
|
|
127
|
+
lines.append("**Evidence:**")
|
|
128
|
+
for i, ev in enumerate(self.evidence, 1):
|
|
129
|
+
lines.append(f" {i}. `{ev.source}` @ `{ev.location}`: {ev.description}")
|
|
130
|
+
if ev.raw_data:
|
|
131
|
+
lines.append(f" ```")
|
|
132
|
+
lines.append(f" {ev.raw_data[:200]}{'...' if len(ev.raw_data) > 200 else ''}")
|
|
133
|
+
lines.append(f" ```")
|
|
134
|
+
|
|
135
|
+
if self.mitre_techniques:
|
|
136
|
+
lines.append(f"")
|
|
137
|
+
lines.append(f"**MITRE ATT&CK**: {', '.join(self.mitre_techniques)}")
|
|
138
|
+
|
|
139
|
+
return "\n".join(lines)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@dataclass
|
|
143
|
+
class MITRETechnique:
|
|
144
|
+
"""MITRE ATT&CK technique with confidence tracking."""
|
|
145
|
+
technique_id: str # e.g., "T1055"
|
|
146
|
+
technique_name: str
|
|
147
|
+
tactic: str
|
|
148
|
+
confidence: MITREConfidence
|
|
149
|
+
evidence: list[Evidence] = field(default_factory=list)
|
|
150
|
+
|
|
151
|
+
def to_dict(self) -> dict[str, Any]:
|
|
152
|
+
return {
|
|
153
|
+
"technique_id": self.technique_id,
|
|
154
|
+
"technique_name": self.technique_name,
|
|
155
|
+
"tactic": self.tactic,
|
|
156
|
+
"confidence": self.confidence.value,
|
|
157
|
+
"evidence_count": len(self.evidence),
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
def format_markdown_row(self) -> str:
|
|
161
|
+
"""Format as markdown table row."""
|
|
162
|
+
conf_symbol = {
|
|
163
|
+
"confirmed": "✅",
|
|
164
|
+
"high": "🟢",
|
|
165
|
+
"medium": "🟡",
|
|
166
|
+
"low": "🔴",
|
|
167
|
+
}[self.confidence.value]
|
|
168
|
+
|
|
169
|
+
return f"| {self.technique_id} | {self.technique_name} | {self.tactic} | {conf_symbol} {self.confidence.value} |"
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@dataclass
|
|
173
|
+
class AnalysisMetadata:
|
|
174
|
+
"""Unified metadata for analysis session (single source of truth)."""
|
|
175
|
+
session_id: str
|
|
176
|
+
sample_name: str
|
|
177
|
+
sample_hash: str # SHA256
|
|
178
|
+
start_time: datetime
|
|
179
|
+
end_time: Optional[datetime] = None
|
|
180
|
+
analyst: str = "Reversecore MCP"
|
|
181
|
+
tools_used: list[str] = field(default_factory=list)
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def duration_seconds(self) -> float:
|
|
185
|
+
"""Calculate analysis duration in seconds."""
|
|
186
|
+
if self.end_time:
|
|
187
|
+
return (self.end_time - self.start_time).total_seconds()
|
|
188
|
+
return (datetime.now() - self.start_time).total_seconds()
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def duration_formatted(self) -> str:
|
|
192
|
+
"""Return human-readable duration."""
|
|
193
|
+
seconds = self.duration_seconds
|
|
194
|
+
if seconds < 60:
|
|
195
|
+
return f"{seconds:.1f} seconds"
|
|
196
|
+
elif seconds < 3600:
|
|
197
|
+
return f"{seconds / 60:.1f} minutes"
|
|
198
|
+
else:
|
|
199
|
+
return f"{seconds / 3600:.1f} hours"
|
|
200
|
+
|
|
201
|
+
def to_dict(self) -> dict[str, Any]:
|
|
202
|
+
return {
|
|
203
|
+
"session_id": self.session_id,
|
|
204
|
+
"sample_name": self.sample_name,
|
|
205
|
+
"sample_hash": self.sample_hash,
|
|
206
|
+
"start_time": self.start_time.isoformat(),
|
|
207
|
+
"end_time": self.end_time.isoformat() if self.end_time else None,
|
|
208
|
+
"duration": self.duration_formatted,
|
|
209
|
+
"duration_seconds": round(self.duration_seconds, 2),
|
|
210
|
+
"analyst": self.analyst,
|
|
211
|
+
"tools_used": self.tools_used,
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# Helper functions for quick finding creation
|
|
216
|
+
def observed_finding(title: str, description: str, category: str, **kwargs) -> Finding:
|
|
217
|
+
"""Create an OBSERVED level finding."""
|
|
218
|
+
return Finding(title=title, description=description,
|
|
219
|
+
level=EvidenceLevel.OBSERVED, category=category, **kwargs)
|
|
220
|
+
|
|
221
|
+
def inferred_finding(title: str, description: str, category: str, **kwargs) -> Finding:
|
|
222
|
+
"""Create an INFERRED level finding."""
|
|
223
|
+
return Finding(title=title, description=description,
|
|
224
|
+
level=EvidenceLevel.INFERRED, category=category, **kwargs)
|
|
225
|
+
|
|
226
|
+
def possible_finding(title: str, description: str, category: str, **kwargs) -> Finding:
|
|
227
|
+
"""Create a POSSIBLE level finding."""
|
|
228
|
+
return Finding(title=title, description=description,
|
|
229
|
+
level=EvidenceLevel.POSSIBLE, category=category, **kwargs)
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom exception classes for Reversecore_MCP.
|
|
3
|
+
|
|
4
|
+
All exceptions inherit from ReversecoreError to allow for centralized
|
|
5
|
+
exception handling at the MCP server level.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ReversecoreError(Exception):
|
|
10
|
+
"""Base exception for all Reversecore_MCP errors."""
|
|
11
|
+
|
|
12
|
+
error_code: str = "RCMCP-E000"
|
|
13
|
+
error_type: str = "UNKNOWN_ERROR"
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
message: str,
|
|
18
|
+
error_code: str | None = None,
|
|
19
|
+
error_type: str | None = None,
|
|
20
|
+
):
|
|
21
|
+
self.message = message
|
|
22
|
+
if error_code:
|
|
23
|
+
self.error_code = error_code
|
|
24
|
+
if error_type:
|
|
25
|
+
self.error_type = error_type
|
|
26
|
+
super().__init__(message)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ToolNotFoundError(ReversecoreError):
|
|
30
|
+
"""Raised when a required CLI tool is not found in the system."""
|
|
31
|
+
|
|
32
|
+
error_code = "RCMCP-E003"
|
|
33
|
+
error_type = "TOOL_ERROR"
|
|
34
|
+
|
|
35
|
+
def __init__(self, tool_name: str):
|
|
36
|
+
self.tool_name = tool_name
|
|
37
|
+
message = f"Tool '{tool_name}' not found. Please install it."
|
|
38
|
+
super().__init__(message, self.error_code, self.error_type)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ExecutionTimeoutError(ReversecoreError):
|
|
42
|
+
"""Raised when a subprocess execution exceeds the timeout limit."""
|
|
43
|
+
|
|
44
|
+
error_code = "RCMCP-E002"
|
|
45
|
+
error_type = "TIMEOUT_ERROR"
|
|
46
|
+
|
|
47
|
+
def __init__(self, timeout_seconds: int):
|
|
48
|
+
self.timeout_seconds = timeout_seconds
|
|
49
|
+
message = f"Operation timed out after {timeout_seconds} seconds."
|
|
50
|
+
super().__init__(message, self.error_code, self.error_type)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class OutputLimitExceededError(ReversecoreError):
|
|
54
|
+
"""Raised when subprocess output exceeds the maximum allowed size."""
|
|
55
|
+
|
|
56
|
+
error_code = "RCMCP-E004"
|
|
57
|
+
error_type = "OUTPUT_ERROR"
|
|
58
|
+
|
|
59
|
+
def __init__(self, max_size: int, actual_size: int):
|
|
60
|
+
self.max_size = max_size
|
|
61
|
+
self.actual_size = actual_size
|
|
62
|
+
message = (
|
|
63
|
+
f"Output limit exceeded: {actual_size} bytes (max: {max_size} bytes). "
|
|
64
|
+
"Output has been truncated."
|
|
65
|
+
)
|
|
66
|
+
super().__init__(message, self.error_code, self.error_type)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ValidationError(ReversecoreError):
|
|
70
|
+
"""Raised when input validation fails."""
|
|
71
|
+
|
|
72
|
+
error_code = "RCMCP-E001"
|
|
73
|
+
error_type = "VALIDATION_ERROR"
|
|
74
|
+
|
|
75
|
+
def __init__(self, message: str, details: dict | None = None):
|
|
76
|
+
self.details = details or {}
|
|
77
|
+
super().__init__(message, self.error_code, self.error_type)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ToolExecutionError(ReversecoreError):
|
|
81
|
+
"""Raised when a tool execution fails."""
|
|
82
|
+
|
|
83
|
+
error_code = "RCMCP-E005"
|
|
84
|
+
error_type = "EXECUTION_ERROR"
|
|
85
|
+
|
|
86
|
+
def __init__(self, message: str):
|
|
87
|
+
super().__init__(message, self.error_code, self.error_type)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# ============================================================================
|
|
91
|
+
# Binary Analysis Exceptions
|
|
92
|
+
# ============================================================================
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class BinaryAnalysisError(ReversecoreError):
|
|
96
|
+
"""Base exception for binary analysis operations."""
|
|
97
|
+
|
|
98
|
+
error_code = "RCMCP-E100"
|
|
99
|
+
error_type = "BINARY_ANALYSIS_ERROR"
|
|
100
|
+
|
|
101
|
+
def __init__(
|
|
102
|
+
self,
|
|
103
|
+
message: str,
|
|
104
|
+
binary_path: str | None = None,
|
|
105
|
+
tool_name: str | None = None,
|
|
106
|
+
):
|
|
107
|
+
self.binary_path = binary_path
|
|
108
|
+
self.tool_name = tool_name
|
|
109
|
+
super().__init__(message, self.error_code, self.error_type)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class DecompilationError(BinaryAnalysisError):
|
|
113
|
+
"""Raised when decompilation of a function fails."""
|
|
114
|
+
|
|
115
|
+
error_code = "RCMCP-E101"
|
|
116
|
+
error_type = "DECOMPILATION_ERROR"
|
|
117
|
+
|
|
118
|
+
def __init__(
|
|
119
|
+
self,
|
|
120
|
+
message: str,
|
|
121
|
+
function_address: str | None = None,
|
|
122
|
+
binary_path: str | None = None,
|
|
123
|
+
tool_name: str = "ghidra",
|
|
124
|
+
):
|
|
125
|
+
self.function_address = function_address
|
|
126
|
+
detailed_message = message
|
|
127
|
+
if function_address:
|
|
128
|
+
detailed_message = f"Failed to decompile function at {function_address}: {message}"
|
|
129
|
+
super().__init__(detailed_message, binary_path, tool_name)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class DisassemblyError(BinaryAnalysisError):
|
|
133
|
+
"""Raised when disassembly of code fails."""
|
|
134
|
+
|
|
135
|
+
error_code = "RCMCP-E102"
|
|
136
|
+
error_type = "DISASSEMBLY_ERROR"
|
|
137
|
+
|
|
138
|
+
def __init__(
|
|
139
|
+
self,
|
|
140
|
+
message: str,
|
|
141
|
+
address: str | None = None,
|
|
142
|
+
binary_path: str | None = None,
|
|
143
|
+
):
|
|
144
|
+
self.address = address
|
|
145
|
+
detailed_message = message
|
|
146
|
+
if address:
|
|
147
|
+
detailed_message = f"Failed to disassemble at {address}: {message}"
|
|
148
|
+
super().__init__(detailed_message, binary_path, tool_name="radare2")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class StructureRecoveryError(BinaryAnalysisError):
|
|
152
|
+
"""Raised when structure recovery fails."""
|
|
153
|
+
|
|
154
|
+
error_code = "RCMCP-E103"
|
|
155
|
+
error_type = "STRUCTURE_RECOVERY_ERROR"
|
|
156
|
+
|
|
157
|
+
def __init__(
|
|
158
|
+
self,
|
|
159
|
+
message: str,
|
|
160
|
+
function_address: str | None = None,
|
|
161
|
+
binary_path: str | None = None,
|
|
162
|
+
):
|
|
163
|
+
self.function_address = function_address
|
|
164
|
+
super().__init__(message, binary_path, tool_name="ghidra")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class SignatureGenerationError(BinaryAnalysisError):
|
|
168
|
+
"""Raised when YARA signature generation fails."""
|
|
169
|
+
|
|
170
|
+
error_code = "RCMCP-E104"
|
|
171
|
+
error_type = "SIGNATURE_GENERATION_ERROR"
|
|
172
|
+
|
|
173
|
+
def __init__(
|
|
174
|
+
self,
|
|
175
|
+
message: str,
|
|
176
|
+
address: str | None = None,
|
|
177
|
+
binary_path: str | None = None,
|
|
178
|
+
):
|
|
179
|
+
self.address = address
|
|
180
|
+
super().__init__(message, binary_path, tool_name="radare2")
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class EmulationError(BinaryAnalysisError):
|
|
184
|
+
"""Raised when code emulation fails."""
|
|
185
|
+
|
|
186
|
+
error_code = "RCMCP-E105"
|
|
187
|
+
error_type = "EMULATION_ERROR"
|
|
188
|
+
|
|
189
|
+
def __init__(
|
|
190
|
+
self,
|
|
191
|
+
message: str,
|
|
192
|
+
start_address: str | None = None,
|
|
193
|
+
binary_path: str | None = None,
|
|
194
|
+
):
|
|
195
|
+
self.start_address = start_address
|
|
196
|
+
super().__init__(message, binary_path, tool_name="radare2")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ============================================================================
|
|
200
|
+
# Tool-Specific Exceptions
|
|
201
|
+
# ============================================================================
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class ToolTimeoutError(ReversecoreError):
|
|
205
|
+
"""Raised when a specific tool exceeds its timeout."""
|
|
206
|
+
|
|
207
|
+
error_code = "RCMCP-E200"
|
|
208
|
+
error_type = "TOOL_TIMEOUT_ERROR"
|
|
209
|
+
|
|
210
|
+
def __init__(
|
|
211
|
+
self,
|
|
212
|
+
tool_name: str,
|
|
213
|
+
timeout_seconds: int,
|
|
214
|
+
operation: str | None = None,
|
|
215
|
+
):
|
|
216
|
+
self.tool_name = tool_name
|
|
217
|
+
self.timeout_seconds = timeout_seconds
|
|
218
|
+
self.operation = operation
|
|
219
|
+
if operation:
|
|
220
|
+
message = f"{tool_name} timed out after {timeout_seconds}s during {operation}"
|
|
221
|
+
else:
|
|
222
|
+
message = f"{tool_name} timed out after {timeout_seconds} seconds"
|
|
223
|
+
super().__init__(message, self.error_code, self.error_type)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class GhidraConnectionError(ReversecoreError):
|
|
227
|
+
"""Raised when connection to Ghidra server fails."""
|
|
228
|
+
|
|
229
|
+
error_code = "RCMCP-E201"
|
|
230
|
+
error_type = "GHIDRA_CONNECTION_ERROR"
|
|
231
|
+
|
|
232
|
+
def __init__(self, message: str, host: str | None = None, port: int | None = None):
|
|
233
|
+
self.host = host
|
|
234
|
+
self.port = port
|
|
235
|
+
detailed_message = message
|
|
236
|
+
if host and port:
|
|
237
|
+
detailed_message = f"Failed to connect to Ghidra at {host}:{port}: {message}"
|
|
238
|
+
super().__init__(detailed_message, self.error_code, self.error_type)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class Radare2Error(ReversecoreError):
|
|
242
|
+
"""Raised when radare2 operation fails."""
|
|
243
|
+
|
|
244
|
+
error_code = "RCMCP-E202"
|
|
245
|
+
error_type = "RADARE2_ERROR"
|
|
246
|
+
|
|
247
|
+
def __init__(
|
|
248
|
+
self,
|
|
249
|
+
message: str,
|
|
250
|
+
command: str | None = None,
|
|
251
|
+
binary_path: str | None = None,
|
|
252
|
+
):
|
|
253
|
+
self.command = command
|
|
254
|
+
self.binary_path = binary_path
|
|
255
|
+
detailed_message = message
|
|
256
|
+
if command:
|
|
257
|
+
detailed_message = f"radare2 command '{command}' failed: {message}"
|
|
258
|
+
super().__init__(detailed_message, self.error_code, self.error_type)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# ============================================================================
|
|
262
|
+
# Workspace & Security Exceptions
|
|
263
|
+
# ============================================================================
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class WorkspaceError(ReversecoreError):
|
|
267
|
+
"""Raised when workspace operation fails."""
|
|
268
|
+
|
|
269
|
+
error_code = "RCMCP-E300"
|
|
270
|
+
error_type = "WORKSPACE_ERROR"
|
|
271
|
+
|
|
272
|
+
def __init__(self, message: str, path: str | None = None):
|
|
273
|
+
self.path = path
|
|
274
|
+
super().__init__(message, self.error_code, self.error_type)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class SecurityViolationError(ReversecoreError):
|
|
278
|
+
"""Raised when a security policy is violated."""
|
|
279
|
+
|
|
280
|
+
error_code = "RCMCP-E301"
|
|
281
|
+
error_type = "SECURITY_VIOLATION"
|
|
282
|
+
|
|
283
|
+
def __init__(self, message: str, attempted_path: str | None = None):
|
|
284
|
+
self.attempted_path = attempted_path
|
|
285
|
+
super().__init__(message, self.error_code, self.error_type)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class PathTraversalError(SecurityViolationError):
|
|
289
|
+
"""Raised when path traversal attack is detected."""
|
|
290
|
+
|
|
291
|
+
error_code = "RCMCP-E302"
|
|
292
|
+
error_type = "PATH_TRAVERSAL"
|
|
293
|
+
|
|
294
|
+
def __init__(self, attempted_path: str):
|
|
295
|
+
message = f"Path traversal detected: {attempted_path}"
|
|
296
|
+
super().__init__(message, attempted_path)
|