Windows ETW 之监控进程

发布于 2024-09-02 19:32:37 字数 40477 浏览 57 评论 0

没有找到合适的图片,最近一段时间因为一些事稍微去研究了一下 Windows ETW。现在在博客里记录一下

正文

使用 EtwExplorer 可以查看系统中的事件提供者

image.png

我这里选择使用的是 Microsoft-Windows-Kernel-Process 它可以监控进程相关的事件

代码示例

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <strsafe.h>
#include <wbemidl.h>
#include <wmistr.h>
#include <evntrace.h>
#include <Evntcons.h>
#include <vector>
#include <tdh.h>
#include <string>
#include <in6addr.h>

#pragma comment(lib, "tdh.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "Advapi32.lib")

using namespace std;

#define filename(x) strrchr(x,'\\')?strrchr(x,'\\')+1:x
#define GetLastErrorEx(lpszFunction){LPVOID lpMsgBuf;DWORD dw = GetLastError();FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS,NULL,dw,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPSTR)&lpMsgBuf,0, NULL);\
if (lpszFunction)\
printf("%s(Line:%d)->Funtion:%s\ndiyInfo:%s\nError(%d)->Info:%s\n",\
filename(__FILE__), __LINE__, __FUNCTION__, lpszFunction, dw, lpMsgBuf);\
else \
printf("%s(Line:%d)->Funtion:%s\nError(%d)->Info:%s\n",\
filename(__FILE__), __LINE__, __FUNCTION__, dw, lpMsgBuf);\
LocalFree(lpMsgBuf);\
}

#define LOGSESSION_NAME L"Testing ETW Consumer"

TRACEHANDLE sessionHandle;
TRACEHANDLE traceHandle;

DWORD PointerSize = 0;

int bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME) + sizeof(WCHAR);

auto pSessionProperties = static_cast<PEVENT_TRACE_PROPERTIES>(malloc(bufferSize));

//"{b2bcc945-9eb9-4231-883c-d6455cf4d86b}"
static const GUID SessionGuid =
{ 0xb2bcc945, 0x9eb9, 0x4231,{ 0x88, 0x3c, 0xd6, 0x45, 0x5c, 0xf4, 0xd8, 0x6b } };

// Microsoft-Windows-Kernel-Process {22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}
static const GUID PROCESS_PROVIDER_GUID =
{ 0x22fb2cd6, 0x0e7b, 0x422b,{ 0xa0, 0xc7, 0x2f, 0xad, 0x1f, 0xd0, 0xe7, 0x16 } };

void PrintMapString(PEVENT_MAP_INFO pMapInfo, PBYTE pData)
{
BOOL MatchFound = FALSE;

if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP) == EVENTMAP_INFO_FLAG_MANIFEST_VALUEMAP ||
((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_VALUEMAP) == EVENTMAP_INFO_FLAG_WBEM_VALUEMAP &&
(pMapInfo->Flag & (~EVENTMAP_INFO_FLAG_WBEM_VALUEMAP)) != EVENTMAP_INFO_FLAG_WBEM_FLAG))
{
if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_NO_MAP) == EVENTMAP_INFO_FLAG_WBEM_NO_MAP)
{
wprintf(L"%s\n", (LPWSTR)((PBYTE)pMapInfo + pMapInfo->MapEntryArray[*(PULONG)pData].OutputOffset));
}
else
{
for (DWORD i = 0; i < pMapInfo->EntryCount; i++)
{
if (pMapInfo->MapEntryArray[i].Value == *(PULONG)pData)
{
wprintf(L"%s\n", (LPWSTR)((PBYTE)pMapInfo + pMapInfo->MapEntryArray[i].OutputOffset));
MatchFound = TRUE;
break;
}
}

if (FALSE == MatchFound)
{
wprintf(L"%lu\n", *(PULONG)pData);
}
}
}
else if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_MANIFEST_BITMAP) == EVENTMAP_INFO_FLAG_MANIFEST_BITMAP ||
(pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_BITMAP) == EVENTMAP_INFO_FLAG_WBEM_BITMAP ||
((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_VALUEMAP) == EVENTMAP_INFO_FLAG_WBEM_VALUEMAP &&
(pMapInfo->Flag & (~EVENTMAP_INFO_FLAG_WBEM_VALUEMAP)) == EVENTMAP_INFO_FLAG_WBEM_FLAG))
{
if ((pMapInfo->Flag & EVENTMAP_INFO_FLAG_WBEM_NO_MAP) == EVENTMAP_INFO_FLAG_WBEM_NO_MAP)
{
DWORD BitPosition = 0;

for (DWORD i = 0; i < pMapInfo->EntryCount; i++)
{
if ((*(PULONG)pData & (BitPosition = (1 << i))) == BitPosition)
{
wprintf(L"%s%s",
(MatchFound) ? L" | " : L"",
(LPWSTR)((PBYTE)pMapInfo + pMapInfo->MapEntryArray[i].OutputOffset));

MatchFound = TRUE;
}
}
}
else
{
for (DWORD i = 0; i < pMapInfo->EntryCount; i++)
{
if ((pMapInfo->MapEntryArray[i].Value & *(PULONG)pData) == pMapInfo->MapEntryArray[i].Value)
{
wprintf(L"%s%s",
(MatchFound) ? L" | " : L"",
(LPWSTR)((PBYTE)pMapInfo + pMapInfo->MapEntryArray[i].OutputOffset));

MatchFound = TRUE;
}
}
}

if (MatchFound)
{
wprintf(L"\n");
}
else
{
wprintf(L"%lu\n", *(PULONG)pData);
}
}
}

