概述:Yara 入门,资料、工具整理

[toc]

本文部分内容由 AI 生成,经人工修订。
## GitHub

Yara Style Guide

  1. Neo23x0/YARA-Style-Guide: A specification and style guide for YARA rules
  2. Neo23x0/YARA-Performance-Guidelines: A guide on how to write fast and memory friendly YARA rules

Yara 相关技术文章

Yara 工具

  1. threatexpert/yarchk: 基于yara规则并发扫描Windows终端所有进程(Scan all processes on a Windows terminal quickly based on YARA rules.)
    • 可以一次性检测系统内所有进程,但是不能根据事件检测

快速手册

  1. 帮助命令

    YARA 4.5.4, the pattern matching swiss army knife.
    Usage: yara [OPTION]... [NAMESPACE:]RULES_FILE... FILE | DIR | PID
     
    Mandatory arguments to long options are mandatory for short options too.
     
           --atom-quality-table=FILE           path to a file with the atom quality table
      -C,  --compiled-rules                    load compiled rules
      -c,  --count                             print only number of matches
      -E,  --strict-escape                     warn on unknown escape sequences
      -d,  --define=VAR=VALUE                  define external variable
      -q,  --disable-console-logs              disable printing console log messages
           --fail-on-warnings                  fail on warnings
      -f,  --fast-scan                         fast matching mode
      -h,  --help                              show this help and exit
      -i,  --identifier=IDENTIFIER             print only rules named IDENTIFIER
           --max-process-memory-chunk=NUMBER   set maximum chunk size while reading process memory (default=1073741824)
      -l,  --max-rules=NUMBER                  abort scanning after matching a NUMBER of rules
           --max-strings-per-rule=NUMBER       set maximum number of strings per rule (default=10000)
      -x,  --module-data=MODULE=FILE           pass FILE's content as extra data to MODULE
      -n,  --negate                            print only not satisfied rules (negate)
      -N,  --no-follow-symlinks                do not follow symlinks when scanning
      -w,  --no-warnings                       disable warnings
      -m,  --print-meta                        print metadata
      -D,  --print-module-data                 print module data
      -M,  --module-names                      show module names
      -e,  --print-namespace                   print rules' namespace
      -S,  --print-stats                       print rules' statistics
      -s,  --print-strings                     print matching strings
      -L,  --print-string-length               print length of matched strings
      -X,  --print-xor-key                     print xor key and plaintext of matched strings
      -g,  --print-tags                        print tags
      -r,  --recursive                         recursively search directories
           --scan-list                         scan files listed in FILE, one per line
      -z,  --skip-larger=NUMBER                skip files larger than the given size when scanning a directory
      -k,  --stack-size=SLOTS                  set maximum stack size (default=16384)
      -t,  --tag=TAG                           print only rules tagged as TAG
      -p,  --threads=NUMBER                    use the specified NUMBER of threads to scan a directory
      -a,  --timeout=SECONDS                   abort scanning after the given number of SECONDS
      -v,  --version                           show version information
  2. 检测目标进程

    yara32.exe {yara规则文件} {PID}
  3. 检测文件夹

    yara32.exe {yara规则文件} {待检测目录}

libyara

由于我个人 C++ 的偏向,所以需要静态库使用,因此需要 libyara 相关内容。这一部分包括 libyara 的编译、使用、扩展等

下载 Yara 项目,打开 Windows 目录下的 sln 文件即可,编译时所需工具集调整为自己当前环境的工具集即可。

改动

检测所有进程

// 如何使用 
// cmd 输入 `yara32.exe {yara规则文件} -1`,即可检测所有进程
if (result == ERROR_COULD_NOT_OPEN_FILE)  // 替换这个括号里边的内容即可
    {
      // Is it a PID? To be a PID it must be made up entirely of digits.
      char_t* endptr = NULL;
      long pid = _tcstol(argv[argc - 1], &endptr, 10);
 
      if (pid >= -1 && argv[argc - 1] != NULL && *endptr == '\x00')
      {
        // 扫描所有进程
        if (argc == 2 && atoi(argv[1]) == -1)
        {
          printf("Now will scan all the Process\n");
          int count;
          int* pids = GetAllProcessPids(&count);
 
          // 不扫描自己,消除误报
          DWORD this_pid = GetCurrentProcessId();
          if (pids != NULL)
          {
            printf("Total processes: %d\n", count);
            printf("Total processes %d\n", count);
            printf("Process PIDs:\n");
            printf(stderr, "Now will scan all the Process\n");
            for (int i = 0; i < count; i++)
            {
              printf(stderr, "Now scanning PID is %d\n", pids[i]);
              if (this_pid == pids[i])
              {
                continue;
              }
              // 复现问题,不能用printf,不清楚为什么会直接崩溃,疑似内存冲突
              printf(stderr, "Now scanning PID is %d\n", pids[i]);
              result = yr_scanner_scan_proc(scanner, pids[i]);
              if (result == 2)
              {
                printf(
                    stderr,
                    "Scanning %d Fail, Maybe You should get to priv level to "
                    "system or your EDR protect it and you can ignore it \n",
                    pids[i]);
              }
            }
            free(pids);  // 释放内存
          }
        }
        else
        {
          // 扫描指定进程
          result = yr_scanner_scan_proc(scanner, atoi(argv[1]));
        }
        // result = yr_scanner_scan_proc(scanner, (int) pid);
      }
    }

