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.
Files changed (79) hide show
  1. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/METADATA +543 -0
  2. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/RECORD +79 -0
  3. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/WHEEL +5 -0
  4. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/entry_points.txt +2 -0
  5. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/licenses/LICENSE +21 -0
  6. iflow_mcp_developermode_korea_reversecore_mcp-1.0.0.dist-info/top_level.txt +1 -0
  7. reversecore_mcp/__init__.py +9 -0
  8. reversecore_mcp/core/__init__.py +78 -0
  9. reversecore_mcp/core/audit.py +101 -0
  10. reversecore_mcp/core/binary_cache.py +138 -0
  11. reversecore_mcp/core/command_spec.py +357 -0
  12. reversecore_mcp/core/config.py +432 -0
  13. reversecore_mcp/core/container.py +288 -0
  14. reversecore_mcp/core/decorators.py +152 -0
  15. reversecore_mcp/core/error_formatting.py +93 -0
  16. reversecore_mcp/core/error_handling.py +142 -0
  17. reversecore_mcp/core/evidence.py +229 -0
  18. reversecore_mcp/core/exceptions.py +296 -0
  19. reversecore_mcp/core/execution.py +240 -0
  20. reversecore_mcp/core/ghidra.py +642 -0
  21. reversecore_mcp/core/ghidra_helper.py +481 -0
  22. reversecore_mcp/core/ghidra_manager.py +234 -0
  23. reversecore_mcp/core/json_utils.py +131 -0
  24. reversecore_mcp/core/loader.py +73 -0
  25. reversecore_mcp/core/logging_config.py +206 -0
  26. reversecore_mcp/core/memory.py +721 -0
  27. reversecore_mcp/core/metrics.py +198 -0
  28. reversecore_mcp/core/mitre_mapper.py +365 -0
  29. reversecore_mcp/core/plugin.py +45 -0
  30. reversecore_mcp/core/r2_helpers.py +404 -0
  31. reversecore_mcp/core/r2_pool.py +403 -0
  32. reversecore_mcp/core/report_generator.py +268 -0
  33. reversecore_mcp/core/resilience.py +252 -0
  34. reversecore_mcp/core/resource_manager.py +169 -0
  35. reversecore_mcp/core/result.py +132 -0
  36. reversecore_mcp/core/security.py +213 -0
  37. reversecore_mcp/core/validators.py +238 -0
  38. reversecore_mcp/dashboard/__init__.py +221 -0
  39. reversecore_mcp/prompts/__init__.py +56 -0
  40. reversecore_mcp/prompts/common.py +24 -0
  41. reversecore_mcp/prompts/game.py +280 -0
  42. reversecore_mcp/prompts/malware.py +1219 -0
  43. reversecore_mcp/prompts/report.py +150 -0
  44. reversecore_mcp/prompts/security.py +136 -0
  45. reversecore_mcp/resources.py +329 -0
  46. reversecore_mcp/server.py +727 -0
  47. reversecore_mcp/tools/__init__.py +49 -0
  48. reversecore_mcp/tools/analysis/__init__.py +74 -0
  49. reversecore_mcp/tools/analysis/capa_tools.py +215 -0
  50. reversecore_mcp/tools/analysis/die_tools.py +180 -0
  51. reversecore_mcp/tools/analysis/diff_tools.py +643 -0
  52. reversecore_mcp/tools/analysis/lief_tools.py +272 -0
  53. reversecore_mcp/tools/analysis/signature_tools.py +591 -0
  54. reversecore_mcp/tools/analysis/static_analysis.py +479 -0
  55. reversecore_mcp/tools/common/__init__.py +58 -0
  56. reversecore_mcp/tools/common/file_operations.py +352 -0
  57. reversecore_mcp/tools/common/memory_tools.py +516 -0
  58. reversecore_mcp/tools/common/patch_explainer.py +230 -0
  59. reversecore_mcp/tools/common/server_tools.py +115 -0
  60. reversecore_mcp/tools/ghidra/__init__.py +19 -0
  61. reversecore_mcp/tools/ghidra/decompilation.py +975 -0
  62. reversecore_mcp/tools/ghidra/ghidra_tools.py +1052 -0
  63. reversecore_mcp/tools/malware/__init__.py +61 -0
  64. reversecore_mcp/tools/malware/adaptive_vaccine.py +579 -0
  65. reversecore_mcp/tools/malware/dormant_detector.py +756 -0
  66. reversecore_mcp/tools/malware/ioc_tools.py +228 -0
  67. reversecore_mcp/tools/malware/vulnerability_hunter.py +519 -0
  68. reversecore_mcp/tools/malware/yara_tools.py +214 -0
  69. reversecore_mcp/tools/patch_explainer.py +19 -0
  70. reversecore_mcp/tools/radare2/__init__.py +13 -0
  71. reversecore_mcp/tools/radare2/r2_analysis.py +972 -0
  72. reversecore_mcp/tools/radare2/r2_session.py +376 -0
  73. reversecore_mcp/tools/radare2/radare2_mcp_tools.py +1183 -0
  74. reversecore_mcp/tools/report/__init__.py +4 -0
  75. reversecore_mcp/tools/report/email.py +82 -0
  76. reversecore_mcp/tools/report/report_mcp_tools.py +344 -0
  77. reversecore_mcp/tools/report/report_tools.py +1076 -0
  78. reversecore_mcp/tools/report/session.py +194 -0
  79. reversecore_mcp/tools/report_tools.py +11 -0
