Process Hacker
hndlprv.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * handle provider
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 <kphuser.h>
25 #include <extmgri.h>
26 
27 typedef struct _PHP_CREATE_HANDLE_ITEM_CONTEXT
28 {
29  PPH_HANDLE_PROVIDER Provider;
32 
34  _In_ PVOID Object,
35  _In_ ULONG Flags
36  );
37 
39  _In_ PVOID Object,
40  _In_ ULONG Flags
41  );
42 
45 
47  VOID
48  )
49 {
50  PhHandleProviderType = PhCreateObjectType(L"HandleProvider", 0, PhpHandleProviderDeleteProcedure);
51  PhHandleItemType = PhCreateObjectType(L"HandleItem", 0, PhpHandleItemDeleteProcedure);
52 
53  return TRUE;
54 }
55 
57  _In_ HANDLE ProcessId
58  )
59 {
60  PPH_HANDLE_PROVIDER handleProvider;
61 
62  handleProvider = PhCreateObject(
64  PhHandleProviderType
65  );
66 
67  handleProvider->HandleHashSetSize = 128;
68  handleProvider->HandleHashSet = PhCreateHashSet(handleProvider->HandleHashSetSize);
69  handleProvider->HandleHashSetCount = 0;
70  PhInitializeQueuedLock(&handleProvider->HandleHashSetLock);
71 
72  PhInitializeCallback(&handleProvider->HandleAddedEvent);
73  PhInitializeCallback(&handleProvider->HandleModifiedEvent);
74  PhInitializeCallback(&handleProvider->HandleRemovedEvent);
75  PhInitializeCallback(&handleProvider->UpdatedEvent);
76 
77  handleProvider->ProcessId = ProcessId;
78  handleProvider->ProcessHandle = NULL;
79 
80  handleProvider->RunStatus = PhOpenProcess(
81  &handleProvider->ProcessHandle,
82  PROCESS_DUP_HANDLE,
83  ProcessId
84  );
85 
86  handleProvider->TempListHashtable = PhCreateSimpleHashtable(20);
87 
89 
90  return handleProvider;
91 }
92 
94  _In_ PVOID Object,
95  _In_ ULONG Flags
96  )
97 {
98  PPH_HANDLE_PROVIDER handleProvider = (PPH_HANDLE_PROVIDER)Object;
99 
101 
102  // Dereference all handle items (we referenced them
103  // when we added them to the hashtable).
104  PhDereferenceAllHandleItems(handleProvider);
105 
106  PhFree(handleProvider->HandleHashSet);
107  PhDeleteCallback(&handleProvider->HandleAddedEvent);
108  PhDeleteCallback(&handleProvider->HandleModifiedEvent);
109  PhDeleteCallback(&handleProvider->HandleRemovedEvent);
110 
111  if (handleProvider->ProcessHandle) NtClose(handleProvider->ProcessHandle);
112 
113  PhDereferenceObject(handleProvider->TempListHashtable);
114 }
115 
117  _In_opt_ PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handle
118  )
119 {
120  PPH_HANDLE_ITEM handleItem;
121 
122  handleItem = PhCreateObject(
124  PhHandleItemType
125  );
126  memset(handleItem, 0, sizeof(PH_HANDLE_ITEM));
127 
128  if (Handle)
129  {
130  handleItem->Handle = (HANDLE)Handle->HandleValue;
131  PhPrintPointer(handleItem->HandleString, (PVOID)handleItem->Handle);
132  handleItem->Object = Handle->Object;
133  PhPrintPointer(handleItem->ObjectString, handleItem->Object);
134  handleItem->Attributes = Handle->HandleAttributes;
135  handleItem->GrantedAccess = (ACCESS_MASK)Handle->GrantedAccess;
136  PhPrintPointer(handleItem->GrantedAccessString, (PVOID)handleItem->GrantedAccess);
137  }
138 
140 
141  return handleItem;
142 }
143 
145  _In_ PVOID Object,
146  _In_ ULONG Flags
147  )
148 {
149  PPH_HANDLE_ITEM handleItem = (PPH_HANDLE_ITEM)Object;
150 
152 
153  if (handleItem->TypeName) PhDereferenceObject(handleItem->TypeName);
154  if (handleItem->ObjectName) PhDereferenceObject(handleItem->ObjectName);
155  if (handleItem->BestObjectName) PhDereferenceObject(handleItem->BestObjectName);
156 }
157 
158 FORCEINLINE BOOLEAN PhCompareHandleItem(
159  _In_ PPH_HANDLE_ITEM Value1,
160  _In_ PPH_HANDLE_ITEM Value2
161  )
162 {
163  return Value1->Handle == Value2->Handle;
164 }
165 
166 FORCEINLINE ULONG PhHashHandleItem(
167  _In_ PPH_HANDLE_ITEM Value
168  )
169 {
170  return (ULONG)Value->Handle / 4;
171 }
172 
174  _In_ PPH_HANDLE_PROVIDER HandleProvider,
175  _In_ HANDLE Handle
176  )
177 {
178  PH_HANDLE_ITEM lookupHandleItem;
179  PPH_HASH_ENTRY entry;
180  PPH_HANDLE_ITEM handleItem;
181 
182  lookupHandleItem.Handle = Handle;
183  entry = PhFindEntryHashSet(
184  HandleProvider->HandleHashSet,
185  HandleProvider->HandleHashSetSize,
186  PhHashHandleItem(&lookupHandleItem)
187  );
188 
189  for (; entry; entry = entry->Next)
190  {
191  handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry);
192 
193  if (PhCompareHandleItem(&lookupHandleItem, handleItem))
194  return handleItem;
195  }
196 
197  return NULL;
198 }
199 
201  _In_ PPH_HANDLE_PROVIDER HandleProvider,
202  _In_ HANDLE Handle
203  )
204 {
205  PPH_HANDLE_ITEM handleItem;
206 
207  PhAcquireQueuedLockShared(&HandleProvider->HandleHashSetLock);
208 
209  handleItem = PhpLookupHandleItem(HandleProvider, Handle);
210 
211  if (handleItem)
212  PhReferenceObject(handleItem);
213 
214  PhReleaseQueuedLockShared(&HandleProvider->HandleHashSetLock);
215 
216  return handleItem;
217 }
218 
220  _In_ PPH_HANDLE_PROVIDER HandleProvider
221  )
222 {
223  ULONG i;
224  PPH_HASH_ENTRY entry;
225  PPH_HANDLE_ITEM handleItem;
226 
227  PhAcquireQueuedLockExclusive(&HandleProvider->HandleHashSetLock);
228 
229  for (i = 0; i < HandleProvider->HandleHashSetSize; i++)
230  {
231  entry = HandleProvider->HandleHashSet[i];
232 
233  while (entry)
234  {
235  handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry);
236  entry = entry->Next;
237  PhDereferenceObject(handleItem);
238  }
239  }
240 
241  PhReleaseQueuedLockExclusive(&HandleProvider->HandleHashSetLock);
242 }
243 
245  _In_ PPH_HANDLE_PROVIDER HandleProvider,
246  _In_ _Assume_refs_(1) PPH_HANDLE_ITEM HandleItem
247  )
248 {
249  if (HandleProvider->HandleHashSetSize < HandleProvider->HandleHashSetCount + 1)
250  {
252  &HandleProvider->HandleHashSet,
253  &HandleProvider->HandleHashSetSize,
254  HandleProvider->HandleHashSetSize * 2
255  );
256  }
257 
259  HandleProvider->HandleHashSet,
260  HandleProvider->HandleHashSetSize,
261  &HandleItem->HashEntry,
262  PhHashHandleItem(HandleItem)
263  );
264  HandleProvider->HandleHashSetCount++;
265 }
266 
268  _In_ PPH_HANDLE_PROVIDER HandleProvider,
269  _In_ PPH_HANDLE_ITEM HandleItem
270  )
271 {
272  PhRemoveEntryHashSet(HandleProvider->HandleHashSet, HandleProvider->HandleHashSetSize, &HandleItem->HashEntry);
273  HandleProvider->HandleHashSetCount--;
274  PhDereferenceObject(HandleItem);
275 }
276 
288  _In_ HANDLE ProcessId,
289  _In_ HANDLE ProcessHandle,
290  _Out_ PSYSTEM_HANDLE_INFORMATION_EX *Handles,
291  _Out_ PBOOLEAN FilterNeeded
292  )
293 {
294  NTSTATUS status;
295 
296  // There are three ways of enumerating handles:
297  // * When KProcessHacker is available, using KphEnumerateProcessHandles
298  // is the most efficient method.
299  // * On Windows XP and later, NtQuerySystemInformation with
300  // SystemExtendedHandleInformation can be used.
301  // * Otherwise, NtQuerySystemInformation with SystemHandleInformation
302  // can be used.
303 
304  if (KphIsConnected())
305  {
307  PSYSTEM_HANDLE_INFORMATION_EX convertedHandles;
308  ULONG i;
309 
310  // Enumerate handles using KProcessHacker. Unlike with NtQuerySystemInformation,
311  // this only enumerates handles for a single process and saves a lot of processing.
312 
313  if (NT_SUCCESS(status = KphEnumerateProcessHandles2(ProcessHandle, &handles)))
314  {
315  convertedHandles = PhAllocate(
316  FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) +
318  );
319 
320  convertedHandles->NumberOfHandles = handles->HandleCount;
321 
322  for (i = 0; i < handles->HandleCount; i++)
323  {
324  convertedHandles->Handles[i].Object = handles->Handles[i].Object;
325  convertedHandles->Handles[i].UniqueProcessId = (ULONG_PTR)ProcessId;
326  convertedHandles->Handles[i].HandleValue = (ULONG_PTR)handles->Handles[i].Handle;
327  convertedHandles->Handles[i].GrantedAccess = (ULONG)handles->Handles[i].GrantedAccess;
328  convertedHandles->Handles[i].CreatorBackTraceIndex = 0;
329  convertedHandles->Handles[i].ObjectTypeIndex = handles->Handles[i].ObjectTypeIndex;
330  convertedHandles->Handles[i].HandleAttributes = handles->Handles[i].HandleAttributes;
331  }
332 
333  PhFree(handles);
334 
335  *Handles = convertedHandles;
336  *FilterNeeded = FALSE;
337 
338  return status;
339  }
340  }
341 
342  if (WindowsVersion >= WINDOWS_XP)
343  {
345 
346  // Enumerate handles using the new method; no conversion
347  // necessary.
348 
349  if (!NT_SUCCESS(status = PhEnumHandlesEx(&handles)))
350  return status;
351 
352  *Handles = handles;
353  *FilterNeeded = TRUE;
354  }
355  else
356  {
358  PSYSTEM_HANDLE_INFORMATION_EX convertedHandles;
359  ULONG count;
360  ULONG allocatedCount;
361  ULONG i;
362 
363  // Enumerate handles using the old info class and convert
364  // the relevant entries to the new format.
365 
366  if (!NT_SUCCESS(status = PhEnumHandles(&handles)))
367  return status;
368 
369  count = 0;
370  allocatedCount = 100;
371 
372  convertedHandles = PhAllocate(
373  FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) +
374  sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * allocatedCount
375  );
376 
377  for (i = 0; i < handles->NumberOfHandles; i++)
378  {
379  if ((HANDLE)handles->Handles[i].UniqueProcessId != ProcessId)
380  continue;
381 
382  if (count == allocatedCount)
383  {
384  allocatedCount *= 2;
385  convertedHandles = PhReAllocate(
386  convertedHandles,
387  FIELD_OFFSET(SYSTEM_HANDLE_INFORMATION_EX, Handles) +
388  sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX) * allocatedCount
389  );
390  }
391 
392  convertedHandles->Handles[count].Object = handles->Handles[i].Object;
393  convertedHandles->Handles[count].UniqueProcessId = (ULONG_PTR)handles->Handles[i].UniqueProcessId;
394  convertedHandles->Handles[count].HandleValue = (ULONG_PTR)handles->Handles[i].HandleValue;
395  convertedHandles->Handles[count].GrantedAccess = handles->Handles[i].GrantedAccess;
396  convertedHandles->Handles[count].CreatorBackTraceIndex = handles->Handles[i].CreatorBackTraceIndex;
397  convertedHandles->Handles[count].ObjectTypeIndex = handles->Handles[i].ObjectTypeIndex;
398  convertedHandles->Handles[count].HandleAttributes = (ULONG)handles->Handles[i].HandleAttributes;
399 
400  count++;
401  }
402 
403  convertedHandles->NumberOfHandles = count;
404 
405  PhFree(handles);
406 
407  *Handles = convertedHandles;
408  *FilterNeeded = FALSE;
409  }
410 
411  return STATUS_SUCCESS;
412 }
413 
415  _In_ PVOID Parameter
416  )
417 {
418  PPHP_CREATE_HANDLE_ITEM_CONTEXT context = Parameter;
419  PPH_HANDLE_ITEM handleItem;
420 
421  handleItem = PhCreateHandleItem(context->Handle);
422 
424  context->Provider->ProcessHandle,
425  handleItem->Handle,
426  context->Handle->ObjectTypeIndex,
427  0,
428  NULL,
429  NULL,
430  &handleItem->TypeName,
431  &handleItem->ObjectName,
432  &handleItem->BestObjectName,
433  NULL
434  );
435 
436  if (handleItem->TypeName)
437  {
438  // Add the handle item to the hashtable.
439  PhAcquireQueuedLockExclusive(&context->Provider->HandleHashSetLock);
440  PhpAddHandleItem(context->Provider, handleItem);
441  PhReleaseQueuedLockExclusive(&context->Provider->HandleHashSetLock);
442 
443  // Raise the handle added event.
444  PhInvokeCallback(&context->Provider->HandleAddedEvent, handleItem);
445  }
446  else
447  {
448  PhDereferenceObject(handleItem);
449  }
450 
451  PhFree(context);
452 
453  return STATUS_SUCCESS;
454 }
455 
457  _In_ PVOID Object
458  )
459 {
460  static PH_INITONCE initOnce = PH_INITONCE_INIT;
461  static ULONG fileObjectTypeIndex = -1;
462 
463  PPH_HANDLE_PROVIDER handleProvider = (PPH_HANDLE_PROVIDER)Object;
465  BOOLEAN filterNeeded;
467  ULONG numberOfHandles;
468  ULONG i;
469  PH_HASHTABLE_ENUM_CONTEXT enumContext;
470  PPH_KEY_VALUE_PAIR handlePair;
471  BOOLEAN useWorkQueue = FALSE;
472  PH_WORK_QUEUE workQueue;
473 
474  if (!handleProvider->ProcessHandle)
475  goto UpdateExit;
476 
477  if (!NT_SUCCESS(handleProvider->RunStatus = PhEnumHandlesGeneric(
478  handleProvider->ProcessId,
479  handleProvider->ProcessHandle,
480  &handleInfo,
481  &filterNeeded
482  )))
483  goto UpdateExit;
484 
486  {
487  useWorkQueue = TRUE;
488  PhInitializeWorkQueue(&workQueue, 1, 20, 1000);
489 
490  if (PhBeginInitOnce(&initOnce))
491  {
492  UNICODE_STRING fileTypeName;
493 
494  RtlInitUnicodeString(&fileTypeName, L"File");
495  fileObjectTypeIndex = PhGetObjectTypeNumber(&fileTypeName);
496  PhEndInitOnce(&initOnce);
497  }
498  }
499 
500  handles = handleInfo->Handles;
501  numberOfHandles = (ULONG)handleInfo->NumberOfHandles;
502 
503  // Make a list of the relevant handles.
504  if (filterNeeded)
505  {
506  for (i = 0; i < (ULONG)numberOfHandles; i++)
507  {
508  PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handles[i];
509 
510  if (handle->UniqueProcessId == (USHORT)handleProvider->ProcessId)
511  {
513  handleProvider->TempListHashtable,
514  (PVOID)handle->HandleValue,
515  handle
516  );
517  }
518  }
519  }
520  else
521  {
522  for (i = 0; i < (ULONG)numberOfHandles; i++)
523  {
524  PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handles[i];
525 
527  handleProvider->TempListHashtable,
528  (PVOID)handle->HandleValue,
529  handle
530  );
531  }
532  }
533 
534  // Look for closed handles.
535  {
536  PPH_LIST handlesToRemove = NULL;
537  PPH_HASH_ENTRY entry;
538  PPH_HANDLE_ITEM handleItem;
539  PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX *tempHashtableValue;
540 
541  for (i = 0; i < handleProvider->HandleHashSetSize; i++)
542  {
543  for (entry = handleProvider->HandleHashSet[i]; entry; entry = entry->Next)
544  {
545  BOOLEAN found = FALSE;
546 
547  handleItem = CONTAINING_RECORD(entry, PH_HANDLE_ITEM, HashEntry);
548 
549  // Check if the handle still exists.
550 
552  handleProvider->TempListHashtable,
553  (PVOID)(handleItem->Handle)
554  );
555 
556  if (tempHashtableValue)
557  {
558  // Also compare the object pointers to make sure a
559  // different object wasn't re-opened with the same
560  // handle value. This isn't 100% accurate as pool
561  // addresses may be re-used, but it works well.
562  if (handleItem->Object == (*tempHashtableValue)->Object)
563  {
564  found = TRUE;
565  }
566  }
567 
568  if (!found)
569  {
570  // Raise the handle removed event.
571  PhInvokeCallback(&handleProvider->HandleRemovedEvent, handleItem);
572 
573  if (!handlesToRemove)
574  handlesToRemove = PhCreateList(2);
575 
576  PhAddItemList(handlesToRemove, handleItem);
577  }
578  }
579  }
580 
581  if (handlesToRemove)
582  {
584 
585  for (i = 0; i < handlesToRemove->Count; i++)
586  {
588  handleProvider,
589  (PPH_HANDLE_ITEM)handlesToRemove->Items[i]
590  );
591  }
592 
594  PhDereferenceObject(handlesToRemove);
595  }
596  }
597 
598  // Look for new handles and update existing ones.
599 
600  PhBeginEnumHashtable(handleProvider->TempListHashtable, &enumContext);
601 
602  while (handlePair = PhNextEnumHashtable(&enumContext))
603  {
604  PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = handlePair->Value;
605  PPH_HANDLE_ITEM handleItem;
606 
607  handleItem = PhpLookupHandleItem(handleProvider, (HANDLE)handle->HandleValue);
608 
609  if (!handleItem)
610  {
611  // When we don't have KPH, query handle information in parallel to take full advantage of the
612  // PhCallWithTimeout functionality.
613  if (useWorkQueue && handle->ObjectTypeIndex == fileObjectTypeIndex)
614  {
616 
617  context = PhAllocate(sizeof(PHP_CREATE_HANDLE_ITEM_CONTEXT));
618  context->Provider = handleProvider;
619  context->Handle = handle;
621  continue;
622  }
623 
624  handleItem = PhCreateHandleItem(handle);
625 
627  handleProvider->ProcessHandle,
628  handleItem->Handle,
629  handle->ObjectTypeIndex,
630  0,
631  NULL,
632  NULL,
633  &handleItem->TypeName,
634  &handleItem->ObjectName,
635  &handleItem->BestObjectName,
636  NULL
637  );
638 
639  // We need at least a type name to continue.
640  if (!handleItem->TypeName)
641  {
642  PhDereferenceObject(handleItem);
643  continue;
644  }
645 
646  if (PhEqualString2(handleItem->TypeName, L"File", TRUE) && KphIsConnected())
647  {
648  KPH_FILE_OBJECT_INFORMATION objectInfo;
649 
651  handleProvider->ProcessHandle,
652  handleItem->Handle,
654  &objectInfo,
656  NULL
657  )))
658  {
659  if (objectInfo.SharedRead)
660  handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_READ;
661  if (objectInfo.SharedWrite)
662  handleItem->FileFlags |= PH_HANDLE_FILE_SHARED_WRITE;
663  if (objectInfo.SharedDelete)
665  }
666  }
667 
668  // Add the handle item to the hashtable.
670  PhpAddHandleItem(handleProvider, handleItem);
672 
673  // Raise the handle added event.
674  PhInvokeCallback(&handleProvider->HandleAddedEvent, handleItem);
675  }
676  else
677  {
678  BOOLEAN modified = FALSE;
679 
680  if (handleItem->Attributes != handle->HandleAttributes)
681  {
682  handleItem->Attributes = handle->HandleAttributes;
683  modified = TRUE;
684  }
685 
686  if (modified)
687  {
688  // Raise the handle modified event.
689  PhInvokeCallback(&handleProvider->HandleModifiedEvent, handleItem);
690  }
691  }
692  }
693 
694  if (useWorkQueue)
695  {
696  PhWaitForWorkQueue(&workQueue);
697  PhDeleteWorkQueue(&workQueue);
698  }
699 
700  PhFree(handleInfo);
701 
702  // Re-create the temporary hashtable if it got too big.
703  if (handleProvider->TempListHashtable->AllocatedEntries > 8192)
704  {
705  PhDereferenceObject(handleProvider->TempListHashtable);
706  handleProvider->TempListHashtable = PhCreateSimpleHashtable(512);
707  }
708  else
709  {
710  PhClearHashtable(handleProvider->TempListHashtable);
711  }
712 
713 UpdateExit:
714  PhInvokeCallback(&handleProvider->UpdatedEvent, NULL);
715 }