Simovits

Event Tracing for Windows (ETW) – C++exempel för filskrivningslogg

Då man arbetar med säkerhet finns flera tillfällen då det är viktigt att kunna analysera exakt vilka filer som öppnas i ett system. Detta kan exempelvis vara:

Vanligen finns redan färdiga verktyg för detta, exempelvis Sysinternals gamla vanliga verktyg. Windows har sedan ett antal år tillbaks ett väl utbyggt stöd för att kunna logga kernel eller applikationshändelser i Windows (Event Tracing For Windows, ETW, https://msdn.microsoft.com/en-us/library/windows/desktop/aa363668(v=vs.85).aspx). Ett problem med detta API är dock att det kunde varit bättre dokumenterat. Det har även uppdaterats en del senare åren, med större förändringar från Windows Vista.

Överssiktligt fungerar ETW enligt följande:

1. Starta igång trace (StartTrace)

PEVENT_TRACE_PROPERTIES createTraceProps(HANDLE hProgHeap)
{
	PEVENT_TRACE_PROPERTIES traceProps = NULL;
	ULONG bufSize = sizeof(EVENT_TRACE_PROPERTIES) + (MAXIMUM_SESSION_NAME + MAX_PATH) * sizeof(WCHAR);

	traceProps = (PEVENT_TRACE_PROPERTIES)HeapAlloc(hProgHeap, 0, bufSize);

	if (traceProps == NULL) 
	{
		return NULL;
	}

	ZeroMemory(traceProps, bufSize);
	traceProps->Wnode.BufferSize = bufSize;
	traceProps->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
	traceProps->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
	traceProps->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + (MAXIMUM_SESSION_NAME * sizeof(WCHAR));
	traceProps->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
	traceProps->Wnode.ClientContext = 1;	
	traceProps->MaximumFileSize = 100;		
	traceProps->BufferSize = 512;			
	traceProps->MaximumBuffers = 128;
	traceProps->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
	traceProps->MinimumBuffers = 1;
	traceProps->FlushTimer = 1;
	traceProps->EnableFlags = EVENT_TRACE_FLAG_DISK_IO | EVENT_TRACE_FLAG_DISK_FILE_IO | EVENT_TRACE_FLAG_FILE_IO_INIT ;

	return traceProps;
}


 

 

// Skapa trace-struct.
traceProps = createTraceProps(hProgHeap);

if (traceProps == NULL)
{
wprintf(L"[-] Could not create trace properties\n");
goto Exit;
}

result = StartTraceW(&hSession, LoggerName, traceProps);

// Loggern är redan startad. Så vi startar om den.
if (result == ERROR_ALREADY_EXISTS)
{
result = ControlTrace(0, LoggerName, traceProps, EVENT_TRACE_CONTROL_UPDATE);

if (result != ERROR_SUCCESS)
{
wprintf(L"[-] Could not update trace \n");
goto Exit;
}
}

// TODO: Hantera detta fel om det uppstår.
if (result == ERROR_SHARING_VIOLATION)
{
wprintf(L"[-] StartTrace() failed with %lu. Sharing violation.\n", result);
goto Exit;
}

// Om vi inte kan hantera felet, så terminerar vi.
if (result != ERROR_SUCCESS )
{
wprintf(L"[-] StartTrace() failed with %lu\n", result);
goto Exit;
}

 

2. Instruera vart trace ska lämna av informationen (ProcessTrace) exempelvis till en loggfil (.etl-fil) eller till en callback-funktion. Notera att ProcessTrace blockerar till dess att trace är stoppad så den behöver placeras i en tråd.  (https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx)

DWORD WINAPI  processTraceFunction(LPVOID lpParam)
{
	LPProcessTraceArguments traceArgs = (LPProcessTraceArguments)lpParam;
	ULONG result = ProcessTrace(traceArgs->traceHandle, 1, 0, 0);

	if (result != ERROR_SUCCESS && result != ERROR_CANCELLED)
	{
		wprintf(L"[-] ProcessTrace FEL %lu\n", result);
	}

	return 0;
}


DWORD dwThreadId = 0;
HANDLE hThread = CreateThread(NULL,0, processTraceFunction,traceArgs,0, &dwThreadId);

 

3. Hantera inkommande trace-logg – Antagligen själva applikationens syfte (skriv till fil eller dylikt)

4. Stoppa trace (StopTrace)

		ULONG result = ControlTrace(hOpenedTrace, NULL, traceProps, EVENT_TRACE_CONTROL_STOP);
		if (result != ERROR_SUCCESS)
		{
			wprintf(L"[-] StopTrace() failed:  %lu\n", result);
		}


		if (INVALID_PROCESSTRACE_HANDLE != hOpenedTrace)
		{
			result = CloseTrace(hOpenedTrace);
		}

 

För att kunna använda ETW för att tracka filskrivningar behöver man processen som exekverar vara medlem i Windows-gruppen Performance Log Users. Ett litet exempel på ett program som använder ETW har jag lagt upp under: https://github.com/jamesrep/enkellog.

//James