#define MAX_NAME 256
DWORD FormatAndPrintData(PEVENT_RECORD pEvent, USHORT InType, USHORT OutType, PBYTE pData, DWORD DataSize, PEVENT_MAP_INFO pMapInfo)
{
UNREFERENCED_PARAMETER(pEvent);

DWORD status = ERROR_SUCCESS;

switch (InType)
{
case TDH_INTYPE_UNICODESTRING:
case TDH_INTYPE_COUNTEDSTRING:
case TDH_INTYPE_REVERSEDCOUNTEDSTRING:
case TDH_INTYPE_NONNULLTERMINATEDSTRING:
{
size_t StringLength = 0;

if (TDH_INTYPE_COUNTEDSTRING == InType)
{
StringLength = *(PUSHORT)pData;
}
else if (TDH_INTYPE_REVERSEDCOUNTEDSTRING == InType)
{
StringLength = MAKEWORD(HIBYTE((PUSHORT)pData), LOBYTE((PUSHORT)pData));
}
else if (TDH_INTYPE_NONNULLTERMINATEDSTRING == InType)
{
StringLength = DataSize;
}
else
{
StringLength = wcslen((LPWSTR)pData);
}

wprintf(L"%.*s\n", StringLength, (LPWSTR)pData);
break;
}

case TDH_INTYPE_ANSISTRING:
case TDH_INTYPE_COUNTEDANSISTRING:
case TDH_INTYPE_REVERSEDCOUNTEDANSISTRING:
case TDH_INTYPE_NONNULLTERMINATEDANSISTRING:
{
size_t StringLength = 0;

if (TDH_INTYPE_COUNTEDANSISTRING == InType)
{
StringLength = *(PUSHORT)pData;
}
else if (TDH_INTYPE_REVERSEDCOUNTEDANSISTRING == InType)
{
StringLength = MAKEWORD(HIBYTE((PUSHORT)pData), LOBYTE((PUSHORT)pData));
}
else if (TDH_INTYPE_NONNULLTERMINATEDANSISTRING == InType)
{
StringLength = DataSize;
}
else
{
StringLength = strlen((LPSTR)pData);
}

wprintf(L"%.*S\n", StringLength, (LPSTR)pData);
break;
}

case TDH_INTYPE_INT8:
{
wprintf(L"%hd\n", *(PCHAR)pData);
break;
}

case TDH_INTYPE_UINT8:
{
if (TDH_OUTTYPE_HEXINT8 == OutType)
{
wprintf(L"0x%x\n", *(PBYTE)pData);
}
else
{
wprintf(L"%hu\n", *(PBYTE)pData);
}

break;
}

case TDH_INTYPE_INT16:
{
wprintf(L"%hd\n", *(PSHORT)pData);
break;
}

case TDH_INTYPE_UINT16:
{
if (TDH_OUTTYPE_HEXINT16 == OutType)
{
wprintf(L"0x%x\n", *(PUSHORT)pData);
}
else if (TDH_OUTTYPE_PORT == OutType)
{
wprintf(L"%hu\n", ntohs(*(PUSHORT)pData));
}
else
{
wprintf(L"%hu\n", *(PUSHORT)pData);
}

break;
}

case TDH_INTYPE_INT32:
{
if (TDH_OUTTYPE_HRESULT == OutType)
{
wprintf(L"0x%x\n", *(PLONG)pData);
}
else
{
wprintf(L"%d\n", *(PLONG)pData);
}

break;
}

case TDH_INTYPE_UINT32:
{
if (TDH_OUTTYPE_HRESULT == OutType ||
TDH_OUTTYPE_WIN32ERROR == OutType ||
TDH_OUTTYPE_NTSTATUS == OutType ||
TDH_OUTTYPE_HEXINT32 == OutType)
{
wprintf(L"0x%x\n", *(PULONG)pData);
}
else if (TDH_OUTTYPE_IPV4 == OutType)
{
wprintf(L"%d.%d.%d.%d\n", (*(PLONG)pData >> 0) & 0xff,
(*(PLONG)pData >> 8) & 0xff,
(*(PLONG)pData >> 16) & 0xff,
(*(PLONG)pData >> 24) & 0xff);
}
else
{
if (pMapInfo)
{
PrintMapString(pMapInfo, pData);
}
else
{
wprintf(L"%lu\n", *(PULONG)pData);
}
}

break;
}

case TDH_INTYPE_INT64:
{
wprintf(L"%I64d\n", *(PLONGLONG)pData);

break;
}

case TDH_INTYPE_UINT64:
{
if (TDH_OUTTYPE_HEXINT64 == OutType)
{
wprintf(L"0x%x\n", *(PULONGLONG)pData);
}
else
{
wprintf(L"%I64u\n", *(PULONGLONG)pData);
}

break;
}

case TDH_INTYPE_FLOAT:
{
wprintf(L"%f\n", *(PFLOAT)pData);

break;
}

case TDH_INTYPE_DOUBLE:
{
wprintf(L"%I64f\n", *(DOUBLE*)pData);

break;
}

case TDH_INTYPE_BOOLEAN:
{
wprintf(L"%s\n", (0 == (PBOOL)pData) ? L"false" : L"true");

break;
}

case TDH_INTYPE_BINARY:
{
if (TDH_OUTTYPE_IPV6 == OutType)
{
//WCHAR IPv6AddressAsString[46];
//PIPV6ADDRTOSTRING fnRtlIpv6AddressToString;

//fnRtlIpv6AddressToString = (PIPV6ADDRTOSTRING)GetProcAddress(
// GetModuleHandle(L"ntdll"), "RtlIpv6AddressToStringW");

//if (NULL == fnRtlIpv6AddressToString)
//{
// wprintf(L"GetProcAddress failed with %lu.\n", status = GetLastError());
// goto cleanup;
//}

//fnRtlIpv6AddressToString((IN6_ADDR*)pData, IPv6AddressAsString);

//wprintf(L"%s\n", IPv6AddressAsString);
}
else
{
for (DWORD i = 0; i < DataSize; i++)
{
wprintf(L"%.2x", pData[i]);
}

wprintf(L"\n");
}

break;
}

case TDH_INTYPE_GUID:
{
WCHAR szGuid[50];

StringFromGUID2(*(GUID*)pData, szGuid, sizeof(szGuid) - 1);
wprintf(L"%s\n", szGuid);

break;
}

case TDH_INTYPE_POINTER:
case TDH_INTYPE_SIZET:
{
if (4 == PointerSize)
{
wprintf(L"0x%x\n", *(PULONG)pData);
}
else
{
wprintf(L"0x%x\n", *(PULONGLONG)pData);
}

break;
}

case TDH_INTYPE_FILETIME:
{
ULONGLONG TimeStamp = 0;
SYSTEMTIME st;
SYSTEMTIME stLocal;
FILETIME ft;
ft.dwHighDateTime = pEvent->EventHeader.TimeStamp.HighPart;
ft.dwLowDateTime = pEvent->EventHeader.TimeStamp.LowPart;

FileTimeToSystemTime(&ft, &st);
SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal);

TimeStamp = pEvent->EventHeader.TimeStamp.QuadPart;

//Local System Time(YYYY-MM-DD HH:MM:SS):
wprintf(L"%d-%d-%d %d:%d:%d\n", stLocal.wYear, stLocal.wMonth,stLocal.wDay, stLocal.wHour, stLocal.wMinute, stLocal.wSecond);

break;
}

case TDH_INTYPE_SYSTEMTIME:
{
break;
}

case TDH_INTYPE_SID:
{
WCHAR UserName[MAX_NAME];
WCHAR DomainName[MAX_NAME];
DWORD cchUserSize = MAX_NAME;
DWORD cchDomainSize = MAX_NAME;
SID_NAME_USE eNameUse;

if (!LookupAccountSid(NULL, (PSID)pData, UserName, &cchUserSize, DomainName, &cchDomainSize, &eNameUse))
{
if (ERROR_NONE_MAPPED == status)
{
wprintf(L"Unable to locate account for the specified SID\n");
status = ERROR_SUCCESS;
}
else
{
wprintf(L"LookupAccountSid failed with %lu\n", status = GetLastError());
}

goto cleanup;
}
else
{
wprintf(L"%s\\%s\n", DomainName, UserName);
}

break;
}

case TDH_INTYPE_HEXINT32:
{
wprintf(L"0x%x\n", (PULONG)pData);
break;
}

case TDH_INTYPE_HEXINT64:
{
wprintf(L"0x%x\n", (PULONGLONG)pData);
break;
}

case TDH_INTYPE_UNICODECHAR:
{
wprintf(L"%c\n", *(PWCHAR)pData);
break;
}

case TDH_INTYPE_ANSICHAR:
{
wprintf(L"%C\n", *(PCHAR)pData);
break;
}

case TDH_INTYPE_WBEMSID:
{
WCHAR UserName[MAX_NAME];
WCHAR DomainName[MAX_NAME];
DWORD cchUserSize = MAX_NAME;
DWORD cchDomainSize = MAX_NAME;
SID_NAME_USE eNameUse;

if ((PULONG)pData > 0)
{
// A WBEM SID is actually a TOKEN_USER structure followed by the SID. The size of the
// TOKEN_USER structure differs depending on whether the events were generated on a
// 32-bit or 64-bit architecture. Also the structure is aligned on an 8-byte boundary,
// so its size is 8 bytes on a 32-bit computer and 16 bytes on a 64-bit computer.
// Doubling the pointer size handles both cases.

pData += PointerSize * 2;

if (!LookupAccountSid(NULL, (PSID)pData, UserName, &cchUserSize, DomainName, &cchDomainSize, &eNameUse))
{
if (ERROR_NONE_MAPPED == status)
{
wprintf(L"Unable to locate account for the specified SID\n");
status = ERROR_SUCCESS;
}
else
{
wprintf(L"LookupAccountSid failed with %lu\n", status = GetLastError());
}

goto cleanup;
}
else
{
wprintf(L"%s\\%s\n", DomainName, UserName);
}
}

break;
}

default:
status = ERROR_NOT_FOUND;
}

