reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for reverse engineering ransomware encryption routines.
|
||||
|
||||
Identifies encryption algorithms, extracts key material from
|
||||
memory dumps or binary analysis, detects IV/nonce patterns,
|
||||
and documents the cryptographic implementation for decryptor
|
||||
development.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import re
|
||||
import struct
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
CRYPTO_CONSTANTS = {
|
||||
"AES S-Box": bytes([0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5]),
|
||||
"AES Inv S-Box": bytes([0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38]),
|
||||
"RSA Marker": b"\x30\x82",
|
||||
"ChaCha20 Constant": b"expand 32-byte k",
|
||||
"Salsa20 Constant": b"expand 32-byte k",
|
||||
"RC4 State Init": bytes(range(8)),
|
||||
"SHA256 Init H0": struct.pack(">I", 0x6a09e667),
|
||||
}
|
||||
|
||||
RANSOMWARE_PATTERNS = {
|
||||
"file_extension_change": re.compile(rb'\.\w{3,10}(?=\x00)'),
|
||||
"ransom_note_name": re.compile(rb'(?:README|RECOVER|DECRYPT|HOW.TO)[\w.-]*\.(?:txt|html|hta)', re.I),
|
||||
"bitcoin_address": re.compile(rb'[13][a-km-zA-HJ-NP-Z1-9]{25,34}'),
|
||||
"onion_url": re.compile(rb'[\w]{16,56}\.onion'),
|
||||
"email_address": re.compile(rb'[\w.+-]+@[\w-]+\.[\w.]{2,}'),
|
||||
}
|
||||
|
||||
|
||||
class RansomwareREAgent:
|
||||
"""Analyzes ransomware encryption implementation."""
|
||||
|
||||
def __init__(self, sample_path, output_dir="./ransomware_re"):
|
||||
self.sample_path = Path(sample_path)
|
||||
self.output_dir = Path(output_dir)
|
||||
self.output_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.findings = []
|
||||
|
||||
def identify_crypto_algorithms(self):
|
||||
"""Scan binary for cryptographic algorithm constants."""
|
||||
data = self.sample_path.read_bytes()
|
||||
detected = []
|
||||
|
||||
for name, constant in CRYPTO_CONSTANTS.items():
|
||||
offset = data.find(constant)
|
||||
if offset != -1:
|
||||
detected.append({
|
||||
"algorithm": name,
|
||||
"offset": hex(offset),
|
||||
"context": data[max(0, offset - 16):offset + len(constant) + 16].hex(),
|
||||
})
|
||||
self.findings.append({
|
||||
"type": "Crypto Algorithm Detected",
|
||||
"algorithm": name, "offset": hex(offset),
|
||||
})
|
||||
return detected
|
||||
|
||||
def extract_encryption_indicators(self):
|
||||
"""Extract ransomware-specific indicators from the binary."""
|
||||
data = self.sample_path.read_bytes()
|
||||
indicators = {}
|
||||
|
||||
for name, pattern in RANSOMWARE_PATTERNS.items():
|
||||
matches = pattern.findall(data)
|
||||
if matches:
|
||||
decoded = []
|
||||
for m in matches[:10]:
|
||||
try:
|
||||
decoded.append(m.decode("utf-8", errors="ignore"))
|
||||
except (UnicodeDecodeError, AttributeError):
|
||||
decoded.append(m.hex())
|
||||
indicators[name] = decoded
|
||||
|
||||
return indicators
|
||||
|
||||
def analyze_encrypted_file(self, encrypted_path, original_path=None):
|
||||
"""Analyze an encrypted file to determine encryption characteristics."""
|
||||
enc_data = Path(encrypted_path).read_bytes()
|
||||
analysis = {
|
||||
"file_size": len(enc_data),
|
||||
"entropy": self._calculate_entropy(enc_data),
|
||||
"header_bytes": enc_data[:64].hex(),
|
||||
"footer_bytes": enc_data[-64:].hex() if len(enc_data) > 64 else "",
|
||||
}
|
||||
|
||||
if analysis["entropy"] > 7.9:
|
||||
analysis["encryption_type"] = "Full file encryption"
|
||||
elif analysis["entropy"] > 6.0:
|
||||
analysis["encryption_type"] = "Partial/intermittent encryption"
|
||||
else:
|
||||
analysis["encryption_type"] = "Possibly not encrypted or header-only"
|
||||
|
||||
if original_path and Path(original_path).exists():
|
||||
orig_data = Path(original_path).read_bytes()
|
||||
analysis["size_difference"] = len(enc_data) - len(orig_data)
|
||||
if analysis["size_difference"] > 0:
|
||||
analysis["appended_bytes"] = analysis["size_difference"]
|
||||
analysis["footer_metadata"] = enc_data[len(orig_data):len(orig_data) + 128].hex()
|
||||
|
||||
return analysis
|
||||
|
||||
def _calculate_entropy(self, data):
|
||||
"""Calculate Shannon entropy of data."""
|
||||
if not data:
|
||||
return 0.0
|
||||
import math
|
||||
freq = [0] * 256
|
||||
for byte in data:
|
||||
freq[byte] += 1
|
||||
length = len(data)
|
||||
entropy = 0.0
|
||||
for count in freq:
|
||||
if count > 0:
|
||||
p = count / length
|
||||
entropy -= p * math.log2(p)
|
||||
return round(entropy, 4)
|
||||
|
||||
def extract_key_material(self, memory_dump_path=None):
|
||||
"""Search for potential encryption key material."""
|
||||
search_data = (Path(memory_dump_path).read_bytes()
|
||||
if memory_dump_path else self.sample_path.read_bytes())
|
||||
potential_keys = []
|
||||
|
||||
for offset in range(0, min(len(search_data), 10_000_000), 16):
|
||||
block = search_data[offset:offset + 32]
|
||||
if len(block) < 16:
|
||||
break
|
||||
entropy = self._calculate_entropy(block)
|
||||
if entropy > 4.5 and all(b != 0 for b in block[:16]):
|
||||
if not all(b == block[0] for b in block[:16]):
|
||||
potential_keys.append({
|
||||
"offset": hex(offset),
|
||||
"length": len(block),
|
||||
"entropy": entropy,
|
||||
"sha256": hashlib.sha256(block).hexdigest()[:16],
|
||||
})
|
||||
if len(potential_keys) >= 50:
|
||||
break
|
||||
|
||||
return potential_keys[:20]
|
||||
|
||||
def generate_report(self):
|
||||
crypto = self.identify_crypto_algorithms()
|
||||
indicators = self.extract_encryption_indicators()
|
||||
sha256 = hashlib.sha256(self.sample_path.read_bytes()).hexdigest()
|
||||
|
||||
report = {
|
||||
"sample": str(self.sample_path),
|
||||
"sha256": sha256,
|
||||
"report_date": datetime.utcnow().isoformat(),
|
||||
"crypto_algorithms": crypto,
|
||||
"ransomware_indicators": indicators,
|
||||
"findings": self.findings,
|
||||
}
|
||||
report_path = self.output_dir / "ransomware_re_report.json"
|
||||
with open(report_path, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(json.dumps(report, indent=2))
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: agent.py <sample_path> [encrypted_file] [original_file]")
|
||||
sys.exit(1)
|
||||
agent = RansomwareREAgent(sys.argv[1])
|
||||
agent.generate_report()
|
||||
if len(sys.argv) > 2:
|
||||
orig = sys.argv[3] if len(sys.argv) > 3 else None
|
||||
analysis = agent.analyze_encrypted_file(sys.argv[2], orig)
|
||||
print(json.dumps(analysis, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Ransomware Encryption Routine Analyzer
|
||||
|
||||
Analyzes ransomware samples to identify encryption algorithms,
|
||||
key generation methods, and potential decryption opportunities.
|
||||
|
||||
Requirements:
|
||||
pip install pefile pycryptodome
|
||||
|
||||
Usage:
|
||||
python process.py --sample ransomware.exe
|
||||
python process.py --encrypted-file encrypted.docx.locked
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import pefile
|
||||
except ImportError:
|
||||
pefile = None
|
||||
|
||||
|
||||
CRYPTO_APIS = {
|
||||
"CryptAcquireContextA": ("CryptoAPI", "context"),
|
||||
"CryptAcquireContextW": ("CryptoAPI", "context"),
|
||||
"CryptGenKey": ("CryptoAPI", "keygen"),
|
||||
"CryptEncrypt": ("CryptoAPI", "encrypt"),
|
||||
"CryptDecrypt": ("CryptoAPI", "decrypt"),
|
||||
"CryptImportKey": ("CryptoAPI", "import"),
|
||||
"CryptGenRandom": ("CryptoAPI", "random"),
|
||||
"BCryptOpenAlgorithmProvider": ("CNG", "init"),
|
||||
"BCryptGenerateSymmetricKey": ("CNG", "keygen"),
|
||||
"BCryptEncrypt": ("CNG", "encrypt"),
|
||||
"BCryptDecrypt": ("CNG", "decrypt"),
|
||||
"EVP_EncryptInit_ex": ("OpenSSL", "init"),
|
||||
"EVP_EncryptUpdate": ("OpenSSL", "encrypt"),
|
||||
"RSA_public_encrypt": ("OpenSSL", "rsa_encrypt"),
|
||||
"AES_set_encrypt_key": ("OpenSSL", "aes_init"),
|
||||
}
|
||||
|
||||
AES_SBOX_PREFIX = bytes([0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5])
|
||||
CHACHA_CONST = b"expand 32-byte k"
|
||||
SALSA_CONST = b"expand 32-byte k"
|
||||
|
||||
|
||||
def entropy(data):
|
||||
if not data:
|
||||
return 0.0
|
||||
freq = Counter(data)
|
||||
length = len(data)
|
||||
return -sum(
|
||||
(c / length) * math.log2(c / length) for c in freq.values()
|
||||
)
|
||||
|
||||
|
||||
def analyze_sample(filepath):
|
||||
report = {"file": str(filepath), "crypto_apis": [], "constants": [],
|
||||
"embedded_keys": [], "target_extensions": []}
|
||||
|
||||
with open(filepath, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
report["size"] = len(data)
|
||||
report["entropy"] = round(entropy(data), 3)
|
||||
|
||||
# Import analysis
|
||||
if pefile:
|
||||
try:
|
||||
pe = pefile.PE(filepath)
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
dll = entry.dll.decode('utf-8', errors='replace')
|
||||
for imp in entry.imports:
|
||||
if imp.name:
|
||||
name = imp.name.decode('utf-8', errors='replace')
|
||||
if name in CRYPTO_APIS:
|
||||
framework, op = CRYPTO_APIS[name]
|
||||
report["crypto_apis"].append({
|
||||
"dll": dll,
|
||||
"function": name,
|
||||
"framework": framework,
|
||||
"operation": op,
|
||||
})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Crypto constants
|
||||
if data.find(AES_SBOX_PREFIX) != -1:
|
||||
report["constants"].append("AES S-Box")
|
||||
if data.find(CHACHA_CONST) != -1:
|
||||
report["constants"].append("ChaCha20/Salsa20")
|
||||
|
||||
# RSA keys
|
||||
pem_markers = [b'-----BEGIN PUBLIC KEY-----',
|
||||
b'-----BEGIN RSA PUBLIC KEY-----']
|
||||
for marker in pem_markers:
|
||||
idx = data.find(marker)
|
||||
if idx != -1:
|
||||
end = data.find(b'-----END', idx)
|
||||
if end != -1:
|
||||
key_data = data[idx:end + 30].decode('ascii', errors='replace')
|
||||
report["embedded_keys"].append({
|
||||
"type": "PEM RSA Public Key",
|
||||
"offset": f"0x{idx:x}",
|
||||
"preview": key_data[:100],
|
||||
})
|
||||
|
||||
# Target extensions
|
||||
ext_pattern = re.compile(rb'\.(?:doc|docx|xls|xlsx|pdf|ppt|pptx|'
|
||||
rb'jpg|png|sql|mdb|bak|zip|rar|7z|'
|
||||
rb'psd|dwg|vmdk|raw|db)\b', re.I)
|
||||
for m in ext_pattern.finditer(data):
|
||||
ext = m.group().decode('ascii', errors='replace').lower()
|
||||
if ext not in report["target_extensions"]:
|
||||
report["target_extensions"].append(ext)
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def analyze_encrypted_file(filepath):
|
||||
with open(filepath, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
report = {
|
||||
"file": str(filepath),
|
||||
"size": len(data),
|
||||
"entropy": round(entropy(data), 3),
|
||||
"high_entropy": entropy(data) > 7.9,
|
||||
"possible_appended_key": [],
|
||||
}
|
||||
|
||||
# Check tail for appended encrypted key
|
||||
for key_size in [128, 256, 512, 1024, 2048]:
|
||||
if len(data) > key_size + 16:
|
||||
tail = data[-key_size:]
|
||||
tail_entropy = entropy(tail)
|
||||
if tail_entropy > 7.5:
|
||||
report["possible_appended_key"].append({
|
||||
"size": key_size,
|
||||
"entropy": round(tail_entropy, 3),
|
||||
})
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Ransomware Encryption Analyzer"
|
||||
)
|
||||
parser.add_argument("--sample", help="Ransomware binary")
|
||||
parser.add_argument("--encrypted-file", help="Encrypted file to analyze")
|
||||
parser.add_argument("--output", help="Output JSON report")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.sample:
|
||||
report = analyze_sample(args.sample)
|
||||
elif args.encrypted_file:
|
||||
report = analyze_encrypted_file(args.encrypted_file)
|
||||
else:
|
||||
parser.print_help()
|
||||
return
|
||||
|
||||
print(json.dumps(report, indent=2))
|
||||
if args.output:
|
||||
with open(args.output, 'w') as f:
|
||||
json.dump(report, f, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user