4、用户态监控:API Hook技术、ETW利用、用户态日志采集

聊到EDR的用户态监控,我脑子里第一个蹦出来的词就是「暗战」。

为什么这么说?因为攻击者最常活动的区域就在用户态。他们调用API读写文件、创建进程、注入代码,这些动作全得经过操作系统提供的接口。而我们EDR要做的,就是在这些接口上「安插眼线」。

说白了,用户态监控就是一场钩子与反钩子的博弈。今天我就把三种最核心的手段掰开揉碎了讲给你听。

4.1 API Hook技术:最经典的「截胡」手段

API Hook,顾名思义,就是把系统API的执行流程给「钩」住。当某个进程想调用CreateFile打开一个恶意文件时,我们的代码先于原始函数执行,检查参数、记录日志,甚至直接拦截。

常见的Hook点

  • IAT Hook:修改导入地址表,把函数地址指向我们的检测函数。简单,但容易被绕过。
  • Inline Hook:直接修改函数前几个字节,跳转到我们的代码。我在项目中遇到过,有些恶意软件会做完整性校验,一改就崩。
  • Detour Hook:微软的Detours库提供的方式,相对稳定,但部署起来稍重。

核心思路:Hook不是目的,上下文才是。你不仅要记录「调用了什么API」,还要记录「谁调用的、参数是什么、调用栈长什么样」。

举个例子,检测进程注入时,我会重点关注OpenProcess + VirtualAllocEx + WriteProcessMemory + CreateRemoteThread这个组合拳。单独看每个API都合法,但串在一起,基本就是恶意行为。

// 伪代码:Inline Hook 示例
// 原始函数地址
LPVOID pOriginalCreateFile = GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateFileW");

// 保存前5字节
BYTE originalBytes[5];
memcpy(originalBytes, pOriginalCreateFile, 5);

// 写入JMP指令跳转到我们的检测函数
BYTE jmpCode[5] = { 0xE9, 0x00, 0x00, 0x00, 0x00 };
DWORD dwRelativeOffset = (DWORD)MyCreateFileHook - (DWORD)pOriginalCreateFile - 5;
memcpy(&jmpCode[1], &dwRelativeOffset, 4);
WriteProcessMemory(GetCurrentProcess(), pOriginalCreateFile, jmpCode, 5, NULL);

// 检测函数
HANDLE WINAPI MyCreateFileHook(LPCWSTR lpFileName, DWORD dwDesiredAccess, ...) {
    // 记录日志
    LogApiCall("CreateFileW", lpFileName, GetCurrentProcessId());
    // 恢复原始字节,调用原始函数
    WriteProcessMemory(GetCurrentProcess(), pOriginalCreateFile, originalBytes, 5, NULL);
    HANDLE hResult = CreateFileW(lpFileName, dwDesiredAccess, ...);
    // 重新Hook
    WriteProcessMemory(GetCurrentProcess(), pOriginalCreateFile, jmpCode, 5, NULL);
    return hResult;
}

避坑指南:我曾经在Hook NtCreateThreadEx时,没处理好递归调用,导致死循环蓝屏。记住:Hook函数内部绝对不能直接调用被Hook的原始函数,必须先恢复原始字节,调用完再重新Hook。

4.2 ETW(Event Tracing for Windows):微软官方给的「后门」

说实话,我个人越来越喜欢用ETW。为什么?因为它是操作系统原生支持的,不需要修改任何代码,稳定性比API Hook高一个量级。

ETW的原理很简单:Windows内核和许多系统组件(如.NET、CLR、内核)都会在关键操作时主动抛出事件。我们只需要写一个消费者程序,订阅这些事件即可。

EDR常用的ETW Provider

Provider名称 GUID 监控内容
Microsoft-Windows-Kernel-Process {22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716} 进程创建、终止、线程创建
Microsoft-Windows-Kernel-File {EDD08927-9CC4-4E65-B970-C2560FB5C289} 文件创建、删除、重命名、读写
Microsoft-Windows-Kernel-Registry {70EB4F03-C1DE-4F73-A051-33D13D5413BD} 注册表操作
Microsoft-Windows-DotNETRuntime {E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4} .NET程序集加载、JIT编译

