Files
haproxy-mcp/docs/YARA_GUIDE.md
kaffa 02a17a62b4 docs: Add WAF integration docs and ignore lock files
- Add YARA guide, WAF integration docs, and webshell rules
- Ignore *.lock files in .gitignore

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 04:34:49 +00:00

25 KiB

YARA 규칙 가이드

악성코드 및 웹쉘 탐지를 위한 YARA 사용법 정리

목차

  1. YARA란?
  2. 설치
  3. 규칙 문법
  4. 웹쉘 탐지 규칙 예시
  5. CLI 사용법
  6. Python 연동
  7. 실시간 모니터링
  8. 공개 규칙 모음
  9. 다른 방식과 비교

YARA란?

YARA는 악성코드 연구자가 악성 샘플을 식별하고 분류하기 위해 만든 패턴 매칭 도구입니다.

항목 설명
개발 VirusTotal (Google 소속)
용도 악성코드, 웹쉘, 악성 문서 탐지
방식 문자열 + 바이트 패턴 + 조건 매칭
AI 여부 규칙 기반 (AI 없음)
속도 매우 빠름 (1-10ms/파일)
라이선스 BSD-3-Clause (무료)

핵심 개념

YARA = 악성코드 전용 grep/정규식

파일 내용에서 특정 패턴을 찾고,
여러 조건을 조합하여 악성 여부를 판단

설치

Linux (Ubuntu/Debian)

# 패키지 설치
sudo apt update
sudo apt install yara

# 버전 확인
yara --version

macOS

brew install yara

Python 바인딩

pip install yara-python

소스 컴파일 (최신 버전)

# 의존성 설치
sudo apt install automake libtool make gcc pkg-config libssl-dev

# 소스 다운로드 및 컴파일
git clone https://github.com/VirusTotal/yara.git
cd yara
./bootstrap.sh
./configure
make
sudo make install
sudo ldconfig

규칙 문법

기본 구조

rule 규칙이름 {
    meta:
        // 메타데이터 (선택사항)
        description = "규칙 설명"
        author = "작성자"
        date = "2025-01-01"
        severity = "high"

    strings:
        // 탐지할 문자열/패턴 정의
        $string1 = "탐지할 문자열"
        $string2 = { 48 65 6C 6C 6F }  // 헥스 바이트
        $regex1 = /정규식 패턴/

    condition:
        // 탐지 조건
        any of them
}

문자열 정의 방식