cleanup:

return status;
}

DWORD GetArraySize(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT ArraySize)
{
DWORD status = ERROR_SUCCESS;
PROPERTY_DATA_DESCRIPTOR DataDescriptor;
DWORD PropertySize = 0;

if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamCount) == PropertyParamCount)
{
DWORD Count = 0; // Expects the count to be defined by a UINT16 or UINT32
DWORD j = pInfo->EventPropertyInfoArray[i].countPropertyIndex;
ZeroMemory(&DataDescriptor, sizeof(PROPERTY_DATA_DESCRIPTOR));
DataDescriptor.PropertyName = (ULONGLONG)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[j].NameOffset);
DataDescriptor.ArrayIndex = ULONG_MAX;
status = TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &PropertySize);
status = TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, PropertySize, (PBYTE)&Count);
*ArraySize = (USHORT)Count;
}
else
{
*ArraySize = pInfo->EventPropertyInfoArray[i].count;
}

return status;
}

void RemoveTrailingSpace(PEVENT_MAP_INFO pMapInfo)
{
SIZE_T ByteLength = 0;

for (DWORD i = 0; i < pMapInfo->EntryCount; i++)
{
ByteLength = (wcslen((LPWSTR)((PBYTE)pMapInfo + pMapInfo->MapEntryArray[i].OutputOffset)) - 1) * 2;
*((LPWSTR)((PBYTE)pMapInfo + (pMapInfo->MapEntryArray[i].OutputOffset + ByteLength))) = L'\0';
}
}

