Reverse engineers ransomware encryption routines to identify crypto algorithms, key flaws, and decryption opportunities via static/dynamic analysis. For malware analysts.
npx claudepluginhub killvxk/cybersecurity-skills-zhThis skill uses the workspace's default tool permissions.
现代勒索软件使用混合加密方案,将对称算法(AES-256-CBC/CTR、ChaCha20、Salsa20)用于文件加密,将非对称算法(RSA-2048/4096、Curve25519)用于密钥保护。加密例程通常为每个文件生成一个随机对称密钥,加密文件内容,然后用攻击者内嵌的公钥加密对称密钥。对这些例程进行逆向工程,可以识别具体算法、密钥派生方法、初始化向量(IV)、文件定向模式,以及可能在不支付赎金的情况下实现解密的实现缺陷。典型案例包括 Rhysida(AES-256-CTR + RSA-4096)、Qilin.B(带 AES-NI 的 AES-256-CTR 或 ChaCha20 回退)和 Medusa(AES-256 + RSA)。
Reverse engineers ransomware encryption routines to identify cryptographic algorithms, key generation flaws, and decryption opportunities using static and dynamic analysis. For authorized malware analysis.
Reverse engineers ransomware encryption routines to identify algorithms like AES/RSA, key flaws, and decryption opportunities using static/dynamic analysis in controlled environments.
Analyzes ransomware encryption algorithms like AES, RSA, ChaCha20; evaluates key management, decryption feasibility, and implementation weaknesses for recovery.
Share bugs, ideas, or general feedback.
现代勒索软件使用混合加密方案,将对称算法(AES-256-CBC/CTR、ChaCha20、Salsa20)用于文件加密,将非对称算法(RSA-2048/4096、Curve25519)用于密钥保护。加密例程通常为每个文件生成一个随机对称密钥,加密文件内容,然后用攻击者内嵌的公钥加密对称密钥。对这些例程进行逆向工程,可以识别具体算法、密钥派生方法、初始化向量(IV)、文件定向模式,以及可能在不支付赎金的情况下实现解密的实现缺陷。典型案例包括 Rhysida(AES-256-CTR + RSA-4096)、Qilin.B(带 AES-NI 的 AES-256-CTR 或 ChaCha20 回退)和 Medusa(AES-256 + RSA)。
pycryptodome、pefile勒索软件为每个文件生成唯一的 AES 密钥和 IV。文件内容使用该对称密钥加密。对称密钥再用攻击者的 RSA 公钥(内嵌于二进制文件中或从 C2 获取)加密。加密后的密钥追加或前置到加密后的文件中。只有持有 RSA 私钥的攻击者才能解密每个文件的对称密钥。
Windows 勒索软件通常使用 CryptoAPI(CryptAcquireContext、CryptGenKey、CryptEncrypt)或 CNG(BCryptGenerateSymmetricKey、BCryptEncrypt)。部分使用 OpenSSL 或自定义实现。识别这些 API 调用可立即获取算法、密钥大小和操作模式的信息。
解密机会来自:硬编码加密密钥、密钥生成使用弱伪随机数生成器(PRNG)(以 GetTickCount 或 time() 为种子)、跨文件复用 IV、使用 ECB 模式、加密后密钥仍留在内存中,以及在加密过程中可捕获密钥的竞争条件。
#!/usr/bin/env python3
"""识别勒索软件 PE 文件中的密码学函数。"""
import pefile
import sys
CRYPTO_APIS = {
# Windows CryptoAPI
"CryptAcquireContextA": "CryptoAPI 上下文获取",
"CryptAcquireContextW": "CryptoAPI 上下文获取",
"CryptGenKey": "密钥生成",
"CryptDeriveKey": "密钥派生",
"CryptEncrypt": "加密操作",
"CryptDecrypt": "解密操作",
"CryptImportKey": "密钥导入(公钥?)",
"CryptExportKey": "密钥导出",
"CryptGenRandom": "随机数生成",
"CryptCreateHash": "哈希创建",
"CryptHashData": "哈希操作",
# Windows CNG(BCrypt)
"BCryptOpenAlgorithmProvider": "CNG 算法初始化",
"BCryptGenerateSymmetricKey": "CNG 对称密钥生成",
"BCryptEncrypt": "CNG 加密",
"BCryptDecrypt": "CNG 解密",
"BCryptGenerateKeyPair": "CNG 密钥对生成",
"BCryptImportKeyPair": "CNG 密钥导入",
# OpenSSL
"EVP_EncryptInit_ex": "OpenSSL 加密初始化",
"EVP_EncryptUpdate": "OpenSSL 加密更新",
"EVP_EncryptFinal_ex": "OpenSSL 加密完成",
"RSA_public_encrypt": "OpenSSL RSA 加密",
"AES_set_encrypt_key": "OpenSSL AES 密钥设置",
# 文件操作
"CreateFileW": "文件打开(目标文件)",
"ReadFile": "文件读取(加密前)",
"WriteFile": "文件写入(加密后)",
"FindFirstFileW": "文件枚举(目标定向)",
"FindNextFileW": "文件枚举",
"MoveFileW": "文件重命名(扩展名更改)",
"DeleteFileW": "文件删除(原始文件)",
}
AES_SBOX = bytes([
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
])
CHACHA20_CONSTANT = b"expand 32-byte k"
def analyze_imports(filepath):
"""分析 PE 导入表中的密码学 API。"""
try:
pe = pefile.PE(filepath)
except pefile.PEFormatError:
print("[-] 非有效 PE 文件")
return
print("[+] 密码学 API 分析")
print("=" * 60)
crypto_imports = []
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:
desc = CRYPTO_APIS[name]
crypto_imports.append((dll, name, desc))
print(f" [{dll}] {name}: {desc}")
if not crypto_imports:
print(" 导入表中未发现已知密码学 API")
print(" 恶意软件可能使用自定义实现或动态加载")
return crypto_imports
def find_crypto_constants(filepath):
"""搜索内嵌的密码学常量。"""
with open(filepath, 'rb') as f:
data = f.read()
print("\n[+] 密码学常量搜索")
print("=" * 60)
# AES S-Box
offset = data.find(AES_SBOX)
if offset != -1:
print(f" AES S-Box 位于偏移量 0x{offset:x}")
# ChaCha20/Salsa20 常量
offset = data.find(CHACHA20_CONSTANT)
if offset != -1:
print(f" ChaCha20 常量位于偏移量 0x{offset:x}")
# RSA 公钥标记
rsa_markers = [
b'-----BEGIN PUBLIC KEY-----',
b'-----BEGIN RSA PUBLIC KEY-----',
b'\x30\x82', # ASN.1 SEQUENCE
]
for marker in rsa_markers:
offset = data.find(marker)
if offset != -1:
print(f" RSA 密钥标记位于偏移量 0x{offset:x}")
# 常见勒索软件文件扩展名模式
import re
ext_pattern = re.compile(rb'\.\w{3,10}(?=\x00)', re.IGNORECASE)
extensions = set()
for match in ext_pattern.finditer(data):
ext = match.group().decode('ascii', errors='replace').lower()
target_exts = [
'.doc', '.docx', '.xls', '.xlsx', '.pdf', '.ppt',
'.jpg', '.png', '.sql', '.mdb', '.bak', '.zip',
]
if ext in target_exts:
extensions.add(ext)
if extensions:
print(f"\n 目标文件扩展名: {', '.join(sorted(extensions))}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"用法: {sys.argv[0]} <勒索软件样本>")
sys.exit(1)
analyze_imports(sys.argv[1])
find_crypto_constants(sys.argv[1])
def analyze_encryption_pattern(filepath):
"""从勒索软件产物分析文件加密模式。"""
import os
import struct
with open(filepath, 'rb') as f:
data = f.read()
file_size = len(data)
print(f"\n[+] 加密文件分析: {filepath}")
print(f" 大小: {file_size:,} 字节")
# 检查追加的密钥材料(常见模式)
# 许多勒索软件家族在文件末尾追加加密后的密钥
tail_sizes = [256, 512, 1024, 2048] # 常见 RSA 密文大小
for size in tail_sizes:
if file_size > size + 16:
tail = data[-size:]
# 高熵值表明为加密数据
entropy = calculate_entropy(tail)
if entropy > 7.5:
print(f" 文件末尾可能存在加密密钥({size} 字节),"
f"熵值: {entropy:.2f}")
# 检查头部修改
# 许多勒索软件会前置元数据
header = data[:64]
print(f" 前 16 字节: {header[:16].hex()}")
# 检查原始文件头是否被保留
known_headers = {
b'PK': 'ZIP/Office',
b'\x89PNG': 'PNG',
b'\xff\xd8\xff': 'JPEG',
b'%PDF': 'PDF',
b'\xd0\xcf\x11\xe0': 'OLE(DOC/XLS)',
}
for magic, ftype in known_headers.items():
if header.startswith(magic):
print(f" 原始格式已保留: {ftype}")
break
else:
print(" 原始文件头已被销毁/加密")
def calculate_entropy(data):
"""计算数据的香农熵。"""
from collections import Counter
import math
if not data:
return 0
freq = Counter(data)
length = len(data)
entropy = -sum(
(count / length) * math.log2(count / length)
for count in freq.values()
)
return entropy