【Day 23】为美好的 Windows 献上 ETW - Event Tracing for Windows

环境

  • Windows 10 21H1

ETW 介绍

历史

ETW (Event Tracing for Windows) 在 Windows 2000 引入,从这时候起,OS Kernel 和 Service 都会通过 ETW 记录。Windows Vista 之後引入了统一的事件提供程序模型和 API,并在 Windows7 得到增强。

由於 ETW 的功能和性能,第三方的日志系统逐渐式微。ETW 的优点包含

  • 提供 Process 与 ETW Session 相分离,应用程序的崩溃不会对 ETW 造成影响
  • 能够动态地啓用和禁用日志记录
  • 关闭事件跟踪时间几乎不消耗系统资源
  • 自定义消息格式,便於扩展
  • 日志记录机制使用每处理器的缓冲区,由非同步 Thread 将这些缓冲区写入磁盘
  • 收集事件的时间戳的时钟分辨率可精确到 100 ns

运作机制

ETW 的运作主要有三个角色,Controller、Provider、Consumer。

Controller 负责控制 Session 开关,决定要不要在 Session 接收 Provider。每个 Session 可以同时从多个 Provider 抓取事件,Controller 也能在程序运行的途中开启或关闭 Session。

Provider 负责提供事件,把事件产生给 Session 处理。Provider 是可以扩展的,能够自己定义事件栏位。

Consumer 负责透过 Session 接收从 Provider 来的事件。

实际用途

篮队

蓝队的部分,因为有 Callback 机制,可以被动且即时的收到事件。

以 Provider Microsoft-Windows-Threat-Intelligence 为例,它能够监测 AllocationType、RegionSizes、ProtectionMask,其中的 ProtectionMask 如果是可读、可写、可执行的一块记忆体,则比较有可能是恶意程序。虽然无法断言只要看到可读、可写、可执行的记忆体就代表是恶意程序,不过经过种种方式寻找证据,就可以有一定的信心判断它是不是恶意程序。

再以 Provider Microsoft-Windows-Kernel-Registry 为例,它可以监测有没有透过 Registry 实作的 Persistence 行为。比如侦测一个程序是否有写入以下 Registry

  • RunOnce
  • Run
  • RunServices
  • RunServicesOnce
  • RunEx
  • RunOnceEx

红队

既然 ETW 对於篮队是一个十分有效的工具,那红队这边当然也会有对应策略。

在 Usermode,也就是 Ring3 中,可以透过 Hook EtwEventWrite 的方式拦截事件写入的函数;或是使用 EtwEventUnregister 取消注册 ETW Event。之後的文章会介绍一些在 Usermode 绕过 ETW 的方法。

在 Kernelmode,也就是 Ring0 中,有个方法叫做 GITL(Ghost In The Logs),原理是透过 Hook Kernel API NtTraceEvent 达到绕过 ETW 的效果。

ETW 工具

简介

Windows 有个内建工具 - Logman,可以用来查看 Provider 的资讯和 Session 的状态,也可以建立 Session 收取事件。

使用方法

查看所有 Provider

指令logman query providers,回传结果如下图。输出有两栏,分别是 Provider 和 GUID,其中 GUID 是用来辨别 Provider 的唯一的 ID。

查看特定 provider

如果想要知道一个 Provider 更详细的资讯,只要在刚刚指令的 providers 後面加上 Provider 名称或 GUID。指令 logman query providers <provider name>logman query providers <GUID>,回传结果如下。

可以看到除了之前看到的 Provider 和 GUID 外,还多了下面两个表格。
其中第二个表格是这个 Provider 的 Flag,因为一个 Provider 可能可以监听不同种类的事件,这些 Flag 就是用来从 Provider 提供的事件中挑选我们需要的事件。假设我们想要监听其中的 GCKeyword 和 GCHandleKeyword,则输入的 Flag 就是 0x1 + 0x10 = 0x11,等等在建立 Session 时会需要填入 Flag。

第三个表格则是指定的安全层级(Security Level),每个事件都会有个安全层级。如果选了安全层级高的,则比选择低的选项也都会一起被启用。这个安全层级在等等建立 Session 时也需要填入。

# logman query providers ".NET Common Language Runtime"

Provider                                 GUID
-------------------------------------------------------------------------------
.NET Common Language Runtime             {E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4}

