用于安全研究和取证的 ETW 内部结构

原文链接:https://blog.trailofbits.com/2023/11/22/etw-internals-for-security-research-and-forensics/

为什么 Windows 事件跟踪 (ETW) 对于 Windows 10 和 11 中的终端检测和响应 (EDR) 解决方案变得如此关键?答案在于它通过安全 ETW 通道向安全工具提供的情报价值,这些通道现在也是寻求绕过检测的攻击性研究人员的目标。

在本次深入探讨中,我们不仅仅是在讨论 ETW 的功能。我们正在探索 ETW 的内部工作原理,以便你可以对系统进行新研究或取证分析。安全研究人员和恶意软件作者已经在研究 ETW。他们开发了多种技术来篡改或绕过基于 ETW 的 EDR、HOOK 系统调用或访问通常为反恶意软件解决方案保留的 ETW 提供程序。最近,Lazarus 组织通过禁用 ETW Providers 绕过了 EDR 检测。在这里,我们将解释 ETW 的工作原理以及它为何成为如此诱人的目标,并且我们将踏上深入 Windows 的激动人心的旅程。

ETW 内部概述

ETW 的两个主要组成部分是 providers 和 consumers。Providers 将事件发送到 ETW 全局唯一标识符 (GUID),并将事件写入文件、内存中的缓冲区或两者。每个 Windows 系统都注册了数百或数千个 providers。可以通过运行命令 logman query providers 来查看可用的 providers:

img

通过检查系统,可以看到有近 1200 个已注册的 Providers

img

每个 ETW Providers 都在清单文件中定义自己的事件,Consumers 使用该文件来解析 Providers 生成的数据。 ETW Providers 可能定义了数百种不同的事件类型,因此可以从 ETW 获得的信息量是巨大的。大多数这些事件都可以在事件查看器中看到,事件查看器是一个使用 ETW 事件的内置 Windows 工具。但你只会看到部分数据。默认情况下,事件查看器中并未启用所有日志,并且并非每个日志都显示所有事件 ID。

另一方面,Consumers 跟踪从一个或多个 Providers 接收事件的日志记录会话。例如,依赖 ETW 数据进行检测的 EDR 将使用来自安全相关 ETW 通道 (例如威胁情报通道) 的事件。

可以通过性能监视器查看所有正在运行的 ETW Consumers,单击其中一个会话将显示其订阅的 Providers (可能需要以 SYSTEM 身份运行才能查看所有 ETW 日志记录会话)

img

从该日志会话接收事件的进程列表是有用的信息,但不容易获取。据我所知,根本没有办法从用户模式获取这些信息,甚至从内核模式获取这些信息也不是一件容易的事,除非你非常熟悉 ETW 内部结构。因此,我们将了解可以从使用 WinDbg 的内核调试会话中学到什么。

查找 ETW Consumer 进程

有多种方法可以从用户模式查找 ETW 日志会话的 Consumer。然而,他们只提供部分信息,在所有情况下都不够。因此,我们将进入内核调试器会话。从调试器获取有关 ETW 会话信息的一种方法是使用内置扩展 !wmitrace。这个非常有用的扩展允许用户调查所有正在运行的记录器及其属性、使用者和缓冲区。它甚至允许用户启动和停止日志会话 (在实时调试器连接上)。尽管如此,与所有遗留扩展一样,它也有其局限性:它不能轻易实现自动化,而且由于它是预编译的二进制文件,因此无法使用新功能进行扩展。

因此,我们将编写一个 JavaScript 脚本,脚本更容易扩展和修改,可以使用它们来获取所需的尽可能多的数据,而不受旧扩展的现有功能的限制。

每个句柄都包含一个指向对象的指针,例如,文件句柄将指向 FILE_OBJECT 类型的内核结构。EtwConsumer 类型的对象句柄将指向一个名为 ETW_REALTIME_CONSUMER 的未记录数据结构。该结构包含指向打开它的进程的指针、收到不同操作通知的事件、标志以及一条将引导我们返回日志会话的信息 LoggerId。使用自定义脚本,可以扫描所有进程的句柄表以查找 EtwConsumer 对象的句柄。对于每一个,都可以获得链接的 ETW_REALTIME_CONSUMER 结构并打印 LoggerId :

