概述:Yara 入门,资料、工具整理
[toc]
-
- [alexander-hanel/IDA_yara: A python script that can be used to scan data within in an IDB using Yara.](https://github.com/alexander-hanel/[[【工具】IDA 使用详记|IDA]]_yara)
- 集齐了 Yara 学习过程中需要的工具、资料等,一个仓库就够
-
- threatexpert/yarchk: 基于yara规则并发扫描Windows终端所有进程(Scan all processes on a Windows terminal quickly based on YARA rules.)
- 一个方便快速使用 Yara 扫描系统所有进程内存的魔改,解决yara工具本身只支持一次执行只扫描一个进程内存的不便。
Yara Style Guide
- Neo23x0/YARA-Style-Guide: A specification and style guide for YARA rules
- Neo23x0/YARA-Performance-Guidelines: A guide on how to write fast and memory friendly YARA rules
Yara 相关技术文章
-
- 利用Yara快速狩猎内存中的威胁-先知社区
- 这篇文章的介绍和使用相对全面,新手友好
-
- YARA-规则匹配神器-简单使用篇 - 知乎
- YARA 的使用说明,可以简单参考下
Yara 工具
- threatexpert/yarchk: 基于yara规则并发扫描Windows终端所有进程(Scan all processes on a Windows terminal quickly based on YARA rules.)
- 可以一次性检测系统内所有进程,但是不能根据事件检测
快速手册
-
帮助命令
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 -
检测目标进程
yara32.exe {yara规则文件} {PID} -
检测文件夹
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();
}
};注意事项
- 权限要求:扫描其他进程需要管理员或 SYSTEM 权限
- 性能影响:频繁扫描可能影响系统性能,建议设置白名单
- 误报处理:部分进程可能拒绝访问,需要优雅处理
- 竞态条件:进程可能快速退出,扫描前需检查进程是否存在
TODO
- 根据创建进程事件动态检测