rule StringTypes {
    strings:
        // 1. 일반 문자열
        $text = "eval("

        // 2. 대소문자 무시
        $nocase = "system(" nocase

        // 3. 와이드 문자 (UTF-16)
        $wide = "malware" wide

        // 4. 둘 다
        $both = "shell" wide ascii nocase

        // 5. 헥스 바이트 (바이너리)
        $hex = { 4D 5A 90 00 }  // MZ 헤더

        // 6. 헥스 와일드카드
        $hex_wild = { 4D 5A ?? ?? }  // ??는 아무 바이트

        // 7. 헥스 점프
        $hex_jump = { 4D 5A [2-4] 00 }  // 2-4바이트 사이

        // 8. 정규식
        $regex = /eval\s*\(\s*\$_(GET|POST)/

        // 9. XOR 인코딩 탐지
        $xor = "password" xor

        // 10. Base64 인코딩 탐지
        $b64 = "eval" base64

    condition:
        any of them
}

조건문 (condition)

rule ConditionExamples {
    strings:
        $a = "eval("
        $b = "system("
        $c = "$_POST"
        $d = "$_GET"

    condition:
        // 기본 조건
        $a                          // $a가 존재
        $a and $b                   // 둘 다 존재
        $a or $b                    // 하나라도 존재
        not $a                      // $a가 없음

        // 개수 조건
        any of them                 // 하나라도 존재
        all of them                 // 모두 존재
        2 of them                   // 2개 이상 존재
        2 of ($a, $b, $c)          // 지정된 것 중 2개 이상

        // 위치 조건
        $a at 0                     // 파일 시작 위치
        $a in (0..100)             // 0-100 바이트 범위

        // 개수 세기
        #a > 5                      // $a가 5번 이상 출현

        // 파일 크기
        filesize < 1MB
        filesize > 100 and filesize < 10KB

        // 매직 바이트
        uint16(0) == 0x5A4D        // MZ 헤더 (PE 파일)
        uint32(0) == 0x464C457F    // ELF 헤더

        // 복합 조건
        ($a or $b) and ($c or $d)

        // 반복문
        for any i in (1..#a) : (@a[i] < 100)
}

웹쉘 탐지 규칙 예시

webshell_rules.yar

/*
 * PHP 웹쉘 탐지 규칙
 * Author: Security Team
 * Last Updated: 2025-02-01
 */

// ============================================
// 기본 웹쉘 탐지
// ============================================

rule PHP_Webshell_Generic {
    meta:
        description = "일반적인 PHP 웹쉘 패턴"
        severity = "high"

    strings:
        // 코드 실행 함수
        $exec1 = "eval(" ascii nocase
        $exec2 = "assert(" ascii nocase
        $exec3 = "create_function(" ascii nocase
        $exec4 = "call_user_func(" ascii nocase
        $exec5 = /preg_replace\s*\([^)]*\/e/ ascii

        // 시스템 명령 실행
        $cmd1 = "system(" ascii nocase
        $cmd2 = "exec(" ascii nocase
        $cmd3 = "shell_exec(" ascii nocase
        $cmd4 = "passthru(" ascii nocase
        $cmd5 = "popen(" ascii nocase
        $cmd6 = "proc_open(" ascii nocase
        $cmd7 = /`[^`]+`/ ascii  // 백틱

        // 사용자 입력
        $input1 = "$_GET" ascii
        $input2 = "$_POST" ascii
        $input3 = "$_REQUEST" ascii
        $input4 = "$_COOKIE" ascii
        $input5 = "$_FILES" ascii

    condition:
        // PHP 파일 시그니처
        (uint16(0) == 0x3F3C or uint16(0) == 0x683C) and  // <? or <h

        // 실행 함수 + 사용자 입력
        (any of ($exec*) or any of ($cmd*)) and
        any of ($input*)
}

// ============================================
// 난독화 웹쉘 탐지
// ============================================

rule PHP_Webshell_Obfuscated {
    meta:
        description = "난독화된 PHP 웹쉘"
        severity = "critical"

    strings:
        // 인코딩 함수
        $enc1 = "base64_decode(" ascii nocase
        $enc2 = "gzinflate(" ascii nocase
        $enc3 = "gzuncompress(" ascii nocase
        $enc4 = "gzdecode(" ascii nocase
        $enc5 = "str_rot13(" ascii nocase
        $enc6 = "convert_uudecode(" ascii nocase

        // 문자 조합
        $chr1 = /chr\s*\(\s*\d+\s*\)/ ascii
        $chr2 = /\\x[0-9a-fA-F]{2}/ ascii
        $chr3 = /\\[0-7]{3}/ ascii

        // 동적 함수 호출
        $dyn1 = /\$\w+\s*\(\s*\$/ ascii
        $dyn2 = /\$\{\s*\$/ ascii
        $dyn3 = /\$\w+\s*=\s*['"]\w+['"].*\$\w+\s*\(/ ascii

        // Base64 인코딩된 위험 함수
        $b64_eval = "ZXZhbC" ascii      // base64("eval")
        $b64_exec = "ZXhlYy" ascii      // base64("exec")
        $b64_system = "c3lzdGVt" ascii  // base64("system")
        $b64_assert = "YXNzZXJ0" ascii  // base64("assert")

    condition:
        // 다중 인코딩 레이어
        (2 of ($enc*)) or

        // 문자 조합으로 함수 생성
        (#chr1 > 5 or #chr2 > 10) or

        // 동적 함수 호출 + 인코딩
        (any of ($dyn*) and any of ($enc*)) or

        // Base64 인코딩된 위험 함수
        any of ($b64*)
}

// ============================================
// 유명 웹쉘 시그니처
// ============================================

rule Webshell_C99 {
    meta:
        description = "C99 웹쉘"
        severity = "critical"
        reference = "https://github.com/tennc/webshell"

    strings:
        $s1 = "c99shell" ascii nocase
        $s2 = "c99_buff_prepare" ascii
        $s3 = "c99_sess_put" ascii
        $s4 = "c99ftpbrutecheck" ascii
        $s5 = "c99fsearch" ascii

    condition:
        2 of them
}

rule Webshell_R57 {
    meta:
        description = "R57 웹쉘"
        severity = "critical"

    strings:
        $s1 = "r57shell" ascii nocase
        $s2 = "r57_pwd_hash" ascii
        $s3 = "r57" ascii nocase
        $s4 = "Safe_Mode Bypass" ascii
        $s5 = "Dumper" ascii

    condition:
        3 of them
}

rule Webshell_WSO {
    meta:
        description = "WSO (Web Shell by oRb) 웹쉘"
        severity = "critical"

    strings:
        $s1 = "WSO" ascii
        $s2 = "Web Shell by oRb" ascii nocase
        $s3 = "wso_version" ascii
        $s4 = "FilesMan" ascii

    condition:
        2 of them
}

rule Webshell_B374k {
    meta:
        description = "B374k 웹쉘"
        severity = "critical"

    strings:
        $s1 = "b374k" ascii nocase
        $s2 = "b374k_config" ascii
        $s3 = "b374k 2.8" ascii

    condition:
        any of them
}

rule Webshell_Weevely {
    meta:
        description = "Weevely 웹쉘 (백도어 생성기)"
        severity = "critical"

    strings:
        $s1 = /\$\w=\$\w\(\'\',\$\w\(\$\w\(\$\w/ ascii
        $s2 = "str_replace" ascii
        $s3 = "base64" ascii

    condition:
        $s1 and $s2 and $s3 and filesize < 5KB
}

// ============================================
// 파일 업로드 취약점 악용
// ============================================

rule PHP_File_Upload_Shell {
    meta:
        description = "파일 업로드를 통한 웹쉘"
        severity = "high"

    strings:
        $upload1 = "move_uploaded_file" ascii
        $upload2 = "$_FILES" ascii
        $upload3 = "copy(" ascii

        $write1 = "file_put_contents" ascii
        $write2 = "fwrite(" ascii
        $write3 = "fputs(" ascii

        $exec = /(eval|assert|system|exec|shell_exec|passthru)\s*\(/ ascii nocase

    condition:
        (any of ($upload*) or any of ($write*)) and $exec
}

// ============================================
// 숨겨진 백도어
// ============================================

rule PHP_Hidden_Backdoor {
    meta:
        description = "숨겨진 PHP 백도어"
        severity = "critical"

    strings:
        // HTTP 헤더를 통한 명령 수신
        $hdr1 = "$_SERVER['HTTP_" ascii
        $hdr2 = "getallheaders()" ascii
        $hdr3 = "apache_request_headers()" ascii

        // 특정 파라미터 체크
        $chk1 = /if\s*\(\s*isset\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)\s*\[\s*['"][^'"]{32,}['"]\s*\]/ ascii
        $chk2 = /md5\s*\(\s*\$_(GET|POST|REQUEST)/ ascii

        // 실행
        $exec = /(eval|assert|system|exec|passthru|shell_exec)\s*\(/ ascii nocase

    condition:
        (any of ($hdr*) or any of ($chk*)) and $exec
}

// ============================================
// ASP/ASPX 웹쉘
// ============================================

rule ASPX_Webshell_Generic {
    meta:
        description = "ASP.NET 웹쉘"
        severity = "high"

    strings:
        $asp1 = "Request.Form" ascii nocase
        $asp2 = "Request.QueryString" ascii nocase
        $asp3 = "Request[" ascii nocase

        $exec1 = "Process.Start" ascii
        $exec2 = "cmd.exe" ascii nocase
        $exec3 = "powershell" ascii nocase
        $exec4 = "Eval(" ascii nocase
        $exec5 = "Execute(" ascii nocase

    condition:
        any of ($asp*) and any of ($exec*)
}

// ============================================
// JSP 웹쉘
// ============================================

rule JSP_Webshell_Generic {
    meta:
        description = "JSP 웹쉘"
        severity = "high"

    strings:
        $jsp1 = "request.getParameter" ascii
        $jsp2 = "Runtime.getRuntime().exec" ascii
        $jsp3 = "ProcessBuilder" ascii

        $cmd1 = "cmd.exe" ascii nocase
        $cmd2 = "/bin/sh" ascii
        $cmd3 = "/bin/bash" ascii

    condition:
        $jsp1 and ($jsp2 or $jsp3) or
        ($jsp2 or $jsp3) and any of ($cmd*)
}

CLI 사용법

기본 스캔

# 단일 파일 스캔
yara rules.yar target_file.php

# 디렉토리 재귀 스캔
yara -r rules.yar /var/www/html/

# 여러 규칙 파일 사용
yara -r rules1.yar rules2.yar /var/www/html/

출력 옵션

# 매칭된 문자열 표시
yara -s rules.yar file.php

# 메타데이터 표시
yara -m rules.yar file.php

# 태그만 표시
yara -t webshell rules.yar file.php

# 매칭 개수 제한
yara -l 10 rules.yar file.php

# 에러 무시
yara -w rules.yar /var/www/html/

# 타임아웃 설정 (초)
yara -a 30 rules.yar large_file.bin

출력 예시

$ yara -s webshell_rules.yar suspicious.php

PHP_Webshell_Generic suspicious.php
0x15:$exec1: eval(
0x25:$input2: $_POST

PHP_Webshell_Obfuscated suspicious.php
0x35:$enc1: base64_decode(
0x50:$dyn1: $func($

Python 연동

기본 사용

#!/usr/bin/env python3
"""YARA Python 바인딩 사용 예시"""

import yara
import os
from pathlib import Path

class YaraScanner:
    def __init__(self, rules_path):
        """YARA 규칙 로드"""
        if os.path.isdir(rules_path):
            # 디렉토리의 모든 .yar 파일 컴파일
            rule_files = {}
            for f in Path(rules_path).glob("*.yar"):
                rule_files[f.stem] = str(f)
            self.rules = yara.compile(filepaths=rule_files)
        else:
            # 단일 파일
            self.rules = yara.compile(filepath=rules_path)

    def scan_file(self, filepath):
        """파일 스캔"""
        try:
            matches = self.rules.match(filepath)

            if matches:
                return {
                    "file": filepath,
                    "malicious": True,
                    "matches": [
                        {
                            "rule": m.rule,
                            "tags": m.tags,
                            "meta": m.meta,
                            "strings": [
                                {
                                    "offset": s[0],
                                    "identifier": s[1],
                                    "data": s[2].decode("utf-8", errors="replace")[:100]
                                }
                                for s in m.strings
                            ]
                        }
                        for m in matches
                    ]
                }

            return {"file": filepath, "malicious": False}

        except yara.Error as e:
            return {"file": filepath, "error": str(e)}

    def scan_data(self, data, filename="memory"):
        """메모리 데이터 스캔"""
        if isinstance(data, str):
            data = data.encode()

        matches = self.rules.match(data=data)

        if matches:
            return {
                "source": filename,
                "malicious": True,
                "rules": [m.rule for m in matches]
            }

        return {"source": filename, "malicious": False}

    def scan_directory(self, directory, extensions=None):
        """디렉토리 스캔"""
        if extensions is None:
            extensions = {".php", ".phtml", ".inc", ".asp", ".aspx", ".jsp"}

        findings = []
        scanned = 0

        for root, dirs, files in os.walk(directory):
            # 숨김 디렉토리 제외
            dirs[:] = [d for d in dirs if not d.startswith(".")]

            for filename in files:
                ext = os.path.splitext(filename)[1].lower()
                if ext in extensions:
                    filepath = os.path.join(root, filename)
                    result = self.scan_file(filepath)
                    scanned += 1

                    if result.get("malicious"):
                        findings.append(result)
                        print(f"🚨 탐지: {filepath}")
                        for m in result["matches"]:
                            print(f"   규칙: {m['rule']}")

        return {
            "scanned": scanned,
            "findings": findings
        }


# 사용 예시
if __name__ == "__main__":
    scanner = YaraScanner("webshell_rules.yar")

    # 파일 스캔
    result = scanner.scan_file("/var/www/html/upload/shell.php")
    print(result)

    # 문자열 스캔
    code = '<?php eval($_POST["cmd"]); ?>'
    result = scanner.scan_data(code)
    print(result)

    # 디렉토리 스캔
    results = scanner.scan_directory("/var/www/html")
    print(f"스캔 완료: {results['scanned']}개 파일, {len(results['findings'])}개 위협")

콜백 사용 (대용량 스캔)

"""콜백을 사용한 효율적인 스캔"""

import yara

def match_callback(data):
    """매치 발견 시 콜백"""
    print(f"규칙: {data['rule']}")
    print(f"태그: {data['tags']}")
    print(f"메타: {data['meta']}")

    # CALLBACK_CONTINUE: 계속 스캔
    # CALLBACK_ABORT: 스캔 중단
    return yara.CALLBACK_CONTINUE

rules = yara.compile(filepath="rules.yar")

# 콜백과 함께 스캔
matches = rules.match(
    "/path/to/file",
    callback=match_callback,
    which_callbacks=yara.CALLBACK_MATCHES
)

실시간 모니터링

watchdog + YARA

#!/usr/bin/env python3
"""파일 시스템 실시간 모니터링 + YARA 스캔"""

import yara
import os
import shutil
import logging
from datetime import datetime
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

# 로깅 설정
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class WebshellMonitor(FileSystemEventHandler):
    def __init__(self, rules_path, quarantine_dir="/var/quarantine"):
        self.rules = yara.compile(filepath=rules_path)
        self.quarantine_dir = quarantine_dir
        self.extensions = {".php", ".phtml", ".inc", ".asp", ".aspx", ".jsp"}

        os.makedirs(quarantine_dir, exist_ok=True)
        logger.info(f"YARA 규칙 로드 완료")

    def should_scan(self, filepath):
        """스캔 대상 여부 확인"""
        ext = os.path.splitext(filepath)[1].lower()
        return ext in self.extensions

    def on_created(self, event):
        """파일 생성 시"""
        if event.is_directory:
            return
        if self.should_scan(event.src_path):
            self.scan_file(event.src_path, "created")

    def on_modified(self, event):
        """파일 수정 시"""
        if event.is_directory:
            return
        if self.should_scan(event.src_path):
            self.scan_file(event.src_path, "modified")

    def on_moved(self, event):
        """파일 이동 시"""
        if event.is_directory:
            return
        if self.should_scan(event.dest_path):
            self.scan_file(event.dest_path, "moved")

    def scan_file(self, filepath, event_type):
        """파일 스캔"""
        try:
            if not os.path.exists(filepath):
                return

            matches = self.rules.match(filepath)

            if matches:
                rules = [m.rule for m in matches]
                logger.warning(f"🚨 웹쉘 탐지 [{event_type}]: {filepath}")
                logger.warning(f"   매칭 규칙: {rules}")

                self.quarantine_file(filepath, rules)
                self.send_alert(filepath, rules, event_type)
            else:
                logger.debug(f"✅ 정상: {filepath}")

        except Exception as e:
            logger.error(f"스캔 오류 ({filepath}): {e}")

    def quarantine_file(self, filepath, rules):
        """파일 격리"""
        try:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            basename = os.path.basename(filepath)
            quarantine_name = f"{timestamp}_{basename}"
            quarantine_path = os.path.join(self.quarantine_dir, quarantine_name)

            # 메타데이터 저장
            meta_path = quarantine_path + ".meta"
            with open(meta_path, "w") as f:
                f.write(f"원본 경로: {filepath}\n")
                f.write(f"탐지 시간: {datetime.now()}\n")
                f.write(f"매칭 규칙: {rules}\n")

            # 파일 이동
            shutil.move(filepath, quarantine_path)
            logger.info(f"   격리 완료: {quarantine_path}")

        except Exception as e:
            logger.error(f"격리 실패: {e}")

    def send_alert(self, filepath, rules, event_type):
        """알림 전송 (구현 필요)"""
        # Slack, Telegram, Email 등으로 알림
        pass


def main():
    import argparse

    parser = argparse.ArgumentParser(description="YARA 기반 웹쉘 모니터링")
    parser.add_argument("-r", "--rules", default="webshell_rules.yar", help="YARA 규칙 파일")
    parser.add_argument("-d", "--directory", default="/var/www/html", help="모니터링 디렉토리")
    parser.add_argument("-q", "--quarantine", default="/var/quarantine", help="격리 디렉토리")
    args = parser.parse_args()

    event_handler = WebshellMonitor(args.rules, args.quarantine)
    observer = Observer()
    observer.schedule(event_handler, args.directory, recursive=True)
    observer.start()

    logger.info(f"🔍 모니터링 시작: {args.directory}")
    logger.info(f"   규칙: {args.rules}")
    logger.info(f"   격리 디렉토리: {args.quarantine}")

    try:
        while True:
            import time
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
        logger.info("모니터링 종료")

    observer.join()


if __name__ == "__main__":
    main()

systemd 서비스로 등록

# /etc/systemd/system/yara-monitor.service

[Unit]
Description=YARA Webshell Monitor
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/bin/python3 /opt/security/yara_monitor.py \
    --rules /opt/security/rules/webshell_rules.yar \
    --directory /var/www/html \
    --quarantine /var/quarantine
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
# 서비스 등록 및 시작
sudo systemctl daemon-reload
sudo systemctl enable yara-monitor
sudo systemctl start yara-monitor
sudo systemctl status yara-monitor

공개 규칙 모음

주요 소스

저장소 규칙 수 주요 내용
Neo23x0/signature-base 3,000+ 웹쉘, APT, 악성코드
Yara-Rules/rules 2,000+ 범용
InQuest/yara-rules 500+ 문서 악성코드
bartblaze/Yara-rules 300+ 랜섬웨어
kevthehermit/YaraRules 200+ 악성코드
cuckoosandbox/community 500+ 샌드박스 분석용

규칙 다운로드

# 디렉토리 생성
mkdir -p /opt/yara-rules
cd /opt/yara-rules

# signature-base (웹쉘 전문)
git clone https://github.com/Neo23x0/signature-base.git

# Yara-Rules (범용)
git clone https://github.com/Yara-Rules/rules.git yara-rules

# 규칙 확인
ls signature-base/yara/
# apt_*.yar        - APT 공격
# crime_*.yar      - 사이버 범죄
# gen_*.yar        - 일반 악성코드
# thor_webshells.yar - 웹쉘 (추천)

규칙 통합 사용

"""여러 규칙 소스 통합"""

import yara
from pathlib import Path

def compile_all_rules(rules_dirs):
    """모든 규칙 디렉토리 통합 컴파일"""
    rule_files = {}

    for rules_dir in rules_dirs:
        for yar_file in Path(rules_dir).rglob("*.yar"):
            # 파일명 중복 방지
            key = f"{rules_dir.name}_{yar_file.stem}"
            rule_files[key] = str(yar_file)

    print(f"총 {len(rule_files)}개 규칙 파일 로드")

    return yara.compile(filepaths=rule_files)

# 사용
rules = compile_all_rules([
    Path("/opt/yara-rules/signature-base/yara"),
    Path("/opt/yara-rules/yara-rules/malware"),
])

다른 방식과 비교

탐지 방식 비교표

방식 원리 속도 알려진 위협 변형 제로데이 AI
해시 비교 파일 지문 0.01ms ✓✓✓
YARA 패턴 매칭 1-10ms ✓✓
정규식 텍스트 매칭 1-5ms
ML 분류기 학습된 모델 10-50ms ✓✓ ✓✓ ⚠️
LLM 코드 이해 100ms+ ✓✓ ✓✓✓ ✓✓

각 방식의 장단점

해시 비교

장점: 오탐 0%, 가장 빠름
단점: 1비트만 달라도 탐지 못함
용도: 알려진 악성코드 즉시 차단

YARA

장점: 유연한 패턴, 커뮤니티 활성화, 업계 표준
단점: 규칙 작성 필요, 고급 난독화 취약
용도: 알려진 패턴 + 변형 탐지

AI/ML

장점: 새로운 위협 탐지, 난독화 강함
단점: 오탐 가능, 상대적으로 느림
용도: 제로데이, 고도화된 공격

권장 조합

Layer 1: 해시 비교 (0.01ms)
    │     알려진 악성코드 즉시 차단
    │     오탐: 0%
    ▼
Layer 2: YARA (1-10ms)
    │     패턴 기반 탐지
    │     오탐: 매우 낮음
    ▼
Layer 3: AI 모델 (50-100ms)
    │     난독화/제로데이 탐지
    │     오탐: 낮음 (검토 필요)
    ▼
[최종 판정]

참고 자료