"use strict";

function initializeScript()
{
    return [new host.apiVersionSupport(1, 7)];
}


function EtwConsumersForProcess(process)
{
    let dbgOutput = host.diagnostics.debugLog;
    let handles = process.Io.Handles;

    try 
    {
        for (let handle of handles)
        {
            try
            {
                let objType = handle.Object.ObjectType;
                if (objType === "EtwConsumer")
                {
                    let consumer = host.createTypedObject(handle.Object.Body.address, "nt", "_ETW_REALTIME_CONSUMER");
                    let loggerId = consumer.LoggerId;

                    dbgOutput("Process ", process.Name, " with ID ", process.Id, " has handle ", handle.Handle, " to Logger ID ", loggerId, "\n");
                }
            } catch (e) {
                dbgOutput("\tException parsing handle ", handle.Handle, "in process ", process.Name, "!\n");
            }
        }
    } catch (e) {

    }
}

接下来,我们使用 .scriptload 将脚本加载到调试器中,并调用函数来识别哪个进程消耗 ETW 事件:

dx @$cursession.Processes.Select(p => @$scriptContents.EtwConsumersForProcess(p))
@$cursession.Processes.Select(p => @$scriptContents.EtwConsumersForProcess(p))                
Process svchost.exe with ID 0x558 has handle 0x7cc to Logger ID 31
Process svchost.exe with ID 0x114c has handle 0x40c to Logger ID 36
Process svchost.exe with ID 0x11f8 has handle 0x2d8 to Logger ID 17
Process svchost.exe with ID 0x11f8 has handle 0x2e8 to Logger ID 3
Process svchost.exe with ID 0x11f8 has handle 0x2f4 to Logger ID 9
Process NVDisplay.Container.exe with ID 0x1478 has handle 0x890 to Logger ID 38
Process svchost.exe with ID 0x1cec has handle 0x1dc to Logger ID 7
Process svchost.exe with ID 0x1d2c has handle 0x780 to Logger ID 8
Process CSFalconService.exe with ID 0x1e54 has handle 0x760 to Logger ID 3
Process CSFalconService.exe with ID 0x1e54 has handle 0x79c to Logger ID 45
Process CSFalconService.exe with ID 0x1e54 has handle 0xbb0 to Logger ID 10
Process Dell.TechHub.Instrumentation.SubAgent.exe with ID 0x25c4 has handle 0xcd8 to Logger ID 41
Process Dell.TechHub.Instrumentation.SubAgent.exe with ID 0x25c4 has handle 0xdb8 to Logger ID 35
Process Dell.TechHub.Instrumentation.SubAgent.exe with ID 0x25c4 has handle 0xf54 to Logger ID 44
Process SgrmBroker.exe with ID 0x17b8 has handle 0x178 to Logger ID 15
Process SystemInformer.exe with ID 0x4304 has handle 0x30c to Logger ID 16
Process PerfWatson2.exe with ID 0xa60 has handle 0xa3c to Logger ID 46
Process PerfWatson2.exe with ID 0x81a4 has handle 0x9c4 to Logger ID 40
Process PerfWatson2.exe with ID 0x76f0 has handle 0x9a8 to Logger ID 47
Process operfmon.exe with ID 0x3388 has handle 0x88c to Logger ID 48
Process operfmon.exe with ID 0x3388 has handle 0x8f4 to Logger ID 49

虽然我们仍然没有获得日志会话的名称,但已经拥有比用户模式下更多的数据。例如,可以看到某些进程有多个 Consumers 句柄,因为它们订阅了多个日志会话。不幸的是,除了标识符之外,ETW_REALTIME_CONSUMER 结构没有任何有关日志会话的信息,因此我们必须找到一种方法将标识符与人类可读的名称相匹配。

