Process Hacker
hidnproc.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * hidden processes detection
4  *
5  * Copyright (C) 2010-2013 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 /*
24  * There are two methods of hidden process detection implemented in this module.
25  *
26  * Brute Force. This attempts to open all possible PIDs within a certain range
27  * in order to find processes which have been unlinked from the active process
28  * list (EPROCESS.ActiveProcessLinks). This method is not effective when
29  * either NtOpenProcess is hooked or PsLookupProcessByProcessId is hooked
30  * (KProcessHacker cannot bypass this).
31  *
32  * CSR Handles. This enumerates handles in all running CSR processes, and works
33  * even when a process has been unlinked from the active process list and
34  * has been removed from the client ID table (PspCidTable). However, the method
35  * does not detect native executables since CSR is not notified about them.
36  * Some rootkits hook NtQuerySystemInformation in order to modify the returned
37  * handle information; Process Hacker bypasses this by using KProcessHacker,
38  * which calls ExEnumHandleTable directly. Note that both process and thread
39  * handles are examined.
40  */
41 
42 #include <phapp.h>
43 #include <kphuser.h>
44 #include <settings.h>
45 #include <hidnproc.h>
46 #include <windowsx.h>
47 
48 INT_PTR CALLBACK PhpHiddenProcessesDlgProc(
49  _In_ HWND hwndDlg,
50  _In_ UINT uMsg,
51  _In_ WPARAM wParam,
52  _In_ LPARAM lParam
53  );
54 
55 COLORREF NTAPI PhpHiddenProcessesColorFunction(
56  _In_ INT Index,
57  _In_ PVOID Param,
58  _In_opt_ PVOID Context
59  );
60 
61 BOOLEAN NTAPI PhpHiddenProcessesCallback(
62  _In_ PPH_HIDDEN_PROCESS_ENTRY Process,
63  _In_opt_ PVOID Context
64  );
65 
67  _In_ PPH_HIDDEN_PROCESS_ENTRY Entry
68  );
69 
72 static PH_LAYOUT_MANAGER WindowLayoutManager;
73 static RECT MinimumSize;
74 
75 static PH_HIDDEN_PROCESS_METHOD ProcessesMethod;
76 static PPH_LIST ProcessesList = NULL;
77 static ULONG NumberOfHiddenProcesses;
78 static ULONG NumberOfTerminatedProcesses;
79 
81  VOID
82  )
83 {
84  if (!KphIsConnected())
85  {
88  L"Hidden process detection cannot function properly without KProcessHacker. "
89  L"Make sure Process Hacker is running with administrative privileges."
90  );
91  }
92 
94  {
95  PhHiddenProcessesWindowHandle = CreateDialog(
97  MAKEINTRESOURCE(IDD_HIDDENPROCESSES),
100  );
101  }
102 
103  if (!IsWindowVisible(PhHiddenProcessesWindowHandle))
104  ShowWindow(PhHiddenProcessesWindowHandle, SW_SHOW);
105  else
106  SetForegroundWindow(PhHiddenProcessesWindowHandle);
107 }
108 
109 static INT_PTR CALLBACK PhpHiddenProcessesDlgProc(
110  _In_ HWND hwndDlg,
111  _In_ UINT uMsg,
112  _In_ WPARAM wParam,
113  _In_ LPARAM lParam
114  )
115 {
116  switch (uMsg)
117  {
118  case WM_INITDIALOG:
119  {
120  HWND lvHandle;
121 
122  PhCenterWindow(hwndDlg, GetParent(hwndDlg));
123  PhHiddenProcessesListViewHandle = lvHandle = GetDlgItem(hwndDlg, IDC_PROCESSES);
124 
125  PhInitializeLayoutManager(&WindowLayoutManager, hwndDlg);
126  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_INTRO),
128  PhAddLayoutItem(&WindowLayoutManager, lvHandle,
129  NULL, PH_ANCHOR_ALL);
130  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_DESCRIPTION),
132  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_METHOD),
134  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_TERMINATE),
136  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SAVE),
138  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDC_SCAN),
140  PhAddLayoutItem(&WindowLayoutManager, GetDlgItem(hwndDlg, IDOK),
142 
143  MinimumSize.left = 0;
144  MinimumSize.top = 0;
145  MinimumSize.right = 330;
146  MinimumSize.bottom = 140;
147  MapDialogRect(hwndDlg, &MinimumSize);
148 
149  PhRegisterDialog(hwndDlg);
150 
151  PhLoadWindowPlacementFromSetting(L"HiddenProcessesWindowPosition", L"HiddenProcessesWindowSize", hwndDlg);
152 
153  PhSetListViewStyle(lvHandle, TRUE, TRUE);
154  PhSetControlTheme(lvHandle, L"explorer");
155  PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 320, L"Process");
156  PhAddListViewColumn(lvHandle, 1, 1, 1, LVCFMT_LEFT, 60, L"PID");
157 
158  PhSetExtendedListView(lvHandle);
159  PhLoadListViewColumnsFromSetting(L"HiddenProcessesListViewColumns", lvHandle);
163 
164  ComboBox_AddString(GetDlgItem(hwndDlg, IDC_METHOD), L"Brute Force");
165  ComboBox_AddString(GetDlgItem(hwndDlg, IDC_METHOD), L"CSR Handles");
166  PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_METHOD), L"CSR Handles", FALSE);
167 
168  EnableWindow(GetDlgItem(hwndDlg, IDC_TERMINATE), FALSE);
169  }
170  break;
171  case WM_DESTROY:
172  {
173  PhSaveWindowPlacementToSetting(L"HiddenProcessesWindowPosition", L"HiddenProcessesWindowSize", hwndDlg);
174  PhSaveListViewColumnsToSetting(L"HiddenProcessesListViewColumns", PhHiddenProcessesListViewHandle);
175  }
176  break;
177  case WM_CLOSE:
178  {
179  // Hide, don't close.
180  ShowWindow(hwndDlg, SW_HIDE);
181  SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, 0);
182  }
183  return TRUE;
184  case WM_COMMAND:
185  {
186  switch (LOWORD(wParam))
187  {
188  case IDCANCEL:
189  case IDOK:
190  {
191  SendMessage(hwndDlg, WM_CLOSE, 0, 0);
192  }
193  break;
194  case IDC_SCAN:
195  {
196  NTSTATUS status;
197  PPH_STRING method;
198 
199  method = PhGetWindowText(GetDlgItem(hwndDlg, IDC_METHOD));
200  PhAutoDereferenceObject(method);
201 
202  if (ProcessesList)
203  {
204  ULONG i;
205 
206  for (i = 0; i < ProcessesList->Count; i++)
207  {
208  PPH_HIDDEN_PROCESS_ENTRY entry = ProcessesList->Items[i];
209 
210  if (entry->FileName)
212 
213  PhFree(entry);
214  }
215 
216  PhDereferenceObject(ProcessesList);
217  }
218 
219  ListView_DeleteAllItems(PhHiddenProcessesListViewHandle);
220 
221  ProcessesList = PhCreateList(40);
222 
223  ProcessesMethod =
224  PhEqualString2(method, L"Brute Force", TRUE) ?
227  NumberOfHiddenProcesses = 0;
228  NumberOfTerminatedProcesses = 0;
229 
231  status = PhEnumHiddenProcesses(
232  ProcessesMethod,
234  NULL
235  );
238 
239  if (NT_SUCCESS(status))
240  {
241  SetDlgItemText(hwndDlg, IDC_DESCRIPTION,
242  PhaFormatString(L"%u hidden process(es), %u terminated process(es).",
243  NumberOfHiddenProcesses, NumberOfTerminatedProcesses)->Buffer
244  );
245  InvalidateRect(GetDlgItem(hwndDlg, IDC_DESCRIPTION), NULL, TRUE);
246  }
247  else
248  {
249  PhShowStatus(hwndDlg, L"Unable to perform the scan", status, 0);
250  }
251  }
252  break;
253  case IDC_TERMINATE:
254  {
255  PPH_HIDDEN_PROCESS_ENTRY *entries;
256  ULONG numberOfEntries;
257  ULONG i;
258 
260 
261  if (numberOfEntries != 0)
262  {
263  if (!PhGetIntegerSetting(L"EnableWarnings") ||
265  hwndDlg,
266  L"terminate",
267  L"the selected process(es)",
268  L"Terminating a hidden process may cause the system to become unstable "
269  L"or crash.",
270  TRUE
271  ))
272  {
273  NTSTATUS status;
274  HANDLE processHandle;
275  BOOLEAN refresh;
276 
277  refresh = FALSE;
278 
279  for (i = 0; i < numberOfEntries; i++)
280  {
281  if (ProcessesMethod == BruteForceScanMethod)
282  {
283  status = PhOpenProcess(
284  &processHandle,
286  entries[i]->ProcessId
287  );
288  }
289  else
290  {
291  status = PhOpenProcessByCsrHandles(
292  &processHandle,
294  entries[i]->ProcessId
295  );
296  }
297 
298  if (NT_SUCCESS(status))
299  {
300  status = PhTerminateProcess(processHandle, STATUS_SUCCESS);
301  NtClose(processHandle);
302 
303  if (NT_SUCCESS(status))
304  refresh = TRUE;
305  }
306  else
307  {
308  PhShowStatus(hwndDlg, L"Unable to terminate the process", status, 0);
309  }
310  }
311 
312  if (refresh)
313  {
314  LARGE_INTEGER interval;
315 
316  // Sleep for a bit before continuing. It seems to help avoid
317  // BSODs.
318  interval.QuadPart = -250 * PH_TIMEOUT_MS;
319  NtDelayExecution(FALSE, &interval);
320  SendMessage(hwndDlg, WM_COMMAND, IDC_SCAN, 0);
321  }
322  }
323  }
324 
325  PhFree(entries);
326  }
327  break;
328  case IDC_SAVE:
329  {
330  static PH_FILETYPE_FILTER filters[] =
331  {
332  { L"Text files (*.txt)", L"*.txt" },
333  { L"All files (*.*)", L"*.*" }
334  };
335  PVOID fileDialog;
336 
337  fileDialog = PhCreateSaveFileDialog();
338 
339  PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER));
340  PhSetFileDialogFileName(fileDialog, L"Hidden Processes.txt");
341 
342  if (PhShowFileDialog(hwndDlg, fileDialog))
343  {
344  NTSTATUS status;
345  PPH_STRING fileName;
346  PPH_FILE_STREAM fileStream;
347 
348  fileName = PhGetFileDialogFileName(fileDialog);
349  PhAutoDereferenceObject(fileName);
350 
351  if (NT_SUCCESS(status = PhCreateFileStream(
352  &fileStream,
353  fileName->Buffer,
354  FILE_GENERIC_WRITE,
355  FILE_SHARE_READ,
357  0
358  )))
359  {
361  PhWritePhTextHeader(fileStream);
362  PhWriteStringAsUtf8FileStream2(fileStream, L"Method: ");
364  ProcessesMethod == BruteForceScanMethod ? L"Brute Force\r\n" : L"CSR Handles\r\n");
366  fileStream,
367  L"Hidden: %u\r\nTerminated: %u\r\n\r\n",
368  NumberOfHiddenProcesses,
369  NumberOfTerminatedProcesses
370  );
371 
372  if (ProcessesList)
373  {
374  ULONG i;
375 
376  for (i = 0; i < ProcessesList->Count; i++)
377  {
378  PPH_HIDDEN_PROCESS_ENTRY entry = ProcessesList->Items[i];
379 
380  if (entry->Type == HiddenProcess)
381  PhWriteStringAsUtf8FileStream2(fileStream, L"[HIDDEN] ");
382  else if (entry->Type == TerminatedProcess)
383  PhWriteStringAsUtf8FileStream2(fileStream, L"[Terminated] ");
384  else if (entry->Type != NormalProcess)
385  continue;
386 
388  fileStream,
389  L"%s (%u)\r\n",
390  entry->FileName->Buffer,
391  (ULONG)entry->ProcessId
392  );
393  }
394  }
395 
396  PhDereferenceObject(fileStream);
397  }
398 
399  if (!NT_SUCCESS(status))
400  PhShowStatus(hwndDlg, L"Unable to create the file", status, 0);
401  }
402 
403  PhFreeFileDialog(fileDialog);
404  }
405  break;
406  }
407  }
408  break;
409  case WM_NOTIFY:
410  {
411  LPNMHDR header = (LPNMHDR)lParam;
412 
414 
415  switch (header->code)
416  {
417  case LVN_ITEMCHANGED:
418  {
419  if (header->hwndFrom == PhHiddenProcessesListViewHandle)
420  {
421  EnableWindow(
422  GetDlgItem(hwndDlg, IDC_TERMINATE),
423  ListView_GetSelectedCount(PhHiddenProcessesListViewHandle) > 0
424  );
425  }
426  }
427  break;
428  case NM_DBLCLK:
429  {
430  if (header->hwndFrom == PhHiddenProcessesListViewHandle)
431  {
433 
435 
436  if (entry)
437  {
438  PPH_PROCESS_ITEM processItem;
439 
440  if (processItem = PhpCreateProcessItemForHiddenProcess(entry))
441  {
443  PhDereferenceObject(processItem);
444  }
445  else
446  {
447  PhShowError(hwndDlg, L"Unable to create a process structure for the selected process.");
448  }
449  }
450  }
451  }
452  break;
453  }
454  }
455  break;
456  case WM_SIZE:
457  {
458  PhLayoutManagerLayout(&WindowLayoutManager);
459  }
460  break;
461  case WM_SIZING:
462  {
463  PhResizingMinimumSize((PRECT)lParam, wParam, MinimumSize.right, MinimumSize.bottom);
464  }
465  break;
466  case WM_CTLCOLORSTATIC:
467  {
468  if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_DESCRIPTION))
469  {
470  if (NumberOfHiddenProcesses != 0)
471  {
472  SetTextColor((HDC)wParam, RGB(0xff, 0x00, 0x00));
473  }
474 
475  SetBkColor((HDC)wParam, GetSysColor(COLOR_3DFACE));
476 
477  return (INT_PTR)GetSysColorBrush(COLOR_3DFACE);
478  }
479  }
480  break;
481  }
482 
483  REFLECT_MESSAGE_DLG(hwndDlg, PhHiddenProcessesListViewHandle, uMsg, wParam, lParam);
484 
485  return FALSE;
486 }
487 
488 static COLORREF NTAPI PhpHiddenProcessesColorFunction(
489  _In_ INT Index,
490  _In_ PVOID Param,
491  _In_opt_ PVOID Context
492  )
493 {
494  PPH_HIDDEN_PROCESS_ENTRY entry = Param;
495 
496  switch (entry->Type)
497  {
498  case UnknownProcess:
499  case HiddenProcess:
500  return RGB(0xff, 0x00, 0x00);
501  case TerminatedProcess:
502  return RGB(0x77, 0x77, 0x77);
503  }
504 
505  return GetSysColor(COLOR_WINDOW);
506 }
507 
508 static BOOLEAN NTAPI PhpHiddenProcessesCallback(
509  _In_ PPH_HIDDEN_PROCESS_ENTRY Process,
510  _In_opt_ PVOID Context
511  )
512 {
514  INT lvItemIndex;
515  WCHAR pidString[PH_INT32_STR_LEN_1];
516 
517  entry = PhAllocateCopy(Process, sizeof(PH_HIDDEN_PROCESS_ENTRY));
518 
519  if (entry->FileName)
520  PhReferenceObject(entry->FileName);
521 
522  PhAddItemList(ProcessesList, entry);
523 
525  PhGetStringOrDefault(entry->FileName, L"(unknown)"), entry);
526  PhPrintUInt32(pidString, (ULONG)entry->ProcessId);
527  PhSetListViewSubItem(PhHiddenProcessesListViewHandle, lvItemIndex, 1, pidString);
528 
529  if (entry->Type == HiddenProcess)
530  NumberOfHiddenProcesses++;
531  else if (entry->Type == TerminatedProcess)
532  NumberOfTerminatedProcesses++;
533 
534  return TRUE;
535 }
536 
538  _In_ PPH_HIDDEN_PROCESS_ENTRY Entry
539  )
540 {
541  NTSTATUS status;
542  PPH_PROCESS_ITEM processItem;
543  PPH_PROCESS_ITEM idleProcessItem;
544  HANDLE processHandle;
545  PROCESS_BASIC_INFORMATION basicInfo;
546  KERNEL_USER_TIMES times;
547  PROCESS_PRIORITY_CLASS priorityClass;
548  ULONG handleCount;
549  HANDLE processHandle2;
550 
551  if (Entry->Type == NormalProcess)
552  {
553  processItem = PhReferenceProcessItem(Entry->ProcessId);
554 
555  if (processItem)
556  return processItem;
557  }
558 
559  processItem = PhCreateProcessItem(Entry->ProcessId);
560 
561  // Mark the process as terminated if necessary.
562  if (Entry->Type == TerminatedProcess)
563  processItem->State |= PH_PROCESS_ITEM_REMOVED;
564 
565  // We need a process record. Just use the record of System Idle Process.
566  if (idleProcessItem = PhReferenceProcessItem(SYSTEM_IDLE_PROCESS_ID))
567  {
568  processItem->Record = idleProcessItem->Record;
569  PhReferenceProcessRecord(processItem->Record);
570  }
571  else
572  {
573  PhDereferenceObject(processItem);
574  return NULL;
575  }
576 
577  // Set up the file name and process name.
578 
579  PhSwapReference(&processItem->FileName, Entry->FileName);
580 
581  if (processItem->FileName)
582  {
583  processItem->ProcessName = PhGetBaseName(processItem->FileName);
584  }
585  else
586  {
587  processItem->ProcessName = PhCreateString(L"Unknown");
588  }
589 
590  if (ProcessesMethod == BruteForceScanMethod)
591  {
592  status = PhOpenProcess(
593  &processHandle,
595  Entry->ProcessId
596  );
597  }
598  else
599  {
600  status = PhOpenProcessByCsrHandles(
601  &processHandle,
603  Entry->ProcessId
604  );
605  }
606 
607  if (NT_SUCCESS(status))
608  {
609  // Basic information and not-so-dynamic information
610 
611  processItem->QueryHandle = processHandle;
612 
613  if (NT_SUCCESS(PhGetProcessBasicInformation(processHandle, &basicInfo)))
614  {
615  processItem->ParentProcessId = basicInfo.InheritedFromUniqueProcessId;
616  processItem->BasePriority = basicInfo.BasePriority;
617  }
618 
619  PhGetProcessSessionId(processHandle, &processItem->SessionId);
620 
621  PhPrintUInt32(processItem->ParentProcessIdString, (ULONG)processItem->ParentProcessId);
622  PhPrintUInt32(processItem->SessionIdString, processItem->SessionId);
623 
624  if (NT_SUCCESS(PhGetProcessTimes(processHandle, &times)))
625  {
626  processItem->CreateTime = times.CreateTime;
627  processItem->KernelTime = times.KernelTime;
628  processItem->UserTime = times.UserTime;
629  }
630 
631  // TODO: Token information?
632 
633  if (NT_SUCCESS(NtQueryInformationProcess(
634  processHandle,
635  ProcessPriorityClass,
636  &priorityClass,
637  sizeof(PROCESS_PRIORITY_CLASS),
638  NULL
639  )))
640  {
641  processItem->PriorityClass = priorityClass.PriorityClass;
642  }
643 
644  if (NT_SUCCESS(NtQueryInformationProcess(
645  processHandle,
646  ProcessHandleCount,
647  &handleCount,
648  sizeof(ULONG),
649  NULL
650  )))
651  {
652  processItem->NumberOfHandles = handleCount;
653  }
654  }
655 
656  // Stage 1
657  // Some copy and paste magic here...
658 
659  if (processItem->FileName)
660  {
661  // Small icon, large icon.
662  ExtractIconEx(
663  processItem->FileName->Buffer,
664  0,
665  &processItem->LargeIcon,
666  &processItem->SmallIcon,
667  1
668  );
669 
670  // Version info.
671  PhInitializeImageVersionInfo(&processItem->VersionInfo, processItem->FileName->Buffer);
672  }
673 
674  // Use the default EXE icon if we didn't get the file's icon.
675  {
676  if (!processItem->SmallIcon || !processItem->LargeIcon)
677  {
678  if (processItem->SmallIcon)
679  {
680  DestroyIcon(processItem->SmallIcon);
681  processItem->SmallIcon = NULL;
682  }
683  else if (processItem->LargeIcon)
684  {
685  DestroyIcon(processItem->LargeIcon);
686  processItem->LargeIcon = NULL;
687  }
688 
689  PhGetStockApplicationIcon(&processItem->SmallIcon, &processItem->LargeIcon);
690  processItem->SmallIcon = DuplicateIcon(NULL, processItem->SmallIcon);
691  processItem->LargeIcon = DuplicateIcon(NULL, processItem->LargeIcon);
692  }
693  }
694 
695  // POSIX, command line
696 
697  status = PhOpenProcess(
698  &processHandle2,
700  Entry->ProcessId
701  );
702 
703  if (NT_SUCCESS(status))
704  {
705  BOOLEAN isPosix = FALSE;
706  PPH_STRING commandLine;
707  ULONG i;
708 
709  status = PhGetProcessIsPosix(processHandle2, &isPosix);
710  processItem->IsPosix = isPosix;
711 
712  if (!NT_SUCCESS(status) || !isPosix)
713  {
714  status = PhGetProcessCommandLine(processHandle2, &commandLine);
715 
716  if (NT_SUCCESS(status))
717  {
718  // Some command lines (e.g. from taskeng.exe) have nulls in them.
719  // Since Windows can't display them, we'll replace them with
720  // spaces.
721  for (i = 0; i < (ULONG)commandLine->Length / 2; i++)
722  {
723  if (commandLine->Buffer[i] == 0)
724  commandLine->Buffer[i] = ' ';
725  }
726  }
727  }
728  else
729  {
730  // Get the POSIX command line.
731  status = PhGetProcessPosixCommandLine(processHandle2, &commandLine);
732  }
733 
734  if (NT_SUCCESS(status))
735  {
736  processItem->CommandLine = commandLine;
737  }
738 
739  NtClose(processHandle2);
740  }
741 
742  // TODO: Other stage 1 tasks.
743 
744  PhSetEvent(&processItem->Stage1Event);
745 
746  return processItem;
747 }
748 
751  _In_opt_ PVOID Context
752  )
753 {
754  NTSTATUS status;
755  PVOID processes;
757  PPH_LIST pids;
758  ULONG pid;
759  BOOLEAN stop = FALSE;
760 
761  if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
762  return status;
763 
764  pids = PhCreateList(40);
765 
766  process = PH_FIRST_PROCESS(processes);
767 
768  do
769  {
770  PhAddItemList(pids, process->UniqueProcessId);
771  } while (process = PH_NEXT_PROCESS(process));
772 
773  PhFree(processes);
774 
775  for (pid = 8; pid <= 65536; pid += 4)
776  {
777  NTSTATUS status2;
778  HANDLE processHandle;
780  KERNEL_USER_TIMES times;
781  PPH_STRING fileName;
782 
783  status2 = PhOpenProcess(
784  &processHandle,
786  (HANDLE)pid
787  );
788 
789  if (NT_SUCCESS(status2))
790  {
791  entry.ProcessId = (HANDLE)pid;
792 
793  if (NT_SUCCESS(status2 = PhGetProcessTimes(
794  processHandle,
795  &times
796  )) &&
798  processHandle,
799  &fileName
800  )))
801  {
802  entry.FileName = PhGetFileName(fileName);
803  PhDereferenceObject(fileName);
804 
805  if (times.ExitTime.QuadPart != 0)
806  entry.Type = TerminatedProcess;
807  else if (PhFindItemList(pids, (HANDLE)pid) != -1)
808  entry.Type = NormalProcess;
809  else
810  entry.Type = HiddenProcess;
811 
812  if (!Callback(&entry, Context))
813  stop = TRUE;
814 
816  }
817 
818  NtClose(processHandle);
819  }
820 
821  // Use an alternative method if we don't have sufficient access.
822  if (status2 == STATUS_ACCESS_DENIED && WindowsVersion >= WINDOWS_VISTA)
823  {
824  if (NT_SUCCESS(status2 = PhGetProcessImageFileNameByProcessId((HANDLE)pid, &fileName)))
825  {
826  entry.ProcessId = (HANDLE)pid;
827  entry.FileName = PhGetFileName(fileName);
828  PhDereferenceObject(fileName);
829 
830  if (PhFindItemList(pids, (HANDLE)pid) != -1)
831  entry.Type = NormalProcess;
832  else
833  entry.Type = HiddenProcess;
834 
835  if (!Callback(&entry, Context))
836  stop = TRUE;
837 
839  }
840  }
841 
842  if (status2 == STATUS_INVALID_CID || status2 == STATUS_INVALID_PARAMETER)
843  status2 = STATUS_SUCCESS;
844 
845  if (!NT_SUCCESS(status2))
846  {
847  entry.ProcessId = (HANDLE)pid;
848  entry.FileName = NULL;
849  entry.Type = UnknownProcess;
850 
851  if (!Callback(&entry, Context))
852  stop = TRUE;
853  }
854 
855  if (stop)
856  break;
857  }
858 
859  PhDereferenceObject(pids);
860 
861  return status;
862 }
863 
864 typedef struct _CSR_HANDLES_CONTEXT
865 {
867  PVOID Context;
868  PPH_LIST Pids;
870 
871 static BOOLEAN NTAPI PhpCsrProcessHandlesCallback(
872  _In_ PPH_CSR_HANDLE_INFO Handle,
873  _In_opt_ PVOID Context
874  )
875 {
876  NTSTATUS status;
877  BOOLEAN cont = TRUE;
878  PCSR_HANDLES_CONTEXT context = Context;
879  HANDLE processHandle;
880  KERNEL_USER_TIMES times;
881  PPH_STRING fileName;
883 
884  entry.ProcessId = Handle->ProcessId;
885 
887  &processHandle,
889  Handle
890  )))
891  {
892  if (NT_SUCCESS(status = PhGetProcessTimes(
893  processHandle,
894  &times
895  )) &&
897  processHandle,
898  &fileName
899  )))
900  {
901  entry.FileName = PhGetFileName(fileName);
902  PhDereferenceObject(fileName);
903 
904  if (times.ExitTime.QuadPart != 0)
905  entry.Type = TerminatedProcess;
906  else if (PhFindItemList(context->Pids, Handle->ProcessId) != -1)
907  entry.Type = NormalProcess;
908  else
909  entry.Type = HiddenProcess;
910 
911  if (!context->Callback(&entry, context->Context))
912  cont = FALSE;
913 
915  }
916 
917  NtClose(processHandle);
918  }
919 
920  if (!NT_SUCCESS(status))
921  {
922  entry.FileName = NULL;
923  entry.Type = UnknownProcess;
924 
925  if (!context->Callback(&entry, context->Context))
926  cont = FALSE;
927  }
928 
929  return cont;
930 }
931 
934  _In_opt_ PVOID Context
935  )
936 {
937  NTSTATUS status;
938  PVOID processes;
940  PPH_LIST pids;
941  CSR_HANDLES_CONTEXT context;
942 
943  if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
944  return status;
945 
946  pids = PhCreateList(40);
947 
948  process = PH_FIRST_PROCESS(processes);
949 
950  do
951  {
952  PhAddItemList(pids, process->UniqueProcessId);
953  } while (process = PH_NEXT_PROCESS(process));
954 
955  PhFree(processes);
956 
957  context.Callback = Callback;
958  context.Context = Context;
959  context.Pids = pids;
960 
961  status = PhEnumCsrProcessHandles(PhpCsrProcessHandlesCallback, &context);
962 
963  PhDereferenceObject(pids);
964 
965  return status;
966 }
967 
969  _In_ PH_HIDDEN_PROCESS_METHOD Method,
971  _In_opt_ PVOID Context
972  )
973 {
974  if (Method == BruteForceScanMethod)
975  {
977  Callback,
978  Context
979  );
980  }
981  else
982  {
984  Callback,
985  Context
986  );
987  }
988 }
989 
991  _Out_ PHANDLE *ProcessHandles,
992  _Out_ PULONG NumberOfProcessHandles
993  )
994 {
995  NTSTATUS status;
996  PVOID processes;
998  PPH_LIST processHandleList;
999 
1000  if (!NT_SUCCESS(status = PhEnumProcesses(&processes)))
1001  return status;
1002 
1003  processHandleList = PhCreateList(8);
1004 
1005  process = PH_FIRST_PROCESS(processes);
1006 
1007  do
1008  {
1009  HANDLE processHandle;
1010  PH_KNOWN_PROCESS_TYPE knownProcessType;
1011 
1013  &processHandle,
1014  ProcessQueryAccess | PROCESS_DUP_HANDLE,
1015  process->UniqueProcessId
1016  )))
1017  {
1019  processHandle,
1020  &knownProcessType
1021  )) &&
1022  (knownProcessType & KnownProcessTypeMask) == WindowsSubsystemProcessType)
1023  {
1024  PhAddItemList(processHandleList, processHandle);
1025  }
1026  else
1027  {
1028  NtClose(processHandle);
1029  }
1030  }
1031  } while (process = PH_NEXT_PROCESS(process));
1032 
1033  PhFree(processes);
1034 
1035  *ProcessHandles = PhAllocateCopy(processHandleList->Items, processHandleList->Count * sizeof(HANDLE));
1036  *NumberOfProcessHandles = processHandleList->Count;
1037 
1038  PhDereferenceObject(processHandleList);
1039 
1040  return status;
1041 }
1042 
1044  _Inout_ PPH_CSR_HANDLE_INFO Handle
1045  )
1046 {
1047  NTSTATUS status;
1048  PROCESS_BASIC_INFORMATION processBasicInfo;
1049  THREAD_BASIC_INFORMATION threadBasicInfo;
1050 
1051  Handle->IsThreadHandle = FALSE;
1052  Handle->ProcessId = NULL;
1053 
1054  // Assume the handle is a process handle, and get the
1055  // process ID.
1056 
1057  status = KphQueryInformationObject(
1058  Handle->CsrProcessHandle,
1059  Handle->Handle,
1061  &processBasicInfo,
1062  sizeof(PROCESS_BASIC_INFORMATION),
1063  NULL
1064  );
1065 
1066  if (NT_SUCCESS(status))
1067  {
1068  Handle->ProcessId = processBasicInfo.UniqueProcessId;
1069  }
1070  else
1071  {
1072  // We failed to get the process ID. Assume the handle
1073  // is a thread handle, and get the process ID.
1074 
1075  status = KphQueryInformationObject(
1076  Handle->CsrProcessHandle,
1077  Handle->Handle,
1079  &threadBasicInfo,
1080  sizeof(THREAD_BASIC_INFORMATION),
1081  NULL
1082  );
1083 
1084  if (NT_SUCCESS(status))
1085  {
1086  Handle->ProcessId = threadBasicInfo.ClientId.UniqueProcess;
1087  Handle->IsThreadHandle = TRUE;
1088  }
1089  }
1090 
1091  return status;
1092 }
1093 
1096  _In_opt_ PVOID Context
1097  )
1098 {
1099  NTSTATUS status;
1100  PHANDLE csrProcessHandles;
1101  ULONG numberOfCsrProcessHandles;
1102  ULONG i;
1103  BOOLEAN stop = FALSE;
1104  PPH_LIST pids;
1105 
1106  if (!NT_SUCCESS(status = PhpOpenCsrProcesses(
1107  &csrProcessHandles,
1108  &numberOfCsrProcessHandles
1109  )))
1110  return status;
1111 
1112  pids = PhCreateList(40);
1113 
1114  for (i = 0; i < numberOfCsrProcessHandles; i++)
1115  {
1117  ULONG j;
1118 
1119  if (stop)
1120  break;
1121 
1122  if (NT_SUCCESS(KphEnumerateProcessHandles2(csrProcessHandles[i], &handles)))
1123  {
1124  for (j = 0; j < handles->HandleCount; j++)
1125  {
1126  PH_CSR_HANDLE_INFO handle;
1127 
1128  handle.CsrProcessHandle = csrProcessHandles[i];
1129  handle.Handle = handles->Handles[j].Handle;
1130 
1131  // Get the process ID associated with the handle.
1132  // This call will fail if the handle is not a
1133  // process or thread handle.
1134  if (!NT_SUCCESS(PhpGetCsrHandleProcessId(&handle)))
1135  continue;
1136 
1137  // Avoid duplicate PIDs.
1138  if (PhFindItemList(pids, handle.ProcessId) != -1)
1139  continue;
1140 
1141  PhAddItemList(pids, handle.ProcessId);
1142 
1143  if (!Callback(&handle, Context))
1144  {
1145  stop = TRUE;
1146  break;
1147  }
1148  }
1149 
1150  PhFree(handles);
1151  }
1152  }
1153 
1154  PhDereferenceObject(pids);
1155 
1156  for (i = 0; i < numberOfCsrProcessHandles; i++)
1157  NtClose(csrProcessHandles[i]);
1158 
1159  PhFree(csrProcessHandles);
1160 
1161  return status;
1162 }
1163 
1165  _Out_ PHANDLE ProcessHandle,
1166  _In_ ACCESS_MASK DesiredAccess,
1167  _In_ PPH_CSR_HANDLE_INFO Handle
1168  )
1169 {
1170  NTSTATUS status;
1171 
1172  if (!Handle->IsThreadHandle)
1173  {
1174  status = PhDuplicateObject(
1175  Handle->CsrProcessHandle,
1176  Handle->Handle,
1177  NtCurrentProcess(),
1178  ProcessHandle,
1179  DesiredAccess,
1180  0,
1181  0
1182  );
1183  }
1184  else
1185  {
1186  HANDLE threadHandle;
1187 
1188  if (!NT_SUCCESS(status = PhDuplicateObject(
1189  Handle->CsrProcessHandle,
1190  Handle->Handle,
1191  NtCurrentProcess(),
1192  &threadHandle,
1194  0,
1195  0
1196  )))
1197  return status;
1198 
1199  status = KphOpenThreadProcess(
1200  threadHandle,
1201  DesiredAccess,
1202  ProcessHandle
1203  );
1204  NtClose(threadHandle);
1205  }
1206 
1207  return status;
1208 }
1209 
1210 typedef struct _OPEN_PROCESS_BY_CSR_CONTEXT
1211 {
1212  NTSTATUS Status;
1213  PHANDLE ProcessHandle;
1214  ACCESS_MASK DesiredAccess;
1215  HANDLE ProcessId;
1217 
1218 static BOOLEAN NTAPI PhpOpenProcessByCsrHandlesCallback(
1219  _In_ PPH_CSR_HANDLE_INFO Handle,
1220  _In_opt_ PVOID Context
1221  )
1222 {
1223  POPEN_PROCESS_BY_CSR_CONTEXT context = Context;
1224 
1225  if (Handle->ProcessId == context->ProcessId)
1226  {
1227  context->Status = PhOpenProcessByCsrHandle(
1228  context->ProcessHandle,
1229  context->DesiredAccess,
1230  Handle
1231  );
1232 
1233  return FALSE;
1234  }
1235 
1236  return TRUE;
1237 }
1238 
1240  _Out_ PHANDLE ProcessHandle,
1241  _In_ ACCESS_MASK DesiredAccess,
1242  _In_ HANDLE ProcessId
1243  )
1244 {
1245  NTSTATUS status;
1247 
1248  context.Status = STATUS_INVALID_CID;
1249  context.ProcessHandle = ProcessHandle;
1250  context.DesiredAccess = DesiredAccess;
1251  context.ProcessId = ProcessId;
1252 
1253  if (!NT_SUCCESS(status = PhEnumCsrProcessHandles(
1254  PhpOpenProcessByCsrHandlesCallback,
1255  &context
1256  )))
1257  return status;
1258 
1259  return context.Status;
1260 }