mirror of
https://github.com/safing/portmaster
synced 2025-04-07 12:39:09 +00:00
* [service] Add reading of PID in ETW DNS event * [service] Use PID of the ETW DNS events * [service] Fix use of nil pointer * [service] Fix compiler error
197 lines
No EOL
7.5 KiB
C++
197 lines
No EOL
7.5 KiB
C++
// dllmain.cpp : Defines the entry point for the DLL application.
|
|
#include "pch.h"
|
|
|
|
#pragma comment(lib, "tdh.lib")
|
|
|
|
// GUID of the DNS log provider
|
|
static const GUID DNS_CLIENT_PROVIDER_GUID = {
|
|
0x1C95126E,
|
|
0x7EEA,
|
|
0x49A9,
|
|
{0xA3, 0xFE, 0xA3, 0x78, 0xB0, 0x3D, 0xDB, 0x4D} };
|
|
|
|
// GUID of the event session. This should be unique for the application.
|
|
static const GUID PORTMASTER_ETW_SESSION_GUID = {
|
|
0x0211d070,
|
|
0xc3b2,
|
|
0x4609,
|
|
{0x92, 0xf5, 0x28, 0xe7, 0x18, 0xb2, 0x3b, 0x18} };
|
|
|
|
// Name of the session. This is visble when user queries all ETW sessions.
|
|
// (example `logman query -ets`)
|
|
#define LOGSESSION_NAME L"PortmasterDNSEventListener"
|
|
|
|
// Fuction type of the callback that will be called on each event.
|
|
typedef uint64_t(*GoEventRecordCallback)(wchar_t* domain, uint32_t pid, wchar_t* result);
|
|
|
|
// Holds the state of the ETW Session.
|
|
struct ETWSessionState {
|
|
TRACEHANDLE SessionTraceHandle;
|
|
EVENT_TRACE_PROPERTIES* SessionProperties;
|
|
TRACEHANDLE sessionHandle;
|
|
GoEventRecordCallback callback;
|
|
};
|
|
|
|
// getPropertyValue reads a property from the event.
|
|
static bool getPropertyValue(PEVENT_RECORD evt, LPWSTR prop, PBYTE* pData) {
|
|
// Describe the data that needs to be retrieved from the event.
|
|
PROPERTY_DATA_DESCRIPTOR DataDescriptor;
|
|
ZeroMemory(&DataDescriptor, sizeof(DataDescriptor));
|
|
DataDescriptor.PropertyName = (ULONGLONG)(prop);
|
|
DataDescriptor.ArrayIndex = 0;
|
|
|
|
DWORD PropertySize = 0;
|
|
// Check if the data is available and what is the size of it.
|
|
DWORD status =
|
|
TdhGetPropertySize(evt, 0, NULL, 1, &DataDescriptor, &PropertySize);
|
|
if (ERROR_SUCCESS != status) {
|
|
return false;
|
|
}
|
|
|
|
// Allocate memory for the data.
|
|
*pData = (PBYTE)malloc(PropertySize);
|
|
if (NULL == *pData) {
|
|
return false;
|
|
}
|
|
|
|
// Get the data.
|
|
status =
|
|
TdhGetProperty(evt, 0, NULL, 1, &DataDescriptor, PropertySize, *pData);
|
|
if (ERROR_SUCCESS != status) {
|
|
if (*pData) {
|
|
free(*pData);
|
|
*pData = NULL;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// EventRecordCallback is a callback called on each event.
|
|
static void WINAPI EventRecordCallback(PEVENT_RECORD eventRecord) {
|
|
PBYTE resultValue = NULL;
|
|
PBYTE domainValue = NULL;
|
|
|
|
getPropertyValue(eventRecord, (LPWSTR)L"QueryResults", &resultValue);
|
|
getPropertyValue(eventRecord, (LPWSTR)L"QueryName", &domainValue);
|
|
|
|
ETWSessionState* state = (ETWSessionState*)eventRecord->UserContext;
|
|
|
|
if (resultValue != NULL && domainValue != NULL) {
|
|
state->callback((wchar_t*)domainValue, eventRecord->EventHeader.ProcessId, (wchar_t*)resultValue);
|
|
}
|
|
|
|
free(resultValue);
|
|
free(domainValue);
|
|
}
|
|
|
|
extern "C" {
|
|
// PM_ETWCreateState allocates memory for the state and initializes the config for the session. PM_ETWDestroySession must be called to avoid leaks.
|
|
// callback must be set to a valid function pointer.
|
|
__declspec(dllexport) ETWSessionState* PM_ETWCreateState(GoEventRecordCallback callback) {
|
|
// Create trace session properties.
|
|
ULONG BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME);
|
|
EVENT_TRACE_PROPERTIES* SessionProperties =
|
|
(EVENT_TRACE_PROPERTIES*)calloc(1, BufferSize);
|
|
SessionProperties->Wnode.BufferSize = BufferSize;
|
|
SessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
|
SessionProperties->Wnode.ClientContext = 1; // QPC clock resolution
|
|
SessionProperties->Wnode.Guid = PORTMASTER_ETW_SESSION_GUID;
|
|
SessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
|
|
SessionProperties->MaximumFileSize = 1; // MB
|
|
SessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
|
|
|
|
// Create state
|
|
ETWSessionState* state = (ETWSessionState*)calloc(1, sizeof(ETWSessionState));
|
|
state->SessionProperties = SessionProperties;
|
|
state->callback = callback;
|
|
return state;
|
|
}
|
|
|
|
// PM_ETWInitializeSession initializes the session.
|
|
__declspec(dllexport) uint32_t PM_ETWInitializeSession(ETWSessionState* state) {
|
|
return StartTrace(&state->SessionTraceHandle, LOGSESSION_NAME,
|
|
state->SessionProperties);
|
|
}
|
|
|
|
// PM_ETWStartTrace subscribes to the dns events and start listening. The function blocks while the listener is running.
|
|
// Call PM_ETWStopTrace to stop the listener.
|
|
__declspec(dllexport) uint32_t PM_ETWStartTrace(ETWSessionState* state) {
|
|
ULONG status =
|
|
EnableTraceEx2(state->SessionTraceHandle, (LPCGUID)&DNS_CLIENT_PROVIDER_GUID,
|
|
EVENT_CONTROL_CODE_ENABLE_PROVIDER,
|
|
TRACE_LEVEL_INFORMATION, 0, 0, 0, NULL);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
EVENT_TRACE_LOGFILE trace = { 0 };
|
|
|
|
trace.LoggerName = (LPWSTR)(LOGSESSION_NAME);
|
|
trace.ProcessTraceMode =
|
|
PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
|
|
trace.EventRecordCallback = EventRecordCallback;
|
|
trace.Context = state;
|
|
|
|
state->sessionHandle = OpenTrace(&trace);
|
|
if (state->sessionHandle == INVALID_PROCESSTRACE_HANDLE) {
|
|
return 1;
|
|
}
|
|
|
|
status = ProcessTrace(&state->sessionHandle, 1, NULL, NULL);
|
|
if (status != ERROR_SUCCESS) {
|
|
return 1;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// PM_ETWFlushTrace flushes the event buffer.
|
|
__declspec(dllexport) uint32_t PM_ETWFlushTrace(ETWSessionState* state) {
|
|
return ControlTrace(state->SessionTraceHandle, LOGSESSION_NAME,
|
|
state->SessionProperties, EVENT_TRACE_CONTROL_FLUSH);
|
|
}
|
|
|
|
// PM_ETWFlushTrace stops the listener.
|
|
__declspec(dllexport) uint32_t PM_ETWStopTrace(ETWSessionState* state) {
|
|
return ControlTrace(state->SessionTraceHandle, LOGSESSION_NAME, state->SessionProperties,
|
|
EVENT_TRACE_CONTROL_STOP);
|
|
}
|
|
|
|
// PM_ETWFlushTrace Closes the session and frees recourses.
|
|
__declspec(dllexport) uint32_t PM_ETWDestroySession(ETWSessionState* state) {
|
|
if (state == NULL) {
|
|
return 1;
|
|
}
|
|
uint32_t status = CloseTrace(state->sessionHandle);
|
|
|
|
// Free memory.
|
|
free(state->SessionProperties);
|
|
free(state);
|
|
return status;
|
|
}
|
|
|
|
// PM_ETWStopOldSession removes old session with the same name if they exist.
|
|
// It returns success(0) only if its able to delete the old session.
|
|
__declspec(dllexport) ULONG PM_ETWStopOldSession() {
|
|
ULONG status = ERROR_SUCCESS;
|
|
TRACEHANDLE sessionHandle = 0;
|
|
|
|
// Create trace session properties
|
|
size_t bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME);
|
|
EVENT_TRACE_PROPERTIES* sessionProperties = (EVENT_TRACE_PROPERTIES*)calloc(1, bufferSize);
|
|
sessionProperties->Wnode.BufferSize = (ULONG)bufferSize;
|
|
sessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
|
sessionProperties->Wnode.ClientContext = 1; // QPC clock resolution
|
|
sessionProperties->Wnode.Guid = PORTMASTER_ETW_SESSION_GUID;
|
|
sessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
|
|
|
|
// Use Control trace will stop the session which will trigger a delete.
|
|
status = ControlTrace(NULL, LOGSESSION_NAME, sessionProperties, EVENT_TRACE_CONTROL_STOP);
|
|
|
|
free(sessionProperties);
|
|
return status;
|
|
}
|
|
} |