关于 performing-yara-rule-development-for-detection
面向恶意软件研究人员和安全分析人员的 YARA 规则开发技能,涵盖特征选择、规则测试与性能优化,帮助构建针对恶意软件家族的检测规则。
主要功能
- 01提取独特文本与字节特征
- 02编写 YARA 匹配规则
- 03测试检测效果与误报
- 04优化扫描性能
适用场景
- 01为恶意软件家族编写检测规则
- 02验证规则对样本的检出能力
- 03优化大批文件的扫描速度
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill performing-yara-rule-development-for-detection安装后在 Claude Code 中输入/performing-yara-rule-development-for-detection确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEassets/template.mdreferences/api-reference.mdstandards.mdworkflows.mdscripts/agent.pyprocess.py详细介绍
为检测执行 YARA 规则开发
概述
YARA 是恶意软件研究人员的模式匹配瑞士军刀,支持基于文本或二进制模式识别和分类恶意软件。有效的 YARA 规则结合唯一的字符串模式、字节序列、PE 头特征、导入表分析和条件逻辑,以检测恶意软件家族,同时避免误报。现代 YARA-X(用 Rust 重写,自 2025 年 6 月起稳定)带来了改进的性能和新模块。规则应针对解包后的恶意软件产物,如硬编码的堆栈字符串、C2 URL、互斥体名称、加密常量和唯一代码序列,而不是加壳器签名。
何时使用
- 在进行涉及为检测执行 yara 规则开发的安全评估时
- 在遵循相关安全事件的事件响应程序时
- 在执行计划的安全测试或审计活动时
- 通过动手测试验证安全控制时
先决条件
- Python 3.9+ 及
yara-python库 - YARA 4.5+ 或 YARA-X 0.10+
- PE 分析工具(
pefile、pestudio) - 用于识别唯一字节模式的十六进制编辑器
- 访问恶意软件样本(VirusTotal、MalwareBazaar)
- 理解 PE 文件格式、字符串和导入表
关键概念
规则结构
每个 YARA 规则由三个部分组成:meta(可选描述性元数据)、strings(模式定义)和 condition(匹配逻辑)。字符串类型包括文本字符串(ASCII/wide/nocase)、带通配符和跳转的十六进制模式以及正则表达式。条件使用布尔运算符将字符串匹配与文件属性结合起来。
字符串选择策略
有效的规则针对恶意软件家族特有且在重新编译后仍能存活的模式。硬编码的堆栈字符串是极佳选择,因为编译器会一致地嵌入它们。C2 域名模式、自定义加密例程、唯一错误消息和特定 API 调用序列提供了稳定的检测锚点。避免编译器生成的样板和常见库字符串。
性能优化
YARA 以短路方式评估条件。将最具区分度且评估成本最低的条件放在前面。使用 filesize 限制快速跳过无关文件。尽量减少正则表达式使用,优先使用十六进制模式。使用 private 规则作为复杂检测逻辑的构建块,而不生成独立匹配。
工作流
步骤 1:分析样本以寻找唯一模式
#!/usr/bin/env python3
"""Extract candidate strings and byte patterns for YARA rule creation."""
import pefile
import re
import sys
from collections import Counter
def extract_strings(filepath, min_length=6):
"""Extract ASCII and wide strings from binary."""
with open(filepath, 'rb') as f:
data = f.read()
# ASCII strings
ascii_strings = re.findall(
rb'[\x20-\x7e]{' + str(min_length).encode() + rb',}', data
)
# Wide (UTF-16LE) strings
wide_strings = re.findall(
rb'(?:[\x20-\x7e]\x00){' + str(min_length).encode() + rb',}', data
)
return {
'ascii': [s.decode('ascii') for s in ascii_strings],
'wide': [s.decode('utf-16-le') for s in wide_strings],
}
def analyze_pe_imports(filepath):
"""Extract import table for API-based detection."""
try:
pe = pefile.PE(filepath)
except pefile.PEFormatError:
return []
imports = []
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
for entry in pe.DIRECTORY_ENTRY_IMPORT:
dll_name = entry.dll.decode('utf-8', errors='replace')
for imp in entry.imports:
if imp.name:
func_name = imp.name.decode('utf-8', errors='replace')
imports.append(f"{dll_name}!{func_name}")
return imports
def find_unique_byte_patterns(filepath, pattern_length=16):
"""Find unique byte sequences suitable for YARA hex patterns."""
with open(filepath, 'rb') as f:
data = f.read()
try:
pe = pefile.PE(filepath)
# Focus on code section
for section in pe.sections:
if section.Characteristics & 0x20000000: # IMAGE_SCN_MEM_EXECUTE
code_start = section.PointerToRawData
code_end = code_start + section.SizeOfRawData
code_data = data[code_start:code_end]
break
else:
code_data = data
except Exception:
code_data = data
# Find byte patterns that appear exactly once
patterns = []
for i in range(0, len(code_data) - pattern_length, 4):
pattern = code_data[i:i+pattern_length]
if pattern.count(b'\x00') < pattern_length // 3: # Skip null-heavy
hex_pattern = ' '.join(f'{b:02X}' for b in pattern)
patterns.append(hex_pattern)
# Count frequency and return unique ones
freq = Counter(patterns)
unique = [p for p, count in freq.items() if count == 1]
return unique[:20] # Top 20 candidates
def suggest_rule_strings(filepath):
"""Suggest strings and patterns for YARA rule."""
print(f"[+] Analyzing: {filepath}")
# Extract strings
strings = extract_strings(filepath)
# Filter for suspicious/unique strings
suspicious_keywords = [
'http', 'https', 'cmd', 'powershell', 'mutex', 'pipe',
'password', 'credential', 'inject', 'hook', 'debug',
'sandbox', 'virtual', 'vmware', 'vbox',
]
print("\n[+] Suspicious ASCII strings:")
for s in strings['ascii']:
if any(kw in s.lower() for kw in suspicious_keywords):
print(f" $ = \"{s}\" ascii")
print("\n[+] Suspicious wide strings:")
for s in strings['wide']:
if any(kw in s.lower() for kw in suspicious_keywords):
print(f" $ = \"{s}\" wide")
# Import analysis
imports = analyze_pe_imports(filepath)
suspicious_apis = [
'VirtualAlloc', 'VirtualProtect', 'WriteProcessMemory',
'CreateRemoteThread', 'NtUnmapViewOfSection', 'RtlMoveMemory',
'OpenProcess', 'CreateToolhelp32Snapshot',
'InternetOpenA', 'HttpSendRequestA',
'CryptEncrypt', 'CryptDecrypt',
]
print("\n[+] Suspicious imports:")
for imp in imports:
func = imp.split('!')[-1]
if func in suspicious_apis:
print(f" {imp}")
# Byte patterns
print("\n[+] Candidate hex patterns:")
patterns = find_unique_byte_patterns(filepath)
for p in patterns[:5]:
print(f" $hex = {{ {p} }}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <sample_path>")
sys.exit(1)
suggest_rule_strings(sys.argv[1])
步骤 2:编写并测试 YARA 规则
import yara
import os
def create_yara_rule(rule_name, meta, strings, condition):
"""Generate a YARA rule from components."""
meta_str = "\n".join(f' {k} = "{v}"' for k, v in meta.items())
strings_str = "\n".join(f" {s}" for s in strings)
rule = f"""rule {rule_name} {{
meta:
{meta_str}
strings:
{strings_str}
condition:
{condition}
}}"""
return rule
def test_yara_rule(rule_text, test_dir):
"""Compile and test YARA rule against sample directory."""
try:
rules = yara.compile(source=rule_text)
except yara.SyntaxError as e:
print(f"[-] YARA syntax error: {e}")
return None
results = {"matches": [], "no_match": []}
for filename in os.listdir(test_dir):
filepath = os.path.join(test_dir, filename)
if not os.path.isfile(filepath):
continue
matches = rules.match(filepath)
if matches:
results["matches"].append({
"file": filename,
"rules": [m.rule for m in matches],
})
else:
results["no_match"].append(filename)
print(f"[+] Matches: {len(results['matches'])}")
print(f"[-] No match: {len(results['no_match'])}")
return results
# Example: Create a rule for a hypothetical malware family
example_rule = create_yara_rule(
rule_name="MalwareFamily_Variant_A",
meta={
"description": "Detects MalwareFamily Variant A",
"author": "Malware Analysis Team",
"date": "2025-01-01",
"hash": "abc123...",
"tlp": "WHITE",
},
strings=[
'$mutex = "Global\\\\UniqueM4lwareMutex" ascii wide',
'$c2_pattern = /https?:\\/\\/[a-z]{5,10}\\.(xyz|top|buzz)\\/gate\\.php/',
'$api1 = "VirtualAllocEx" ascii',
'$api2 = "WriteProcessMemory" ascii',
'$api3 = "CreateRemoteThread" ascii',
'$hex_decrypt = { 8B 45 ?? 33 C1 89 45 ?? 83 C1 04 }',
'$pdb = "C:\\\\Users\\\\" ascii',
],
condition=(
'uint16(0) == 0x5A4D and filesize < 2MB and '
'($mutex or $c2_pattern) and '
'2 of ($api*) and '
'$hex_decrypt'
),
)
print(example_rule)
步骤 3:性能测试与优化
import time
def benchmark_rule(rule_text, scan_directory, iterations=3):
"""Benchmark YARA rule scan performance."""
rules = yara.compile(source=rule_text)
files = []
for root, _, filenames in os.walk(scan_directory):
for f in filenames:
files.append(os.path.join(root, f))
print(f"[+] Benchmarking against {len(files)} files "
f"({iterations} iterations)")
times = []
for i in range(iterations):
start = time.perf_counter()
matches = 0
for filepath in files:
try:
result = rules.match(filepath)
if result:
matches += 1
except Exception:
pass
elapsed = time.perf_counter() - start
times.append(elapsed)
print(f" Iteration {i+1}: {elapsed:.3f}s ({matches} matches)")
avg_time = sum(times) / len(times)
files_per_sec = len(files) / avg_time
print(f"\n[+] Average: {avg_time:.3f}s ({files_per_sec:.0f} files/sec)")
return avg_time
验证标准
- YARA 规则编译无语法错误
- 规则检测目标恶意软件家族样本,零漏报
- 对干净文件语料库扫描时误报率低于 0.1%
- 规则性能允许每秒扫描 1000+ 个文件
- 规则在轻微恶意软件修改(重新编译、字符串更改)后仍能存活
- 元数据包括哈希、作者、日期、描述和 TLP 标记