注册的记录器及其 ID 存储在记录器的全局列表中,或者至少在引入服务器 silos 之前是这样,现在,每个隔离进程都将拥有自己单独的 ETW 记录器,而非隔离进程将使用全局列表,我也将在这篇文章中使用它。全局列表存储在 host silo globals,nt!PspHostSiloGlobals 内的 ETW_SILODRIVERSTATE 结构内:

dx ((nt!_ESERVERSILO_GLOBALS*)&nt!PspHostSiloGlobals)->EtwSiloState
((nt!_ESERVERSILO_GLOBALS*)&nt!PspHostSiloGlobals)->EtwSiloState                 : 0xffffe38f3deeb000 [Type: _ETW_SILODRIVERSTATE *]
    [+0x000] Silo             : 0x0 [Type: _EJOB *]
    [+0x008] SiloGlobals      : 0xfffff8052bd489c0 [Type: _ESERVERSILO_GLOBALS *]
    [+0x010] MaxLoggers       : 0x50 [Type: unsigned long]
    [+0x018] EtwpSecurityProviderGuidEntry [Type: _ETW_GUID_ENTRY]
    [+0x1c0] EtwpLoggerRundown : 0xffffe38f3deca040 [Type: _EX_RUNDOWN_REF_CACHE_AWARE * *]
    [+0x1c8] EtwpLoggerContext : 0xffffe38f3deca2c0 [Type: _WMI_LOGGER_CONTEXT * *]
    [+0x1d0] EtwpGuidHashTable [Type: _ETW_HASH_BUCKET [64]]
    [+0xfd0] EtwpSecurityLoggers [Type: unsigned short [8]]
    [+0xfe0] EtwpSecurityProviderEnableMask : 0x3 [Type: unsigned char]
    [+0xfe4] EtwpShutdownInProgress : 0 [Type: long]
    [+0xfe8] EtwpSecurityProviderPID : 0x798 [Type: unsigned long]
    [+0xff0] PrivHandleDemuxTable [Type: _ETW_PRIV_HANDLE_DEMUX_TABLE]
    [+0x1010] RTBacklogFileRoot : 0x0 [Type: wchar_t *]
    [+0x1018] EtwpCounters     [Type: _ETW_COUNTERS]
    [+0x1028] LogfileBytesWritten : {4391651513} [Type: _LARGE_INTEGER]
    [+0x1030] ProcessorBlocks  : 0x0 [Type: _ETW_SILO_TRACING_BLOCK *]
    [+0x1038] ContainerStateWnfSubscription : 0xffffaf8de0386130 [Type: _EX_WNF_SUBSCRIPTION *]
    [+0x1040] ContainerStateWnfCallbackCalled : 0x0 [Type: unsigned long]
    [+0x1048] UnsubscribeWorkItem : 0xffffaf8de0202170 [Type: _WORK_QUEUE_ITEM *]
    [+0x1050] PartitionId      : {00000000-0000-0000-0000-000000000000} [Type: _GUID]
    [+0x1060] ParentId         : {00000000-0000-0000-0000-000000000000} [Type: _GUID]
    [+0x1070] QpcOffsetFromRoot : {0} [Type: _LARGE_INTEGER]
    [+0x1078] PartitionName    : 0x0 [Type: char *]
    [+0x1080] PartitionNameSize : 0x0 [Type: unsigned short]
    [+0x1082] UnusedPadding    : 0x0 [Type: unsigned short]
    [+0x1084] PartitionType    : 0x0 [Type: unsigned long]
    [+0x1088] SystemLoggerSettings [Type: _ETW_SYSTEM_LOGGER_SETTINGS]
    [+0x1200] EtwpStartTraceMutex [Type: _KMUTANT]

EtwpLoggerContext 字段指向一组指向 WMI_LOGGER_CONTEXT 结构的指针,每个结构都描述一个记录器会话。数组的大小保存在 ETW_SILODRIVERSTATE 的 MaxLoggers 字段中,并非必须使用数组的所有条目,未使用的条目将被设置为 1。知道这一点,我们可以转储数组中所有已初始化的条目。为了方便起见,我对数组大小进行了硬编码:

dx ((nt!_WMI_LOGGER_CONTEXT*(*)[0x50])(((nt!_ESERVERSILO_GLOBALS*)&nt!PspHostSiloGlobals)->EtwSiloState->EtwpLoggerContext))->Where(l => l != 1)
((nt!_WMI_LOGGER_CONTEXT*(*)[0x50])(((nt!_ESERVERSILO_GLOBALS*)&nt!PspHostSiloGlobals)->EtwSiloState->EtwpLoggerContext))->Where(l => l != 1)                
    [2]              : 0xffffe38f3f0c9040 [Type: _WMI_LOGGER_CONTEXT *]
    [3]              : 0xffffe38f3fe07640 [Type: _WMI_LOGGER_CONTEXT *]
    [4]              : 0xffffe38f3f0c75c0 [Type: _WMI_LOGGER_CONTEXT *]
    [5]              : 0xffffe38f3f0c9780 [Type: _WMI_LOGGER_CONTEXT *]
    [6]              : 0xffffe38f3f0cb040 [Type: _WMI_LOGGER_CONTEXT *]
    [7]              : 0xffffe38f3f0cb600 [Type: _WMI_LOGGER_CONTEXT *]
    [8]              : 0xffffe38f3f0ce040 [Type: _WMI_LOGGER_CONTEXT *]
    [9]              : 0xffffe38f3f0ce600 [Type: _WMI_LOGGER_CONTEXT *]
    [10]             : 0xffffe38f79832a40 [Type: _WMI_LOGGER_CONTEXT *]
    [11]             : 0xffffe38f3f0d1640 [Type: _WMI_LOGGER_CONTEXT *]
    [12]             : 0xffffe38f89535a00 [Type: _WMI_LOGGER_CONTEXT *]
    [13]             : 0xffffe38f3dacc940 [Type: _WMI_LOGGER_CONTEXT *]
    [14]             : 0xffffe38f3fe04040 [Type: _WMI_LOGGER_CONTEXT *]
       …

每个记录器上下文都包含有关记录器会话的信息,例如其名称、存储事件的文件、安全描述符等,每个结构还包含一个记录器 ID,它与我们刚刚转储的数组中记录器的索引相匹配。因此,给定一个记录器 ID,我们可以找到它的详细信息,如下所示:

dx (((nt!_ESERVERSILO_GLOBALS*)&nt!PspHostSiloGlobals)->EtwSiloState->EtwpLoggerContext)[@$loggerId]
 (((nt!_ESERVERSILO_GLOBALS*)&nt!PspHostSiloGlobals)->EtwSiloState->EtwpLoggerContext)[@$loggerId]                 : 0xffffe38f3f0ce600 [Type: _WMI_LOGGER_CONTEXT *]
    [+0x000] LoggerId         : 0x9 [Type: unsigned long]
    [+0x004] BufferSize       : 0x10000 [Type: unsigned long]
    [+0x008] MaximumEventSize : 0xffb8 [Type: unsigned long]
    [+0x00c] LoggerMode       : 0x19800180 [Type: unsigned long]
    [+0x010] AcceptNewEvents  : 0 [Type: long]
    [+0x018] GetCpuClock      : 0x0 [Type: unsigned __int64]
    [+0x020] LoggerThread     : 0xffffe38f3f0d0040 [Type: _ETHREAD *]
    [+0x028] LoggerStatus     : 0 [Type: long]
       …

现在我们可以将其实现为一个函数 (在 DX 或 JavaScript 中) 并打印我们找到的每个打开的 Consumers 句柄的记录器名称:

dx @$cursession.Processes.Select(p => @$scriptContents.EtwConsumersForProcess(p))
@$cursession.Processes.Select(p => @$scriptContents.EtwConsumersForProcess(p))                
Process svchost.exe with ID 0x558 has handle 0x7cc to Logger ID 31
    Logger Name: "UBPM"

Process svchost.exe with ID 0x114c has handle 0x40c to Logger ID 36
    Logger Name: "WFP-IPsec Diagnostics"

Process svchost.exe with ID 0x11f8 has handle 0x2d8 to Logger ID 17
    Logger Name: "EventLog-System"