@@ -0,0 +1,591 @@
1
+ """Signature generation tools for creating YARA rules and binary signatures."""
2
+
3
+ import re
4
+ from functools import lru_cache
5
+ from pathlib import Path
6
+
7
+ from reversecore_mcp.core.config import get_config
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.execution import execute_subprocess_async
11
+ from reversecore_mcp.core.logging_config import get_logger
12
+ from reversecore_mcp.core.metrics import track_metrics
13
+ from reversecore_mcp.core.r2_helpers import (
14
+ build_r2_cmd as _build_r2_cmd,
15
+ )
16
+
17
+ # Import shared R2 helper functions from core (avoids circular dependencies)
18
+ from reversecore_mcp.core.r2_helpers import (
19
+ execute_r2_command as _execute_r2_command,
20
+ )
21
+ from reversecore_mcp.core.r2_helpers import (
22
+ parse_json_output as _parse_json_output,
23
+ )
24
+ from reversecore_mcp.core.result import ToolResult, failure, success
25
+ from reversecore_mcp.core.security import validate_file_path
26
+ from reversecore_mcp.core.validators import validate_tool_parameters
27
+
28
+ # Load default timeout from configuration
29
+ DEFAULT_TIMEOUT = get_config().default_tool_timeout
30
+
31
+ logger = get_logger(__name__)
32
+
33
+ # OPTIMIZATION: Character translation table for filename sanitization
34
+ _FILENAME_SANITIZE_TRANS = str.maketrans({"-": "_", ".": "_"})
35
+
36
+ # OPTIMIZATION: Pre-compile regex patterns used in hot paths
37
+ # Note: _HEX_PATTERN is used for both addresses and byte sequences
38
+ _HEX_PATTERN = re.compile(r"^[0-9a-fA-F]+$")
39
+ _ALL_FF_PATTERN = re.compile(r"^(ff)+$", re.IGNORECASE)
40
+ _ALL_00_PATTERN = re.compile(r"^(00)+$")
41
+ _RULE_NAME_PATTERN = re.compile(r"^[a-zA-Z][a-zA-Z0-9_]*$")
42
+
43
+
44
+ def _validate_address_or_fail(address: str, param_name: str = "address"):
45
+ """
46
+ Validate address format and return failure ToolResult if invalid.
47
+
48
+ Args:
49
+ address: Address string to validate
50
+ param_name: Parameter name for error messages
51
+
52
+ Returns:
53
+ None if validation passes, or ToolResult failure if invalid
54
+ """
55
+ from reversecore_mcp.core.exceptions import ValidationError
56
+ from reversecore_mcp.core.validators import validate_address_format
57
+
58
+ try:
59
+ validate_address_format(address, param_name)
60
+ return None # Validation passed
61
+ except ValidationError as e:
62
+ return failure("VALIDATION_ERROR", str(e))
63
+
64
+
65
+ def _format_hex_bytes(hex_string: str) -> str:
66
+ """
67
+ Efficiently format hex string as space-separated byte pairs.
68
+
69
+ Optimized to avoid intermediate list creation by using a generator.
70
+
71
+ Args:
72
+ hex_string: Hex string without spaces (e.g., "4883ec20")
73
+
74
+ Returns:
75
+ Space-separated hex bytes (e.g., "48 83 ec 20")
76
+ """
77
+ # Use generator expression to avoid creating intermediate list
78
+ return " ".join(hex_string[i : i + 2] for i in range(0, len(hex_string), 2))
79
+
80
+
81
+ @lru_cache(maxsize=128)
82
+ def _sanitize_filename_for_rule(file_path: str) -> str:
83
+ """
84
+ Extract and sanitize filename for use in YARA rule names.
85
+
86
+ Cached to avoid repeated Path operations and string replacements.
87
+
88
+ Args:
89
+ file_path: Path to the file
90
+
91
+ Returns:
92
+ Sanitized filename with special characters replaced
93
+ """
94
+ # OPTIMIZATION: Use str.translate() instead of chained replace()
95
+ return Path(file_path).stem.translate(_FILENAME_SANITIZE_TRANS)
96
+
97
+
98
+ @log_execution(tool_name="generate_signature")
99
+ @track_metrics("generate_signature")
100
+ @handle_tool_errors
101
+ async def generate_signature(
102
+ file_path: str,
103
+ address: str,
104
+ length: int = 32,
105
+ timeout: int = DEFAULT_TIMEOUT,
106
+ ) -> ToolResult:
107
+ """
108
+ Generate a YARA signature from opcode bytes at a specific address.
109
+
110
+ This tool extracts opcode bytes from a function or code section and formats
111
+ them as a YARA rule, enabling automated malware detection. It attempts to
112
+ mask variable values (addresses, offsets) to create more flexible signatures.
113
+
114
+ **Use Cases:**
115
+ - Generate detection signatures for malware samples
116
+ - Create YARA rules for threat hunting
117
+ - Automate IOC (Indicator of Compromise) generation
118
+ - Build malware family signatures
119
+
120
+ **Workflow:**
121
+ 1. Extract opcode bytes from specified address
122
+ 2. Apply basic masking for variable values (optional)
123
+ 3. Format as YARA rule template
124
+ 4. Return ready-to-use YARA rule
125
+
126
+ Args:
127
+ file_path: Path to the binary file (must be in workspace)
128
+ address: Start address for signature extraction (e.g., 'main', '0x401000')
129
+ length: Number of bytes to extract (default 32, recommended 16-64)
130
+ timeout: Execution timeout in seconds (default 300)
131
+
132
+ Returns:
133
+ ToolResult with YARA rule string
134
+
135
+ Example:
136
+ generate_signature("/app/workspace/malware.exe", "0x401000", 48)
137
+ # Returns a YARA rule with extracted byte pattern
138
+ """
139
+ # 1. Validate parameters
140
+ validated_path = validate_file_path(file_path)
141
+
142
+ if not isinstance(length, int) or length < 1 or length > 1024:
143
+ return failure(
144
+ "VALIDATION_ERROR",
145
+ "Length must be between 1 and 1024 bytes",
146
+ hint="Typical signature lengths are 16-64 bytes for good detection accuracy",
147
+ )
148
+
149
+ # 2. Security check for address
150
+ validation_error = _validate_address_or_fail(address, "address")
151
+ if validation_error:
152
+ return validation_error
153
+
154
+ # 3. Extract hex bytes using radare2's p8 command
155
+ r2_cmds = [
156
+ f"s {address}", # Seek to address
157
+ f"p8 {length}", # Print hex bytes
158
+ ]
159
+
160
+ # Adaptive analysis: if address is hex, we don't need full analysis
161
+ analysis_level = ""
162
+ # OPTIMIZATION: Use pre-compiled regex pattern (faster)
163
+ if address.startswith("0x") or _HEX_PATTERN.match(address):
164
+ analysis_level = "-n"
165
+
166
+ # Extract hex bytes using helper
167
+ # Note: analysis_level may be "" (empty) which means default r2 behavior (parse headers/symbols)
168
+ output, bytes_read = await _execute_r2_command(
169
+ validated_path,
170
+ r2_cmds,
171
+ analysis_level=analysis_level or "aaa",
172
+ max_output_size=1_000_000,
173
+ base_timeout=timeout,
174
+ )
175
+
176
+ # 4. Validate output
177
+ hex_bytes = output.strip()
178
+ # OPTIMIZATION: Use pre-compiled regex pattern (faster)
179
+ if not hex_bytes or not _HEX_PATTERN.match(hex_bytes):
180
+ return failure(
181
+ "SIGNATURE_ERROR",
182
+ f"Failed to extract valid hex bytes from address: {address}",
183
+ hint="Verify the address is valid and contains executable code",
184
+ )
185
+
186
+ # Check for all 0xFF or 0x00 (likely unmapped memory)
187
+ # OPTIMIZATION: Use pre-compiled regex patterns (faster)
188
+ if _ALL_FF_PATTERN.match(hex_bytes) or _ALL_00_PATTERN.match(hex_bytes):
189
+ # If we used -n, try again without it to force mapping
190
+ if analysis_level == "-n":
191
+ from reversecore_mcp.core.r2_helpers import calculate_dynamic_timeout
192
+
193
+ effective_timeout = calculate_dynamic_timeout(str(validated_path))
194
+ cmd = _build_r2_cmd(str(validated_path), r2_cmds, "aaa")
195
+ output, _ = await execute_subprocess_async(
196
+ cmd,
197
+ max_output_size=1_000_000,
198
+ timeout=effective_timeout,
199
+ )
200
+ hex_bytes = output.strip()
201
+
202
+ # Re-check
203
+ # OPTIMIZATION: Use pre-compiled regex patterns (faster)
204
+ if _ALL_FF_PATTERN.match(hex_bytes) or _ALL_00_PATTERN.match(hex_bytes):
205
+ return failure(
206
+ "SIGNATURE_ERROR",
207
+ f"Extracted bytes are all 0xFF or 0x00 at {address}. The memory might be unmapped or empty.",
208
+ hint="Try a different address or ensure the binary is loaded correctly.",
209
+ )
210
+ else:
211
+ return failure(
212
+ "SIGNATURE_ERROR",
213
+ f"Extracted bytes are all 0xFF or 0x00 at {address}. The memory might be unmapped or empty.",
214
+ hint="Try a different address or ensure the binary is loaded correctly.",
215
+ )
216
+
217
+ # 5. Format as YARA hex string (space-separated pairs)
218
+ # Convert: "4883ec20" -> "48 83 ec 20"
219
+ # OPTIMIZED: Use generator expression to avoid intermediate list
220
+ formatted_bytes = _format_hex_bytes(hex_bytes)
221
+
222
+ # 6. Generate YARA rule template
223
+ # Extract filename for rule name using cached helper
224
+ file_name = _sanitize_filename_for_rule(file_path)
225
+ rule_name = f"suspicious_{file_name}_{address.replace('0x', 'x')}"
226
+
227
+ yara_rule = f"""rule {rule_name} {{
228
+ meta:
229
+ description = "Auto-generated signature for {file_name}"
230
+ address = "{address}"
231
+ length = {length}
232
+ author = "Reversecore_MCP"
233
+ date = "auto-generated"
234
+
235
+ strings:
236
+ $code = {{ {formatted_bytes} }}
237
+
238
+ condition:
239
+ $code
240
+ }}"""
241
+
242
+ # 7. Return YARA rule
243
+ return success(
244
+ yara_rule,
245
+ bytes_read=bytes_read,
246
+ address=address,
247
+ length=length,
248
+ format="yara",
249
+ hex_bytes=formatted_bytes,
250
+ description=f"YARA signature generated from {length} bytes at {address}",
251
+ )
252
+
253
+
254
+ @log_execution(tool_name="generate_yara_rule")
255
+ @track_metrics("generate_yara_rule")
256
+ @handle_tool_errors
257
+ async def generate_yara_rule(
258
+ file_path: str,
259
+ function_address: str,
260
+ rule_name: str = "auto_generated_rule",
261
+ byte_length: int = 64,
262
+ timeout: int = 300,
263
+ ) -> ToolResult:
264
+ """
265
+ Generate a YARA rule from function bytes.
266
+
267
+ This tool extracts bytes from a function and generates a ready-to-use
268
+ YARA rule for malware detection and threat hunting.
269
+
270
+ Args:
271
+ file_path: Path to the binary file (must be in workspace)
272
+ function_address: Function address to extract bytes from (e.g., 'main', '0x401000')
273
+ rule_name: Name for the YARA rule (default 'auto_generated_rule')
274
+ byte_length: Number of bytes to extract (default 64, max 1024)
275
+ timeout: Execution timeout in seconds (default 300)
276
+
277
+ Returns:
278
+ ToolResult with YARA rule string
279
+ """
280
+ # 1. Validate parameters
281
+ validate_tool_parameters(
282
+ "generate_yara_rule",
283
+ {
284
+ "function_address": function_address,
285
+ "rule_name": rule_name,
286
+ "byte_length": byte_length,
287
+ },
288
+ )
289
+ validated_path = validate_file_path(file_path)
290
+
291
+ # 2. Validate rule_name format
292
+ if not _RULE_NAME_PATTERN.match(rule_name):
293
+ return failure(
294
+ "VALIDATION_ERROR",
295
+ "rule_name must start with a letter and contain only alphanumeric characters and underscores",
296
+ )
297
+
298
+ # 3. Security check for function address (prevent shell injection)
299
+ validation_error = _validate_address_or_fail(function_address, "function_address")
300
+ if validation_error:
301
+ return validation_error
302
+
303
+ # 4. Extract hex bytes using radare2's p8 command
304
+ r2_cmds = [
305
+ f"s {function_address}", # Seek to address
306
+ f"p8 {byte_length}", # Print hex bytes
307
+ ]
308
+
309
+ # Always use -n for p8 to avoid analysis hang
310
+ analysis_level = "-n"
311
+
312
+ # 4. Extract hex bytes using helper
313
+ output, bytes_read = await _execute_r2_command(
314
+ validated_path,
315
+ r2_cmds,
316
+ analysis_level=analysis_level,
317
+ max_output_size=1_000_000,
318
+ base_timeout=timeout,
319
+ )
320
+
321
+ # 5. Validate output
322
+ hex_bytes = output.strip()
323
+ if not hex_bytes or not _HEX_PATTERN.match(hex_bytes):
324
+ return failure(
325
+ "YARA_GENERATION_ERROR",
326
+ f"Failed to extract valid hex bytes from address: {function_address}",
327
+ hint="Verify the address is valid and contains executable code",
328
+ )
329
+
330
+ # Check for invalid patterns (all 00 or all FF)
331
+ if len(hex_bytes) > 16 and (
332
+ _ALL_00_PATTERN.match(hex_bytes) or _ALL_FF_PATTERN.match(hex_bytes)
333
+ ):
334
+ # Smart Offset Search: Try to find a better address
335
+ # 1. Try 'main' if we weren't already there
336
+ # 2. Try entry point 'entry0'
337
+ # 3. Find largest function
338
+
339
+ logger.info(f"Invalid bytes at {function_address}, attempting smart offset search...")
340
+
341
+ # Try to find a better function
342
+ cmd = "aflj"
343
+ out, _ = await _execute_r2_command(
344
+ validated_path, [cmd], analysis_level="aaa", base_timeout=timeout
345
+ )
346
+
347
+ try:
348
+ funcs = _parse_json_output(out)
349
+ if funcs and isinstance(funcs, list):
350
+ # Find the largest function as a fallback
351
+ largest_func = max(funcs, key=lambda x: x.get("size", 0))
352
+ suggested_addr = hex(largest_func.get("offset", 0))
353
+ suggested_name = largest_func.get("name", "unknown")
354
+
355
+ return failure(
356
+ "YARA_GENERATION_ERROR",
357
+ f"Address {function_address} contains invalid bytes (all 0x00 or 0xFF). "
358
+ f"Try using a different address.",
359
+ hint=f"Suggested alternative: {suggested_name} at {suggested_addr}",
360
+ )
361
+ except Exception:
362
+ pass
363
+
364
+ return failure(
365
+ "YARA_GENERATION_ERROR",
366
+ f"Address {function_address} contains invalid bytes (all 0x00 or 0xFF)",
367
+ hint="Try a different function or address that contains actual code",
368
+ )
369
+
370
+ # 5. Format as YARA hex string (space-separated pairs)
371
+ formatted_bytes = _format_hex_bytes(hex_bytes)
372
+
373
+ # 6. Generate YARA rule
374
+ file_name = _sanitize_filename_for_rule(file_path)
375
+
376
+ yara_rule = f"""rule {rule_name} {{
377
+ meta:
378
+ description = "Auto-generated YARA rule for {file_name}"
379
+ function = "{function_address}"
380
+ length = {byte_length}
381
+ author = "Reversecore_MCP"
382
+ date = "auto-generated"
383
+
384
+ strings:
385
+ $code = {{ {formatted_bytes} }}
386
+
387
+ condition:
388
+ $code
389
+ }}"""
390
+
391
+ # 7. Return YARA rule
392
+ return success(
393
+ yara_rule,
394
+ bytes_read=bytes_read,
395
+ function_address=function_address,
396
+ rule_name=rule_name,
397
+ length=byte_length,
398
+ format="yara",
399
+ hex_bytes=formatted_bytes,
400
+ description=f"YARA rule '{rule_name}' generated from {byte_length} bytes at {function_address}",
401
+ )
402
+
403
+
404
+ @log_execution(tool_name="generate_enhanced_yara_rule")
405
+ @track_metrics("generate_enhanced_yara_rule")
406
+ @handle_tool_errors
407
+ async def generate_enhanced_yara_rule(
408
+ file_path: str,
409
+ rule_name: str,
410
+ strings: list[str],
411
+ imports: list[str] = None,
412
+ file_type: str = "PE",
413
+ min_filesize: int = None,
414
+ max_filesize: int = None,
415
+ section_names: list[str] = None,
416
+ entry_point_pattern: str = None,
417
+ description: str = "",
418
+ author: str = "Reversecore_MCP",
419
+ min_string_matches: int = None,
420
+ ) -> ToolResult:
421
+ """
422
+ Generate an enhanced YARA rule with structural conditions to reduce false positives.
423
+
424
+ This function creates YARA rules that combine:
425
+ - String patterns (required)
426
+ - Structural conditions (PE characteristics, file size)
427
+ - Import table checks (optional)
428
+ - Section name checks (optional)
429
+ - Entry point patterns (optional)
430
+
431
+ **Why Enhanced Rules?**
432
+ Simple string-only rules cause high false positive rates. By adding structural
433
+ conditions, rules become more precise and suitable for production use.
434
+
435
+ Args:
436
+ file_path: Path to reference binary (for metadata extraction)
437
+ rule_name: Name for the YARA rule
438
+ strings: List of strings to include in the rule
439
+ imports: List of imported functions to check (e.g., ["CryptEncrypt", "WriteFile"])
440
+ file_type: Target file type - "PE" or "ELF" (default: "PE")
441
+ min_filesize: Minimum file size in bytes (optional)
442
+ max_filesize: Maximum file size in bytes (optional)
443
+ section_names: Required section names (e.g., [".rsrc", ".text"])
444
+ entry_point_pattern: Hex pattern at entry point (optional)
445
+ description: Rule description for metadata
446
+ author: Rule author for metadata
447
+ min_string_matches: Minimum number of strings that must match (default: 2/3 of total)
448
+
449
+ Returns:
450
+ ToolResult with enhanced YARA rule string
451
+
452
+ Example:
453
+ generate_enhanced_yara_rule(
454
+ "/app/workspace/wannacry.exe",
455
+ "WannaCry_Ransomware",
456
+ strings=["WANACRY!", "WNcry@2ol7", "bitcoin"],
457
+ imports=["CryptEncrypt", "CreateServiceA"],
458
+ min_filesize=3000000,
459
+ max_filesize=4000000,
460
+ section_names=[".rsrc"],
461
+ )
462
+ """
463
+ # 1. Validate parameters
464
+ validated_path = validate_file_path(file_path)
465
+
466
+ if not _RULE_NAME_PATTERN.match(rule_name):
467
+ return failure(
468
+ "VALIDATION_ERROR",
469
+ "rule_name must start with a letter and contain only alphanumeric characters and underscores",
470
+ )
471
+
472
+ if not strings or len(strings) == 0:
473
+ return failure(
474
+ "VALIDATION_ERROR",
475
+ "At least one string is required for YARA rule generation",
476
+ )
477
+
478
+ # 2. Build strings section (max 10 strings)
479
+ string_definitions = []
480
+ for i, s in enumerate(strings[:10]):
481
+ # Escape special characters
482
+ escaped = s.replace("\\", "\\\\").replace('"', '\\"')
483
+ string_definitions.append(f' $str{i} = "{escaped}" ascii wide nocase')
484
+
485
+ strings_section = "\n".join(string_definitions)
486
+
487
+ # 3. Build imports section (optional)
488
+ import_definitions = []
489
+ if imports:
490
+ for i, imp in enumerate(imports[:10]):
491
+ escaped = imp.replace("\\", "\\\\").replace('"', '\\"')
492
+ import_definitions.append(f' $imp{i} = "{escaped}" ascii')
493
+ strings_section += "\n" + "\n".join(import_definitions)
494
+
495
+ # 4. Calculate min_string_matches (default: 2/3 of total, minimum 1)
496
+ total_strings = len(strings[:10])
497
+ if min_string_matches is None:
498
+ min_string_matches = max(1, (total_strings * 2) // 3)
499
+ min_string_matches = min(min_string_matches, total_strings)
500
+
501
+ # 5. Build condition section
502
+ conditions = []
503
+
504
+ # File type condition
505
+ if file_type.upper() == "PE":
506
+ conditions.append("uint16(0) == 0x5A4D") # MZ header
507
+ conditions.append("uint32(uint32(0x3C)) == 0x00004550") # PE signature
508
+ elif file_type.upper() == "ELF":
509
+ conditions.append("uint32(0) == 0x464C457F") # ELF magic
510
+
511
+ # File size conditions
512
+ if min_filesize:
513
+ conditions.append(f"filesize > {min_filesize}")
514
+ if max_filesize:
515
+ conditions.append(f"filesize < {max_filesize}")
516
+
517
+ # String match condition
518
+ if total_strings > 1:
519
+ conditions.append(f"{min_string_matches} of ($str*)")
520
+ else:
521
+ conditions.append("$str0")
522
+
523
+ # Import conditions (if provided)
524
+ if imports and len(imports) > 0:
525
+ min_import_matches = max(1, len(imports[:10]) // 2)
526
+ conditions.append(f"{min_import_matches} of ($imp*)")
527
+
528
+ # Section name conditions (optional)
529
+ if section_names:
530
+ for section in section_names[:5]:
531
+ # Use pe module for section checks
532
+ conditions.append(f'pe.sections[pe.number_of_sections - 1].name contains "{section}"')
533
+
534
+ # Entry point pattern (optional)
535
+ if entry_point_pattern:
536
+ if file_type_upper == "PE":
537
+ conditions.append(f"$ep at pe.entry_point")
538
+ elif file_type_upper == "ELF":
539
+ conditions.append(f"$ep at elf.entry_point")
540
+ string_definitions.append(f' $ep = {{ {entry_point_pattern} }}')
541
+
542
+ # 6. Build condition string
543
+ condition_str = " and\n ".join(conditions)
544
+ file_type = file_type.upper()
545
+
546
+ # Imports section
547
+ imports_declaration = ""
548
+ # LOGIC FIX: Don't import "pe" for ELF files or others
549
+ if "PE" in file_type and (section_names or entry_point_pattern):
550
+ imports_declaration = 'import "pe"\n\n'
551
+ elif "ELF" in file_type and (section_names or entry_point_pattern):
552
+ imports_declaration = 'import "elf"\n\n'
553
+
554
+ # Handle tags
555
+ tags_str = ""
556
+ if tags:
557
+ tags_str = " : " + " ".join(tags)
558
+
559
+ # 7. Generate complete rule
560
+ yara_rule = f'''{imports_declaration}rule {rule_name}{tags_str} {{
561
+ meta:
562
+ description = "{description or f'Enhanced detection rule for {rule_name}'}"
563
+ author = "{author}"
564
+ date = "{datetime.now().strftime('%Y-%m-%d')}"
565
+ reference = "Generated by Reversecore MCP"
566
+ false_positive_reduction = "Structural conditions applied"
567
+ min_string_matches = {min_string_matches}
568
+ hash = "{hash_value}"
569
+ confidence = "verdict"
570
+
571
+ strings:
572
+ {strings_section}
573
+
574
+ condition:
575
+ {condition_str}
576
+ }}'''
577
+
578
+ return success(
579
+ yara_rule,
580
+ rule_name=rule_name,
581
+ string_count=len(strings[:10]),
582
+ import_count=len(imports[:10]) if imports else 0,
583
+ condition_count=len(conditions),
584
+ min_string_matches=min_string_matches,
585
+ format="yara",
586
+ description=f"Enhanced YARA rule '{rule_name}' with {len(conditions)} structural conditions",
587
+ )
588
+
589
+
590
+ # Note: SignatureToolsPlugin has been removed.
591
+ # The signature tools are now registered via AnalysisToolsPlugin in analysis/__init__.py.