Process Hacker
trigger.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Extended Services -
3  * trigger editor
4  *
5  * Copyright (C) 2011-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include <phdk.h>
24 #include <windowsx.h>
25 #include "extsrv.h"
26 #include "resource.h"
27 
28 typedef struct _ES_TRIGGER_DATA
29 {
30  ULONG Type;
31  union
32  {
33  PPH_STRING String;
34  struct
35  {
36  PVOID Binary;
37  ULONG BinaryLength;
38  };
39  UCHAR Byte;
40  ULONG64 UInt64;
41  };
43 
44 typedef struct _ES_TRIGGER_INFO
45 {
46  ULONG Type;
47  PGUID Subtype;
48  ULONG Action;
49  PPH_LIST DataList;
50  GUID SubtypeBuffer;
52 
53 typedef struct _ES_TRIGGER_CONTEXT
54 {
55  PPH_SERVICE_ITEM ServiceItem;
56  HWND WindowHandle;
57  HWND TriggersLv;
58  BOOLEAN Dirty;
59  ULONG InitialNumberOfTriggers;
60  PPH_LIST InfoList;
61 
62  // Trigger dialog box
63  PES_TRIGGER_INFO EditingInfo;
64  ULONG LastSelectedType;
65  PPH_STRING LastCustomSubType;
66 
67  // Value dialog box
68  PPH_STRING EditingValue;
70 
71 typedef struct _TYPE_ENTRY
72 {
73  ULONG Type;
74  PWSTR Name;
76 
77 typedef struct _SUBTYPE_ENTRY
78 {
79  ULONG Type;
80  PGUID Guid;
81  PWSTR Name;
83 
84 typedef struct _ETW_PUBLISHER_ENTRY
85 {
86  PPH_STRING PublisherName;
87  GUID Guid;
89 
90 INT_PTR CALLBACK EspServiceTriggerDlgProc(
91  _In_ HWND hwndDlg,
92  _In_ UINT uMsg,
93  _In_ WPARAM wParam,
94  _In_ LPARAM lParam
95  );
96 
97 INT_PTR CALLBACK ValueDlgProc(
98  _In_ HWND hwndDlg,
99  _In_ UINT uMsg,
100  _In_ WPARAM wParam,
101  _In_ LPARAM lParam
102  );
103 
104 static GUID NetworkManagerFirstIpAddressArrivalGuid = { 0x4f27f2de, 0x14e2, 0x430b, { 0xa5, 0x49, 0x7c, 0xd4, 0x8c, 0xbc, 0x82, 0x45 } };
105 static GUID NetworkManagerLastIpAddressRemovalGuid = { 0xcc4ba62a, 0x162e, 0x4648, { 0x84, 0x7a, 0xb6, 0xbd, 0xf9, 0x93, 0xe3, 0x35 } };
106 static GUID DomainJoinGuid = { 0x1ce20aba, 0x9851, 0x4421, { 0x94, 0x30, 0x1d, 0xde, 0xb7, 0x66, 0xe8, 0x09 } };
107 static GUID DomainLeaveGuid = { 0xddaf516e, 0x58c2, 0x4866, { 0x95, 0x74, 0xc3, 0xb6, 0x15, 0xd4, 0x2e, 0xa1 } };
108 static GUID FirewallPortOpenGuid = { 0xb7569e07, 0x8421, 0x4ee0, { 0xad, 0x10, 0x86, 0x91, 0x5a, 0xfd, 0xad, 0x09 } };
109 static GUID FirewallPortCloseGuid = { 0xa144ed38, 0x8e12, 0x4de4, { 0x9d, 0x96, 0xe6, 0x47, 0x40, 0xb1, 0xa5, 0x24 } };
110 static GUID MachinePolicyPresentGuid = { 0x659fcae6, 0x5bdb, 0x4da9, { 0xb1, 0xff, 0xca, 0x2a, 0x17, 0x8d, 0x46, 0xe0 } };
111 static GUID UserPolicyPresentGuid = { 0x54fb46c8, 0xf089, 0x464c, { 0xb1, 0xfd, 0x59, 0xd1, 0xb6, 0x2c, 0x3b, 0x50 } };
112 static GUID RpcInterfaceEventGuid = { 0xbc90d167, 0x9470, 0x4139, { 0xa9, 0xba, 0xbe, 0x0b, 0xbb, 0xf5, 0xb7, 0x4d } };
113 static GUID NamedPipeEventGuid = { 0x1f81d131, 0x3fac, 0x4537, { 0x9e, 0x0c, 0x7e, 0x7b, 0x0c, 0x2f, 0x4b, 0x55 } };
114 static GUID SubTypeUnknownGuid; // dummy
115 
116 static TYPE_ENTRY TypeEntries[] =
117 {
118  { SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL, L"Device interface arrival" },
119  { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, L"IP address availability" },
120  { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, L"Domain join" },
121  { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, L"Firewall port event" },
122  { SERVICE_TRIGGER_TYPE_GROUP_POLICY, L"Group policy" },
123  { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, L"Network endpoint" },
124  { SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE, L"Custom system state change" },
125  { SERVICE_TRIGGER_TYPE_CUSTOM, L"Custom" }
126 };
127 
128 static SUBTYPE_ENTRY SubTypeEntries[] =
129 {
130  { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, NULL, L"IP address" },
131  { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerFirstIpAddressArrivalGuid, L"IP address: First arrival" },
132  { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerLastIpAddressRemovalGuid, L"IP address: Last removal" },
133  { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &SubTypeUnknownGuid, L"IP address: Unknown" },
134  { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, NULL, L"Domain" },
135  { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainJoinGuid, L"Domain: Join" },
136  { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainLeaveGuid, L"Domain: Leave" },
137  { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &SubTypeUnknownGuid, L"Domain: Unknown" },
138  { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, NULL, L"Firewall port" },
139  { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortOpenGuid, L"Firewall port: Open" },
140  { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortCloseGuid, L"Firewall port: Close" },
141  { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &SubTypeUnknownGuid, L"Firewall port: Unknown" },
142  { SERVICE_TRIGGER_TYPE_GROUP_POLICY, NULL, L"Group policy change" },
143  { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &MachinePolicyPresentGuid, L"Group policy change: Machine" },
144  { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &UserPolicyPresentGuid, L"Group policy change: User" },
145  { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &SubTypeUnknownGuid, L"Group policy change: Unknown" },
146  { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, NULL, L"Network endpoint" },
147  { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &RpcInterfaceEventGuid, L"Network endpoint: RPC interface" },
148  { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &NamedPipeEventGuid, L"Network endpoint: Named pipe" },
149  { SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT, &SubTypeUnknownGuid, L"Network endpoint: Unknown" }
150 };
151 
152 static PH_STRINGREF PublishersKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\");
153 
155  _In_opt_ PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM DataItem
156  )
157 {
158  PES_TRIGGER_DATA data;
159 
160  data = PhAllocate(sizeof(ES_TRIGGER_DATA));
161  memset(data, 0, sizeof(ES_TRIGGER_DATA));
162 
163  if (DataItem)
164  {
165  data->Type = DataItem->dwDataType;
166 
167  if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
168  {
169  if (DataItem->pData && DataItem->cbData >= 2)
170  data->String = PhCreateStringEx((PWSTR)DataItem->pData, DataItem->cbData - 2); // exclude final null terminator
171  else
172  data->String = PhReferenceEmptyString();
173  }
174  else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
175  {
176  data->BinaryLength = DataItem->cbData;
177  data->Binary = PhAllocateCopy(DataItem->pData, DataItem->cbData);
178  }
179  else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL)
180  {
181  if (DataItem->cbData == sizeof(UCHAR))
182  data->Byte = *(PUCHAR)DataItem->pData;
183  }
184  else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY || data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL)
185  {
186  if (DataItem->cbData == sizeof(ULONG64))
187  data->UInt64 = *(PULONG64)DataItem->pData;
188  }
189  }
190 
191  return data;
192 }
193 
195  _In_ PES_TRIGGER_DATA Data
196  )
197 {
198  PES_TRIGGER_DATA newData;
199 
200  newData = PhAllocateCopy(Data, sizeof(ES_TRIGGER_DATA));
201 
202  if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
203  {
204  if (newData->String)
205  newData->String = PhDuplicateString(newData->String);
206  }
207  else if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
208  {
209  if (newData->Binary)
210  newData->Binary = PhAllocateCopy(newData->Binary, newData->BinaryLength);
211  }
212 
213  return newData;
214 }
215 
217  _In_ PES_TRIGGER_DATA Data
218  )
219 {
220  if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
221  {
222  if (Data->String)
223  PhDereferenceObject(Data->String);
224  }
225  else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
226  {
227  if (Data->Binary)
228  PhFree(Data->Binary);
229  }
230 
231  PhFree(Data);
232 }
233 
235  _In_opt_ PSERVICE_TRIGGER Trigger
236  )
237 {
238  PES_TRIGGER_INFO info;
239 
240  info = PhAllocate(sizeof(ES_TRIGGER_INFO));
241  memset(info, 0, sizeof(ES_TRIGGER_INFO));
242 
243  if (Trigger)
244  {
245  info->Type = Trigger->dwTriggerType;
246 
247  if (Trigger->pTriggerSubtype)
248  {
249  info->SubtypeBuffer = *Trigger->pTriggerSubtype;
250  info->Subtype = &info->SubtypeBuffer;
251  }
252 
253  info->Action = Trigger->dwAction;
254 
255  if (
256  info->Type == SERVICE_TRIGGER_TYPE_CUSTOM ||
257  info->Type == SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL ||
258  info->Type == SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT ||
259  info->Type == SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT
260  )
261  {
262  ULONG i;
263 
264  info->DataList = PhCreateList(Trigger->cDataItems);
265 
266  for (i = 0; i < Trigger->cDataItems; i++)
267  {
268  PhAddItemList(info->DataList, EspCreateTriggerData(&Trigger->pDataItems[i]));
269  }
270  }
271  }
272 
273  return info;
274 }
275 
277  _In_ PES_TRIGGER_INFO Info
278  )
279 {
280  PES_TRIGGER_INFO newInfo;
281 
282  newInfo = PhAllocateCopy(Info, sizeof(ES_TRIGGER_INFO));
283 
284  if (newInfo->Subtype == &Info->SubtypeBuffer)
285  newInfo->Subtype = &newInfo->SubtypeBuffer;
286 
287  if (newInfo->DataList)
288  {
289  ULONG i;
290 
291  newInfo->DataList = PhCreateList(Info->DataList->AllocatedCount);
292  newInfo->DataList->Count = Info->DataList->Count;
293 
294  for (i = 0; i < Info->DataList->Count; i++)
295  newInfo->DataList->Items[i] = EspCloneTriggerData(Info->DataList->Items[i]);
296  }
297 
298  return newInfo;
299 }
300 
302  _In_ PES_TRIGGER_INFO Info
303  )
304 {
305  if (Info->DataList)
306  {
307  ULONG i;
308 
309  for (i = 0; i < Info->DataList->Count; i++)
310  {
311  EspDestroyTriggerData(Info->DataList->Items[i]);
312  }
313 
314  PhDereferenceObject(Info->DataList);
315  }
316 
317  PhFree(Info);
318 }
319 
321  _In_ PPH_LIST List
322  )
323 {
324  ULONG i;
325 
326  for (i = 0; i < List->Count; i++)
327  {
328  EspDestroyTriggerInfo(List->Items[i]);
329  }
330 
331  PhClearList(List);
332 }
333 
334 struct _ES_TRIGGER_CONTEXT *EsCreateServiceTriggerContext(
335  _In_ PPH_SERVICE_ITEM ServiceItem,
336  _In_ HWND WindowHandle,
337  _In_ HWND TriggersLv
338  )
339 {
340  PES_TRIGGER_CONTEXT context;
341 
342  context = PhAllocate(sizeof(ES_TRIGGER_CONTEXT));
343  memset(context, 0, sizeof(ES_TRIGGER_CONTEXT));
344  context->ServiceItem = ServiceItem;
345  context->WindowHandle = WindowHandle;
346  context->TriggersLv = TriggersLv;
347  context->InfoList = PhCreateList(4);
348 
349  PhSetListViewStyle(TriggersLv, FALSE, TRUE);
350  PhSetControlTheme(TriggersLv, L"explorer");
351  PhAddListViewColumn(TriggersLv, 0, 0, 0, LVCFMT_LEFT, 300, L"Trigger");
352  PhAddListViewColumn(TriggersLv, 1, 1, 1, LVCFMT_LEFT, 60, L"Action");
353  PhSetExtendedListView(TriggersLv);
354 
355  EnableWindow(GetDlgItem(WindowHandle, IDC_EDIT), FALSE);
356  EnableWindow(GetDlgItem(WindowHandle, IDC_DELETE), FALSE);
357 
358  return context;
359 }
360 
362  _In_ struct _ES_TRIGGER_CONTEXT *Context
363  )
364 {
365  ULONG i;
366 
367  for (i = 0; i < Context->InfoList->Count; i++)
368  {
369  EspDestroyTriggerInfo(Context->InfoList->Items[i]);
370  }
371 
372  PhDereferenceObject(Context->InfoList);
373  PhFree(Context);
374 }
375 
377  _In_ PGUID Guid
378  )
379 {
380  PPH_STRING guidString;
381  PPH_STRING keyName;
382  HANDLE keyHandle;
383  PPH_STRING publisherName = NULL;
384 
385  // Copied from ProcessHacker\hndlinfo.c.
386 
387  guidString = PhFormatGuid(Guid);
388 
389  keyName = PhConcatStringRef2(&PublishersKeyName, &guidString->sr);
390 
391  if (NT_SUCCESS(PhOpenKey(
392  &keyHandle,
393  KEY_READ,
395  &keyName->sr,
396  0
397  )))
398  {
399  publisherName = PhQueryRegistryString(keyHandle, NULL);
400 
401  if (publisherName && publisherName->Length == 0)
402  {
403  PhDereferenceObject(publisherName);
404  publisherName = NULL;
405  }
406 
407  NtClose(keyHandle);
408  }
409 
410  PhDereferenceObject(keyName);
411 
412  if (publisherName)
413  {
414  PhDereferenceObject(guidString);
415  return publisherName;
416  }
417  else
418  {
419  return guidString;
420  }
421 }
422 
424  _Out_ PETW_PUBLISHER_ENTRY *Entries,
425  _Out_ PULONG NumberOfEntries
426  )
427 {
428  NTSTATUS status;
429  HANDLE publishersKeyHandle;
430  ULONG index;
431  PKEY_BASIC_INFORMATION buffer;
432  ULONG bufferSize;
433  PETW_PUBLISHER_ENTRY entries;
434  ULONG numberOfEntries;
435  ULONG allocatedEntries;
436 
437  if (!NT_SUCCESS(PhOpenKey(
438  &publishersKeyHandle,
439  KEY_READ,
441  &PublishersKeyName,
442  0
443  )))
444  {
445  return FALSE;
446  }
447 
448  numberOfEntries = 0;
449  allocatedEntries = 256;
450  entries = PhAllocate(allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY));
451 
452  index = 0;
453  bufferSize = 0x100;
454  buffer = PhAllocate(0x100);
455 
456  while (TRUE)
457  {
458  status = NtEnumerateKey(
459  publishersKeyHandle,
460  index,
462  buffer,
463  bufferSize,
464  &bufferSize
465  );
466 
467  if (NT_SUCCESS(status))
468  {
469  UNICODE_STRING nameUs;
470  PH_STRINGREF name;
471  HANDLE keyHandle;
472  GUID guid;
473  PPH_STRING publisherName;
474 
475  nameUs.Buffer = buffer->Name;
476  nameUs.Length = (USHORT)buffer->NameLength;
477  name.Buffer = buffer->Name;
478  name.Length = buffer->NameLength;
479 
480  // Make sure this is a valid publisher key.
481  if (NT_SUCCESS(RtlGUIDFromString(&nameUs, &guid)))
482  {
483  if (NT_SUCCESS(PhOpenKey(
484  &keyHandle,
485  KEY_READ,
486  publishersKeyHandle,
487  &name,
488  0
489  )))
490  {
491  publisherName = PhQueryRegistryString(keyHandle, NULL);
492 
493  if (publisherName)
494  {
495  if (publisherName->Length != 0)
496  {
497  PETW_PUBLISHER_ENTRY entry;
498 
499  if (numberOfEntries == allocatedEntries)
500  {
501  allocatedEntries *= 2;
502  entries = PhReAllocate(entries, allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY));
503  }
504 
505  entry = &entries[numberOfEntries++];
506  entry->PublisherName = publisherName;
507  entry->Guid = guid;
508  }
509  else
510  {
511  PhDereferenceObject(publisherName);
512  }
513  }
514 
515  NtClose(keyHandle);
516  }
517  }
518 
519  index++;
520  }
521  else if (status == STATUS_NO_MORE_ENTRIES)
522  {
523  break;
524  }
525  else if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL)
526  {
527  PhFree(buffer);
528  buffer = PhAllocate(bufferSize);
529  }
530  else
531  {
532  break;
533  }
534  }
535 
536  PhFree(buffer);
537  NtClose(publishersKeyHandle);
538 
539  *Entries = entries;
540  *NumberOfEntries = numberOfEntries;
541 
542  return TRUE;
543 }
544 
546  _In_ PPH_STRINGREF PublisherName,
547  _Out_ PGUID Guid
548  )
549 {
550  BOOLEAN result;
551  PETW_PUBLISHER_ENTRY entries;
552  ULONG numberOfEntries;
553  ULONG i;
554 
555  if (!EspEnumerateEtwPublishers(&entries, &numberOfEntries))
556  return FALSE;
557 
558  result = FALSE;
559 
560  for (i = 0; i < numberOfEntries; i++)
561  {
562  if (!result && PhEqualStringRef(&entries[i].PublisherName->sr, PublisherName, TRUE))
563  {
564  *Guid = entries[i].Guid;
565  result = TRUE;
566  }
567 
568  PhDereferenceObject(entries[i].PublisherName);
569  }
570 
571  PhFree(entries);
572 
573  return result;
574 }
575 
577  _In_ PES_TRIGGER_INFO Info,
578  _Out_ PWSTR *TriggerString,
579  _Out_ PWSTR *ActionString,
580  _Out_ PPH_STRING *StringUsed
581  )
582 {
583  PPH_STRING stringUsed = NULL;
584  PWSTR triggerString = NULL;
585  PWSTR actionString;
586  ULONG i;
587  BOOLEAN typeFound;
588  BOOLEAN subTypeFound;
589 
590  switch (Info->Type)
591  {
592  case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL:
593  {
594  PPH_STRING guidString;
595 
596  if (!Info->Subtype)
597  {
598  triggerString = L"Device interface arrival";
599  }
600  else
601  {
602  guidString = PhFormatGuid(Info->Subtype);
603  stringUsed = PhConcatStrings2(L"Device interface arrival: ", guidString->Buffer);
604  triggerString = stringUsed->Buffer;
605  }
606  }
607  break;
608  case SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE:
609  {
610  PPH_STRING guidString;
611 
612  if (!Info->Subtype)
613  {
614  triggerString = L"Custom system state change";
615  }
616  else
617  {
618  guidString = PhFormatGuid(Info->Subtype);
619  stringUsed = PhConcatStrings2(L"Custom system state change: ", guidString->Buffer);
620  triggerString = stringUsed->Buffer;
621  }
622  }
623  break;
624  case SERVICE_TRIGGER_TYPE_CUSTOM:
625  {
626  if (Info->Subtype)
627  {
628  PPH_STRING publisherName;
629 
630  // Try to lookup the publisher name from the GUID.
631  publisherName = EspLookupEtwPublisherName(Info->Subtype);
632  stringUsed = PhConcatStrings2(L"Custom: ", publisherName->Buffer);
633  PhDereferenceObject(publisherName);
634  triggerString = stringUsed->Buffer;
635  }
636  else
637  {
638  triggerString = L"Custom";
639  }
640  }
641  break;
642  default:
643  {
644  typeFound = FALSE;
645  subTypeFound = FALSE;
646 
647  for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
648  {
649  if (SubTypeEntries[i].Type == Info->Type)
650  {
651  typeFound = TRUE;
652 
653  if (!Info->Subtype && !SubTypeEntries[i].Guid)
654  {
655  subTypeFound = TRUE;
656  triggerString = SubTypeEntries[i].Name;
657  break;
658  }
659  else if (Info->Subtype && SubTypeEntries[i].Guid && memcmp(Info->Subtype, SubTypeEntries[i].Guid, sizeof(GUID)) == 0)
660  {
661  subTypeFound = TRUE;
662  triggerString = SubTypeEntries[i].Name;
663  break;
664  }
665  else if (!subTypeFound && SubTypeEntries[i].Guid == &SubTypeUnknownGuid)
666  {
667  triggerString = SubTypeEntries[i].Name;
668  break;
669  }
670  }
671  }
672 
673  if (!typeFound)
674  {
675  triggerString = L"Unknown";
676  }
677  }
678  break;
679  }
680 
681  switch (Info->Action)
682  {
683  case SERVICE_TRIGGER_ACTION_SERVICE_START:
684  actionString = L"Start";
685  break;
686  case SERVICE_TRIGGER_ACTION_SERVICE_STOP:
687  actionString = L"Stop";
688  break;
689  default:
690  actionString = L"Unknown";
691  break;
692  }
693 
694  *TriggerString = triggerString;
695  *ActionString = actionString;
696  *StringUsed = stringUsed;
697 }
698 
700  _In_ struct _ES_TRIGGER_CONTEXT *Context,
701  _In_ SC_HANDLE ServiceHandle
702  )
703 {
704  PSERVICE_TRIGGER_INFO triggerInfo;
705  ULONG i;
706 
707  EspClearTriggerInfoList(Context->InfoList);
708 
709  if (triggerInfo = PhQueryServiceVariableSize(ServiceHandle, SERVICE_CONFIG_TRIGGER_INFO))
710  {
711  for (i = 0; i < triggerInfo->cTriggers; i++)
712  {
713  PSERVICE_TRIGGER trigger = &triggerInfo->pTriggers[i];
714  PES_TRIGGER_INFO info;
715  PWSTR triggerString;
716  PWSTR actionString;
717  PPH_STRING stringUsed;
718  INT lvItemIndex;
719 
720  info = EspCreateTriggerInfo(trigger);
721  PhAddItemList(Context->InfoList, info);
722 
723  EspFormatTriggerInfo(info, &triggerString, &actionString, &stringUsed);
724 
725  lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, info);
726  PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
727 
728  if (stringUsed)
729  PhDereferenceObject(stringUsed);
730  }
731 
732  Context->InitialNumberOfTriggers = triggerInfo->cTriggers;
733 
734  ExtendedListView_SortItems(Context->TriggersLv);
735 
736  PhFree(triggerInfo);
737  }
738 }
739 
741  _In_ struct _ES_TRIGGER_CONTEXT *Context,
742  _Out_ PULONG Win32Result
743  )
744 {
745  BOOLEAN result = TRUE;
746  PH_AUTO_POOL autoPool;
747  SC_HANDLE serviceHandle;
748  SERVICE_TRIGGER_INFO triggerInfo;
749  ULONG i;
750  ULONG j;
751 
752  if (!Context->Dirty)
753  return TRUE;
754 
755  // Do not try to change trigger information if we didn't have any triggers before and we don't
756  // have any now. ChangeServiceConfig2 returns an error in this situation.
757  if (Context->InitialNumberOfTriggers == 0 && Context->InfoList->Count == 0)
758  return TRUE;
759 
760  PhInitializeAutoPool(&autoPool);
761 
762  memset(&triggerInfo, 0, sizeof(SERVICE_TRIGGER_INFO));
763  triggerInfo.cTriggers = Context->InfoList->Count;
764 
765  // pTriggers needs to be NULL when there are no triggers.
766  if (Context->InfoList->Count != 0)
767  {
768  triggerInfo.pTriggers = PhAutoDereferenceObject(PhCreateAlloc(Context->InfoList->Count * sizeof(SERVICE_TRIGGER)));
769  memset(triggerInfo.pTriggers, 0, Context->InfoList->Count * sizeof(SERVICE_TRIGGER));
770 
771  for (i = 0; i < Context->InfoList->Count; i++)
772  {
773  PSERVICE_TRIGGER trigger = &triggerInfo.pTriggers[i];
774  PES_TRIGGER_INFO info = Context->InfoList->Items[i];
775 
776  trigger->dwTriggerType = info->Type;
777  trigger->dwAction = info->Action;
778  trigger->pTriggerSubtype = info->Subtype;
779 
780  if (info->DataList && info->DataList->Count != 0)
781  {
782  trigger->cDataItems = info->DataList->Count;
783  trigger->pDataItems = PhAutoDereferenceObject(PhCreateAlloc(info->DataList->Count * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM)));
784 
785  for (j = 0; j < info->DataList->Count; j++)
786  {
787  PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM dataItem = &trigger->pDataItems[j];
788  PES_TRIGGER_DATA data = info->DataList->Items[j];
789 
790  dataItem->dwDataType = data->Type;
791 
792  if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
793  {
794  dataItem->cbData = (ULONG)data->String->Length + 2; // include null terminator
795  dataItem->pData = (PBYTE)data->String->Buffer;
796  }
797  else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
798  {
799  dataItem->cbData = data->BinaryLength;
800  dataItem->pData = data->Binary;
801  }
802  else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL)
803  {
804  dataItem->cbData = sizeof(UCHAR);
805  dataItem->pData = (PBYTE)&data->Byte;
806  }
807  else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY || data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL)
808  {
809  dataItem->cbData = sizeof(ULONG64);
810  dataItem->pData = (PBYTE)&data->UInt64;
811  }
812  }
813  }
814  }
815  }
816 
817  if (serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_CHANGE_CONFIG))
818  {
819  if (!ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_TRIGGER_INFO, &triggerInfo))
820  {
821  result = FALSE;
822  *Win32Result = GetLastError();
823  }
824 
825  CloseServiceHandle(serviceHandle);
826  }
827  else
828  {
829  result = FALSE;
830  *Win32Result = GetLastError();
831 
832  if (*Win32Result == ERROR_ACCESS_DENIED && !PhElevated)
833  {
834  // Elevate using phsvc.
835  if (PhUiConnectToPhSvc(Context->WindowHandle, FALSE))
836  {
837  NTSTATUS status;
838 
839  result = TRUE;
840 
841  if (!NT_SUCCESS(status = PhSvcCallChangeServiceConfig2(Context->ServiceItem->Name->Buffer,
842  SERVICE_CONFIG_TRIGGER_INFO, &triggerInfo)))
843  {
844  result = FALSE;
845  *Win32Result = PhNtStatusToDosError(status);
846  }
847 
849  }
850  else
851  {
852  // User cancelled elevation.
853  *Win32Result = ERROR_CANCELLED;
854  }
855  }
856  }
857 
858  PhDeleteAutoPool(&autoPool);
859 
860  return result;
861 }
862 
864  _In_ HWND ListViewHandle,
865  _In_ INT Index,
866  _In_ PVOID Param
867  )
868 {
869  LVITEM item;
870 
871  item.mask = LVIF_PARAM;
872  item.iItem = Index;
873  item.iSubItem = 0;
874  item.lParam = (LPARAM)Param;
875 
876  return ListView_SetItem(ListViewHandle, &item);
877 }
878 
880  _In_ struct _ES_TRIGGER_CONTEXT *Context,
881  _In_ ULONG Event
882  )
883 {
884  switch (Event)
885  {
887  {
888  Context->EditingInfo = EspCreateTriggerInfo(NULL);
889  Context->EditingInfo->Type = SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY;
890  Context->EditingInfo->SubtypeBuffer = NetworkManagerFirstIpAddressArrivalGuid;
891  Context->EditingInfo->Subtype = &Context->EditingInfo->SubtypeBuffer;
892  Context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START;
893 
894  if (DialogBoxParam(
896  MAKEINTRESOURCE(IDD_SRVTRIGGER),
897  Context->WindowHandle,
899  (LPARAM)Context
900  ) == IDOK)
901  {
902  PWSTR triggerString;
903  PWSTR actionString;
904  PPH_STRING stringUsed;
905  INT lvItemIndex;
906 
907  Context->Dirty = TRUE;
908  PhAddItemList(Context->InfoList, Context->EditingInfo);
909 
910  EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed);
911 
912  lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, Context->EditingInfo);
913  PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
914 
915  if (stringUsed)
916  PhDereferenceObject(stringUsed);
917  }
918  else
919  {
920  EspDestroyTriggerInfo(Context->EditingInfo);
921  }
922 
923  Context->EditingInfo = NULL;
924  }
925  break;
927  {
928  INT lvItemIndex;
929  PES_TRIGGER_INFO info;
930  ULONG index;
931 
932  lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED);
933 
934  if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PVOID *)&info))
935  {
936  index = PhFindItemList(Context->InfoList, info);
937 
938  if (index != -1)
939  {
940  Context->EditingInfo = EspCloneTriggerInfo(info);
941 
942  if (DialogBoxParam(
944  MAKEINTRESOURCE(IDD_SRVTRIGGER),
945  Context->WindowHandle,
947  (LPARAM)Context
948  ) == IDOK)
949  {
950  PWSTR triggerString;
951  PWSTR actionString;
952  PPH_STRING stringUsed;
953 
954  Context->Dirty = TRUE;
955  EspDestroyTriggerInfo(Context->InfoList->Items[index]);
956  Context->InfoList->Items[index] = Context->EditingInfo;
957 
958  EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed);
959  PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 0, triggerString);
960  PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
961 
962  if (stringUsed)
963  PhDereferenceObject(stringUsed);
964 
965  EspSetListViewItemParam(Context->TriggersLv, lvItemIndex, Context->EditingInfo);
966  }
967  else
968  {
969  EspDestroyTriggerInfo(Context->EditingInfo);
970  }
971 
972  Context->EditingInfo = NULL;
973  }
974  }
975  }
976  break;
978  {
979  INT lvItemIndex;
980  PES_TRIGGER_INFO info;
981  ULONG index;
982 
983  lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED);
984 
985  if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PVOID *)&info))
986  {
987  index = PhFindItemList(Context->InfoList, info);
988 
989  if (index != -1)
990  {
991  EspDestroyTriggerInfo(info);
992  PhRemoveItemList(Context->InfoList, index);
993  PhRemoveListViewItem(Context->TriggersLv, lvItemIndex);
994  }
995  }
996 
997  Context->Dirty = TRUE;
998  }
999  break;
1001  {
1002  ULONG selectedCount;
1003 
1004  selectedCount = ListView_GetSelectedCount(Context->TriggersLv);
1005 
1006  EnableWindow(GetDlgItem(Context->WindowHandle, IDC_EDIT), selectedCount == 1);
1007  EnableWindow(GetDlgItem(Context->WindowHandle, IDC_DELETE), selectedCount == 1);
1008  }
1009  break;
1010  }
1011 }
1012 
1013 static ULONG EspTriggerTypeStringToInteger(
1014  _In_ PWSTR String
1015  )
1016 {
1017  ULONG i;
1018 
1019  for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++)
1020  {
1021  if (PhEqualStringZ(TypeEntries[i].Name, String, FALSE))
1022  return TypeEntries[i].Type;
1023  }
1024 
1025  return 0;
1026 }
1027 
1028 static int __cdecl EtwPublisherByNameCompareFunction(
1029  _In_ const void *elem1,
1030  _In_ const void *elem2
1031  )
1032 {
1035 
1036  return PhCompareString(entry1->PublisherName, entry2->PublisherName, TRUE);
1037 }
1038 
1039 static VOID EspFixServiceTriggerControls(
1040  _In_ HWND hwndDlg,
1041  _In_ PES_TRIGGER_CONTEXT Context
1042  )
1043 {
1044  HWND typeComboBox;
1045  HWND subTypeComboBox;
1046  ULONG i;
1047  PPH_STRING selectedTypeString;
1048  ULONG type;
1049  PPH_STRING selectedSubTypeString;
1050 
1051  typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE);
1052  subTypeComboBox = GetDlgItem(hwndDlg, IDC_SUBTYPE);
1053 
1054  selectedTypeString = PhGetWindowText(typeComboBox);
1055  type = EspTriggerTypeStringToInteger(selectedTypeString->Buffer);
1056  PhDereferenceObject(selectedTypeString);
1057 
1058  if (Context->LastSelectedType != type)
1059  {
1060  // Change the contents of the subtype combo box based on the type.
1061 
1062  ComboBox_ResetContent(subTypeComboBox);
1063 
1064  switch (type)
1065  {
1066  case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL:
1067  {
1068  ComboBox_AddString(subTypeComboBox, L"Custom");
1069  }
1070  break;
1071  case SERVICE_TRIGGER_TYPE_CUSTOM_SYSTEM_STATE_CHANGE:
1072  {
1073  ComboBox_AddString(subTypeComboBox, L"Custom");
1074  }
1075  break;
1076  case SERVICE_TRIGGER_TYPE_CUSTOM:
1077  {
1078  PETW_PUBLISHER_ENTRY entries;
1079  ULONG numberOfEntries;
1080  ULONG i;
1081 
1082  ComboBox_AddString(subTypeComboBox, L"Custom");
1083 
1084  // Display a list of publishers.
1085  if (EspEnumerateEtwPublishers(&entries, &numberOfEntries))
1086  {
1087  // Sort the list by name.
1088  qsort(entries, numberOfEntries, sizeof(ETW_PUBLISHER_ENTRY), EtwPublisherByNameCompareFunction);
1089 
1090  for (i = 0; i < numberOfEntries; i++)
1091  {
1092  ComboBox_AddString(subTypeComboBox, entries[i].PublisherName->Buffer);
1093  PhDereferenceObject(entries[i].PublisherName);
1094  }
1095 
1096  PhFree(entries);
1097  }
1098  }
1099  break;
1100  default:
1101  for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
1102  {
1103  if (SubTypeEntries[i].Type == type && SubTypeEntries[i].Guid && SubTypeEntries[i].Guid != &SubTypeUnknownGuid)
1104  {
1105  ComboBox_AddString(subTypeComboBox, SubTypeEntries[i].Name);
1106  }
1107  }
1108  break;
1109  }
1110 
1111  ComboBox_SetCurSel(subTypeComboBox, 0);
1112 
1113  Context->LastSelectedType = type;
1114  }
1115 
1116  selectedSubTypeString = PhGetWindowText(subTypeComboBox);
1117 
1118  if (PhEqualString2(selectedSubTypeString, L"Custom", FALSE))
1119  {
1120  EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), TRUE);
1121  SetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM, Context->LastCustomSubType->Buffer);
1122  }
1123  else
1124  {
1125  if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM)))
1126  {
1127  EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), FALSE);
1128  PhMoveReference(&Context->LastCustomSubType, PhGetWindowText(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM)));
1129  SetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM, L"");
1130  }
1131  }
1132 
1133  PhDereferenceObject(selectedSubTypeString);
1134 }
1135 
1137  _In_ PPH_STRING String
1138  )
1139 {
1140  PH_STRING_BUILDER sb;
1141  ULONG i;
1142 
1143  PhInitializeStringBuilder(&sb, String->Length);
1144 
1145  for (i = 0; i < (ULONG)String->Length / 2; i++)
1146  {
1147  if (String->Buffer[i] == 0)
1148  {
1149  PhAppendStringBuilderEx(&sb, L"\r\n", 4);
1150  continue;
1151  }
1152 
1153  PhAppendCharStringBuilder(&sb, String->Buffer[i]);
1154  }
1155 
1156  return PhFinalStringBuilderString(&sb);
1157 }
1158 
1160  _In_ PPH_STRING String
1161  )
1162 {
1163  PPH_STRING text;
1164  SIZE_T count;
1165  SIZE_T i;
1166 
1167  text = PhCreateStringEx(NULL, String->Length + 2); // plus one character for an extra null terminator (see below)
1168  text->Length = 0;
1169  count = 0;
1170 
1171  for (i = 0; i < String->Length / 2; i++)
1172  {
1173  // Lines are terminated by "\r\n".
1174  if (String->Buffer[i] == '\r')
1175  {
1176  continue;
1177  }
1178 
1179  if (String->Buffer[i] == '\n')
1180  {
1181  text->Buffer[count++] = 0;
1182  continue;
1183  }
1184 
1185  text->Buffer[count++] = String->Buffer[i];
1186  }
1187 
1188  if (count != 0)
1189  {
1190  // Make sure we have an extra null terminator at the end, as required of multistrings.
1191  if (text->Buffer[count - 1] != 0)
1192  text->Buffer[count++] = 0;
1193  }
1194 
1195  text->Length = count * 2;
1196 
1197  return text;
1198 }
1199 
1201  _In_ PPH_STRING String
1202  )
1203 {
1204  PPH_STRING text;
1205  SIZE_T j;
1206 
1207  text = PhDuplicateString(String);
1208 
1209  for (j = 0; j < text->Length / 2; j++)
1210  {
1211  if (text->Buffer[j] == 0)
1212  text->Buffer[j] = ' ';
1213  }
1214 
1215  return text;
1216 }
1217 
1219  _In_ PES_TRIGGER_DATA Data,
1220  _Out_ PPH_STRING *Text
1221  )
1222 {
1223  if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
1224  {
1225  // This check works for both normal strings and multistrings.
1226  if (Data->String->Length != 0)
1227  {
1228  // Prepare the text for display by replacing null characters with spaces.
1229  *Text = EspConvertNullsToSpaces(Data->String);
1230  }
1231  else
1232  {
1233  *Text = PhCreateString(L"(empty string)");
1234  }
1235  }
1236  else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
1237  {
1238  *Text = PhCreateString(L"(binary data)");
1239  }
1240  else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_LEVEL)
1241  {
1242  *Text = PhFormatString(L"(level) %u", Data->Byte);
1243  }
1244  else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ANY)
1245  {
1246  *Text = PhFormatString(L"(keyword any) 0x%I64x", Data->UInt64);
1247  }
1248  else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_KEYWORD_ALL)
1249  {
1250  *Text = PhFormatString(L"(keyword all) 0x%I64x", Data->UInt64);
1251  }
1252  else
1253  {
1254  *Text = PhCreateString(L"(unknown type)");
1255  }
1256 }
1257 
1258 INT_PTR CALLBACK EspServiceTriggerDlgProc(
1259  _In_ HWND hwndDlg,
1260  _In_ UINT uMsg,
1261  _In_ WPARAM wParam,
1262  _In_ LPARAM lParam
1263  )
1264 {
1265  PES_TRIGGER_CONTEXT context;
1266 
1267  if (uMsg == WM_INITDIALOG)
1268  {
1269  context = (PES_TRIGGER_CONTEXT)lParam;
1270  SetProp(hwndDlg, L"Context", (HANDLE)context);
1271  }
1272  else
1273  {
1274  context = (PES_TRIGGER_CONTEXT)GetProp(hwndDlg, L"Context");
1275 
1276  if (uMsg == WM_DESTROY)
1277  RemoveProp(hwndDlg, L"Context");
1278  }
1279 
1280  if (!context)
1281  return FALSE;
1282 
1283  switch (uMsg)
1284  {
1285  case WM_INITDIALOG:
1286  {
1287  HWND typeComboBox;
1288  HWND actionComboBox;
1289  HWND lvHandle;
1290  ULONG i;
1291 
1292  context->LastSelectedType = 0;
1293 
1294  if (context->EditingInfo->Subtype)
1295  context->LastCustomSubType = PhFormatGuid(context->EditingInfo->Subtype);
1296  else
1297  context->LastCustomSubType = PhReferenceEmptyString();
1298 
1299  typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE);
1300  actionComboBox = GetDlgItem(hwndDlg, IDC_ACTION);
1301 
1302  for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++)
1303  {
1304  ComboBox_AddString(typeComboBox, TypeEntries[i].Name);
1305 
1306  if (TypeEntries[i].Type == context->EditingInfo->Type)
1307  {
1308  PhSelectComboBoxString(typeComboBox, TypeEntries[i].Name, FALSE);
1309  }
1310  }
1311 
1312  ComboBox_AddString(actionComboBox, L"Start");
1313  ComboBox_AddString(actionComboBox, L"Stop");
1314  ComboBox_SetCurSel(actionComboBox, context->EditingInfo->Action == SERVICE_TRIGGER_ACTION_SERVICE_START ? 0 : 1);
1315 
1316  EspFixServiceTriggerControls(hwndDlg, context);
1317 
1318  if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM)
1319  {
1320  for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
1321  {
1322  if (
1323  SubTypeEntries[i].Type == context->EditingInfo->Type &&
1324  SubTypeEntries[i].Guid &&
1325  context->EditingInfo->Subtype &&
1326  memcmp(SubTypeEntries[i].Guid, context->EditingInfo->Subtype, sizeof(GUID)) == 0
1327  )
1328  {
1329  PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SUBTYPE), SubTypeEntries[i].Name, FALSE);
1330  break;
1331  }
1332  }
1333  }
1334  else
1335  {
1336  if (context->EditingInfo->Subtype)
1337  {
1338  PPH_STRING publisherName;
1339 
1340  // Try to select the publisher name in the subtype list.
1341  publisherName = EspLookupEtwPublisherName(context->EditingInfo->Subtype);
1342  PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SUBTYPE), publisherName->Buffer, FALSE);
1343  PhDereferenceObject(publisherName);
1344  }
1345  }
1346 
1347  // Call a second time since the state of the custom subtype text box may have changed.
1348  EspFixServiceTriggerControls(hwndDlg, context);
1349 
1350  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
1351  PhSetListViewStyle(lvHandle, FALSE, TRUE);
1352  PhSetControlTheme(lvHandle, L"explorer");
1353  PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 280, L"Data");
1354 
1355  if (context->EditingInfo->DataList)
1356  {
1357  for (i = 0; i < context->EditingInfo->DataList->Count; i++)
1358  {
1359  PES_TRIGGER_DATA data;
1360  PPH_STRING text;
1361  INT lvItemIndex;
1362 
1363  data = context->EditingInfo->DataList->Items[i];
1364 
1365  EspFormatTriggerData(data, &text);
1366  lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data);
1367  PhDereferenceObject(text);
1368  }
1369  }
1370 
1371  EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE);
1372  EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
1373  }
1374  break;
1375  case WM_DESTROY:
1376  {
1377  PhClearReference(&context->LastCustomSubType);
1378  }
1379  break;
1380  case WM_COMMAND:
1381  {
1382  switch (LOWORD(wParam))
1383  {
1384  case IDC_TYPE:
1385  if (HIWORD(wParam) == CBN_SELCHANGE)
1386  {
1387  EspFixServiceTriggerControls(hwndDlg, context);
1388  }
1389  break;
1390  case IDC_SUBTYPE:
1391  if (HIWORD(wParam) == CBN_SELCHANGE)
1392  {
1393  EspFixServiceTriggerControls(hwndDlg, context);
1394  }
1395  break;
1396  case IDC_NEW:
1397  {
1398  HWND lvHandle;
1399 
1400  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
1401  context->EditingValue = PhReferenceEmptyString();
1402 
1403  if (DialogBoxParam(
1405  MAKEINTRESOURCE(IDD_VALUE),
1406  hwndDlg,
1407  ValueDlgProc,
1408  (LPARAM)context
1409  ) == IDOK)
1410  {
1411  PES_TRIGGER_DATA data;
1412  PPH_STRING text;
1413  INT lvItemIndex;
1414 
1415  data = EspCreateTriggerData(NULL);
1416  data->Type = SERVICE_TRIGGER_DATA_TYPE_STRING;
1417  data->String = EspConvertNewLinesToNulls(context->EditingValue);
1418 
1419  if (!context->EditingInfo->DataList)
1420  context->EditingInfo->DataList = PhCreateList(4);
1421 
1422  PhAddItemList(context->EditingInfo->DataList, data);
1423 
1424  EspFormatTriggerData(data, &text);
1425  lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data);
1426  PhDereferenceObject(text);
1427  }
1428 
1429  PhClearReference(&context->EditingValue);
1430  }
1431  break;
1432  case IDC_EDIT:
1433  {
1434  HWND lvHandle;
1435  INT lvItemIndex;
1436  PES_TRIGGER_DATA data;
1437  ULONG index;
1438 
1439  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
1440  lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED);
1441 
1442  if (
1443  lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PVOID *)&data) &&
1444  data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING // editing binary values is not supported
1445  )
1446  {
1447  index = PhFindItemList(context->EditingInfo->DataList, data);
1448 
1449  if (index != -1)
1450  {
1451  context->EditingValue = EspConvertNullsToNewLines(data->String);
1452 
1453  if (DialogBoxParam(
1455  MAKEINTRESOURCE(IDD_VALUE),
1456  hwndDlg,
1457  ValueDlgProc,
1458  (LPARAM)context
1459  ) == IDOK)
1460  {
1461  PPH_STRING text;
1462 
1463  PhMoveReference(&data->String, EspConvertNewLinesToNulls(context->EditingValue));
1464 
1465  EspFormatTriggerData(data, &text);
1466  PhSetListViewSubItem(lvHandle, lvItemIndex, 0, text->Buffer);
1467  PhDereferenceObject(text);
1468  }
1469 
1470  PhClearReference(&context->EditingValue);
1471  }
1472  }
1473  }
1474  break;
1475  case IDC_DELETE:
1476  {
1477  HWND lvHandle;
1478  INT lvItemIndex;
1479  PES_TRIGGER_DATA data;
1480  ULONG index;
1481 
1482  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
1483  lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED);
1484 
1485  if (lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PVOID *)&data))
1486  {
1487  index = PhFindItemList(context->EditingInfo->DataList, data);
1488 
1489  if (index != -1)
1490  {
1491  EspDestroyTriggerData(data);
1492  PhRemoveItemList(context->EditingInfo->DataList, index);
1493  PhRemoveListViewItem(lvHandle, lvItemIndex);
1494  }
1495  }
1496  }
1497  break;
1498  case IDCANCEL:
1499  EndDialog(hwndDlg, IDCANCEL);
1500  break;
1501  case IDOK:
1502  {
1503  PH_AUTO_POOL autoPool;
1504  PPH_STRING typeString;
1505  PPH_STRING subTypeString;
1506  PPH_STRING customSubTypeString;
1507  PPH_STRING actionString;
1508  ULONG i;
1509 
1510  PhInitializeAutoPool(&autoPool);
1511 
1512  typeString = PhaGetDlgItemText(hwndDlg, IDC_TYPE);
1513  subTypeString = PhaGetDlgItemText(hwndDlg, IDC_SUBTYPE);
1514  customSubTypeString = PhaGetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM);
1515  actionString = PhaGetDlgItemText(hwndDlg, IDC_ACTION);
1516 
1517  for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++)
1518  {
1519  if (PhEqualStringZ(TypeEntries[i].Name, typeString->Buffer, FALSE))
1520  {
1521  context->EditingInfo->Type = TypeEntries[i].Type;
1522  break;
1523  }
1524  }
1525 
1526  if (!PhEqualString2(subTypeString, L"Custom", FALSE))
1527  {
1528  if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM)
1529  {
1530  for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
1531  {
1532  if (
1533  SubTypeEntries[i].Type == context->EditingInfo->Type &&
1534  PhEqualString2(subTypeString, SubTypeEntries[i].Name, FALSE)
1535  )
1536  {
1537  context->EditingInfo->SubtypeBuffer = *SubTypeEntries[i].Guid;
1538  context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer;
1539  break;
1540  }
1541  }
1542  }
1543  else
1544  {
1545  if (!EspLookupEtwPublisherGuid(&subTypeString->sr, &context->EditingInfo->SubtypeBuffer))
1546  {
1547  PhShowError(hwndDlg, L"Unable to find the ETW publisher GUID.");
1548  goto DoNotClose;
1549  }
1550 
1551  context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer;
1552  }
1553  }
1554  else
1555  {
1556  UNICODE_STRING guidString;
1557 
1558  PhStringRefToUnicodeString(&customSubTypeString->sr, &guidString);
1559 
1560  // Trim whitespace.
1561 
1562  while (guidString.Length != 0 && *guidString.Buffer == ' ')
1563  {
1564  guidString.Buffer++;
1565  guidString.Length -= 2;
1566  }
1567 
1568  while (guidString.Length != 0 && guidString.Buffer[guidString.Length / 2 - 1] == ' ')
1569  {
1570  guidString.Length -= 2;
1571  }
1572 
1573  if (NT_SUCCESS(RtlGUIDFromString(&guidString, &context->EditingInfo->SubtypeBuffer)))
1574  {
1575  context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer;
1576  }
1577  else
1578  {
1579  PhShowError(hwndDlg, L"The custom subtype is invalid. Please ensure that the string is a valid GUID: \"{x-x-x-x-x}\".");
1580  goto DoNotClose;
1581  }
1582  }
1583 
1584  if (PhEqualString2(actionString, L"Start", FALSE))
1585  context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START;
1586  else
1587  context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_STOP;
1588 
1589  if (
1590  context->EditingInfo->DataList &&
1591  context->EditingInfo->DataList->Count != 0 &&
1592  context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL &&
1593  context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT &&
1594  context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_NETWORK_ENDPOINT &&
1595  context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM
1596  )
1597  {
1598  // This trigger has data items, but the trigger type doesn't allow them.
1599  if (PhShowMessage(
1600  hwndDlg,
1601  MB_OKCANCEL | MB_ICONWARNING,
1602  L"The trigger type \"%s\" does not allow data items to be configured. "
1603  L"If you continue, they will be removed.",
1604  typeString->Buffer
1605  ) != IDOK)
1606  {
1607  goto DoNotClose;
1608  }
1609 
1610  for (i = 0; i < context->EditingInfo->DataList->Count; i++)
1611  {
1612  EspDestroyTriggerData(context->EditingInfo->DataList->Items[i]);
1613  }
1614 
1615  PhClearReference(&context->EditingInfo->DataList);
1616  }
1617 
1618  EndDialog(hwndDlg, IDOK);
1619 
1620 DoNotClose:
1621  PhDeleteAutoPool(&autoPool);
1622  }
1623  break;
1624  }
1625  }
1626  break;
1627  case WM_NOTIFY:
1628  {
1629  LPNMHDR header = (LPNMHDR)lParam;
1630  HWND lvHandle;
1631 
1632  lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
1633 
1634  switch (header->code)
1635  {
1636  case LVN_ITEMCHANGED:
1637  {
1638  if (header->hwndFrom == lvHandle)
1639  {
1640  if (ListView_GetSelectedCount(lvHandle) == 1)
1641  {
1642  PES_TRIGGER_DATA data = PhGetSelectedListViewItemParam(GetDlgItem(hwndDlg, IDC_LIST));
1643 
1644  // Editing binary data is not supported.
1645  EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), data && data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING);
1646  EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), TRUE);
1647  }
1648  else
1649  {
1650  EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE);
1651  EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
1652  }
1653  }
1654  }
1655  break;
1656  case NM_DBLCLK:
1657  {
1658  if (header->hwndFrom == lvHandle)
1659  {
1660  SendMessage(hwndDlg, WM_COMMAND, IDC_EDIT, 0);
1661  }
1662  }
1663  break;
1664  }
1665  }
1666  break;
1667  }
1668 
1669  return FALSE;
1670 }
1671 
1672 static INT_PTR CALLBACK ValueDlgProc(
1673  _In_ HWND hwndDlg,
1674  _In_ UINT uMsg,
1675  _In_ WPARAM wParam,
1676  _In_ LPARAM lParam
1677  )
1678 {
1679  PES_TRIGGER_CONTEXT context;
1680 
1681  if (uMsg == WM_INITDIALOG)
1682  {
1683  context = (PES_TRIGGER_CONTEXT)lParam;
1684  SetProp(hwndDlg, L"Context", (HANDLE)context);
1685  }
1686  else
1687  {
1688  context = (PES_TRIGGER_CONTEXT)GetProp(hwndDlg, L"Context");
1689 
1690  if (uMsg == WM_DESTROY)
1691  RemoveProp(hwndDlg, L"Context");
1692  }
1693 
1694  if (!context)
1695  return FALSE;
1696 
1697  switch (uMsg)
1698  {
1699  case WM_INITDIALOG:
1700  {
1701  SetDlgItemText(hwndDlg, IDC_VALUES, context->EditingValue->Buffer);
1702  SetFocus(GetDlgItem(hwndDlg, IDC_VALUES));
1703  Edit_SetSel(GetDlgItem(hwndDlg, IDC_VALUES), 0, -1);
1704  }
1705  break;
1706  case WM_COMMAND:
1707  {
1708  switch (LOWORD(wParam))
1709  {
1710  case IDCANCEL:
1711  EndDialog(hwndDlg, IDCANCEL);
1712  break;
1713  case IDOK:
1714  PhMoveReference(&context->EditingValue, PhGetWindowText(GetDlgItem(hwndDlg, IDC_VALUES)));
1715  EndDialog(hwndDlg, IDOK);
1716  break;
1717  }
1718  }
1719  break;
1720  }
1721 
1722  return FALSE;
1723 }