Process svchost.exe with ID 0x11f8 has handle 0x2e8 to Logger ID 3
    Logger Name: "Eventlog-Security"

Process svchost.exe with ID 0x11f8 has handle 0x2f4 to Logger ID 9
    Logger Name: "EventLog-Application"

Process NVDisplay.Container.exe with ID 0x1478 has handle 0x890 to Logger ID 38
    Logger Name: "NOCAT"

Process svchost.exe with ID 0x1cec has handle 0x1dc to Logger ID 7
    Logger Name: "DiagLog"

Process svchost.exe with ID 0x1d2c has handle 0x780 to Logger ID 8
    Logger Name: "Diagtrack-Listener"

Process CSFalconService.exe with ID 0x1e54 has handle 0x760 to Logger ID 3
    Logger Name: "Eventlog-Security"
...

事实上,通过使用记录器数组,我们可以构建一种更好的方法来枚举 ETW 日志会话 Consumers。每个记录器上下文都有一个 Consumers 字段,它是一个链接列表,连接订阅此日志会话的所有 ETW_REALTIME_CONSUMER 结构:

img

因此,我们可以直接进入 loggers 数组并找到每个进程的注册进程,而不是扫描系统中每个进程的句柄表:

function EtwLoggersWithConsumerProcesses()
{
    let dbgOutput = host.diagnostics.debugLog;
    let hostSiloGlobals = host.getModuleSymbolAddress("nt", "PspHostSiloGlobals");
    let typedhostSiloGlobals = host.createTypedObject(hostSiloGlobals, "nt", "_ESERVERSILO_GLOBALS");

    let maxLoggers = typedhostSiloGlobals.EtwSiloState.MaxLoggers;
    for (let i = 0; i < maxLoggers; i++)
    {
        let logger = typedhostSiloGlobals.EtwSiloState.EtwpLoggerContext[i];
        if (host.parseInt64(logger.address, 16).compareTo(host.parseInt64("0x1")) != 0)
        {
            dbgOutput("Logger Name: ", logger.LoggerName, "\n");

            let consumers = host.namespace.Debugger.Utility.Collections.FromListEntry(logger.Consumers, "nt!_ETW_REALTIME_CONSUMER", "Links");
            if (consumers.Count() != 0)
            {
                for (let consumer of consumers)
                {
                    dbgOutput("\tProcess Name: ", consumer.ProcessObject.SeAuditProcessCreationInfo.ImageFileName.Name, "\n");
                    dbgOutput("\tProcess Id: ", host.parseInt64(consumer.ProcessObject.UniqueProcessId.address, 16).toString(10), "\n");
                    dbgOutput("\n");
                }
            }
            else
            {
                dbgOutput("\tThis logger has no consumers\n\n");
            }
        }
    }
}

调用这个函数应该会得到与之前完全相同的结果,而且速度更快!获得这部分内容后,可以继续搜索另一条可能有用的信息,向日志会话提供事件的 GUID 列表。

查找 Provider GUIDs

找到 ETW 日志会话的使用者只是成功的一半,我们还想知道哪些 Providers 通知每个日志会话。我们之前看到可以从性能监视器获取该信息,看看如何从调试器会话中获取它,因为当实时计算机不可用或查找未提供的详细信息时,它可能很有用通过诸如性能监视器之类的用户模式工具。

如果查看 WMI_LOGGER_CONTEXT 结构,将不会看到有关通知日志会话的 Providers 的任何详细信息。要查找此信息,我们需要返回到之前的 ETW_SILODRIVERSTATE 结构并查看 EtwpGuidHashTable 字段。这是存储所有已注册 Providers GUID 的存储桶数组。出于性能原因,将 GUID 哈希后的数据存储在 64 个存储桶中,每个桶包含三个链接 ETW_GUID_ENTRY 结构的列表,每个 ETW_GUID_TYPE 有一个列表:

  • EtwpTraceGuidType
  • EtwpNotificationGuidType
  • EtwpGroupGuidType

