Process Hacker
object.c
Go to the documentation of this file.
1 /*
2  * KProcessHacker
3  *
4  * Copyright (C) 2010-2015 wj32
5  *
6  * This file is part of Process Hacker.
7  *
8  * Process Hacker is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * Process Hacker is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <kph.h>
23 #include <dyndata.h>
24 
25 #ifdef _X86_
26 #define KERNEL_HANDLE_BIT (0x80000000)
27 #else
28 #define KERNEL_HANDLE_BIT (0xffffffff80000000)
29 #endif
30 
31 #define IsKernelHandle(Handle) ((LONG_PTR)(Handle) < 0)
32 #define MakeKernelHandle(Handle) ((HANDLE)((ULONG_PTR)(Handle) | KERNEL_HANDLE_BIT))
33 
34 typedef struct _KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT
35 {
36  PVOID Buffer;
37  PVOID BufferLimit;
38  PVOID CurrentEntry;
39  ULONG Count;
40  NTSTATUS Status;
42 
44  __inout PHANDLE_TABLE_ENTRY HandleTableEntry,
45  __in HANDLE Handle,
46  __in PVOID Context
47  );
48 
50  __in PHANDLE_TABLE HandleTable,
51  __inout PHANDLE_TABLE_ENTRY HandleTableEntry,
52  __in HANDLE Handle,
53  __in PVOID Context
54  );
55 
56 #ifdef ALLOC_PRAGMA
57 #pragma alloc_text(PAGE, KphGetObjectType)
58 #pragma alloc_text(PAGE, KphReferenceProcessHandleTable)
59 #pragma alloc_text(PAGE, KphDereferenceProcessHandleTable)
60 #pragma alloc_text(PAGE, KphUnlockHandleTableEntry)
61 #pragma alloc_text(PAGE, KphpEnumerateProcessHandlesEnumCallback61)
62 #pragma alloc_text(PAGE, KphpEnumerateProcessHandlesEnumCallback)
63 #pragma alloc_text(PAGE, KpiEnumerateProcessHandles)
64 #pragma alloc_text(PAGE, KphQueryNameObject)
65 #pragma alloc_text(PAGE, KphQueryNameFileObject)
66 #pragma alloc_text(PAGE, KpiQueryInformationObject)
67 #pragma alloc_text(PAGE, KpiSetInformationObject)
68 #pragma alloc_text(PAGE, KphDuplicateObject)
69 #pragma alloc_text(PAGE, KpiDuplicateObject)
70 #pragma alloc_text(PAGE, KphOpenNamedObject)
71 #endif
72 
81 POBJECT_TYPE KphGetObjectType(
82  __in PVOID Object
83  )
84 {
85  PAGED_CODE();
86 
87  // XP to Vista: A pointer to the object type is
88  // stored in the object header.
89  if (
92  )
93  {
94  return OBJECT_TO_OBJECT_HEADER(Object)->Type;
95  }
96  // Seven and above: An index to an internal object type
97  // table is stored in the object header. Luckily we have
98  // a new exported function, ObGetObjectType, to get
99  // the object type.
100  else if (KphDynNtVersion >= PHNT_WIN7)
101  {
102  if (ObGetObjectType_I)
103  return ObGetObjectType_I(Object);
104  else
105  return NULL;
106  }
107  else
108  {
109  return NULL;
110  }
111 }
112 
124  __in PEPROCESS Process
125  )
126 {
127  PHANDLE_TABLE handleTable = NULL;
128 
129  PAGED_CODE();
130 
131  // Fail if we don't have an offset.
132  if (KphDynEpObjectTable == -1)
133  return NULL;
134 
135  // Prevent the process from terminating and get its handle table.
137  {
138  handleTable = *(PHANDLE_TABLE *)((ULONG_PTR)Process + KphDynEpObjectTable);
139 
140  if (!handleTable)
142  }
143 
144  return handleTable;
145 }
146 
153  __in PEPROCESS Process
154  )
155 {
156  PAGED_CODE();
157 
159 }
160 
162  __in PHANDLE_TABLE HandleTable,
163  __in PHANDLE_TABLE_ENTRY HandleTableEntry
164  )
165 {
166  PEX_PUSH_LOCK handleContentionEvent;
167 
168  PAGED_CODE();
169 
170  // Set the unlocked bit.
171 
172 #ifdef _M_X64
173  InterlockedExchangeAdd64(&HandleTableEntry->Value, 1);
174 #else
175  InterlockedExchangeAdd(&HandleTableEntry->Value, 1);
176 #endif
177 
178  // Allow waiters to wake up.
179 
180  handleContentionEvent = (PEX_PUSH_LOCK)((ULONG_PTR)HandleTable + KphDynHtHandleContentionEvent);
181 
182  if (*(PULONG_PTR)handleContentionEvent != 0)
183  ExfUnblockPushLock_I(handleContentionEvent, NULL);
184 }
185 
187  __inout PHANDLE_TABLE_ENTRY HandleTableEntry,
188  __in HANDLE Handle,
189  __in PVOID Context
190  )
191 {
192  PKPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT context = Context;
193  KPH_PROCESS_HANDLE handleInfo;
194  POBJECT_HEADER objectHeader;
195  POBJECT_TYPE objectType;
196  PKPH_PROCESS_HANDLE entryInBuffer;
197 
198  PAGED_CODE();
199 
200  objectHeader = ObpDecodeObject(HandleTableEntry->Object);
201  handleInfo.Handle = Handle;
202  handleInfo.Object = objectHeader ? &objectHeader->Body : NULL;
203  handleInfo.GrantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess);
204  handleInfo.ObjectTypeIndex = -1;
205  handleInfo.Reserved1 = 0;
206  handleInfo.HandleAttributes = ObpGetHandleAttributes(HandleTableEntry);
207  handleInfo.Reserved2 = 0;
208 
209  if (handleInfo.Object)
210  {
211  objectType = KphGetObjectType(handleInfo.Object);
212 
213  if (objectType && KphDynOtIndex != -1)
214  {
215  if (KphDynNtVersion >= PHNT_WIN7)
216  handleInfo.ObjectTypeIndex = (USHORT)*(PUCHAR)((ULONG_PTR)objectType + KphDynOtIndex);
217  else
218  handleInfo.ObjectTypeIndex = (USHORT)*(PULONG)((ULONG_PTR)objectType + KphDynOtIndex);
219  }
220  }
221 
222  // Advance the current entry pointer regardless of whether the information will be written;
223  // this will allow the parent function to report the correct return length.
224  entryInBuffer = context->CurrentEntry;
225  context->CurrentEntry = (PVOID)((ULONG_PTR)context->CurrentEntry + sizeof(KPH_PROCESS_HANDLE));
226  context->Count++;
227 
228  // Only write if we have not exceeded the buffer length.
229  // Also check for a potential overflow (if the process has an extremely large number of
230  // handles, the buffer pointer may wrap).
231  if (
232  (ULONG_PTR)entryInBuffer >= (ULONG_PTR)context->Buffer &&
233  (ULONG_PTR)entryInBuffer + sizeof(KPH_PROCESS_HANDLE) <= (ULONG_PTR)context->BufferLimit
234  )
235  {
236  __try
237  {
238  *entryInBuffer = handleInfo;
239  }
240  __except (EXCEPTION_EXECUTE_HANDLER)
241  {
242  // Report an error.
243  if (context->Status == STATUS_SUCCESS)
244  context->Status = GetExceptionCode();
245  }
246  }
247  else
248  {
249  // Report that the buffer is too small.
250  if (context->Status == STATUS_SUCCESS)
251  context->Status = STATUS_BUFFER_TOO_SMALL;
252  }
253 
254  return FALSE;
255 }
256 
258  __in PHANDLE_TABLE HandleTable,
259  __inout PHANDLE_TABLE_ENTRY HandleTableEntry,
260  __in HANDLE Handle,
261  __in PVOID Context
262  )
263 {
264  BOOLEAN result;
265 
266  PAGED_CODE();
267 
268  result = KphpEnumerateProcessHandlesEnumCallback61(HandleTableEntry, Handle, Context);
269  KphUnlockHandleTableEntry(HandleTable, HandleTableEntry);
270 
271  return result;
272 }
273 
286  __in HANDLE ProcessHandle,
287  __out_bcount(BufferLength) PVOID Buffer,
288  __in_opt ULONG BufferLength,
289  __out_opt PULONG ReturnLength,
290  __in KPROCESSOR_MODE AccessMode
291  )
292 {
293  NTSTATUS status;
294  BOOLEAN result;
295  PEPROCESS process;
296  PHANDLE_TABLE handleTable;
298 
299  PAGED_CODE();
300 
301  if (KphDynNtVersion >= PHNT_WIN8 &&
302  (!ExfUnblockPushLock_I || KphDynHtHandleContentionEvent == -1))
303  {
304  return STATUS_NOT_SUPPORTED;
305  }
306 
307  if (AccessMode != KernelMode)
308  {
309  __try
310  {
311  ProbeForWrite(Buffer, BufferLength, sizeof(ULONG));
312 
313  if (ReturnLength)
314  ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
315  }
316  __except (EXCEPTION_EXECUTE_HANDLER)
317  {
318  return GetExceptionCode();
319  }
320  }
321 
322  // Reference the process object.
323  status = ObReferenceObjectByHandle(
324  ProcessHandle,
325  0,
326  *PsProcessType,
327  AccessMode,
328  &process,
329  NULL
330  );
331 
332  if (!NT_SUCCESS(status))
333  return status;
334 
335  // Get its handle table.
336  handleTable = KphReferenceProcessHandleTable(process);
337 
338  if (!handleTable)
339  {
340  ObDereferenceObject(process);
341  return STATUS_UNSUCCESSFUL;
342  }
343 
344  // Initialize the enumeration context.
345  context.Buffer = Buffer;
346  context.BufferLimit = (PVOID)((ULONG_PTR)Buffer + BufferLength);
347  context.CurrentEntry = ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->Handles;
348  context.Count = 0;
349  context.Status = STATUS_SUCCESS;
350 
351  // Enumerate the handles.
352 
353  if (KphDynNtVersion >= PHNT_WIN8)
354  {
355  result = ExEnumHandleTable(
356  handleTable,
358  &context,
359  NULL
360  );
361  }
362  else
363  {
364  result = ExEnumHandleTable(
365  handleTable,
367  &context,
368  NULL
369  );
370  }
371 
373  ObDereferenceObject(process);
374 
375  // Write the number of handles if we can.
376  if (BufferLength >= FIELD_OFFSET(KPH_PROCESS_HANDLE_INFORMATION, Handles))
377  {
378  if (AccessMode != KernelMode)
379  {
380  __try
381  {
382  ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->HandleCount = context.Count;
383  }
384  __except (EXCEPTION_EXECUTE_HANDLER)
385  {
386  return GetExceptionCode();
387  }
388  }
389  else
390  {
391  ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->HandleCount = context.Count;
392  }
393  }
394 
395  // Supply the return length if the caller wanted it.
396  if (ReturnLength)
397  {
398  ULONG returnLength;
399 
400  // Note: if the CurrentEntry pointer wrapped, this will give the wrong
401  // return length.
402  returnLength = (ULONG)((ULONG_PTR)context.CurrentEntry - (ULONG_PTR)Buffer);
403 
404  if (AccessMode != KernelMode)
405  {
406  __try
407  {
408  *ReturnLength = returnLength;
409  }
410  __except (EXCEPTION_EXECUTE_HANDLER)
411  {
412  return GetExceptionCode();
413  }
414  }
415  else
416  {
417  *ReturnLength = returnLength;
418  }
419  }
420 
421  return context.Status;
422 }
423 
434  __in PVOID Object,
435  __out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer,
436  __in ULONG BufferLength,
437  __out PULONG ReturnLength
438  )
439 {
440  NTSTATUS status;
441  POBJECT_TYPE objectType;
442 
443  PAGED_CODE();
444 
445  objectType = KphGetObjectType(Object);
446 
447  // Check if we are going to hang when querying the object, and use
448  // the special file object query function if needed.
449  if (objectType == *IoFileObjectType &&
450  (((PFILE_OBJECT)Object)->Busy || ((PFILE_OBJECT)Object)->Waiters))
451  {
452  status = KphQueryNameFileObject(Object, Buffer, BufferLength, ReturnLength);
453  dprintf("KphQueryNameFileObject: status 0x%x\n", status);
454  }
455  else
456  {
457  status = ObQueryNameString(Object, Buffer, BufferLength, ReturnLength);
458  dprintf("ObQueryNameString: status 0x%x\n", status);
459  }
460 
461  // Make the error returns consistent.
462  if (status == STATUS_BUFFER_OVERFLOW) // returned by I/O subsystem
463  status = STATUS_BUFFER_TOO_SMALL;
464  if (status == STATUS_INFO_LENGTH_MISMATCH) // returned by ObQueryNameString
465  status = STATUS_BUFFER_TOO_SMALL;
466 
467  if (NT_SUCCESS(status))
468  dprintf("KphQueryNameObject: %.*S\n", Buffer->Name.Length / sizeof(WCHAR), Buffer->Name.Buffer);
469  else
470  dprintf("KphQueryNameObject: status 0x%x\n", status);
471 
472  return status;
473 }
474 
485  __in PFILE_OBJECT FileObject,
486  __out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer,
487  __in ULONG BufferLength,
488  __out PULONG ReturnLength
489  )
490 {
491  NTSTATUS status = STATUS_SUCCESS;
492  ULONG returnLength;
493  PCHAR objectName;
494  ULONG usedLength;
495  ULONG subNameLength;
496  PFILE_OBJECT relatedFileObject;
497 
498  PAGED_CODE();
499 
500  // We need at least the size of OBJECT_NAME_INFORMATION to
501  // continue.
502  if (BufferLength < sizeof(OBJECT_NAME_INFORMATION))
503  {
504  *ReturnLength = sizeof(OBJECT_NAME_INFORMATION);
505 
506  return STATUS_BUFFER_TOO_SMALL;
507  }
508 
509  // Assume failure.
510  Buffer->Name.Length = 0;
511  // We will place the object name directly after the
512  // UNICODE_STRING structure in the buffer.
513  Buffer->Name.Buffer = (PWSTR)((ULONG_PTR)Buffer + sizeof(OBJECT_NAME_INFORMATION));
514  // Retain a local pointer to the object name so we
515  // can manipulate the pointer.
516  objectName = (PCHAR)Buffer->Name.Buffer;
517  // A variable that keeps track of how much space we
518  // have used.
519  usedLength = sizeof(OBJECT_NAME_INFORMATION);
520 
521  // Check if the file object has an associated device
522  // (e.g. "\Device\NamedPipe", "\Device\Mup"). We can
523  // use the user-supplied buffer for this since if the
524  // buffer isn't big enough, we can't proceed anyway
525  // (we are going to use the name).
526  if (FileObject->DeviceObject)
527  {
528  status = ObQueryNameString(
529  FileObject->DeviceObject,
530  Buffer,
531  BufferLength,
532  &returnLength
533  );
534 
535  if (!NT_SUCCESS(status))
536  {
537  if (status == STATUS_INFO_LENGTH_MISMATCH)
538  status = STATUS_BUFFER_TOO_SMALL;
539 
540  *ReturnLength = returnLength;
541 
542  return status;
543  }
544 
545  // The UNICODE_STRING in the buffer is now filled in.
546  // We will append to the object name later, so
547  // we need to fix the object name pointer by adding
548  // the length, in bytes, of the device name string we
549  // just got.
550  objectName += Buffer->Name.Length;
551  usedLength += Buffer->Name.Length;
552  }
553 
554  // Check if the file object has a file name component. If not,
555  // we can't do anything else, so we just return the name we
556  // have already.
557  if (!FileObject->FileName.Buffer)
558  {
559  *ReturnLength = usedLength;
560 
561  return STATUS_SUCCESS;
562  }
563 
564  // The file object has a name. We need to walk up the file
565  // object chain and append the names of the related file
566  // objects in reverse order. This means we need to calculate
567  // the total length first.
568 
569  relatedFileObject = FileObject;
570  subNameLength = 0;
571 
572  do
573  {
574  subNameLength += relatedFileObject->FileName.Length;
575 
576  // Avoid infinite loops.
577  if (relatedFileObject == relatedFileObject->RelatedFileObject)
578  break;
579 
580  relatedFileObject = relatedFileObject->RelatedFileObject;
581  }
582  while (relatedFileObject);
583 
584  usedLength += subNameLength;
585 
586  // Check if we have enough space to write the whole thing.
587  if (usedLength > BufferLength)
588  {
589  *ReturnLength = usedLength;
590 
591  return STATUS_BUFFER_TOO_SMALL;
592  }
593 
594  // We're ready to begin copying the names.
595 
596  // Add the name length because we're copying in reverse order.
597  objectName += subNameLength;
598 
599  relatedFileObject = FileObject;
600 
601  do
602  {
603  objectName -= relatedFileObject->FileName.Length;
604  memcpy(objectName, relatedFileObject->FileName.Buffer, relatedFileObject->FileName.Length);
605 
606  // Avoid infinite loops.
607  if (relatedFileObject == relatedFileObject->RelatedFileObject)
608  break;
609 
610  relatedFileObject = relatedFileObject->RelatedFileObject;
611  }
612  while (relatedFileObject);
613 
614  // Update the length.
615  Buffer->Name.Length += (USHORT)subNameLength;
616 
617  // Pass the return length back.
618  *ReturnLength = usedLength;
619 
620  return STATUS_SUCCESS;
621 }
622 
638  __in HANDLE ProcessHandle,
639  __in HANDLE Handle,
640  __in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
641  __out_bcount(ObjectInformationLength) PVOID ObjectInformation,
642  __in ULONG ObjectInformationLength,
643  __out_opt PULONG ReturnLength,
644  __in KPROCESSOR_MODE AccessMode
645  )
646 {
647  NTSTATUS status;
648  PEPROCESS process;
649  KPROCESSOR_MODE referenceMode;
650  KAPC_STATE apcState;
651  ULONG returnLength;
652 
653  PAGED_CODE();
654 
655  if (AccessMode != KernelMode)
656  {
657  __try
658  {
659  ProbeForWrite(ObjectInformation, ObjectInformationLength, sizeof(ULONG));
660 
661  if (ReturnLength)
662  ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
663  }
664  __except (EXCEPTION_EXECUTE_HANDLER)
665  {
666  return GetExceptionCode();
667  }
668  }
669 
670  status = ObReferenceObjectByHandle(
671  ProcessHandle,
672  0,
673  *PsProcessType,
674  AccessMode,
675  &process,
676  NULL
677  );
678 
679  if (!NT_SUCCESS(status))
680  return status;
681 
682  if (process == PsInitialSystemProcess)
683  {
684  // A check was added in Windows 7 - if we're attached to the System process,
685  // the handle must be a kernel handle.
686  Handle = MakeKernelHandle(Handle);
687  referenceMode = KernelMode;
688  }
689  else
690  {
691  // Make sure the handle isn't a kernel handle if we're not attached to the
692  // System process. This means we can avoid referencing then opening the objects
693  // later when calling ZwQueryObject, etc.
694  if (IsKernelHandle(Handle))
695  {
696  ObDereferenceObject(process);
697  return STATUS_INVALID_HANDLE;
698  }
699 
700  referenceMode = AccessMode;
701  }
702 
703  switch (ObjectInformationClass)
704  {
706  {
707  OBJECT_BASIC_INFORMATION basicInfo;
708 
709  KeStackAttachProcess(process, &apcState);
710  status = ZwQueryObject(
711  Handle,
712  ObjectBasicInformation,
713  &basicInfo,
714  sizeof(OBJECT_BASIC_INFORMATION),
715  NULL
716  );
717  KeUnstackDetachProcess(&apcState);
718 
719  if (NT_SUCCESS(status))
720  {
721  if (ObjectInformationLength == sizeof(OBJECT_BASIC_INFORMATION))
722  {
723  __try
724  {
725  *(POBJECT_BASIC_INFORMATION)ObjectInformation = basicInfo;
726  }
727  __except (EXCEPTION_EXECUTE_HANDLER)
728  {
729  status = GetExceptionCode();
730  }
731  }
732  else
733  {
734  status = STATUS_INFO_LENGTH_MISMATCH;
735  }
736  }
737 
738  returnLength = sizeof(OBJECT_BASIC_INFORMATION);
739  }
740  break;
742  {
743  PVOID object;
744  ULONG allocateSize;
745  POBJECT_NAME_INFORMATION nameInfo;
746 
747  returnLength = sizeof(OBJECT_NAME_INFORMATION);
748 
749  // Attach to the process a get a pointer to the object.
750  KeStackAttachProcess(process, &apcState);
751  status = ObReferenceObjectByHandle(
752  Handle,
753  0,
754  NULL,
755  referenceMode,
756  &object,
757  NULL
758  );
759  KeUnstackDetachProcess(&apcState);
760 
761  if (NT_SUCCESS(status))
762  {
763  allocateSize = ObjectInformationLength;
764 
765  if (allocateSize < sizeof(OBJECT_NAME_INFORMATION)) // make sure we never try to allocate 0 bytes
766  allocateSize = sizeof(OBJECT_NAME_INFORMATION);
767 
768  nameInfo = ExAllocatePoolWithQuotaTag(PagedPool, allocateSize, 'QhpK');
769 
770  if (nameInfo)
771  {
772  // Make sure we don't leak any data.
773  memset(nameInfo, 0, ObjectInformationLength);
774 
775  status = KphQueryNameObject(
776  object,
777  nameInfo,
778  ObjectInformationLength,
779  &returnLength
780  );
781  dprintf("KpiQueryInformationObject: called KphQueryNameObject: Handle: 0x%Ix, ObjectInformationLength: %u, returnLength: %u\n",
782  Handle, ObjectInformationLength, returnLength);
783 
784  if (NT_SUCCESS(status))
785  {
786  // Fix up the buffer pointer.
787  if (nameInfo->Name.Buffer)
788  nameInfo->Name.Buffer = (PVOID)((ULONG_PTR)nameInfo->Name.Buffer - (ULONG_PTR)nameInfo + (ULONG_PTR)ObjectInformation);
789 
790  __try
791  {
792  memcpy(ObjectInformation, nameInfo, ObjectInformationLength);
793  }
794  __except (EXCEPTION_EXECUTE_HANDLER)
795  {
796  status = GetExceptionCode();
797  }
798  }
799 
800  ExFreePoolWithTag(nameInfo, 'QhpK');
801  }
802  else
803  {
804  status = STATUS_INSUFFICIENT_RESOURCES;
805  }
806 
807  ObDereferenceObject(object);
808  }
809  }
810  break;
812  {
813  ULONG allocateSize;
814  POBJECT_TYPE_INFORMATION typeInfo;
815 
816  returnLength = sizeof(OBJECT_TYPE_INFORMATION);
817  allocateSize = ObjectInformationLength;
818 
819  if (allocateSize < sizeof(OBJECT_TYPE_INFORMATION))
820  allocateSize = sizeof(OBJECT_TYPE_INFORMATION);
821 
822  // ObQueryTypeInfo uses ObjectType->Name.MaximumLength instead of ObjectType->Name.Length + sizeof(WCHAR)
823  // to calculate the required buffer size. In Windows 8, certain object types (e.g. TmTx) do NOT include
824  // the null terminator in MaximumLength, which causes ObQueryTypeInfo to overrun the given buffer.
825  // To work around this bug, we add some (generous) padding to our allocation.
826  allocateSize += sizeof(ULONGLONG);
827 
828  typeInfo = ExAllocatePoolWithQuotaTag(PagedPool, allocateSize, 'QhpK');
829 
830  if (typeInfo)
831  {
832  memset(typeInfo, 0, ObjectInformationLength);
833 
834  KeStackAttachProcess(process, &apcState);
835  status = ZwQueryObject(
836  Handle,
837  ObjectTypeInformation,
838  typeInfo,
839  ObjectInformationLength,
840  &returnLength
841  );
842  KeUnstackDetachProcess(&apcState);
843 
844  if (NT_SUCCESS(status))
845  {
846  // Fix up the buffer pointer.
847  if (typeInfo->TypeName.Buffer)
848  typeInfo->TypeName.Buffer = (PVOID)((ULONG_PTR)typeInfo->TypeName.Buffer - (ULONG_PTR)typeInfo + (ULONG_PTR)ObjectInformation);
849 
850  __try
851  {
852  memcpy(ObjectInformation, typeInfo, ObjectInformationLength);
853  }
854  __except (EXCEPTION_EXECUTE_HANDLER)
855  {
856  status = GetExceptionCode();
857  }
858  }
859 
860  ExFreePoolWithTag(typeInfo, 'QhpK');
861  }
862  else
863  {
864  status = STATUS_INSUFFICIENT_RESOURCES;
865  }
866  }
867  break;
869  {
870  OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo;
871 
872  KeStackAttachProcess(process, &apcState);
873  status = ZwQueryObject(
874  Handle,
876  &handleFlagInfo,
878  NULL
879  );
880  KeUnstackDetachProcess(&apcState);
881 
882  if (NT_SUCCESS(status))
883  {
884  if (ObjectInformationLength == sizeof(OBJECT_HANDLE_FLAG_INFORMATION))
885  {
886  __try
887  {
888  *(POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation = handleFlagInfo;
889  }
890  __except (EXCEPTION_EXECUTE_HANDLER)
891  {
892  status = GetExceptionCode();
893  }
894  }
895  else
896  {
897  status = STATUS_INFO_LENGTH_MISMATCH;
898  }
899  }
900 
901  returnLength = sizeof(OBJECT_HANDLE_FLAG_INFORMATION);
902  }
903  break;
905  {
906  PROCESS_BASIC_INFORMATION basicInfo;
907 
908  KeStackAttachProcess(process, &apcState);
909  status = ZwQueryInformationProcess(
910  Handle,
911  ProcessBasicInformation,
912  &basicInfo,
913  sizeof(PROCESS_BASIC_INFORMATION),
914  NULL
915  );
916  KeUnstackDetachProcess(&apcState);
917 
918  if (NT_SUCCESS(status))
919  {
920  if (ObjectInformationLength == sizeof(PROCESS_BASIC_INFORMATION))
921  {
922  __try
923  {
924  *(PPROCESS_BASIC_INFORMATION)ObjectInformation = basicInfo;
925  }
926  __except (EXCEPTION_EXECUTE_HANDLER)
927  {
928  status = GetExceptionCode();
929  }
930  }
931  else
932  {
933  status = STATUS_INFO_LENGTH_MISMATCH;
934  }
935  }
936 
937  returnLength = sizeof(PROCESS_BASIC_INFORMATION);
938  }
939  break;
941  {
942  THREAD_BASIC_INFORMATION basicInfo;
943 
944  KeStackAttachProcess(process, &apcState);
945  status = ZwQueryInformationThread(
946  Handle,
947  ThreadBasicInformation,
948  &basicInfo,
949  sizeof(THREAD_BASIC_INFORMATION),
950  NULL
951  );
952  KeUnstackDetachProcess(&apcState);
953 
954  if (NT_SUCCESS(status))
955  {
956  if (ObjectInformationLength == sizeof(THREAD_BASIC_INFORMATION))
957  {
958  __try
959  {
960  *(PTHREAD_BASIC_INFORMATION)ObjectInformation = basicInfo;
961  }
962  __except (EXCEPTION_EXECUTE_HANDLER)
963  {
964  status = GetExceptionCode();
965  }
966  }
967  else
968  {
969  status = STATUS_INFO_LENGTH_MISMATCH;
970  }
971  }
972 
973  returnLength = sizeof(THREAD_BASIC_INFORMATION);
974  }
975  break;
977  {
978  PVOID etwReg;
979  PVOID objectType;
980  PUNICODE_STRING objectTypeName;
981  UNICODE_STRING etwRegistrationName;
982  PVOID guidEntry;
983  ETWREG_BASIC_INFORMATION basicInfo;
984 
985  // Check dynamic data requirements.
986  if (
987  KphDynEgeGuid != -1 &&
988  KphDynEreGuidEntry != -1 &&
989  KphDynOtName != -1
990  )
991  {
992  // Attach to the process and get a pointer to the object.
993  // We don't have a pointer to the EtwRegistration object type,
994  // so we'll just have to check the type name.
995 
996  KeStackAttachProcess(process, &apcState);
997  status = ObReferenceObjectByHandle(
998  Handle,
999  0,
1000  NULL,
1001  referenceMode,
1002  &etwReg,
1003  NULL
1004  );
1005  KeUnstackDetachProcess(&apcState);
1006 
1007  if (NT_SUCCESS(status))
1008  {
1009  // Check the type name.
1010 
1011  objectType = KphGetObjectType(etwReg);
1012 
1013  if (objectType)
1014  {
1015  objectTypeName = (PUNICODE_STRING)((ULONG_PTR)objectType + KphDynOtName);
1016  RtlInitUnicodeString(&etwRegistrationName, L"EtwRegistration");
1017 
1018  if (!RtlEqualUnicodeString(objectTypeName, &etwRegistrationName, FALSE))
1019  {
1020  status = STATUS_OBJECT_TYPE_MISMATCH;
1021  }
1022  }
1023  else
1024  {
1025  status = STATUS_NOT_SUPPORTED;
1026  }
1027 
1028  if (NT_SUCCESS(status))
1029  {
1030  guidEntry = *(PVOID *)((ULONG_PTR)etwReg + KphDynEreGuidEntry);
1031 
1032  if (guidEntry)
1033  basicInfo.Guid = *(GUID *)((ULONG_PTR)guidEntry + KphDynEgeGuid);
1034  else
1035  memset(&basicInfo.Guid, 0, sizeof(GUID));
1036 
1037  basicInfo.SessionId = 0; // not implemented
1038 
1039  if (ObjectInformationLength == sizeof(ETWREG_BASIC_INFORMATION))
1040  {
1041  __try
1042  {
1043  *(PETWREG_BASIC_INFORMATION)ObjectInformation = basicInfo;
1044  }
1045  __except (EXCEPTION_EXECUTE_HANDLER)
1046  {
1047  status = GetExceptionCode();
1048  }
1049  }
1050  else
1051  {
1052  status = STATUS_INFO_LENGTH_MISMATCH;
1053  }
1054  }
1055 
1056  ObDereferenceObject(etwReg);
1057  }
1058  }
1059  else
1060  {
1061  status = STATUS_NOT_SUPPORTED;
1062  }
1063 
1064  returnLength = sizeof(ETWREG_BASIC_INFORMATION);
1065  }
1066  break;
1068  {
1069  PFILE_OBJECT fileObject;
1070  KPH_FILE_OBJECT_INFORMATION objectInfo;
1071 
1072  KeStackAttachProcess(process, &apcState);
1073  status = ObReferenceObjectByHandle(
1074  Handle,
1075  0,
1076  *IoFileObjectType,
1077  referenceMode,
1078  &fileObject,
1079  NULL
1080  );
1081  KeUnstackDetachProcess(&apcState);
1082 
1083  if (NT_SUCCESS(status))
1084  {
1085  objectInfo.LockOperation = fileObject->LockOperation;
1086  objectInfo.DeletePending = fileObject->DeletePending;
1087  objectInfo.ReadAccess = fileObject->ReadAccess;
1088  objectInfo.WriteAccess = fileObject->WriteAccess;
1089  objectInfo.DeleteAccess = fileObject->DeleteAccess;
1090  objectInfo.SharedRead = fileObject->SharedRead;
1091  objectInfo.SharedWrite = fileObject->SharedWrite;
1092  objectInfo.SharedDelete = fileObject->SharedDelete;
1093  objectInfo.CurrentByteOffset = fileObject->CurrentByteOffset;
1094  objectInfo.Flags = fileObject->Flags;
1095 
1096  if (ObjectInformationLength == sizeof(KPH_FILE_OBJECT_INFORMATION))
1097  {
1098  __try
1099  {
1100  *(PKPH_FILE_OBJECT_INFORMATION)ObjectInformation = objectInfo;
1101  }
1102  __except (EXCEPTION_EXECUTE_HANDLER)
1103  {
1104  status = GetExceptionCode();
1105  }
1106  }
1107  else
1108  {
1109  status = STATUS_INFO_LENGTH_MISMATCH;
1110  }
1111 
1112  ObDereferenceObject(fileObject);
1113  }
1114 
1115  returnLength = sizeof(KPH_FILE_OBJECT_INFORMATION);
1116  }
1117  break;
1119  {
1120  PFILE_OBJECT fileObject;
1121  HANDLE driverHandle;
1122 
1123  if (ObjectInformationLength == sizeof(KPH_FILE_OBJECT_DRIVER))
1124  {
1125  KeStackAttachProcess(process, &apcState);
1126  status = ObReferenceObjectByHandle(
1127  Handle,
1128  0,
1129  *IoFileObjectType,
1130  referenceMode,
1131  &fileObject,
1132  NULL
1133  );
1134  KeUnstackDetachProcess(&apcState);
1135 
1136  if (NT_SUCCESS(status))
1137  {
1138  if (fileObject->DeviceObject && fileObject->DeviceObject->DriverObject)
1139  {
1140  status = ObOpenObjectByPointer(
1141  fileObject->DeviceObject->DriverObject,
1142  0,
1143  NULL,
1144  0,
1146  KernelMode,
1147  &driverHandle
1148  );
1149  }
1150  else
1151  {
1152  driverHandle = NULL;
1153  }
1154 
1155  if (NT_SUCCESS(status))
1156  {
1157  __try
1158  {
1159  ((PKPH_FILE_OBJECT_DRIVER)ObjectInformation)->DriverHandle = driverHandle;
1160  }
1161  __except (EXCEPTION_EXECUTE_HANDLER)
1162  {
1163  status = GetExceptionCode();
1164  }
1165  }
1166 
1167  ObDereferenceObject(fileObject);
1168  }
1169  }
1170  else
1171  {
1172  status = STATUS_INFO_LENGTH_MISMATCH;
1173  }
1174  }
1175  break;
1176  default:
1177  status = STATUS_INVALID_INFO_CLASS;
1178  returnLength = 0;
1179  break;
1180  }
1181 
1182  ObDereferenceObject(process);
1183 
1184  if (ReturnLength)
1185  {
1186  if (AccessMode != KernelMode)
1187  {
1188  __try
1189  {
1190  *ReturnLength = returnLength;
1191  }
1192  __except (EXCEPTION_EXECUTE_HANDLER)
1193  {
1194  NOTHING;
1195  }
1196  }
1197  else
1198  {
1199  *ReturnLength = returnLength;
1200  }
1201  }
1202 
1203  return status;
1204 }
1205 
1219  __in HANDLE ProcessHandle,
1220  __in HANDLE Handle,
1221  __in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
1222  __in_bcount(ObjectInformationLength) PVOID ObjectInformation,
1223  __in ULONG ObjectInformationLength,
1224  __in KPROCESSOR_MODE AccessMode
1225  )
1226 {
1227  NTSTATUS status;
1228  PEPROCESS process;
1229  KAPC_STATE apcState;
1230 
1231  PAGED_CODE();
1232 
1233  if (AccessMode != KernelMode)
1234  {
1235  ULONG alignment;
1236 
1237  switch (ObjectInformationClass)
1238  {
1240  alignment = sizeof(BOOLEAN);
1241  break;
1242  default:
1243  alignment = sizeof(ULONG);
1244  break;
1245  }
1246 
1247  __try
1248  {
1249  ProbeForRead(ObjectInformation, ObjectInformationLength, alignment);
1250  }
1251  __except (EXCEPTION_EXECUTE_HANDLER)
1252  {
1253  return GetExceptionCode();
1254  }
1255  }
1256 
1257  status = ObReferenceObjectByHandle(
1258  ProcessHandle,
1259  0,
1260  *PsProcessType,
1261  AccessMode,
1262  &process,
1263  NULL
1264  );
1265 
1266  if (!NT_SUCCESS(status))
1267  return status;
1268 
1269  if (process == PsInitialSystemProcess)
1270  {
1271  Handle = MakeKernelHandle(Handle);
1272  }
1273  else
1274  {
1275  if (IsKernelHandle(Handle))
1276  {
1277  ObDereferenceObject(process);
1278  return STATUS_INVALID_HANDLE;
1279  }
1280  }
1281 
1282  switch (ObjectInformationClass)
1283  {
1285  {
1286  OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo;
1287 
1288  if (ObjectInformationLength == sizeof(OBJECT_HANDLE_FLAG_INFORMATION))
1289  {
1290  __try
1291  {
1292  handleFlagInfo = *(POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation;
1293  }
1294  __except (EXCEPTION_EXECUTE_HANDLER)
1295  {
1296  status = GetExceptionCode();
1297  }
1298  }
1299  else
1300  {
1301  status = STATUS_INFO_LENGTH_MISMATCH;
1302  }
1303 
1304  if (NT_SUCCESS(status))
1305  {
1306  KeStackAttachProcess(process, &apcState);
1307  status = ObSetHandleAttributes(Handle, &handleFlagInfo, KernelMode);
1308  KeUnstackDetachProcess(&apcState);
1309  }
1310  }
1311  break;
1312  default:
1313  status = STATUS_INVALID_INFO_CLASS;
1314  break;
1315  }
1316 
1317  ObDereferenceObject(process);
1318 
1319  return status;
1320 }
1321 
1344  __in PEPROCESS SourceProcess,
1345  __in_opt PEPROCESS TargetProcess,
1346  __in HANDLE SourceHandle,
1347  __out_opt PHANDLE TargetHandle,
1348  __in ACCESS_MASK DesiredAccess,
1349  __in ULONG HandleAttributes,
1350  __in ULONG Options,
1351  __in KPROCESSOR_MODE AccessMode
1352  )
1353 {
1354  NTSTATUS status = STATUS_SUCCESS;
1355  KPROCESSOR_MODE referenceMode;
1356  BOOLEAN sourceAttached = FALSE;
1357  BOOLEAN targetAttached = FALSE;
1358  KAPC_STATE apcState;
1359  PVOID object;
1360  HANDLE objectHandle;
1361 
1362  PAGED_CODE();
1363 
1364  // Validate the parameters.
1365 
1366  if (!TargetProcess || !TargetHandle)
1367  {
1368  if (!(Options & DUPLICATE_CLOSE_SOURCE))
1369  return STATUS_INVALID_PARAMETER_MIX;
1370  }
1371 
1372  if (AccessMode != KernelMode && (HandleAttributes & OBJ_KERNEL_HANDLE))
1373  {
1374  return STATUS_INVALID_PARAMETER_6;
1375  }
1376 
1377  // Fix the source handle if the source process is the System process.
1378  if (SourceProcess == PsInitialSystemProcess)
1379  {
1380  SourceHandle = MakeKernelHandle(SourceHandle);
1381  referenceMode = KernelMode;
1382  }
1383  else
1384  {
1385  referenceMode = AccessMode;
1386  }
1387 
1388  // Closing handles in the current process from kernel-mode is *bad*.
1389 
1390  // Example: the handle being closed is a handle to the file object
1391  // on which this very request is being sent. Deadlock.
1392  //
1393  // If we add the current process check, the handle can't possibly
1394  // be the one the request is being sent on, since system calls
1395  // only operate on handles from the current process.
1396  if (SourceProcess == PsGetCurrentProcess())
1397  {
1398  if (Options & DUPLICATE_CLOSE_SOURCE)
1399  return STATUS_CANT_TERMINATE_SELF;
1400  }
1401 
1402  // Check if we need to attach to the source process.
1403  if (SourceProcess != PsGetCurrentProcess())
1404  {
1405  KeStackAttachProcess(SourceProcess, &apcState);
1406  sourceAttached = TRUE;
1407  }
1408 
1409  // If the caller wants us to close the source handle, do it now.
1410  if (Options & DUPLICATE_CLOSE_SOURCE)
1411  {
1412  status = ObCloseHandle(SourceHandle, referenceMode);
1413 
1414  if (sourceAttached)
1415  KeUnstackDetachProcess(&apcState);
1416 
1417  return status;
1418  }
1419 
1420  // Reference the object and detach from the source process.
1421  status = ObReferenceObjectByHandle(
1422  SourceHandle,
1423  0,
1424  NULL,
1425  referenceMode,
1426  &object,
1427  NULL
1428  );
1429  if (sourceAttached)
1430  KeUnstackDetachProcess(&apcState);
1431 
1432  if (!NT_SUCCESS(status))
1433  return status;
1434 
1435  // Check if we need to attach to the target process.
1436  if (TargetProcess != PsGetCurrentProcess())
1437  {
1438  KeStackAttachProcess(TargetProcess, &apcState);
1439  targetAttached = TRUE;
1440  }
1441 
1442  // Open the object and detach from the target process.
1443 
1444  status = ObOpenObjectByPointer(
1445  object,
1446  HandleAttributes,
1447  NULL,
1448  DesiredAccess,
1449  NULL,
1450  KernelMode,
1451  &objectHandle
1452  );
1453  ObDereferenceObject(object);
1454 
1455  if (targetAttached)
1456  KeUnstackDetachProcess(&apcState);
1457 
1458  if (NT_SUCCESS(status))
1459  *TargetHandle = objectHandle;
1460  else
1461  *TargetHandle = NULL;
1462 
1463  return status;
1464 }
1465 
1488  __in HANDLE SourceProcessHandle,
1489  __in HANDLE SourceHandle,
1490  __in_opt HANDLE TargetProcessHandle,
1491  __out_opt PHANDLE TargetHandle,
1492  __in ACCESS_MASK DesiredAccess,
1493  __in ULONG HandleAttributes,
1494  __in ULONG Options,
1495  __in KPROCESSOR_MODE AccessMode
1496  )
1497 {
1498  NTSTATUS status = STATUS_SUCCESS;
1499  PEPROCESS sourceProcess = NULL;
1500  PEPROCESS targetProcess = NULL;
1501  HANDLE targetHandle;
1502 
1503  PAGED_CODE();
1504 
1505  if (AccessMode != KernelMode)
1506  {
1507  __try
1508  {
1509  if (TargetHandle)
1510  ProbeForWrite(TargetHandle, sizeof(HANDLE), 1);
1511  }
1512  __except (EXCEPTION_EXECUTE_HANDLER)
1513  {
1514  return GetExceptionCode();
1515  }
1516  }
1517 
1518  status = ObReferenceObjectByHandle(
1519  SourceProcessHandle,
1520  0,
1521  *PsProcessType,
1522  AccessMode,
1523  &sourceProcess,
1524  NULL
1525  );
1526 
1527  if (!NT_SUCCESS(status))
1528  return status;
1529 
1530  // Target handle is optional.
1531  if (TargetProcessHandle)
1532  {
1533  status = ObReferenceObjectByHandle(
1534  TargetProcessHandle,
1535  0,
1536  *PsProcessType,
1537  AccessMode,
1538  &targetProcess,
1539  NULL
1540  );
1541 
1542  if (!NT_SUCCESS(status))
1543  {
1544  ObDereferenceObject(sourceProcess);
1545  return status;
1546  }
1547  }
1548 
1549  status = KphDuplicateObject(
1550  sourceProcess,
1551  targetProcess,
1552  SourceHandle,
1553  &targetHandle,
1554  DesiredAccess,
1555  HandleAttributes,
1556  Options,
1557  AccessMode
1558  );
1559 
1560  if (TargetHandle)
1561  {
1562  if (AccessMode != KernelMode)
1563  {
1564  __try
1565  {
1566  *TargetHandle = targetHandle;
1567  }
1568  __except (EXCEPTION_EXECUTE_HANDLER)
1569  {
1570  status = STATUS_ACCESS_VIOLATION;
1571  }
1572  }
1573  else
1574  {
1575  *TargetHandle = targetHandle;
1576  }
1577  }
1578 
1579  if (targetProcess)
1580  ObDereferenceObject(targetProcess);
1581 
1582  ObDereferenceObject(sourceProcess);
1583 
1584  return status;
1585 }
1586 
1588  __out PHANDLE ObjectHandle,
1589  __in ACCESS_MASK DesiredAccess,
1590  __in POBJECT_ATTRIBUTES ObjectAttributes,
1591  __in POBJECT_TYPE ObjectType,
1592  __in KPROCESSOR_MODE AccessMode
1593  )
1594 {
1595  NTSTATUS status = STATUS_SUCCESS;
1596  HANDLE objectHandle;
1597  UNICODE_STRING capturedObjectName;
1598  OBJECT_ATTRIBUTES objectAttributes = { 0 };
1599 
1600  PAGED_CODE();
1601 
1602  if (AccessMode != KernelMode)
1603  {
1604  __try
1605  {
1606  ProbeForWrite(ObjectHandle, sizeof(HANDLE), sizeof(HANDLE));
1607  ProbeForRead(ObjectAttributes, sizeof(OBJECT_ATTRIBUTES), sizeof(ULONG));
1608  memcpy(&objectAttributes, ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
1609  }
1610  __except (EXCEPTION_EXECUTE_HANDLER)
1611  {
1612  return GetExceptionCode();
1613  }
1614  }
1615  else
1616  {
1617  memcpy(&objectAttributes, ObjectAttributes, sizeof(OBJECT_ATTRIBUTES));
1618  }
1619 
1620  // We're opening an object, so we need a name.
1621  if (!objectAttributes.ObjectName)
1622  return STATUS_INVALID_PARAMETER;
1623 
1624  // Make sure we don't create a kernel handle if we're from user-mode.
1625  if (AccessMode != KernelMode && (objectAttributes.Attributes & OBJ_KERNEL_HANDLE))
1626  return STATUS_INVALID_PARAMETER;
1627 
1628  // Make sure the root directory handle isn't a kernel handle if
1629  // we're from user-mode.
1630  if (AccessMode != KernelMode && IsKernelHandle(objectAttributes.RootDirectory))
1631  return STATUS_INVALID_PARAMETER;
1632 
1633  // Capture the ObjectName string.
1634  status = KphCaptureUnicodeString(objectAttributes.ObjectName, &capturedObjectName);
1635 
1636  if (!NT_SUCCESS(status))
1637  return status;
1638 
1639  // Set the new string in the object attributes.
1640  objectAttributes.ObjectName = &capturedObjectName;
1641  // Make sure the SecurityDescriptor and SecurityQualityOfService fields are NULL
1642  // since we haven't probed them. They don't apply anyway because we're opening an
1643  // object here.
1644  objectAttributes.SecurityDescriptor = NULL;
1645  objectAttributes.SecurityQualityOfService = NULL;
1646 
1647  // Open the object.
1648  status = ObOpenObjectByName(
1649  &objectAttributes,
1650  ObjectType,
1651  KernelMode,
1652  NULL,
1653  DesiredAccess,
1654  NULL,
1655  &objectHandle
1656  );
1657 
1658  // Free the captured ObjectName.
1659  KphFreeCapturedUnicodeString(&capturedObjectName);
1660 
1661  // Pass the handle back.
1662  if (AccessMode != KernelMode)
1663  {
1664  __try
1665  {
1666  *ObjectHandle = objectHandle;
1667  }
1668  __except (EXCEPTION_EXECUTE_HANDLER)
1669  {
1670  status = GetExceptionCode();
1671  }
1672  }
1673  else
1674  {
1675  *ObjectHandle = objectHandle;
1676  }
1677 
1678  return status;
1679 }