你想想看,攻击者用mimikatz抓密码,或者用cobalt strike的execute-assembly加载.NET payload,这些动作都会触发ETW事件。我们只要订阅了对应的Provider,就能零侵入地拿到第一手情报。

我的经验:ETW的缺点是事件量巨大。一个正常的Windows系统每秒可能产生数万个事件。我建议在EDR中做两级过滤:第一级在内核态或驱动层做粗筛,只保留关键事件;第二级在用户态做深度分析。

// 伪代码:ETW消费者订阅示例
// 1. 开启Trace
EVENT_TRACE_LOGFILEW traceLogFile = { 0 };
traceLogFile.LoggerName = L"EDR-Process-Monitor";
traceLogFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
traceLogFile.EventRecordCallback = (PEVENT_RECORD_CALLBACK)EventRecordCallback;

TRACEHANDLE hTrace = OpenTraceW(&traceLogFile);
if (hTrace == INVALID_PROCESSTRACE_HANDLE) {
    // 处理错误
}

// 2. 启用指定的Provider
ENABLE_TRACE_PARAMETERS enableParams = { 0 };
enableParams.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;
EnableTraceEx2(
    hTrace,
    &GUID_KERNEL_PROCESS,  // 进程Provider
    EVENT_CONTROL_CODE_ENABLE_PROVIDER,
    TRACE_LEVEL_INFORMATION,
    0,
    0,
    0,
    &enableParams
);

// 3. 开始处理事件
ProcessTrace(&hTrace, 1, 0, 0);

// 4. 回调函数
VOID WINAPI EventRecordCallback(PEVENT_RECORD pEventRecord) {
    // 解析事件头,获取进程ID、时间戳等
    // 根据EventHeader.EventId判断事件类型
    // 记录到日志或发送到分析引擎
}

4.3 用户态日志采集:把散落的信息「串」起来

API Hook和ETW解决了「谁干了什么」的问题,但日志采集解决的是「当时环境什么样」的问题。

我个人习惯把用户态日志采集分为三类:

  • 系统日志:Windows Event Log(Security、System、Application),比如登录成功/失败、服务创建、计划任务注册。
  • 应用日志:IIS日志、数据库日志、第三方安全软件日志。这些往往是攻击者忽略的盲区。
  • EDR自采日志:进程内存扫描结果、网络连接快照、文件哈希值、注册表键值快照。

举个例子,有一次我排查一个横向移动事件。攻击者用WMI远程执行了命令,API Hook只抓到了IWbemServices::ExecMethod调用,但不知道执行了什么。我回头去查Windows Event Log 4688(进程创建),发现目标机器上瞬间弹出了一个cmd.exe,父进程是wmiprvse.exe。再结合EDR自采的命令行参数,完整还原了攻击链。

关键点:日志采集一定要关联上下文。单看一条日志可能毫无意义,但把进程树、时间线、网络流串起来,就能看到全貌。

嗯,这里要注意一个坑:日志量太大。我曾经见过一个EDR部署后,每天产生200GB的日志,把存储打满了。我的建议是:

  • 高频低危事件(如DNS查询、文件读取)做采样或聚合
  • 低频高危事件(如驱动加载、进程注入、注册表自启动项修改)做全量记录
  • 设置日志轮转策略,保留最近30天即可

知识体系总览

下面这张图,是我梳理的用户态监控核心逻辑。你可以把它当作一张「作战地图」:

用户态监控核心逻辑 监控目标:检测恶意行为 API Hook ETW利用 日志采集 具体技术 IAT Hook / Inline Hook Detour Hook 调用栈回溯 具体技术 订阅Kernel/CLR Provider 实时事件流处理 两级过滤机制 具体技术 Windows Event Log 应用日志 / 自采日志 日志聚合与轮转 输出:结构化告警 + 完整攻击链

从这张图你能看到,三大手段不是孤立的。我通常的做法是:API Hook做深度检测(比如检测内存修改),ETW做广度覆盖(比如监控所有进程创建),日志采集做上下文补充(比如查历史记录)。三者配合,才能构建一个相对完整的用户态监控体系。

最后说一句:用户态监控再强,也防不住内核态的攻击。如果攻击者加载了驱动,直接绕过所有Hook和ETW,那用户态就彻底瞎了。所以,下一层——内核态监控,才是真正的「硬仗」。


专注资料整理