动态检测:基于进程创建事件

概述:通过监控进程创建事件,实现新进程启动时自动进行 Yara 扫描,相比定时全量扫描更加高效。

实现方案对比

方案优点缺点适用场景
WMI 事件订阅实现简单,用户态即可有一定延迟一般安全工具
ETW性能好,信息丰富需要管理员权限专业 EDR
内核回调最底层,无延迟需要驱动开发内核级防护

方案一:WMI 事件订阅

通过 WMI 监听 Win32_ProcessStartTrace 事件,当有新进程创建时触发回调。

C++ 实现示例

#include <windows.h>
#include <wbemidl.h>
#include <comdef.h>
#include <string>
#include <iostream>
 
#pragma comment(lib, "wbemuuid.lib")
 
// 进程创建回调函数类型
typedef void (*ProcessCreatedCallback)(DWORD pid, const std::wstring& processName);
 
class WMIProcessMonitor {
private:
    IWbemLocator* pLocator = nullptr;
    IWbemServices* pServices = nullptr;
    IWbemStubSink* pStubSink = nullptr;
    ProcessCreatedCallback callback;
 
public:
    WMIProcessMonitor(ProcessCreatedCallback cb) : callback(cb) {}
 
    bool Initialize() {
        HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
        if (FAILED(hr)) return false;
 
        hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
            RPC_C_AUTHN_LEVEL_DEFAULT,
            RPC_C_IMP_LEVEL_IMPERSONATE,
            NULL, EOAC_NONE, NULL);
 
        hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
            IID_IWbemLocator, (LPVOID*)&pLocator);
        if (FAILED(hr)) return false;
 
        hr = pLocator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL,
            0, NULL, 0, 0, &pServices);
        if (FAILED(hr)) return false;
 
        return true;
    }
 
    void StartMonitoring() {
        // 使用 WQL 查询进程创建事件
        // SELECT * FROM __InstanceCreationEvent WITHIN 1 
        // WHERE TargetInstance ISA 'Win32_Process'
        // 实际实现需要设置事件接收器
    }
 
    ~WMIProcessMonitor() {
        if (pServices) pServices->Release();
        if (pLocator) pLocator->Release();
        CoUninitialize();
    }
};
 
// Yara 扫描回调
void OnProcessCreated(DWORD pid, const std::wstring& processName) {
    std::wcout << L"[+] New process: " << processName << L" (PID: " << pid << L")" << std::endl;
    
    // 调用 Yara 扫描
    std::string cmd = "yara32.exe rules.yar " + std::to_string(pid);
    int result = system(cmd.c_str());
    
    if (result == 0) {
        std::cout << "[!] Yara rule matched!" << std::endl;
        // 可以在此添加处置逻辑,如终止进程、记录日志等
    }
}

方案二:ETW (Event Tracing for Windows)

ETW 提供更高效的内核级事件追踪,适合高性能场景。

关键代码

#include <windows.h>
#include <tdh.h>
#include <evntrace.h>
 
#pragma comment(lib, "tdh.lib")
 
// 定义进程创建事件的 Provider GUID
// Microsoft-Windows-Kernel-Process
static const GUID ProcessProviderGuid = 
{ 0x22fb2cd6, 0x0e7b, 0x422b, { 0xa0, 0xc7, 0x2f, 0xad, 0x18, 0x42, 0x6e, 0xef } };
 