每个 ETW_GUID_ENTRY 结构都包含一个具有八个条目的 EnableInfo 数组,每个条目都包含有关 GUID 为其提供事件的一个日志会话的信息 (这意味着事件 GUID 条目可以为以下对象提供事件,最多八个不同的日志会话):

dt nt!_ETW_GUID_ENTRY EnableInfo.
   +0x080 EnableInfo  : [8] 
      +0x000 IsEnabled   : Uint4B
      +0x004 Level       : UChar
      +0x005 Reserved1   : UChar
      +0x006 LoggerId    : Uint2B
      +0x008 EnableProperty : Uint4B
      +0x00c Reserved2   : Uint4B
      +0x010 MatchAnyKeyword : Uint8B
      +0x018 MatchAllKeyword : Uint8B

从视觉上看,这就是整个事情的样子:

img

正如我们所看到的, ETW_GUID_ENTRY 结构包含一个 LoggerId 字段,我们可以将其用作 EtwpLoggerContext 数组的索引来查找日志会话。

考虑到这些新信息,我们可以编写一个简单的 JavaScript 函数来打印与记录器 ID 匹配的 GUID。在本例中,我选择一次只遍历一个 ETW_GUID_TYPE 以使此代码更清晰一些。然后可以更进一步解析 ETW_REG_ENTRY 列表每个 GUID 条目,来找出哪些进程通知它,或者它是否是内核模式提供程序:

function GetGuidsForLoggerId(loggerId, guidType)
{
    let dbgOutput = host.diagnostics.debugLog;

    let hostSiloGlobals = host.getModuleSymbolAddress("nt", "PspHostSiloGlobals");
    let typedhostSiloGlobals = host.createTypedObject(hostSiloGlobals, "nt", "_ESERVERSILO_GLOBALS");
    let guidHashTable = typedhostSiloGlobals.EtwSiloState.EtwpGuidHashTable;
    for (let bucket of guidHashTable)
    {
        let guidEntries = host.namespace.Debugger.Utility.Collections.FromListEntry(bucket.ListHead[guidType], "nt!_ETW_GUID_ENTRY", "GuidList");
        if (guidEntries.Count() != 0)
        {
            for (let guid of guidEntries)
            {
                for (let enableInfo of guid.EnableInfo)
                {
                    if (enableInfo.LoggerId === loggerId)
                    {
                        dbgOutput("\tGuid: ", guid.Guid, "\n");
                        let regEntryLinkField = "RegList";
                        if (guidType == 2)
                        {
                            // group GUIDs registration entries are linked through the GroupRegList field
                            regEntryLinkField = "GroupRegList";
                        }
                        let regEntries = host.namespace.Debugger.Utility.Collections.FromListEntry(guid.RegListHead, "nt!_ETW_REG_ENTRY", regEntryLinkField);
                        if (regEntries.Count() != 0)
                        {
                            dbgOutput("\tProvider Processes:\n");
                            for (let regEntry of regEntries)
                            {
                                if (regEntry.DbgUserRegistration != 0)
                                {
                                    dbgOutput("\t\tProcess: ", regEntry.Process.SeAuditProcessCreationInfo.ImageFileName.Name, " ID: ", host.parseInt64(regEntry.Process.UniqueProcessId.address, 16).toString(10), "\n");
                                }
                                else
                                {
                                    dbgOutput("\t\tKernel Provider\n");
                                }
                            }
                        }
                        break;
                    }
                }
            }
        }
    }
}

作为示例,以下是所有跟踪提供程序 GUID 以及通知它们 ETW 会话 UBPM 的进程 (在我的示例中为 LoggerId 31):