Value               Keyword              Description
-------------------------------------------------------------------------------
0x0000000000000001  GCKeyword            GC
0x0000000000000002  GCHandleKeyword      GCHandle
0x0000000000000004  FusionKeyword        Binder
0x0000000000000008  LoaderKeyword        Loader
0x0000000000000010  JitKeyword           Jit
0x0000000000000020  NGenKeyword          NGen
0x0000000000000040  StartEnumerationKeyword StartEnumeration
0x0000000000000080  EndEnumerationKeyword StopEnumeration
0x0000000000000400  SecurityKeyword      Security
0x0000000000000800  AppDomainResourceManagementKeyword AppDomainResourceManagement
0x0000000000001000  JitTracingKeyword    JitTracing
0x0000000000002000  InteropKeyword       Interop
0x0000000000004000  ContentionKeyword    Contention
0x0000000000008000  ExceptionKeyword     Exception
0x0000000000010000  ThreadingKeyword     Threading
0x0000000000020000  JittedMethodILToNativeMapKeyword JittedMethodILToNativeMap
0x0000000000040000  OverrideAndSuppressNGenEventsKeyword OverrideAndSuppressNGenEvents
0x0000000000080000  TypeKeyword          Type
0x0000000000100000  GCHeapDumpKeyword    GCHeapDump
0x0000000000200000  GCSampledObjectAllocationHighKeyword GCSampledObjectAllocationHigh
0x0000000000400000  GCHeapSurvivalAndMovementKeyword GCHeapSurvivalAndMovement
0x0000000000800000  GCHeapCollectKeyword GCHeapCollect
0x0000000001000000  GCHeapAndTypeNamesKeyword GCHeapAndTypeNames
0x0000000002000000  GCSampledObjectAllocationLowKeyword GCSampledObjectAllocationLow
0x0000000020000000  PerfTrackKeyword     PerfTrack
0x0000000040000000  StackKeyword         Stack
0x0000000080000000  ThreadTransferKeyword ThreadTransfer
0x0000000100000000  DebuggerKeyword      Debugger
0x0000000200000000  MonitoringKeyword    Monitoring

Value               Level                Description
-------------------------------------------------------------------------------
0x00                win:LogAlways        Log Always
0x02                win:Error            Error
0x04                win:Informational    Information
0x05                win:Verbose          Verbose

使用 session

  1. 建立 Session,logman create trace <session name> -p <provider name / GUID> <flags> <security level> -o <path to log file>
  2. 启用 Session,logman start <session name>
  3. 停用 Session,logman stop <session name>
  4. 删除 Session,logman delete <session name>

以下示范使用 Provider Microsoft-Windows-Kernel-Process 的 Session。在启用 Session 之後可以稍微打几个指令,要打会开启 Process 的指令例如 whoaminet user 等,之後查看输出时可以观察到。

:: 1. 建立 Session
logman create trace example -p Microsoft-Windows-Kernel-Process 0x10 0x4 -o C:\example.etl

:: 2. 启用 Session
logman start example

:: 3. 停用 Session
logman stop example

:: 4. 删除 Session
logman delete example

产生出来的档案室 etl 档,可以用 tracerpt 转成 xml 档,指令如下
tracerpt example.etl -o example.xml

打开档案後,有在档案中发现你刚刚打的那些指令的话就成功了。以下是我输入 whoami 指令後产生的输出。

<EventData>
    <Data Name="ProcessID">   23836</Data>
    <Data Name="ProcessSequenceNumber">70760</Data>
    <Data Name="CreateTime">2021-10-05T15:41:44.135049700Z</Data>
    <Data Name="ExitTime">2021-10-05T15:41:44.150097300Z</Data>
    <Data Name="ExitCode">       0</Data>
    <Data Name="TokenElevationType">       1</Data>
    <Data Name="HandleCount">      54</Data>
    <Data Name="CommitCharge">1732608</Data>
    <Data Name="CommitPeak">1937408</Data>
    <Data Name="CPUCycleCount">17963357</Data>
    <Data Name="ReadOperationCount">       0</Data>
    <Data Name="WriteOperationCount">       0</Data>
    <Data Name="ReadTransferKiloBytes">       0</Data>
    <Data Name="WriteTransferKiloBytes">       0</Data>
    <Data Name="HardFaultCount">      15</Data>
    <Data Name="ImageName">whoami.exe</Data>
</EventData>

其他

今天只是简单介绍 ETW 和 Windows 内建的工具,但是其实能做到的不只是产生日志档案。之後会讲解如何写程序,即时的抓取事件并分析操作。


<<:  Day-26 Process Synchronization

>>:  [Day23]ISO 27001 附录 A.11 实体及环境安全

[Day15] TS:在 Mapped Type 中使用 Template Literal 来改物件型别中的所有 key

上面这个是今天会提到的内容,如果你已经可以轻松看懂,欢迎直接左转去看我队友们的精彩文章! 昨天我们...

【Day 3】分散式系统模型、容错、高可用

上一章我们了解了分散式系统是什麽、为什麽要让系统分散, 也大概知道分散式系统会遇到节点死掉、网路断掉...

Day 19 - 效能优化,避免过度 render state

如果有错误,欢迎留言指教~ Q_Q 如果你要成功更新画面,你必须经过两个步骤: render fu...

使用 XmlPullParser (三)

我们现在有了许多的基础的 parser function 了,我们直接来看怎麽样组合这些 funct...

Day 11 - 物品借用纪录系统 (3) 发送到期与逾期通知

我们昨天顺利把借物归还事件可以自动化上传到 Google Calendar 上,但是如果借阅人不看 ...