DWORD GetMapInfo(PEVENT_RECORD pEvent, LPWSTR pMapName, DWORD DecodingSource, PEVENT_MAP_INFO& pMapInfo)
{
DWORD status = ERROR_SUCCESS;
DWORD MapSize = 0;

// Retrieve the required buffer size for the map info.

status = TdhGetEventMapInformation(pEvent, pMapName, pMapInfo, &MapSize);

if (ERROR_INSUFFICIENT_BUFFER == status)
{
pMapInfo = (PEVENT_MAP_INFO)malloc(MapSize);
if (pMapInfo == NULL)
{
wprintf(L"Failed to allocate memory for map info (size=%lu).\n", MapSize);
status = ERROR_OUTOFMEMORY;
goto cleanup;
}

// Retrieve the map info.

status = TdhGetEventMapInformation(pEvent, pMapName, pMapInfo, &MapSize);
}

if (ERROR_SUCCESS == status)
{
if (DecodingSourceXMLFile == DecodingSource)
{
RemoveTrailingSpace(pMapInfo);
}
}
else
{
if (ERROR_NOT_FOUND == status)
{
status = ERROR_SUCCESS; // This case is okay.
}
else
{
wprintf(L"TdhGetEventMapInformation failed with 0x%x.\n", status);
}
}

cleanup:

return status;
}

DWORD PrintProperties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, LPWSTR pStructureName, USHORT StructIndex)
{
DWORD status = ERROR_SUCCESS;
DWORD LastMember = 0; // Last member of a structure
USHORT ArraySize = 0;
PEVENT_MAP_INFO pMapInfo = NULL;
PROPERTY_DATA_DESCRIPTOR DataDescriptors[2];
ULONG DescriptorsCount = 0;
DWORD PropertySize = 0;
PBYTE pData = NULL;

// Get the size of the array if the property is an array.

status = GetArraySize(pEvent, pInfo, i, &ArraySize);

for (USHORT k = 0; k < ArraySize; k++)
{
wprintf(L"%*s%s: ", (pStructureName) ? 4 : 0, L"", (LPWSTR)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].NameOffset));

// If the property is a structure, print the members of the structure.

if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct)
{
wprintf(L"\n");

LastMember = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex +
pInfo->EventPropertyInfoArray[i].structType.NumOfStructMembers;

for (USHORT j = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex; j < LastMember; j++)
{
status = PrintProperties(pEvent, pInfo, j, (LPWSTR)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].NameOffset), k);
if (ERROR_SUCCESS != status)
{
wprintf(L"Printing the members of the structure failed.\n");
goto cleanup;
}
}
}
else
{
ZeroMemory(&DataDescriptors, sizeof(DataDescriptors));

// To retrieve a member of a structure, you need to specify an array of descriptors. The
// first descriptor in the array identifies the name of the structure and the second
// descriptor defines the member of the structure whose data you want to retrieve.

if (pStructureName)
{
DataDescriptors[0].PropertyName = (ULONGLONG)pStructureName;
DataDescriptors[0].ArrayIndex = StructIndex;
DataDescriptors[1].PropertyName = (ULONGLONG)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].NameOffset);
DataDescriptors[1].ArrayIndex = k;
DescriptorsCount = 2;
}
else
{
DataDescriptors[0].PropertyName = (ULONGLONG)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].NameOffset);
DataDescriptors[0].ArrayIndex = k;
DescriptorsCount = 1;
}

