Process Hacker
itemtips.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * item tooltips
4  *
5  * Copyright (C) 2010-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 <phapp.h>
24 #include <phplug.h>
25 #include <verify.h>
26 #define CINTERFACE
27 #define COBJMACROS
28 #include <taskschd.h>
29 
31  _In_ PPH_PROCESS_ITEM Process,
32  _Inout_ PPH_STRING_BUILDER Drivers
33  );
34 
36  _In_ PPH_PROCESS_ITEM Process,
37  _Inout_ PPH_STRING_BUILDER Tasks
38  );
39 
40 static PH_STRINGREF StandardIndent = PH_STRINGREF_INIT(L" ");
41 
43  _Inout_ PPH_STRING_BUILDER StringBuilder,
44  _In_ PPH_STRINGREF String,
45  _In_ ULONG CharactersPerLine,
46  _In_opt_ PPH_STRINGREF IndentAfterFirstLine
47  )
48 {
49  PH_STRINGREF line;
50  SIZE_T bytesPerLine;
51  BOOLEAN afterFirstLine;
52  SIZE_T bytesToAppend;
53 
54  line = *String;
55  bytesPerLine = CharactersPerLine * sizeof(WCHAR);
56  afterFirstLine = FALSE;
57 
58  while (line.Length != 0)
59  {
60  bytesToAppend = line.Length;
61 
62  if (bytesToAppend > bytesPerLine)
63  bytesToAppend = bytesPerLine;
64 
65  if (afterFirstLine)
66  {
67  PhAppendCharStringBuilder(StringBuilder, '\n');
68 
69  if (IndentAfterFirstLine)
70  PhAppendStringBuilder(StringBuilder, IndentAfterFirstLine);
71  }
72 
73  PhAppendStringBuilderEx(StringBuilder, line.Buffer, bytesToAppend);
74  afterFirstLine = TRUE;
75  PhSkipStringRef(&line, bytesToAppend);
76  }
77 }
78 
79 static int __cdecl ServiceForTooltipCompare(
80  _In_ const void *elem1,
81  _In_ const void *elem2
82  )
83 {
84  PPH_SERVICE_ITEM serviceItem1 = *(PPH_SERVICE_ITEM *)elem1;
85  PPH_SERVICE_ITEM serviceItem2 = *(PPH_SERVICE_ITEM *)elem2;
86 
87  return PhCompareString(serviceItem1->Name, serviceItem2->Name, TRUE);
88 }
89 
91  _In_ PPH_PROCESS_ITEM Process,
92  _Out_opt_ PULONG ValidToTickCount
93  )
94 {
95  PH_STRING_BUILDER stringBuilder;
96  ULONG validForMs = 60 * 60 * 1000; // 1 hour
97  PPH_STRING tempString;
98  PH_KNOWN_PROCESS_TYPE knownProcessType = UnknownProcessType;
99 
100  PhInitializeStringBuilder(&stringBuilder, 200);
101 
102  // Command line
103 
104  if (Process->CommandLine)
105  {
106  tempString = PhEllipsisString(Process->CommandLine, 100 * 10);
107 
108  // This is necessary because the tooltip control seems to use some kind of O(n^9999) word-wrapping
109  // algorithm.
110  PhpAppendStringWithLineBreaks(&stringBuilder, &tempString->sr, 100, NULL);
111  PhAppendCharStringBuilder(&stringBuilder, '\n');
112 
113  PhDereferenceObject(tempString);
114  }
115 
116  // File information
117 
118  tempString = PhFormatImageVersionInfo(
119  Process->FileName,
120  &Process->VersionInfo,
121  &StandardIndent,
122  0
123  );
124 
125  if (!PhIsNullOrEmptyString(tempString))
126  {
127  PhAppendStringBuilder2(&stringBuilder, L"File:\n");
128  PhAppendStringBuilder(&stringBuilder, &tempString->sr);
129  PhAppendCharStringBuilder(&stringBuilder, '\n');
130  }
131 
132  if (tempString)
133  PhDereferenceObject(tempString);
134 
135  // Known command line information
136 
137  if (Process->QueryHandle)
138  PhGetProcessKnownType(Process->QueryHandle, &knownProcessType);
139 
140  if (Process->CommandLine && Process->QueryHandle)
141  {
142  PH_KNOWN_PROCESS_COMMAND_LINE knownCommandLine;
143 
144  if (knownProcessType != UnknownProcessType && PhaGetProcessKnownCommandLine(
145  Process->CommandLine,
146  knownProcessType,
147  &knownCommandLine
148  ))
149  {
150  switch (knownProcessType & KnownProcessTypeMask)
151  {
153  PhAppendStringBuilder2(&stringBuilder, L"Service group name:\n ");
154  PhAppendStringBuilder(&stringBuilder, &knownCommandLine.ServiceHost.GroupName->sr);
155  PhAppendCharStringBuilder(&stringBuilder, '\n');
156  break;
158  {
159  PH_IMAGE_VERSION_INFO versionInfo;
160 
162  &versionInfo,
163  knownCommandLine.RunDllAsApp.FileName->Buffer
164  ))
165  {
166  tempString = PhFormatImageVersionInfo(
167  knownCommandLine.RunDllAsApp.FileName,
168  &versionInfo,
169  &StandardIndent,
170  0
171  );
172 
173  if (!PhIsNullOrEmptyString(tempString))
174  {
175  PhAppendStringBuilder2(&stringBuilder, L"Run DLL target file:\n");
176  PhAppendStringBuilder(&stringBuilder, &tempString->sr);
177  PhAppendCharStringBuilder(&stringBuilder, '\n');
178  }
179 
180  if (tempString)
181  PhDereferenceObject(tempString);
182 
183  PhDeleteImageVersionInfo(&versionInfo);
184  }
185  }
186  break;
188  {
189  PH_IMAGE_VERSION_INFO versionInfo;
190  PPH_STRING guidString;
191 
192  PhAppendStringBuilder2(&stringBuilder, L"COM target:\n");
193 
194  if (knownCommandLine.ComSurrogate.Name)
195  {
196  PhAppendStringBuilder(&stringBuilder, &StandardIndent);
197  PhAppendStringBuilder(&stringBuilder, &knownCommandLine.ComSurrogate.Name->sr);
198  PhAppendCharStringBuilder(&stringBuilder, '\n');
199  }
200 
201  if (guidString = PhFormatGuid(&knownCommandLine.ComSurrogate.Guid))
202  {
203  PhAppendStringBuilder(&stringBuilder, &StandardIndent);
204  PhAppendStringBuilder(&stringBuilder, &guidString->sr);
205  PhDereferenceObject(guidString);
206  PhAppendCharStringBuilder(&stringBuilder, '\n');
207  }
208 
209  if (knownCommandLine.ComSurrogate.FileName && PhInitializeImageVersionInfo(
210  &versionInfo,
211  knownCommandLine.ComSurrogate.FileName->Buffer
212  ))
213  {
214  tempString = PhFormatImageVersionInfo(
215  knownCommandLine.ComSurrogate.FileName,
216  &versionInfo,
217  &StandardIndent,
218  0
219  );
220 
221  if (!PhIsNullOrEmptyString(tempString))
222  {
223  PhAppendStringBuilder2(&stringBuilder, L"COM target file:\n");
224  PhAppendStringBuilder(&stringBuilder, &tempString->sr);
225  PhAppendCharStringBuilder(&stringBuilder, '\n');
226  }
227 
228  if (tempString)
229  PhDereferenceObject(tempString);
230 
231  PhDeleteImageVersionInfo(&versionInfo);
232  }
233  }
234  break;
235  }
236  }
237  }
238 
239  // Services
240 
241  if (Process->ServiceList && Process->ServiceList->Count != 0)
242  {
243  ULONG enumerationKey = 0;
244  PPH_SERVICE_ITEM serviceItem;
245  PPH_LIST serviceList;
246  ULONG i;
247 
248  // Copy the service list into our own list so we can sort it.
249 
250  serviceList = PhCreateList(Process->ServiceList->Count);
251 
252  PhAcquireQueuedLockShared(&Process->ServiceListLock);
253 
254  while (PhEnumPointerList(
255  Process->ServiceList,
256  &enumerationKey,
257  &serviceItem
258  ))
259  {
260  PhReferenceObject(serviceItem);
261  PhAddItemList(serviceList, serviceItem);
262  }
263 
264  PhReleaseQueuedLockShared(&Process->ServiceListLock);
265 
266  qsort(serviceList->Items, serviceList->Count, sizeof(PPH_SERVICE_ITEM), ServiceForTooltipCompare);
267 
268  PhAppendStringBuilder2(&stringBuilder, L"Services:\n");
269 
270  // Add the services.
271  for (i = 0; i < serviceList->Count; i++)
272  {
273  serviceItem = serviceList->Items[i];
274 
275  PhAppendStringBuilder(&stringBuilder, &StandardIndent);
276  PhAppendStringBuilder(&stringBuilder, &serviceItem->Name->sr);
277  PhAppendStringBuilder2(&stringBuilder, L" (");
278  PhAppendStringBuilder(&stringBuilder, &serviceItem->DisplayName->sr);
279  PhAppendStringBuilder2(&stringBuilder, L")\n");
280  }
281 
282  PhDereferenceObjects(serviceList->Items, serviceList->Count);
283  PhDereferenceObject(serviceList);
284  }
285 
286  // Tasks, Drivers
287  switch (knownProcessType & KnownProcessTypeMask)
288  {
289  case TaskHostProcessType:
290  {
291  PH_STRING_BUILDER tasks;
292 
293  PhInitializeStringBuilder(&tasks, 40);
294 
295  PhpFillRunningTasks(Process, &tasks);
296 
297  if (tasks.String->Length != 0)
298  {
299  PhAppendStringBuilder2(&stringBuilder, L"Tasks:\n");
300  PhAppendStringBuilder(&stringBuilder, &tasks.String->sr);
301  }
302 
303  PhDeleteStringBuilder(&tasks);
304  }
305  break;
306  case UmdfHostProcessType:
307  {
308  PH_STRING_BUILDER drivers;
309 
310  PhInitializeStringBuilder(&drivers, 40);
311 
312  PhpFillUmdfDrivers(Process, &drivers);
313 
314  if (drivers.String->Length != 0)
315  {
316  PhAppendStringBuilder2(&stringBuilder, L"Drivers:\n");
317  PhAppendStringBuilder(&stringBuilder, &drivers.String->sr);
318  }
319 
320  PhDeleteStringBuilder(&drivers);
321 
322  validForMs = 10 * 1000; // 10 seconds
323  }
324  break;
325  }
326 
327  // Plugin
328  if (PhPluginsEnabled)
329  {
330  PH_PLUGIN_GET_TOOLTIP_TEXT getTooltipText;
331 
332  getTooltipText.Parameter = Process;
333  getTooltipText.StringBuilder = &stringBuilder;
334  getTooltipText.ValidForMs = validForMs;
335 
337  validForMs = getTooltipText.ValidForMs;
338  }
339 
340  // Notes
341 
342  {
343  PH_STRING_BUILDER notes;
344 
345  PhInitializeStringBuilder(&notes, 40);
346 
347  if (Process->FileName)
348  {
349  if (Process->VerifyResult == VrTrusted)
350  {
351  if (!PhIsNullOrEmptyString(Process->VerifySignerName))
352  PhAppendFormatStringBuilder(&notes, L" Signer: %s\n", Process->VerifySignerName->Buffer);
353  else
354  PhAppendStringBuilder2(&notes, L" Signed.\n");
355  }
356  else if (Process->VerifyResult == VrUnknown)
357  {
358  // Nothing
359  }
360  else if (Process->VerifyResult != VrNoSignature)
361  {
362  PhAppendStringBuilder2(&notes, L" Signature invalid.\n");
363  }
364  }
365 
366  if (Process->IsPacked)
367  {
369  &notes,
370  L" Image is probably packed (%u imports over %u modules).\n",
371  Process->ImportFunctions,
372  Process->ImportModules
373  );
374  }
375 
376  if ((ULONG_PTR)Process->ConsoleHostProcessId & ~3)
377  {
378  CLIENT_ID clientId;
379  PWSTR description = L"Console host";
380  PPH_STRING clientIdString;
381 
382  clientId.UniqueProcess = (HANDLE)((ULONG_PTR)Process->ConsoleHostProcessId & ~3);
383  clientId.UniqueThread = NULL;
384 
385  if ((ULONG_PTR)Process->ConsoleHostProcessId & 2)
386  description = L"Console application";
387 
388  clientIdString = PhGetClientIdName(&clientId);
389  PhAppendFormatStringBuilder(&notes, L" %s: %s\n", description, clientIdString->Buffer);
390  PhDereferenceObject(clientIdString);
391  }
392 
393  if (Process->PackageFullName)
394  {
395  PhAppendFormatStringBuilder(&notes, L" Package name: %s\n", Process->PackageFullName->Buffer);
396  }
397 
398  if (Process->IsDotNet)
399  PhAppendStringBuilder2(&notes, L" Process is managed (.NET).\n");
400  if (Process->IsElevated)
401  PhAppendStringBuilder2(&notes, L" Process is elevated.\n");
402  if (Process->IsImmersive)
403  PhAppendStringBuilder2(&notes, L" Process is a Modern UI app.\n");
404  if (Process->IsInJob)
405  PhAppendStringBuilder2(&notes, L" Process is in a job.\n");
406  if (Process->IsPosix)
407  PhAppendStringBuilder2(&notes, L" Process is POSIX.\n");
408  if (Process->IsWow64)
409  PhAppendStringBuilder2(&notes, L" Process is 32-bit (WOW64).\n");
410 
411  if (notes.String->Length != 0)
412  {
413  PhAppendStringBuilder2(&stringBuilder, L"Notes:\n");
414  PhAppendStringBuilder(&stringBuilder, &notes.String->sr);
415  }
416 
417  PhDeleteStringBuilder(&notes);
418  }
419 
420  if (ValidToTickCount)
421  *ValidToTickCount = GetTickCount() + validForMs;
422 
423  // Remove the trailing newline.
424  if (stringBuilder.String->Length != 0)
425  PhRemoveEndStringBuilder(&stringBuilder, 1);
426 
427  return PhFinalStringBuilderString(&stringBuilder);
428 }
429 
431  _In_ PPH_PROCESS_ITEM Process,
432  _Inout_ PPH_STRING_BUILDER Drivers
433  )
434 {
435  static PH_STRINGREF activeDevices = PH_STRINGREF_INIT(L"ACTIVE_DEVICES");
436  static PH_STRINGREF currentControlSetEnum = PH_STRINGREF_INIT(L"System\\CurrentControlSet\\Enum\\");
437 
438  HANDLE processHandle;
439  ULONG flags = 0;
440  PVOID environment;
441  ULONG environmentLength;
442  ULONG enumerationKey;
443  PH_ENVIRONMENT_VARIABLE variable;
444 
446  &processHandle,
448  Process->ProcessId
449  )))
450  return;
451 
452 #ifdef _WIN64
453  // Just in case.
454  if (Process->IsWow64)
456 #endif
457 
459  processHandle,
460  flags,
461  &environment,
462  &environmentLength
463  )))
464  {
465  enumerationKey = 0;
466 
467  while (PhEnumProcessEnvironmentVariables(environment, environmentLength, &enumerationKey, &variable))
468  {
469  PH_STRINGREF part;
470  PH_STRINGREF remainingPart;
471 
472  if (!PhEqualStringRef(&variable.Name, &activeDevices, TRUE))
473  continue;
474 
475  remainingPart = variable.Value;
476 
477  while (remainingPart.Length != 0)
478  {
479  PhSplitStringRefAtChar(&remainingPart, ';', &part, &remainingPart);
480 
481  if (part.Length != 0)
482  {
483  HANDLE driverKeyHandle;
484  PPH_STRING driverKeyPath;
485 
486  driverKeyPath = PhConcatStringRef2(&currentControlSetEnum, &part);
487 
488  if (NT_SUCCESS(PhOpenKey(
489  &driverKeyHandle,
490  KEY_READ,
492  &driverKeyPath->sr,
493  0
494  )))
495  {
496  PPH_STRING deviceDesc;
497  PH_STRINGREF deviceName;
498  PPH_STRING hardwareId;
499 
500  if (deviceDesc = PhQueryRegistryString(driverKeyHandle, L"DeviceDesc"))
501  {
502  PH_STRINGREF firstPart;
503  PH_STRINGREF secondPart;
504 
505  if (PhSplitStringRefAtLastChar(&deviceDesc->sr, ';', &firstPart, &secondPart))
506  deviceName = secondPart;
507  else
508  deviceName = deviceDesc->sr;
509  }
510  else
511  {
512  PhInitializeStringRef(&deviceName, L"Unknown Device");
513  }
514 
515  hardwareId = PhQueryRegistryString(driverKeyHandle, L"HardwareID");
516 
517  PhAppendStringBuilder(Drivers, &StandardIndent);
518  PhAppendStringBuilder(Drivers, &deviceName);
519 
520  if (hardwareId)
521  {
522  PhTrimToNullTerminatorString(hardwareId);
523 
524  if (hardwareId->Length != 0)
525  {
526  PhAppendStringBuilder2(Drivers, L" (");
527  PhAppendStringBuilder(Drivers, &hardwareId->sr);
528  PhAppendCharStringBuilder(Drivers, ')');
529  }
530  }
531 
532  PhAppendCharStringBuilder(Drivers, '\n');
533 
534  PhClearReference(&hardwareId);
535  PhClearReference(&deviceDesc);
536  NtClose(driverKeyHandle);
537  }
538 
539  PhDereferenceObject(driverKeyPath);
540  }
541  }
542  }
543 
544  PhFreePage(environment);
545  }
546 
547  NtClose(processHandle);
548 }
549 
551  _In_ PPH_PROCESS_ITEM Process,
552  _Inout_ PPH_STRING_BUILDER Tasks
553  )
554 {
555  static CLSID CLSID_TaskScheduler_I = { 0x0f87369f, 0xa4e5, 0x4cfc, { 0xbd, 0x3e, 0x73, 0xe6, 0x15, 0x45, 0x72, 0xdd } };
556  static IID IID_ITaskService_I = { 0x2faba4c7, 0x4da9, 0x4013, { 0x96, 0x97, 0x20, 0xcc, 0x3f, 0xd4, 0x0f, 0x85 } };
557 
558  ITaskService *taskService;
559 
560  if (SUCCEEDED(CoCreateInstance(
561  &CLSID_TaskScheduler_I,
562  NULL,
563  CLSCTX_INPROC_SERVER,
564  &IID_ITaskService_I,
565  &taskService
566  )))
567  {
568  VARIANT empty = { 0 };
569 
570  if (SUCCEEDED(ITaskService_Connect(taskService, empty, empty, empty, empty)))
571  {
572  IRunningTaskCollection *runningTasks;
573 
574  if (SUCCEEDED(ITaskService_GetRunningTasks(
575  taskService,
576  TASK_ENUM_HIDDEN,
577  &runningTasks
578  )))
579  {
580  LONG count;
581  LONG i;
582  VARIANT index;
583 
584  index.vt = VT_INT;
585 
586  if (SUCCEEDED(IRunningTaskCollection_get_Count(runningTasks, &count)))
587  {
588  for (i = 1; i <= count; i++) // collections are 1-based
589  {
590  IRunningTask *runningTask;
591 
592  index.lVal = i;
593 
594  if (SUCCEEDED(IRunningTaskCollection_get_Item(runningTasks, index, &runningTask)))
595  {
596  ULONG pid;
597  BSTR action = NULL;
598  BSTR path = NULL;
599 
600  if (
601  SUCCEEDED(IRunningTask_get_EnginePID(runningTask, &pid)) &&
602  pid == (ULONG)Process->ProcessId
603  )
604  {
605  IRunningTask_get_CurrentAction(runningTask, &action);
606  IRunningTask_get_Path(runningTask, &path);
607 
608  PhAppendStringBuilder(Tasks, &StandardIndent);
609  PhAppendStringBuilder2(Tasks, action ? action : L"Unknown Action");
610  PhAppendStringBuilder2(Tasks, L" (");
611  PhAppendStringBuilder2(Tasks, path ? path : L"Unknown Path");
612  PhAppendStringBuilder2(Tasks, L")\n");
613 
614  if (action)
615  SysFreeString(action);
616  if (path)
617  SysFreeString(path);
618  }
619 
620  IRunningTask_Release(runningTask);
621  }
622  }
623  }
624 
625  IRunningTaskCollection_Release(runningTasks);
626  }
627  }
628 
629  ITaskService_Release(taskService);
630  }
631 }
632 
634  _In_ PPH_SERVICE_ITEM Service
635  )
636 {
637  PH_STRING_BUILDER stringBuilder;
638  SC_HANDLE serviceHandle;
639 
640  PhInitializeStringBuilder(&stringBuilder, 200);
641 
642  if (serviceHandle = PhOpenService(Service->Name->Buffer, SERVICE_QUERY_CONFIG))
643  {
644  PPH_STRING fileName;
645  PPH_STRING description;
646 
647  // File information
648 
649  if (fileName = PhGetServiceRelevantFileName(&Service->Name->sr, serviceHandle))
650  {
651  PH_IMAGE_VERSION_INFO versionInfo;
652  PPH_STRING versionInfoText;
653 
655  &versionInfo,
656  fileName->Buffer
657  ))
658  {
659  versionInfoText = PhFormatImageVersionInfo(
660  fileName,
661  &versionInfo,
662  &StandardIndent,
663  0
664  );
665 
666  if (!PhIsNullOrEmptyString(versionInfoText))
667  {
668  PhAppendStringBuilder2(&stringBuilder, L"File:\n");
669  PhAppendStringBuilder(&stringBuilder, &versionInfoText->sr);
670  PhAppendCharStringBuilder(&stringBuilder, '\n');
671  }
672 
673  PhClearReference(&versionInfoText);
674  PhDeleteImageVersionInfo(&versionInfo);
675  }
676 
677  PhDereferenceObject(fileName);
678  }
679 
680  // Description
681 
682  if (description = PhGetServiceDescription(serviceHandle))
683  {
684  PhAppendStringBuilder2(&stringBuilder, L"Description:\n ");
685  PhAppendStringBuilder(&stringBuilder, &description->sr);
686  PhAppendCharStringBuilder(&stringBuilder, '\n');
687  PhDereferenceObject(description);
688  }
689 
690  CloseServiceHandle(serviceHandle);
691  }
692 
693  // Remove the trailing newline.
694  if (stringBuilder.String->Length != 0)
695  PhRemoveEndStringBuilder(&stringBuilder, 1);
696 
697  return PhFinalStringBuilderString(&stringBuilder);
698 }