dx @$scriptContents.GetGuidsForLoggerId(31, 0)
    Guid: {9E03F75A-BCBE-428A-8F3C-D46F2A444935}
    Provider Processes:
        Process: "\Device\HarddiskVolume3\Windows\System32\svchost.exe" ID: 2816
    Guid: {2D7904D8-5C90-4209-BA6A-4C08F409934C}
    Guid: {E46EEAD8-0C54-4489-9898-8FA79D059E0E}
    Provider Processes:
        Process: "\Device\HarddiskVolume3\Windows\System32\dwm.exe" ID: 2268
    Guid: {D02A9C27-79B8-40D6-9B97-CF3F8B7B5D60}
    Guid: {92AAB24D-D9A9-4A60-9F94-201FED3E3E88}
    Provider Processes:
        Process: "\Device\HarddiskVolume3\Windows\System32\svchost.exe" ID: 2100
        Kernel Provider
    Guid: {FBCFAC3F-8460-419F-8E48-1F0B49CDB85E}
    Guid: {199FE037-2B82-40A9-82AC-E1D46C792B99}
    Provider Processes:
        Process: "\Device\HarddiskVolume3\Windows\System32\lsass.exe" ID: 1944
    Guid: {BD2F4252-5E1E-49FC-9A30-F3978AD89EE2}
    Provider Processes:
        Process: "\Device\HarddiskVolume3\Windows\System32\svchost.exe" ID: 16292
    Guid: {22B6D684-FA63-4578-87C9-EFFCBE6643C7}
    Guid: {3635D4B6-77E3-4375-8124-D545B7149337}
    Guid: {0621B9DF-3249-4559-9889-21F76B5C80F3}
    Guid: {BD8FEA17-5549-4B49-AA03-1981D16396A9}
    Guid: {F5528ADA-BE5F-4F14-8AEF-A95DE7281161}
    Guid: {54732EE5-61CA-4727-9DA1-10BE5A4F773D}
    Provider Processes:
        Process: "\Device\HarddiskVolume3\Windows\System32\svchost.exe" ID: 4428
    Guid: {18F4A5FD-FD3B-40A5-8FC2-E5D261C5D02E}
    Guid: {8E6A5303-A4CE-498F-AFDB-E03A8A82B077}
    Provider Processes:
        Kernel Provider
    Guid: {CE20D1C3-A247-4C41-BCB8-3C7F52C8B805}
    Provider Processes:
        Kernel Provider
    Guid: {5EF81E80-CA64-475B-B469-485DBC993FE2}
    Guid: {9B307223-4E4D-4BF5-9BE8-995CD8E7420B}
    Provider Processes:
        Kernel Provider
    Guid: {AA1F73E8-15FD-45D2-ABFD-E7F64F78EB11}
    Provider Processes:
        Kernel Provider
    Guid: {E1BDC95E-0F07-5469-8E64-061EA5BE6A0D}
    Guid: {5B004607-1087-4F16-B10E-979685A8D131}
    Guid: {AEDD909F-41C6-401A-9E41-DFC33006AF5D}
    Guid: {277C9237-51D8-5C1C-B089-F02C683E5BA7}
    Provider Processes:
        Kernel Provider
    Guid: {F230D19A-5D93-47D9-A83F-53829EDFB8DF}
    Provider Processes:
        Process: "\Device\HarddiskVolume3\Windows\System32\svchost.exe" ID: 2816

将所有这些步骤放在一起,我们终于有办法知道计算机上正在运行哪些日志会话、哪些进程通知会话中的每个 GUID,以及哪些进程订阅了它们。这可以帮助我们了解机器上运行的不同 ETW 日志会话的用途,例如识别 EDR 软件或感兴趣的硬件组件使用的日志会话。这些脚本还可以根据需要进行修改,以识别 ETW 违规行为,例如为了迷惑安全产品而禁用的日志会话。从攻击者的角度来看,收集这些信息可以告诉我们机器上使用了哪些 ETW Providers,哪些被忽略,因此不会给我们带来任何检测风险。

总的来说,ETW 是一个非常强大的机制,因此更深入地了解其内部运作对于攻击者和防御者都非常有用。这篇文章只触及了表面,在这个领域还有很多工作可以做。

本文中显示的所有 JavaScript 函数都可以在此 GitHub 存储库中 https://github.com/trailofbits/WinDbg-JS/blob/main/EtwKernelRoutines.js 找到。

posted @ 2023-11-29 15:19  煙沫凡塵  阅读(528)  评论(0编辑  收藏  举报