// The TDH API does not support IPv6 addresses. If the output type is TDH_OUTTYPE_IPV6,
// you will not be able to consume the rest of the event. If you try to consume the
// remainder of the event, you will get ERROR_EVT_INVALID_EVENT_DATA.

if (TDH_INTYPE_BINARY == pInfo->EventPropertyInfoArray[i].nonStructType.InType &&
TDH_OUTTYPE_IPV6 == pInfo->EventPropertyInfoArray[i].nonStructType.OutType)
{
wprintf(L"The event contains an IPv6 address. Skipping event.\n");
status = ERROR_EVT_INVALID_EVENT_DATA;
break;
}
else
{
status = TdhGetPropertySize(pEvent, 0, NULL, DescriptorsCount, &DataDescriptors[0], &PropertySize);

if (ERROR_SUCCESS != status)
{
wprintf(L"TdhGetPropertySize failed with %lu\n", status);
goto cleanup;
}

pData = (PBYTE)malloc(PropertySize);

if (NULL == pData)
{
wprintf(L"Failed to allocate memory for property data\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}

status = TdhGetProperty(pEvent, 0, NULL, DescriptorsCount, &DataDescriptors[0], PropertySize, pData);

// Get the name/value mapping if the property specifies a value map.

status = GetMapInfo(pEvent,
(PWCHAR)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].nonStructType.MapNameOffset),
pInfo->DecodingSource,
pMapInfo);

if (ERROR_SUCCESS != status)
{
wprintf(L"GetMapInfo failed\n");
goto cleanup;
}

status = FormatAndPrintData(pEvent,
pInfo->EventPropertyInfoArray[i].nonStructType.InType,
pInfo->EventPropertyInfoArray[i].nonStructType.OutType,
pData,
PropertySize,
pMapInfo
);

if (ERROR_SUCCESS != status)
{
wprintf(L"GetMapInfo failed\n");
goto cleanup;
}

if (pData)
{
free(pData);
pData = NULL;
}

if (pMapInfo)
{
free(pMapInfo);
pMapInfo = NULL;
}
}
}
}

cleanup:

if (pData)
{
free(pData);
pData = NULL;
}

if (pMapInfo)
{
free(pMapInfo);
pMapInfo = NULL;
}

return status;
}

void Showinfo(PTRACE_EVENT_INFO& pInfo)
{
printf(("Id: %u\n"), pInfo->EventDescriptor.Id);
printf(("Version: %u\n"), pInfo->EventDescriptor.Version);
printf(("Channel: %u\n"), pInfo->EventDescriptor.Channel);
printf(("Level: %u\n"), pInfo->EventDescriptor.Level);
printf(("Opcode: %u\n"), pInfo->EventDescriptor.Opcode);
printf(("Task: %u\n"), pInfo->EventDescriptor.Task);
printf(("Keyword: %lu\n"), pInfo->EventDescriptor.Keyword);

wprintf(L"ProviderName: %s\n", (WCHAR*)pInfo + pInfo->ProviderNameOffset);
wprintf(L"ChannelName: %s\n", (WCHAR*)pInfo + pInfo->ChannelNameOffset);
wprintf(L"KeywordsName: %s\n", (WCHAR*)pInfo + pInfo->KeywordsNameOffset);
wprintf(L"TaskName: %s\n", (WCHAR*)pInfo + pInfo->TaskNameOffset);

//ShowNameString((LPBYTE)pInfo + pInfo->ProviderNameOffset, pInfo->ProviderNameOffset, L"ProviderName");
//ShowNameString((LPBYTE)pInfo + pInfo->ChannelNameOffset, pInfo->ChannelNameOffset, L"ChannelName");
//ShowNameString((LPBYTE)pInfo + pInfo->KeywordsNameOffset, pInfo->KeywordsNameOffset, L"KeywordsName");
//ShowNameString((LPBYTE)pInfo + pInfo->TaskNameOffset, pInfo->TaskNameOffset, L"TaskName");
}

DWORD GetEventInformation(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO& pInfo)
{
DWORD status = ERROR_SUCCESS;
DWORD BufferSize = 0;

status = TdhGetEventInformation(pEvent, 0, NULL, pInfo, &BufferSize);

if (ERROR_INSUFFICIENT_BUFFER == status)
{
pInfo = (TRACE_EVENT_INFO*)malloc(BufferSize);
if (pInfo == NULL)
{
printf("Failed to allocate memory for event info (size=%lu).\n", BufferSize);
status = ERROR_OUTOFMEMORY;
goto cleanup;
}

// Retrieve the event metadata.

status = TdhGetEventInformation(pEvent, 0, NULL, pInfo, &BufferSize);
}

if (ERROR_SUCCESS != status)
{
GetLastErrorEx("TdhGetEventInformation 错误代码", status);
}

cleanup:

return status;
}

