2025-05-13 19:45:22 +03:00

1721 lines
54 KiB
C

/*
* Process Hacker Extended Services -
* trigger editor
*
* Copyright (C) 2011-2015 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include "extsrv.h"
typedef struct _ES_TRIGGER_DATA
{
ULONG Type;
union
{
PPH_STRING String;
struct
{
PVOID Binary;
ULONG BinaryLength;
};
UCHAR Byte;
ULONG64 UInt64;
};
} ES_TRIGGER_DATA, *PES_TRIGGER_DATA;
typedef struct _ES_TRIGGER_INFO
{
ULONG Type;
PGUID Subtype;
ULONG Action;
PPH_LIST DataList;
GUID SubtypeBuffer;
} ES_TRIGGER_INFO, *PES_TRIGGER_INFO;
typedef struct _ES_TRIGGER_CONTEXT
{
PPH_SERVICE_ITEM ServiceItem;
HWND WindowHandle;
HWND TriggersLv;
BOOLEAN Dirty;
ULONG InitialNumberOfTriggers;
PPH_LIST InfoList;
// Trigger dialog box
PES_TRIGGER_INFO EditingInfo;
ULONG LastSelectedType;
PPH_STRING LastCustomSubType;
// Value dialog box
PPH_STRING EditingValue;
} ES_TRIGGER_CONTEXT, *PES_TRIGGER_CONTEXT;
typedef struct _TYPE_ENTRY
{
ULONG Type;
PWSTR Name;
} TYPE_ENTRY, PTYPE_ENTRY;
typedef struct _SUBTYPE_ENTRY
{
ULONG Type;
PGUID Guid;
PWSTR Name;
} SUBTYPE_ENTRY, PSUBTYPE_ENTRY;
typedef struct _ETW_PUBLISHER_ENTRY
{
PPH_STRING PublisherName;
GUID Guid;
} ETW_PUBLISHER_ENTRY, *PETW_PUBLISHER_ENTRY;
INT_PTR CALLBACK EspServiceTriggerDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
INT_PTR CALLBACK ValueDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
static GUID NetworkManagerFirstIpAddressArrivalGuid = { 0x4f27f2de, 0x14e2, 0x430b, { 0xa5, 0x49, 0x7c, 0xd4, 0x8c, 0xbc, 0x82, 0x45 } };
static GUID NetworkManagerLastIpAddressRemovalGuid = { 0xcc4ba62a, 0x162e, 0x4648, { 0x84, 0x7a, 0xb6, 0xbd, 0xf9, 0x93, 0xe3, 0x35 } };
static GUID DomainJoinGuid = { 0x1ce20aba, 0x9851, 0x4421, { 0x94, 0x30, 0x1d, 0xde, 0xb7, 0x66, 0xe8, 0x09 } };
static GUID DomainLeaveGuid = { 0xddaf516e, 0x58c2, 0x4866, { 0x95, 0x74, 0xc3, 0xb6, 0x15, 0xd4, 0x2e, 0xa1 } };
static GUID FirewallPortOpenGuid = { 0xb7569e07, 0x8421, 0x4ee0, { 0xad, 0x10, 0x86, 0x91, 0x5a, 0xfd, 0xad, 0x09 } };
static GUID FirewallPortCloseGuid = { 0xa144ed38, 0x8e12, 0x4de4, { 0x9d, 0x96, 0xe6, 0x47, 0x40, 0xb1, 0xa5, 0x24 } };
static GUID MachinePolicyPresentGuid = { 0x659fcae6, 0x5bdb, 0x4da9, { 0xb1, 0xff, 0xca, 0x2a, 0x17, 0x8d, 0x46, 0xe0 } };
static GUID UserPolicyPresentGuid = { 0x54fb46c8, 0xf089, 0x464c, { 0xb1, 0xfd, 0x59, 0xd1, 0xb6, 0x2c, 0x3b, 0x50 } };
static GUID RpcInterfaceEventGuid = { 0xbc90d167, 0x9470, 0x4139, { 0xa9, 0xba, 0xbe, 0x0b, 0xbb, 0xf5, 0xb7, 0x4d } };
static GUID NamedPipeEventGuid = { 0x1f81d131, 0x3fac, 0x4537, { 0x9e, 0x0c, 0x7e, 0x7b, 0x0c, 0x2f, 0x4b, 0x55 } };
static GUID SubTypeUnknownGuid; // dummy
static TYPE_ENTRY TypeEntries[] =
{
{ SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL, L"Device interface arrival" },
{ SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, L"IP address availability" },
{ SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, L"Domain join" },
{ SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, L"Firewall port event" },
{ SERVICE_TRIGGER_TYPE_GROUP_POLICY, L"Group policy" },
{ SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, L"Network endpoint" },
{ SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE, L"Custom system state change" },
{ SERVICE_TRIGGER_TYPE_CUSTOM, L"Custom" }
};
static SUBTYPE_ENTRY SubTypeEntries[] =
{
{ SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, NULL, L"IP address" },
{ SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerFirstIpAddressArrivalGuid, L"IP address: First arrival" },
{ SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerLastIpAddressRemovalGuid, L"IP address: Last removal" },
{ SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &SubTypeUnknownGuid, L"IP address: Unknown" },
{ SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, NULL, L"Domain" },
{ SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainJoinGuid, L"Domain: Join" },
{ SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainLeaveGuid, L"Domain: Leave" },
{ SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &SubTypeUnknownGuid, L"Domain: Unknown" },
{ SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, NULL, L"Firewall port" },
{ SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortOpenGuid, L"Firewall port: Open" },
{ SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortCloseGuid, L"Firewall port: Close" },
{ SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &SubTypeUnknownGuid, L"Firewall port: Unknown" },
{ SERVICE_TRIGGER_TYPE_GROUP_POLICY, NULL, L"Group policy change" },
{ SERVICE_TRIGGER_TYPE_GROUP_POLICY, &MachinePolicyPresentGuid, L"Group policy change: Machine" },
{ SERVICE_TRIGGER_TYPE_GROUP_POLICY, &UserPolicyPresentGuid, L"Group policy change: User" },
{ SERVICE_TRIGGER_TYPE_GROUP_POLICY, &SubTypeUnknownGuid, L"Group policy change: Unknown" },
{ SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, NULL, L"Network endpoint" },
{ SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &RpcInterfaceEventGuid, L"Network endpoint: RPC interface" },
{ SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &NamedPipeEventGuid, L"Network endpoint: Named pipe" },
{ SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &SubTypeUnknownGuid, L"Network endpoint: Unknown" }
};
static PH_STRINGREF PublishersKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\");
PES_TRIGGER_DATA EspCreateTriggerData(
_In_opt_ PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM DataItem
)
{
PES_TRIGGER_DATA data;
data = PhAllocate(sizeof(ES_TRIGGER_DATA));
memset(data, 0, sizeof(ES_TRIGGER_DATA));
if (DataItem)
{
data->Type = DataItem->dwDataType;
if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
{
if (DataItem->pData && DataItem->cbData >= 2)
data->String = PhCreateStringEx((PWSTR)DataItem->pData, DataItem->cbData - 2); // exclude final null terminator
else
data->String = PhReferenceEmptyString();
}
else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
{
data->BinaryLength = DataItem->cbData;
data->Binary = PhAllocateCopy(DataItem->pData, DataItem->cbData);
}
else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL)
{
if (DataItem->cbData == sizeof(UCHAR))
data->Byte = *(PUCHAR)DataItem->pData;
}
else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY || data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL)
{
if (DataItem->cbData == sizeof(ULONG64))
data->UInt64 = *(PULONG64)DataItem->pData;
}
}
return data;
}
PES_TRIGGER_DATA EspCloneTriggerData(
_In_ PES_TRIGGER_DATA Data
)
{
PES_TRIGGER_DATA newData;
newData = PhAllocateCopy(Data, sizeof(ES_TRIGGER_DATA));
if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
{
if (newData->String)
newData->String = PhDuplicateString(newData->String);
}
else if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
{
if (newData->Binary)
newData->Binary = PhAllocateCopy(newData->Binary, newData->BinaryLength);
}
return newData;
}
VOID EspDestroyTriggerData(
_In_ PES_TRIGGER_DATA Data
)
{
if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
{
if (Data->String)
PhDereferenceObject(Data->String);
}
else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
{
if (Data->Binary)
PhFree(Data->Binary);
}
PhFree(Data);
}
PES_TRIGGER_INFO EspCreateTriggerInfo(
_In_opt_ PSERVICE_TRIGGER Trigger
)
{
PES_TRIGGER_INFO info;
info = PhAllocate(sizeof(ES_TRIGGER_INFO));
memset(info, 0, sizeof(ES_TRIGGER_INFO));
if (Trigger)
{
info->Type = Trigger->dwTriggerType;
if (Trigger->pTriggerSubtype)
{
info->SubtypeBuffer = *Trigger->pTriggerSubtype;
info->Subtype = &info->SubtypeBuffer;
}
info->Action = Trigger->dwAction;
if (
info->Type == SERVICE_TRIGGER_TYPE_CUSTOM ||
info->Type == SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL ||
info->Type == SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT ||
info->Type == SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT
)
{
ULONG i;
info->DataList = PhCreateList(Trigger->cDataItems);
for (i = 0; i < Trigger->cDataItems; i++)
{
PhAddItemList(info->DataList, EspCreateTriggerData(&Trigger->pDataItems[i]));
}
}
}
return info;
}
PES_TRIGGER_INFO EspCloneTriggerInfo(
_In_ PES_TRIGGER_INFO Info
)
{
PES_TRIGGER_INFO newInfo;
newInfo = PhAllocateCopy(Info, sizeof(ES_TRIGGER_INFO));
if (newInfo->Subtype == &Info->SubtypeBuffer)
newInfo->Subtype = &newInfo->SubtypeBuffer;
if (newInfo->DataList)
{
ULONG i;
newInfo->DataList = PhCreateList(Info->DataList->AllocatedCount);
newInfo->DataList->Count = Info->DataList->Count;
for (i = 0; i < Info->DataList->Count; i++)
newInfo->DataList->Items[i] = EspCloneTriggerData(Info->DataList->Items[i]);
}
return newInfo;
}
VOID EspDestroyTriggerInfo(
_In_ PES_TRIGGER_INFO Info
)
{
if (Info->DataList)
{
ULONG i;
for (i = 0; i < Info->DataList->Count; i++)
{
EspDestroyTriggerData(Info->DataList->Items[i]);
}
PhDereferenceObject(Info->DataList);
}
PhFree(Info);
}
VOID EspClearTriggerInfoList(
_In_ PPH_LIST List
)
{
ULONG i;
for (i = 0; i < List->Count; i++)
{
EspDestroyTriggerInfo(List->Items[i]);
}
PhClearList(List);
}
struct _ES_TRIGGER_CONTEXT *EsCreateServiceTriggerContext(
_In_ PPH_SERVICE_ITEM ServiceItem,
_In_ HWND WindowHandle,
_In_ HWND TriggersLv
)
{
PES_TRIGGER_CONTEXT context;
context = PhAllocate(sizeof(ES_TRIGGER_CONTEXT));
memset(context, 0, sizeof(ES_TRIGGER_CONTEXT));
context->ServiceItem = ServiceItem;
context->WindowHandle = WindowHandle;
context->TriggersLv = TriggersLv;
context->InfoList = PhCreateList(4);
PhSetListViewStyle(TriggersLv, FALSE, TRUE);
PhSetControlTheme(TriggersLv, L"explorer");
PhAddListViewColumn(TriggersLv, 0, 0, 0, LVCFMT_LEFT, 300, L"Trigger");
PhAddListViewColumn(TriggersLv, 1, 1, 1, LVCFMT_LEFT, 60, L"Action");
PhSetExtendedListView(TriggersLv);
EnableWindow(GetDlgItem(WindowHandle, IDC_EDIT), FALSE);
EnableWindow(GetDlgItem(WindowHandle, IDC_DELETE), FALSE);
return context;
}
VOID EsDestroyServiceTriggerContext(
_In_ struct _ES_TRIGGER_CONTEXT *Context
)
{
ULONG i;
for (i = 0; i < Context->InfoList->Count; i++)
{
EspDestroyTriggerInfo(Context->InfoList->Items[i]);
}
PhDereferenceObject(Context->InfoList);
PhFree(Context);
}
PPH_STRING EspLookupEtwPublisherName(
_In_ PGUID Guid
)
{
PPH_STRING guidString;
PPH_STRING keyName;
HANDLE keyHandle;
PPH_STRING publisherName = NULL;
// Copied from ProcessHacker\hndlinfo.c.
guidString = PhFormatGuid(Guid);
keyName = PhConcatStringRef2(&PublishersKeyName, &guidString->sr);
if (NT_SUCCESS(PhOpenKey(
&keyHandle,
KEY_READ,
PH_KEY_LOCAL_MACHINE,
&keyName->sr,
0
)))
{
publisherName = PhQueryRegistryString(keyHandle, NULL);
if (publisherName && publisherName->Length == 0)
{
PhDereferenceObject(publisherName);
publisherName = NULL;
}
NtClose(keyHandle);
}
PhDereferenceObject(keyName);
if (publisherName)
{
PhDereferenceObject(guidString);
return publisherName;
}
else
{
return guidString;
}
}
BOOLEAN EspEnumerateEtwPublishers(
_Out_ PETW_PUBLISHER_ENTRY *Entries,
_Out_ PULONG NumberOfEntries
)
{
NTSTATUS status;
HANDLE publishersKeyHandle;
ULONG index;
PKEY_BASIC_INFORMATION buffer;
ULONG bufferSize;
PETW_PUBLISHER_ENTRY entries;
ULONG numberOfEntries;
ULONG allocatedEntries;
if (!NT_SUCCESS(PhOpenKey(
&publishersKeyHandle,
KEY_READ,
PH_KEY_LOCAL_MACHINE,
&PublishersKeyName,
0
)))
{
return FALSE;
}
numberOfEntries = 0;
allocatedEntries = 256;
entries = PhAllocate(allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY));
index = 0;
bufferSize = 0x100;
buffer = PhAllocate(0x100);
while (TRUE)
{
status = NtEnumerateKey(
publishersKeyHandle,
index,
KeyBasicInformation,
buffer,
bufferSize,
&bufferSize
);
if (NT_SUCCESS(status))
{
UNICODE_STRING nameUs;
PH_STRINGREF name;
HANDLE keyHandle;
GUID guid;
PPH_STRING publisherName;
nameUs.Buffer = buffer->Name;
nameUs.Length = (USHORT)buffer->NameLength;
name.Buffer = buffer->Name;
name.Length = buffer->NameLength;
// Make sure this is a valid publisher key.
if (NT_SUCCESS(RtlGUIDFromString(&nameUs, &guid)))
{
if (NT_SUCCESS(PhOpenKey(
&keyHandle,
KEY_READ,
publishersKeyHandle,
&name,
0
)))
{
publisherName = PhQueryRegistryString(keyHandle, NULL);
if (publisherName)
{
if (publisherName->Length != 0)
{
PETW_PUBLISHER_ENTRY entry;
if (numberOfEntries == allocatedEntries)
{
allocatedEntries *= 2;
entries = PhReAllocate(entries, allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY));
}
entry = &entries[numberOfEntries++];
entry->PublisherName = publisherName;
entry->Guid = guid;
}
else
{
PhDereferenceObject(publisherName);
}
}
NtClose(keyHandle);
}
}
index++;
}
else if (status == STATUS_NO_MORE_ENTRIES)
{
break;
}
else if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL)
{
PhFree(buffer);
buffer = PhAllocate(bufferSize);
}
else
{
break;
}
}
PhFree(buffer);
NtClose(publishersKeyHandle);
*Entries = entries;
*NumberOfEntries = numberOfEntries;
return TRUE;
}
BOOLEAN EspLookupEtwPublisherGuid(
_In_ PPH_STRINGREF PublisherName,
_Out_ PGUID Guid
)
{
BOOLEAN result;
PETW_PUBLISHER_ENTRY entries;
ULONG numberOfEntries;
ULONG i;
if (!EspEnumerateEtwPublishers(&entries, &numberOfEntries))
return FALSE;
result = FALSE;
for (i = 0; i < numberOfEntries; i++)
{
if (!result && PhEqualStringRef(&entries[i].PublisherName->sr, PublisherName, TRUE))
{
*Guid = entries[i].Guid;
result = TRUE;
}
PhDereferenceObject(entries[i].PublisherName);
}
PhFree(entries);
return result;
}
VOID EspFormatTriggerInfo(
_In_ PES_TRIGGER_INFO Info,
_Out_ PWSTR *TriggerString,
_Out_ PWSTR *ActionString,
_Out_ PPH_STRING *StringUsed
)
{
PPH_STRING stringUsed = NULL;
PWSTR triggerString = NULL;
PWSTR actionString;
ULONG i;
BOOLEAN typeFound;
BOOLEAN subTypeFound;
switch (Info->Type)
{
case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL:
{
PPH_STRING guidString;
if (!Info->Subtype)
{
triggerString = L"Device interface arrival";
}
else
{
guidString = PhFormatGuid(Info->Subtype);
stringUsed = PhConcatStrings2(L"Device interface arrival: ", guidString->Buffer);
triggerString = stringUsed->Buffer;
}
}
break;
case SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE:
{
PPH_STRING guidString;
if (!Info->Subtype)
{
triggerString = L"Custom system state change";
}
else
{
guidString = PhFormatGuid(Info->Subtype);
stringUsed = PhConcatStrings2(L"Custom system state change: ", guidString->Buffer);
triggerString = stringUsed->Buffer;
}
}
break;
case SERVICE_TRIGGER_TYPE_CUSTOM:
{
if (Info->Subtype)
{
PPH_STRING publisherName;
// Try to lookup the publisher name from the GUID.
publisherName = EspLookupEtwPublisherName(Info->Subtype);
stringUsed = PhConcatStrings2(L"Custom: ", publisherName->Buffer);
PhDereferenceObject(publisherName);
triggerString = stringUsed->Buffer;
}
else
{
triggerString = L"Custom";
}
}
break;
default:
{
typeFound = FALSE;
subTypeFound = FALSE;
for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
{
if (SubTypeEntries[i].Type == Info->Type)
{
typeFound = TRUE;
if (!Info->Subtype && !SubTypeEntries[i].Guid)
{
subTypeFound = TRUE;
triggerString = SubTypeEntries[i].Name;
break;
}
else if (Info->Subtype && SubTypeEntries[i].Guid && memcmp(Info->Subtype, SubTypeEntries[i].Guid, sizeof(GUID)) == 0)
{
subTypeFound = TRUE;
triggerString = SubTypeEntries[i].Name;
break;
}
else if (!subTypeFound && SubTypeEntries[i].Guid == &SubTypeUnknownGuid)
{
triggerString = SubTypeEntries[i].Name;
break;
}
}
}
if (!typeFound)
{
triggerString = L"Unknown";
}
}
break;
}
switch (Info->Action)
{
case SERVICE_TRIGGER_ACTION_SERVICE_START:
actionString = L"Start";
break;
case SERVICE_TRIGGER_ACTION_SERVICE_STOP:
actionString = L"Stop";
break;
default:
actionString = L"Unknown";
break;
}
*TriggerString = triggerString;
*ActionString = actionString;
*StringUsed = stringUsed;
}
VOID EsLoadServiceTriggerInfo(
_In_ struct _ES_TRIGGER_CONTEXT *Context,
_In_ SC_HANDLE ServiceHandle
)
{
PSERVICE_TRIGGER_INFO triggerInfo;
ULONG i;
EspClearTriggerInfoList(Context->InfoList);
if (triggerInfo = PhQueryServiceVariableSize(ServiceHandle, SERVICE_CONFIG_TRIGGER_INFO))
{
for (i = 0; i < triggerInfo->cTriggers; i++)
{
PSERVICE_TRIGGER trigger = &triggerInfo->pTriggers[i];
PES_TRIGGER_INFO info;
PWSTR triggerString;
PWSTR actionString;
PPH_STRING stringUsed;
INT lvItemIndex;
info = EspCreateTriggerInfo(trigger);
PhAddItemList(Context->InfoList, info);
EspFormatTriggerInfo(info, &triggerString, &actionString, &stringUsed);
lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, info);
PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
if (stringUsed)
PhDereferenceObject(stringUsed);
}
Context->InitialNumberOfTriggers = triggerInfo->cTriggers;
ExtendedListView_SortItems(Context->TriggersLv);
PhFree(triggerInfo);
}
}
BOOLEAN EsSaveServiceTriggerInfo(
_In_ struct _ES_TRIGGER_CONTEXT *Context,
_Out_ PULONG Win32Result
)
{
BOOLEAN result = TRUE;
PH_AUTO_POOL autoPool;
SC_HANDLE serviceHandle;
SERVICE_TRIGGER_INFO triggerInfo;
ULONG i;
ULONG j;
if (!Context->Dirty)
return TRUE;
// Do not try to change trigger information if we didn't have any triggers before and we don't
// have any now. ChangeServiceConfig2 returns an error in this situation.
if (Context->InitialNumberOfTriggers == 0 && Context->InfoList->Count == 0)
return TRUE;
PhInitializeAutoPool(&autoPool);
memset(&triggerInfo, 0, sizeof(SERVICE_TRIGGER_INFO));
triggerInfo.cTriggers = Context->InfoList->Count;
// pTriggers needs to be NULL when there are no triggers.
if (Context->InfoList->Count != 0)
{
triggerInfo.pTriggers = PH_AUTO(PhCreateAlloc(Context->InfoList->Count * sizeof(SERVICE_TRIGGER)));
memset(triggerInfo.pTriggers, 0, Context->InfoList->Count * sizeof(SERVICE_TRIGGER));
for (i = 0; i < Context->InfoList->Count; i++)
{
PSERVICE_TRIGGER trigger = &triggerInfo.pTriggers[i];
PES_TRIGGER_INFO info = Context->InfoList->Items[i];
trigger->dwTriggerType = info->Type;
trigger->dwAction = info->Action;
trigger->pTriggerSubtype = info->Subtype;
if (info->DataList && info->DataList->Count != 0)
{
trigger->cDataItems = info->DataList->Count;
trigger->pDataItems = PH_AUTO(PhCreateAlloc(info->DataList->Count * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM)));
for (j = 0; j < info->DataList->Count; j++)
{
PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM dataItem = &trigger->pDataItems[j];
PES_TRIGGER_DATA data = info->DataList->Items[j];
dataItem->dwDataType = data->Type;
if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
{
dataItem->cbData = (ULONG)data->String->Length + 2; // include null terminator
dataItem->pData = (PBYTE)data->String->Buffer;
}
else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
{
dataItem->cbData = data->BinaryLength;
dataItem->pData = data->Binary;
}
else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL)
{
dataItem->cbData = sizeof(UCHAR);
dataItem->pData = (PBYTE)&data->Byte;
}
else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY || data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL)
{
dataItem->cbData = sizeof(ULONG64);
dataItem->pData = (PBYTE)&data->UInt64;
}
}
}
}
}
if (serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_CHANGE_CONFIG))
{
if (!ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_TRIGGER_INFO, &triggerInfo))
{
result = FALSE;
*Win32Result = GetLastError();
}
CloseServiceHandle(serviceHandle);
}
else
{
result = FALSE;
*Win32Result = GetLastError();
if (*Win32Result == ERROR_ACCESS_DENIED && !PhGetOwnTokenAttributes().Elevated)
{
// Elevate using phsvc.
if (PhUiConnectToPhSvc(Context->WindowHandle, FALSE))
{
NTSTATUS status;
result = TRUE;
if (!NT_SUCCESS(status = PhSvcCallChangeServiceConfig2(Context->ServiceItem->Name->Buffer,
SERVICE_CONFIG_TRIGGER_INFO, &triggerInfo)))
{
result = FALSE;
*Win32Result = PhNtStatusToDosError(status);
}
PhUiDisconnectFromPhSvc();
}
else
{
// User cancelled elevation.
*Win32Result = ERROR_CANCELLED;
}
}
}
PhDeleteAutoPool(&autoPool);
return result;
}
LOGICAL EspSetListViewItemParam(
_In_ HWND ListViewHandle,
_In_ INT Index,
_In_ PVOID Param
)
{
LVITEM item;
item.mask = LVIF_PARAM;
item.iItem = Index;
item.iSubItem = 0;
item.lParam = (LPARAM)Param;
return ListView_SetItem(ListViewHandle, &item);
}
VOID EsHandleEventServiceTrigger(
_In_ struct _ES_TRIGGER_CONTEXT *Context,
_In_ ULONG Event
)
{
switch (Event)
{
case ES_TRIGGER_EVENT_NEW:
{
Context->EditingInfo = EspCreateTriggerInfo(NULL);
Context->EditingInfo->Type = SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY;
Context->EditingInfo->SubtypeBuffer = NetworkManagerFirstIpAddressArrivalGuid;
Context->EditingInfo->Subtype = &Context->EditingInfo->SubtypeBuffer;
Context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START;
if (DialogBoxParam(
PluginInstance->DllBase,
MAKEINTRESOURCE(IDD_SRVTRIGGER),
Context->WindowHandle,
EspServiceTriggerDlgProc,
(LPARAM)Context
) == IDOK)
{
PWSTR triggerString;
PWSTR actionString;
PPH_STRING stringUsed;
INT lvItemIndex;
Context->Dirty = TRUE;
PhAddItemList(Context->InfoList, Context->EditingInfo);
EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed);
lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, Context->EditingInfo);
PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
if (stringUsed)
PhDereferenceObject(stringUsed);
}
else
{
EspDestroyTriggerInfo(Context->EditingInfo);
}
Context->EditingInfo = NULL;
}
break;
case ES_TRIGGER_EVENT_EDIT:
{
INT lvItemIndex;
PES_TRIGGER_INFO info;
ULONG index;
lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED);
if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PVOID *)&info))
{
index = PhFindItemList(Context->InfoList, info);
if (index != -1)
{
Context->EditingInfo = EspCloneTriggerInfo(info);
if (DialogBoxParam(
PluginInstance->DllBase,
MAKEINTRESOURCE(IDD_SRVTRIGGER),
Context->WindowHandle,
EspServiceTriggerDlgProc,
(LPARAM)Context
) == IDOK)
{
PWSTR triggerString;
PWSTR actionString;
PPH_STRING stringUsed;
Context->Dirty = TRUE;
EspDestroyTriggerInfo(Context->InfoList->Items[index]);
Context->InfoList->Items[index] = Context->EditingInfo;
EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed);
PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 0, triggerString);
PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
if (stringUsed)
PhDereferenceObject(stringUsed);
EspSetListViewItemParam(Context->TriggersLv, lvItemIndex, Context->EditingInfo);
}
else
{
EspDestroyTriggerInfo(Context->EditingInfo);
}
Context->EditingInfo = NULL;
}
}
}
break;
case ES_TRIGGER_EVENT_DELETE:
{
INT lvItemIndex;
PES_TRIGGER_INFO info;
ULONG index;
lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED);
if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PVOID *)&info))
{
index = PhFindItemList(Context->InfoList, info);
if (index != -1)
{
EspDestroyTriggerInfo(info);
PhRemoveItemList(Context->InfoList, index);
PhRemoveListViewItem(Context->TriggersLv, lvItemIndex);
}
}
Context->Dirty = TRUE;
}
break;
case ES_TRIGGER_EVENT_SELECTIONCHANGED:
{
ULONG selectedCount;
selectedCount = ListView_GetSelectedCount(Context->TriggersLv);
EnableWindow(GetDlgItem(Context->WindowHandle, IDC_EDIT), selectedCount == 1);
EnableWindow(GetDlgItem(Context->WindowHandle, IDC_DELETE), selectedCount == 1);
}
break;
}
}
ULONG EspTriggerTypeStringToInteger(
_In_ PWSTR String
)
{
ULONG i;
for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++)
{
if (PhEqualStringZ(TypeEntries[i].Name, String, FALSE))
return TypeEntries[i].Type;
}
return 0;
}
static int __cdecl EtwPublisherByNameCompareFunction(
_In_ const void *elem1,
_In_ const void *elem2
)
{
PETW_PUBLISHER_ENTRY entry1 = (PETW_PUBLISHER_ENTRY)elem1;
PETW_PUBLISHER_ENTRY entry2 = (PETW_PUBLISHER_ENTRY)elem2;
return PhCompareString(entry1->PublisherName, entry2->PublisherName, TRUE);
}
VOID EspFixServiceTriggerControls(
_In_ HWND hwndDlg,
_In_ PES_TRIGGER_CONTEXT Context
)
{
HWND typeComboBox;
HWND subTypeComboBox;
ULONG i;
PPH_STRING selectedTypeString;
ULONG type;
PPH_STRING selectedSubTypeString;
typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE);
subTypeComboBox = GetDlgItem(hwndDlg, IDC_SUBTYPE);
selectedTypeString = PhGetWindowText(typeComboBox);
type = EspTriggerTypeStringToInteger(selectedTypeString->Buffer);
PhDereferenceObject(selectedTypeString);
if (Context->LastSelectedType != type)
{
// Change the contents of the subtype combo box based on the type.
ComboBox_ResetContent(subTypeComboBox);
switch (type)
{
case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL:
{
ComboBox_AddString(subTypeComboBox, L"Custom");
}
break;
case SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE:
{
ComboBox_AddString(subTypeComboBox, L"Custom");
}
break;
case SERVICE_TRIGGER_TYPE_CUSTOM:
{
PETW_PUBLISHER_ENTRY entries;
ULONG numberOfEntries;
ULONG i;
ComboBox_AddString(subTypeComboBox, L"Custom");
// Display a list of publishers.
if (EspEnumerateEtwPublishers(&entries, &numberOfEntries))
{
// Sort the list by name.
qsort(entries, numberOfEntries, sizeof(ETW_PUBLISHER_ENTRY), EtwPublisherByNameCompareFunction);
for (i = 0; i < numberOfEntries; i++)
{
ComboBox_AddString(subTypeComboBox, entries[i].PublisherName->Buffer);
PhDereferenceObject(entries[i].PublisherName);
}
PhFree(entries);
}
}
break;
default:
for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
{
if (SubTypeEntries[i].Type == type && SubTypeEntries[i].Guid && SubTypeEntries[i].Guid != &SubTypeUnknownGuid)
{
ComboBox_AddString(subTypeComboBox, SubTypeEntries[i].Name);
}
}
break;
}
ComboBox_SetCurSel(subTypeComboBox, 0);
Context->LastSelectedType = type;
}
selectedSubTypeString = PhGetWindowText(subTypeComboBox);
if (PhEqualString2(selectedSubTypeString, L"Custom", FALSE))
{
EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), TRUE);
SetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM, Context->LastCustomSubType->Buffer);
}
else
{
if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM)))
{
EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), FALSE);
PhMoveReference(&Context->LastCustomSubType, PhGetWindowText(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM)));
SetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM, L"");
}
}
PhDereferenceObject(selectedSubTypeString);
}
PPH_STRING EspConvertNullsToNewLines(
_In_ PPH_STRING String
)
{
PH_STRING_BUILDER sb;
ULONG i;
PhInitializeStringBuilder(&sb, String->Length);
for (i = 0; i < (ULONG)String->Length / 2; i++)
{
if (String->Buffer[i] == 0)
{
PhAppendStringBuilderEx(&sb, L"\r\n", 4);
continue;
}
PhAppendCharStringBuilder(&sb, String->Buffer[i]);
}
return PhFinalStringBuilderString(&sb);
}
PPH_STRING EspConvertNewLinesToNulls(
_In_ PPH_STRING String
)
{
PPH_STRING text;
SIZE_T count;
SIZE_T i;
text = PhCreateStringEx(NULL, String->Length + 2); // plus one character for an extra null terminator (see below)
text->Length = 0;
count = 0;
for (i = 0; i < String->Length / 2; i++)
{
// Lines are terminated by "\r\n".
if (String->Buffer[i] == '\r')
{
continue;
}
if (String->Buffer[i] == '\n')
{
text->Buffer[count++] = 0;
continue;
}
text->Buffer[count++] = String->Buffer[i];
}
if (count != 0)
{
// Make sure we have an extra null terminator at the end, as required of multistrings.
if (text->Buffer[count - 1] != 0)
text->Buffer[count++] = 0;
}
text->Length = count * 2;
return text;
}
PPH_STRING EspConvertNullsToSpaces(
_In_ PPH_STRING String
)
{
PPH_STRING text;
SIZE_T j;
text = PhDuplicateString(String);
for (j = 0; j < text->Length / 2; j++)
{
if (text->Buffer[j] == 0)
text->Buffer[j] = ' ';
}
return text;
}
VOID EspFormatTriggerData(
_In_ PES_TRIGGER_DATA Data,
_Out_ PPH_STRING *Text
)
{
if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
{
// This check works for both normal strings and multistrings.
if (Data->String->Length != 0)
{
// Prepare the text for display by replacing null characters with spaces.
*Text = EspConvertNullsToSpaces(Data->String);
}
else
{
*Text = PhCreateString(L"(empty string)");
}
}
else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
{
*Text = PhCreateString(L"(binary data)");
}
else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL)
{
*Text = PhFormatString(L"(level) %u", Data->Byte);
}
else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY)
{
*Text = PhFormatString(L"(keyword any) 0x%I64x", Data->UInt64);
}
else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL)
{
*Text = PhFormatString(L"(keyword all) 0x%I64x", Data->UInt64);
}
else
{
*Text = PhCreateString(L"(unknown type)");
}
}
INT_PTR CALLBACK EspServiceTriggerDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PES_TRIGGER_CONTEXT context;
if (uMsg == WM_INITDIALOG)
{
context = (PES_TRIGGER_CONTEXT)lParam;
SetProp(hwndDlg, L"Context", (HANDLE)context);
}
else
{
context = (PES_TRIGGER_CONTEXT)GetProp(hwndDlg, L"Context");
if (uMsg == WM_DESTROY)
RemoveProp(hwndDlg, L"Context");
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
HWND typeComboBox;
HWND actionComboBox;
HWND lvHandle;
ULONG i;
context->LastSelectedType = 0;
if (context->EditingInfo->Subtype)
context->LastCustomSubType = PhFormatGuid(context->EditingInfo->Subtype);
else
context->LastCustomSubType = PhReferenceEmptyString();
typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE);
actionComboBox = GetDlgItem(hwndDlg, IDC_ACTION);
for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++)
{
ComboBox_AddString(typeComboBox, TypeEntries[i].Name);
if (TypeEntries[i].Type == context->EditingInfo->Type)
{
PhSelectComboBoxString(typeComboBox, TypeEntries[i].Name, FALSE);
}
}
ComboBox_AddString(actionComboBox, L"Start");
ComboBox_AddString(actionComboBox, L"Stop");
ComboBox_SetCurSel(actionComboBox, context->EditingInfo->Action == SERVICE_TRIGGER_ACTION_SERVICE_START ? 0 : 1);
EspFixServiceTriggerControls(hwndDlg, context);
if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM)
{
for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
{
if (
SubTypeEntries[i].Type == context->EditingInfo->Type &&
SubTypeEntries[i].Guid &&
context->EditingInfo->Subtype &&
memcmp(SubTypeEntries[i].Guid, context->EditingInfo->Subtype, sizeof(GUID)) == 0
)
{
PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SUBTYPE), SubTypeEntries[i].Name, FALSE);
break;
}
}
}
else
{
if (context->EditingInfo->Subtype)
{
PPH_STRING publisherName;
// Try to select the publisher name in the subtype list.
publisherName = EspLookupEtwPublisherName(context->EditingInfo->Subtype);
PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SUBTYPE), publisherName->Buffer, FALSE);
PhDereferenceObject(publisherName);
}
}
// Call a second time since the state of the custom subtype text box may have changed.
EspFixServiceTriggerControls(hwndDlg, context);
lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
PhSetListViewStyle(lvHandle, FALSE, TRUE);
PhSetControlTheme(lvHandle, L"explorer");
PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 280, L"Data");
if (context->EditingInfo->DataList)
{
for (i = 0; i < context->EditingInfo->DataList->Count; i++)
{
PES_TRIGGER_DATA data;
PPH_STRING text;
INT lvItemIndex;
data = context->EditingInfo->DataList->Items[i];
EspFormatTriggerData(data, &text);
lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data);
PhDereferenceObject(text);
}
}
EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE);
EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
}
break;
case WM_DESTROY:
{
PhClearReference(&context->LastCustomSubType);
}
break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_TYPE:
if (HIWORD(wParam) == CBN_SELCHANGE)
{
EspFixServiceTriggerControls(hwndDlg, context);
}
break;
case IDC_SUBTYPE:
if (HIWORD(wParam) == CBN_SELCHANGE)
{
EspFixServiceTriggerControls(hwndDlg, context);
}
break;
case IDC_NEW:
{
HWND lvHandle;
lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
context->EditingValue = PhReferenceEmptyString();
if (DialogBoxParam(
PluginInstance->DllBase,
MAKEINTRESOURCE(IDD_VALUE),
hwndDlg,
ValueDlgProc,
(LPARAM)context
) == IDOK)
{
PES_TRIGGER_DATA data;
PPH_STRING text;
INT lvItemIndex;
data = EspCreateTriggerData(NULL);
data->Type = SERVICE_TRIGGER_DATA_TYPE_STRING;
data->String = EspConvertNewLinesToNulls(context->EditingValue);
if (!context->EditingInfo->DataList)
context->EditingInfo->DataList = PhCreateList(4);
PhAddItemList(context->EditingInfo->DataList, data);
EspFormatTriggerData(data, &text);
lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data);
PhDereferenceObject(text);
}
PhClearReference(&context->EditingValue);
}
break;
case IDC_EDIT:
{
HWND lvHandle;
INT lvItemIndex;
PES_TRIGGER_DATA data;
ULONG index;
lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED);
if (
lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PVOID *)&data) &&
data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING // editing binary values is not supported
)
{
index = PhFindItemList(context->EditingInfo->DataList, data);
if (index != -1)
{
context->EditingValue = EspConvertNullsToNewLines(data->String);
if (DialogBoxParam(
PluginInstance->DllBase,
MAKEINTRESOURCE(IDD_VALUE),
hwndDlg,
ValueDlgProc,
(LPARAM)context
) == IDOK)
{
PPH_STRING text;
PhMoveReference(&data->String, EspConvertNewLinesToNulls(context->EditingValue));
EspFormatTriggerData(data, &text);
PhSetListViewSubItem(lvHandle, lvItemIndex, 0, text->Buffer);
PhDereferenceObject(text);
}
PhClearReference(&context->EditingValue);
}
}
}
break;
case IDC_DELETE:
{
HWND lvHandle;
INT lvItemIndex;
PES_TRIGGER_DATA data;
ULONG index;
lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED);
if (lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PVOID *)&data))
{
index = PhFindItemList(context->EditingInfo->DataList, data);
if (index != -1)
{
EspDestroyTriggerData(data);
PhRemoveItemList(context->EditingInfo->DataList, index);
PhRemoveListViewItem(lvHandle, lvItemIndex);
}
}
}
break;
case IDCANCEL:
EndDialog(hwndDlg, IDCANCEL);
break;
case IDOK:
{
PH_AUTO_POOL autoPool;
PPH_STRING typeString;
PPH_STRING subTypeString;
PPH_STRING customSubTypeString;
PPH_STRING actionString;
ULONG i;
PhInitializeAutoPool(&autoPool);
typeString = PhaGetDlgItemText(hwndDlg, IDC_TYPE);
subTypeString = PhaGetDlgItemText(hwndDlg, IDC_SUBTYPE);
customSubTypeString = PhaGetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM);
actionString = PhaGetDlgItemText(hwndDlg, IDC_ACTION);
for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++)
{
if (PhEqualStringZ(TypeEntries[i].Name, typeString->Buffer, FALSE))
{
context->EditingInfo->Type = TypeEntries[i].Type;
break;
}
}
if (!PhEqualString2(subTypeString, L"Custom", FALSE))
{
if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM)
{
for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
{
if (
SubTypeEntries[i].Type == context->EditingInfo->Type &&
PhEqualString2(subTypeString, SubTypeEntries[i].Name, FALSE)
)
{
context->EditingInfo->SubtypeBuffer = *SubTypeEntries[i].Guid;
context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer;
break;
}
}
}
else
{
if (!EspLookupEtwPublisherGuid(&subTypeString->sr, &context->EditingInfo->SubtypeBuffer))
{
PhShowError(hwndDlg, L"Unable to find the ETW publisher GUID.");
goto DoNotClose;
}
context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer;
}
}
else
{
UNICODE_STRING guidString;
PhStringRefToUnicodeString(&customSubTypeString->sr, &guidString);
// Trim whitespace.
while (guidString.Length != 0 && *guidString.Buffer == ' ')
{
guidString.Buffer++;
guidString.Length -= 2;
}
while (guidString.Length != 0 && guidString.Buffer[guidString.Length / 2 - 1] == ' ')
{
guidString.Length -= 2;
}
if (NT_SUCCESS(RtlGUIDFromString(&guidString, &context->EditingInfo->SubtypeBuffer)))
{
context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer;
}
else
{
PhShowError(hwndDlg, L"The custom subtype is invalid. Please ensure that the string is a valid GUID: \"{x-x-x-x-x}\".");
goto DoNotClose;
}
}
if (PhEqualString2(actionString, L"Start", FALSE))
context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START;
else
context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_STOP;
if (
context->EditingInfo->DataList &&
context->EditingInfo->DataList->Count != 0 &&
context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL &&
context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT &&
context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT &&
context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM
)
{
// This trigger has data items, but the trigger type doesn't allow them.
if (PhShowMessage(
hwndDlg,
MB_OKCANCEL | MB_ICONWARNING,
L"The trigger type \"%s\" does not allow data items to be configured. "
L"If you continue, they will be removed.",
typeString->Buffer
) != IDOK)
{
goto DoNotClose;
}
for (i = 0; i < context->EditingInfo->DataList->Count; i++)
{
EspDestroyTriggerData(context->EditingInfo->DataList->Items[i]);
}
PhClearReference(&context->EditingInfo->DataList);
}
EndDialog(hwndDlg, IDOK);
DoNotClose:
PhDeleteAutoPool(&autoPool);
}
break;
}
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
HWND lvHandle;
lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
switch (header->code)
{
case LVN_ITEMCHANGED:
{
if (header->hwndFrom == lvHandle)
{
if (ListView_GetSelectedCount(lvHandle) == 1)
{
PES_TRIGGER_DATA data = PhGetSelectedListViewItemParam(GetDlgItem(hwndDlg, IDC_LIST));
// Editing binary data is not supported.
EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), data && data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING);
EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), TRUE);
}
else
{
EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE);
EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
}
}
}
break;
case NM_DBLCLK:
{
if (header->hwndFrom == lvHandle)
{
SendMessage(hwndDlg, WM_COMMAND, IDC_EDIT, 0);
}
}
break;
}
}
break;
}
return FALSE;
}
INT_PTR CALLBACK ValueDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PES_TRIGGER_CONTEXT context;
if (uMsg == WM_INITDIALOG)
{
context = (PES_TRIGGER_CONTEXT)lParam;
SetProp(hwndDlg, L"Context", (HANDLE)context);
}
else
{
context = (PES_TRIGGER_CONTEXT)GetProp(hwndDlg, L"Context");
if (uMsg == WM_DESTROY)
RemoveProp(hwndDlg, L"Context");
}
if (!context)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
SetDlgItemText(hwndDlg, IDC_VALUES, context->EditingValue->Buffer);
SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_VALUES), TRUE);
Edit_SetSel(GetDlgItem(hwndDlg, IDC_VALUES), 0, -1);
}
break;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hwndDlg, IDCANCEL);
break;
case IDOK:
PhMoveReference(&context->EditingValue, PhGetWindowText(GetDlgItem(hwndDlg, IDC_VALUES)));
EndDialog(hwndDlg, IDOK);
break;
}
}
break;
}
return FALSE;
}