class ETWProcessMonitor {
private:
    TRACEHANDLE hSession = 0;
    TRACEHANDLE hTrace = 0;
    EVENT_TRACE_PROPERTIES* pProperties = nullptr;
 
public:
    bool Start() {
        // 创建 ETW 实时会话
        pProperties = (EVENT_TRACE_PROPERTIES*)malloc(sizeof(EVENT_TRACE_PROPERTIES) + 1024);
        ZeroMemory(pProperties, sizeof(EVENT_TRACE_PROPERTIES) + 1024);
        
        pProperties->Wnode.BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + 1024;
        pProperties->Wnode.Guid = ProcessProviderGuid;
        pProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
        pProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
        pProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
        strcpy_s((char*)pProperties + sizeof(EVENT_TRACE_PROPERTIES), 32, "YaraProcessMonitor");
 
        ULONG status = StartTrace(&hSession, L"YaraProcessMonitor", pProperties);
        if (status != ERROR_SUCCESS) {
            return false;
        }
 
        // 启用进程 Provider
        status = EnableTraceEx2(hSession, &ProcessProviderGuid, 
            EVENT_CONTROL_CODE_ENABLE_PROVIDER,
            TRACE_LEVEL_INFORMATION, 
            KERNEL_PROCESS_PROVIDER_GROUP_GUID,  // Process组
            0, 0, NULL);
 
        return status == ERROR_SUCCESS;
    }
 
    void ProcessEvent(PEVENT_RECORD pEvent) {
        // 解析进程创建事件
        // Event ID 1 = ProcessStart
        if (pEvent->EventHeader.EventDescriptor.Id == 1) {
            DWORD pid = 0;
            // 提取 PID 和进程名
            // ... TDH 解析逻辑
            // 然后调用 Yara 扫描
        }
    }
 
    void Stop() {
        if (hSession) {
            ControlTrace(hSession, NULL, pProperties, EVENT_TRACE_CONTROL_STOP);
        }
        free(pProperties);
    }
};

方案三:配合 Sysmon

如果已部署 Sysmon,可以直接订阅 Sysmon 事件日志,无需自己实现监控。

PowerShell 示例

# 监控 Sysmon 进程创建事件(Event ID 1)
$action = {
    $event = $EventArgs.NewEvent
    $pid = $event.Message -match "ProcessId:\s*(\d+)" | % { $Matches[1] }
    $processName = $event.Message -match "Image:\s*(.+)" | % { $Matches[1] }
    
    Write-Host "[+] New process: $processName (PID: $pid)"
    
    # 调用 Yara 扫描
    & yara32.exe rules.yar $pid
}
 
Register-WmiEvent -Query "SELECT * FROM __InstanceCreationEvent WHERE TargetInstance ISA 'Win32_NTLogEvent' AND TargetInstance.LogFile='Microsoft-Windows-Sysmon/Operational' AND TargetInstance.EventIdentifier=1" -Action $action

整合到 libyara

将上述监控逻辑与 libyara 整合,实现完整的动态扫描工具:

// 整合框架
class YaraDynamicScanner {
private:
    YR_COMPILER* compiler = nullptr;
    YR_RULES* rules = nullptr;
    WMIProcessMonitor* monitor = nullptr;
 
public:
    bool LoadRules(const char* ruleFile) {
        if (yr_compiler_create(&compiler) != ERROR_SUCCESS) return false;
        
        FILE* f = fopen(ruleFile, "r");
        if (!f) return false;
        
        int errors = yr_compiler_add_file(compiler, f, NULL, ruleFile);
        fclose(f);
        
        if (errors > 0) return false;
        
        yr_compiler_get_rules(compiler, &rules);
        return true;
    }
 
    void OnProcessEvent(DWORD pid) {
        YR_SCANNER* scanner;
        yr_scanner_create(rules, &scanner);
        
        int result = yr_scanner_scan_proc(scanner, pid);
        if (result == ERROR_SUCCESS) {
            printf("[+] Scan completed for PID: %d\n", pid);
        }
        
        yr_scanner_destroy(scanner);
    }
 
    void Start() {
        monitor = new WMIProcessMonitor([this](DWORD pid, const std::wstring& name) {
            OnProcessEvent(pid);
        });
        monitor->Initialize();
        monitor->StartMonitoring();
    }
};

注意事项

  1. 权限要求:扫描其他进程需要管理员或 SYSTEM 权限
  2. 性能影响:频繁扫描可能影响系统性能,建议设置白名单
  3. 误报处理:部分进程可能拒绝访问,需要优雅处理
  4. 竞态条件:进程可能快速退出,扫描前需检查进程是否存在

TODO

  • 根据创建进程事件动态检测