VOID WINAPI EventRecordCallback(PEVENT_RECORD pEventRecord)
{
DWORD status = 0;
PTRACE_EVENT_INFO pInfo = NULL;
LPWSTR pwsEventGuid = NULL;
printf("ETW ********************************************************** start\n");
if (!IsEqualGUID(pEventRecord->EventHeader.ProviderId, PROCESS_PROVIDER_GUID))
{
return;
}
else
{
status = GetEventInformation(pEventRecord, pInfo);

if (ERROR_SUCCESS != status)
{
GetLastErrorEx("GetEventInformation 错误代码", status);
exit(0);
}

// Determine whether the event is defined by a MOF class, in an instrumentation manifest, or
// a WPP template; to use TDH to decode the event, it must be defined by one of these three sources.

if (DecodingSourceWbem == pInfo->DecodingSource) // MOF class
{
HRESULT hr = StringFromCLSID(pInfo->EventGuid, &pwsEventGuid);

if (FAILED(hr))
{
GetLastErrorEx("StringFromCLSID 错误代码", hr);
status = hr;
exit(0);
}

wprintf(L"\nEvent GUID: %s\n", pwsEventGuid);
CoTaskMemFree(pwsEventGuid);
pwsEventGuid = NULL;

wprintf(L"Event version: %d\n", pEventRecord->EventHeader.EventDescriptor.Version);
wprintf(L"Event type: %d\n", pEventRecord->EventHeader.EventDescriptor.Opcode);
}
else if (DecodingSourceXMLFile == pInfo->DecodingSource) // Instrumentation manifest
{
}
else // Not handling the WPP case
{
exit(0);
}
}


//打印 etw info
Showinfo(pInfo);

// If the event contains event-specific data use TDH to extract the event data. For this
// example, to extract the data, the event must be defined by a MOF class or an instrumentation manifest.

// Need to get the PointerSize for each event to cover the case where you are consuming events
// from multiple log files that could have been generated on different architectures. Otherwise,
// you could have accessed the pointer size when you opened the trace above (see pHeader->PointerSize).

if (EVENT_HEADER_FLAG_32_BIT_HEADER == (pEventRecord->EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER))
{
PointerSize = 4;
}
else
{
PointerSize = 8;
}


for (USHORT i = 0; i < pInfo->TopLevelPropertyCount; i++)
{
//ShowPropertyInfo(pEventRecord, pInfo, &pInfo->EventPropertyInfoArray[i], NULL);
status = PrintProperties(pEventRecord, pInfo, i, NULL, 0);
if (ERROR_SUCCESS != status)
{
wprintf(L"Printing top level properties failed.\n");
}
}

printf("ETW ********************************************************** end\n\n");
return;
}


static DWORD WINAPI Win32TracingThread(LPVOID Parameter)
{
printf("processing trace...\n");
auto ptStatus = ProcessTrace(&traceHandle, 1, NULL, NULL);
if (ptStatus != ERROR_SUCCESS && ptStatus != ERROR_CANCELLED)
{
GetLastErrorEx("ProcessTrace 错误代码", ptStatus);
}
return(0);
}

int main(void)
{
printf("entering main program...\n");

ZeroMemory(pSessionProperties, bufferSize);
pSessionProperties->Wnode.BufferSize = bufferSize;
pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution
pSessionProperties->Wnode.Guid = SessionGuid;
pSessionProperties->FlushTimer = 0;
//pSessionProperties->EnableFlags = EVENT_TRACE_FLAG_PROCESS | EVENT_TRACE_FLAG_PROCESS_COUNTERS;
pSessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
pSessionProperties->LogFileNameOffset = 0;
pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
StringCbCopy((STRSAFE_LPWSTR)((char*)pSessionProperties + pSessionProperties->LoggerNameOffset), sizeof(LOGSESSION_NAME), LOGSESSION_NAME);

// stop any previous session
auto stopStatus = ControlTrace(0, LOGSESSION_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP);

printf("starting trace...\n");
auto sTrStatus = StartTrace(static_cast<PTRACEHANDLE>(&sessionHandle), LOGSESSION_NAME, pSessionProperties);

ENABLE_TRACE_PARAMETERS params{};
params.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;

printf("enabling trace...\n");
auto eTrExstatus = EnableTraceEx2(sessionHandle, &PROCESS_PROVIDER_GUID,
EVENT_CONTROL_CODE_ENABLE_PROVIDER,TRACE_LEVEL_INFORMATION, 0x10|0x40, 0, 0, &params);
if (eTrExstatus != ERROR_SUCCESS)
{
GetLastErrorEx("EnableTraceEx2 错误代码", eTrExstatus);
ControlTrace(sessionHandle, nullptr, pSessionProperties, EVENT_TRACE_CONTROL_STOP);
return -1;
}

EVENT_TRACE_LOGFILE loggerInfo = { 0 };

loggerInfo.ProcessTraceMode = EVENT_TRACE_REAL_TIME_MODE | PROCESS_TRACE_MODE_EVENT_RECORD;

//loggerInfo.BufferCallback = BufferCallback;

// provide a callback whenever we get an event record
loggerInfo.EventRecordCallback = (PEVENT_RECORD_CALLBACK)EventRecordCallback;
loggerInfo.Context = NULL;

loggerInfo.LoggerName = (WCHAR*)LOGSESSION_NAME;
loggerInfo.LogFileName = NULL;

printf("opening trace...\n");
traceHandle = OpenTrace(&loggerInfo);

// calling thread will be blocked until BufferCallback returns FALSE or all events are delivered
// or CloseTrace is called
DWORD ThreadID;
HANDLE ThreadHandle = CreateThread(0, 0, Win32TracingThread, 0, 0, &ThreadID);

bool exit2 = false;
while (exit2 == false)
{
if (GetAsyncKeyState(VK_ESCAPE))
{
exit2 = true;
printf("escape pressed, exiting loop...\n");
}
}
CloseHandle(ThreadHandle);

if ((TRACEHANDLE)INVALID_HANDLE_VALUE != traceHandle)
{
printf("in cleanup...\n");
printf("closing trace...\n");
auto ctStatus = CloseTrace(traceHandle);
}

auto cTrStatus = ControlTrace(sessionHandle, nullptr, pSessionProperties, EVENT_TRACE_CONTROL_STOP);
auto eTrStatus = EnableTraceEx2(sessionHandle, &PROCESS_PROVIDER_GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr);

free(pSessionProperties);
}

我这里主要是监控进程的创建和退出以及 dll 加载和卸载

效果如下

entering main program...
starting trace...
enabling trace...
opening trace...
processing trace...
ETW ********************************************************** start
Id: 6
Version: 0
Channel: 16
Level: 4
Opcode: 0
Task: 6
Keyword: 64
ProviderName: Microsoft-Windows-Kernel-Process
ChannelName: Microsoft-Windows-Kernel-Process/Analytic
KeywordsName: WINEVENT_KEYWORD_IMAGE
TaskName: ImageUnload
ImageBase: 0xc45b0000
ImageSize: 0x32000
ProcessID: 9644
ImageCheckSum: 181919
TimeDateStamp: 1849399745
DefaultBase: 0xc45b0000
ImageName: \Device\HarddiskVolume4\Windows\System32\notepad.exe
ETW ********************************************************** end

ETW ********************************************************** start
Id: 6
Version: 0
Channel: 16
Level: 4
Opcode: 0
Task: 6
Keyword: 64
ProviderName: Microsoft-Windows-Kernel-Process
ChannelName: Microsoft-Windows-Kernel-Process/Analytic
KeywordsName: WINEVENT_KEYWORD_IMAGE
TaskName: ImageUnload
ImageBase: 0x4be50000
ImageSize: 0xd7000
ProcessID: 9644
ImageCheckSum: 904896
TimeDateStamp: 2571084312
DefaultBase: 0x4be50000
ImageName: \Device\HarddiskVolume4\Windows\System32\efswrt.dll
ETW ********************************************************** end

ETW ********************************************************** start
Id: 6
Version: 0
Channel: 16
Level: 4
Opcode: 0
Task: 6
Keyword: 64
ProviderName: Microsoft-Windows-Kernel-Process
ChannelName: Microsoft-Windows-Kernel-Process/Analytic
KeywordsName: WINEVENT_KEYWORD_IMAGE
TaskName: ImageUnload
ImageBase: 0x71e00000
ImageSize: 0x153000
ProcessID: 9644
ImageCheckSum: 1397918
TimeDateStamp: 1527226666
DefaultBase: 0x71e00000
ImageName: \Device\HarddiskVolume5\Stardock\Fences\FencesMenu64.dll
ETW ********************************************************** end

ETW ********************************************************** start
Id: 6
Version: 0
Channel: 16
Level: 4
Opcode: 0
Task: 6
Keyword: 64
ProviderName: Microsoft-Windows-Kernel-Process
ChannelName: Microsoft-Windows-Kernel-Process/Analytic
KeywordsName: WINEVENT_KEYWORD_IMAGE
TaskName: ImageUnload
ImageBase: 0x73730000
ImageSize: 0xbf000
ProcessID: 9644
ImageCheckSum: 796280
TimeDateStamp: 1118443760
DefaultBase: 0x73730000
ImageName: \Device\HarddiskVolume4\Windows\System32\msctfuimanager.dll
ETW ********************************************************** end

ETW ********************************************************** start
Id: 6
Version: 0
Channel: 16
Level: 4
Opcode: 0
Task: 6
Keyword: 64
ProviderName: Microsoft-Windows-Kernel-Process
ChannelName: Microsoft-Windows-Kernel-Process/Analytic
KeywordsName: WINEVENT_KEYWORD_IMAGE
TaskName: ImageUnload
ImageBase: 0x79530000
ImageSize: 0x270000
ProcessID: 9644
ImageCheckSum: 2550189
TimeDateStamp: 3941062522
DefaultBase: 0x79530000
ImageName: \Device\HarddiskVolume4\Windows\System32\UIAutomationCore.dll
ETW ********************************************************** end

不过自己写 ETW 最难受的地方就是在解析事件信息那里,那个地方需要判断数据类型然后在根据不同的类型做不同的解析,如果不是为了学习 ETW 的机制推荐使用微软的 Krabsetw 库

使用 Krabsetw 库监控进程启动和退出

代码

#include "packages/Microsoft.O365.Security.Krabsetw.4.1.18/lib/native/include/krabs.hpp"
#include <iostream>
int main()
{
// Setup basic Microsoft-Windows-Kernel-Process trace
krabs::user_trace trace(L"My magic trace");
krabs::provider<> provider(krabs::guid(L"{22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}"));

provider.any(0x10);

krabs::predicates::id_is eventid_is_1 = krabs::predicates::id_is(1);
krabs::predicates::id_is eventid_is_2 = krabs::predicates::id_is(2);

krabs::event_filter filter(
krabs::predicates::any_of({
&eventid_is_1,
&eventid_is_2,
})
);

filter.add_on_event_callback([](const EVENT_RECORD& record, const krabs::trace_context& trace_context) {
krabs::schema schema(record, trace_context.schema_locator);
krabs::parser parser(schema);


printf("Event ID: %d || Opcode: %d || Version %d\n", schema.event_id(), schema.event_opcode(), schema.event_version());

if (schema.event_id() == 1)
{
uint32_t pid = parser.parse<uint32_t>(L"ProcessID");
uint32_t ParentProcessID = parser.parse<uint32_t>(L"ParentProcessID");
std::wstring image_name = parser.parse<std::wstring>(L"ImageName");
std::wcout << schema.provider_name();
std::wcout << L" etw id=";
std::wcout << schema.event_id();
std::wcout << L" task_name=" << schema.task_name();
std::wcout << L" ProcessID=" << pid;
std::wcout << L" ImageName=" << image_name;
std::wcout << L" ParentProcessID=" << ParentProcessID;
std::wcout << "\n";
std::wcout << std::endl;
}



else if(schema.event_id() == 2)
{
uint32_t pid = parser.parse<uint32_t>(L"ProcessID");
uint32_t ExitCode = parser.parse<uint32_t>(L"ExitCode");
std::wcout << schema.provider_name();
std::wcout << L" etw id=";
std::wcout << schema.event_id();
std::wcout << L" task_name=" << schema.task_name();
std::wcout << L" ProcessID=" << pid;
std::wcout << L" ExitCode=" << ExitCode;
std::wcout << "\n";
std::wcout << std::endl;
}
});

// Start ETW Session
provider.add_filter(filter);
trace.enable(provider);
printf("Starting trace...\n");
trace.start();
}

效果如下

Starting trace...
Event ID: 1 || Opcode: 1 || Version 3
Microsoft-Windows-Kernel-Process etw id=1 task_name=ProcessStart ProcessID=28208 ImageName=\Device\HarddiskVolume4\Windows\System32\notepad.exe ParentProcessID=20668

Event ID: 2 || Opcode: 2 || Version 2
Microsoft-Windows-Kernel-Process etw id=2 task_name=ProcessStop ProcessID=28208 ExitCode=0

在这里顺便提一下关于 ETW 绕过的问题,用 EtwExplorer 可以查看 ETW 事件提供者,你能看到系统中有很多的提供者,其中大部分提供者运行于 R3 层对于这种直接修补 Advapi32.dll!EventWrite 函数即可(在 Win 高版本上 Advapi32.dll!EventWrite 是直接转发到 ntdll.dll!EtwEventWrite 函数上的)这是一种。

另一种更为优雅的 bypass 方式是找到 EventRegister 函数注册的句柄,然后直接调用 EventUnregister 函数关闭这个句柄这样即使调用 EtwEventWrite 函数也毫无作用,同时还有一些以 Microsoft-Windows-Kernel-XXXXXX 名字开头的提供者是内核提供者它们运行于 R0 进行记录,对于这些提供者在 R3 来说一般是无法绕过的。对于防御者来说应该尽量使用内核提供者它们更加可靠不容易被绕过。

后话

其实最开始想非常细致的写 ETW 中的知识点,而且确实也写了一些但是写到后面发现要写的实在是太多了还是鸽了吧,等有机会再说吧,

相关参考

https://docs.microsoft.com/zh-cn/windows/win32/etw/using-tdhgetproperty-to-consume-event-data
https://stackoverflow.com/questions/61197136/not-receiving-microsoft-windows-kernel-process-events-from-real-time-etw-consume
https://github.com/microsoft/krabsetw

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

深海夜未眠

暂无简介

0 文章
0 评论
24 人气
更多

推荐作者

苍风燃霜

文章 0 评论 0

悸初

文章 0 评论 0

撧情箌佬

文章 0 评论 0

森罗

文章 0 评论 0

lyn1245

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文