Windows-Server-2003/base/ntos/ps/psenum.c

919 lines
22 KiB
C
Raw Normal View History

2024-08-04 01:28:15 +02:00
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
psenum.c
Abstract:
This module enumerates the actve processes in the system
Author:
Neill clift (NeillC) 23-Mar-2000
Revision History:
--*/
#include "psp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PsEnumProcesses)
#pragma alloc_text(PAGE, PsGetNextProcess)
#pragma alloc_text(PAGE, PsQuitNextProcess)
#pragma alloc_text(PAGE, PsEnumProcessThreads)
#pragma alloc_text(PAGE, PsGetNextProcessThread)
#pragma alloc_text(PAGE, PsQuitNextProcessThread)
#pragma alloc_text(PAGE, PsGetNextJob)
#pragma alloc_text(PAGE, PsGetNextJobProcess)
#pragma alloc_text(PAGE, PsQuitNextJob)
#pragma alloc_text(PAGE, PsQuitNextJobProcess)
#pragma alloc_text(PAGE, PspGetNextJobProcess)
#pragma alloc_text(PAGE, PspQuitNextJobProcess)
#pragma alloc_text(PAGE, NtGetNextProcess)
#pragma alloc_text(PAGE, NtGetNextThread)
#endif
NTSTATUS
PsEnumProcesses (
IN PROCESS_ENUM_ROUTINE CallBack,
IN PVOID Context
)
/*++
Routine Description:
This function calls the callback routine for each active process in the system.
Process objects in the process of being deleted are skipped.
Returning anything but a success code from the callback routine terminates the enumeration at that point.
Processes may be referenced and used later safely.
Arguments:
CallBack - Routine to be called with its first parameter the enumerated process
Return Value:
NTSTATUS - Status of call
--*/
{
PLIST_ENTRY ListEntry;
PEPROCESS Process, NewProcess;
PETHREAD CurrentThread;
NTSTATUS Status;
Process = NULL;
CurrentThread = PsGetCurrentThread ();
PspLockProcessList (CurrentThread);
for (ListEntry = PsActiveProcessHead.Flink;
ListEntry != &PsActiveProcessHead;
ListEntry = ListEntry->Flink) {
NewProcess = CONTAINING_RECORD (ListEntry, EPROCESS, ActiveProcessLinks);
if (ObReferenceObjectSafe (NewProcess)) {
PspUnlockProcessList (CurrentThread);
if (Process != NULL) {
ObDereferenceObject (Process);
}
Process = NewProcess;
Status = CallBack (Process, Context);
if (!NT_SUCCESS (Status)) {
ObDereferenceObject (Process);
return Status;
}
PspLockProcessList (CurrentThread);
}
}
PspUnlockProcessList (CurrentThread);
if (Process != NULL) {
ObDereferenceObject (Process);
}
return STATUS_SUCCESS;
}
PEPROCESS
PsGetNextProcess (
IN PEPROCESS Process
)
/*++
Routine Description:
This function allows code to enumerate all the active processes in the system.
The first process (if Process is NULL) or subsequent process (if process not NULL) is returned on
each call.
If process is not NULL then this process must have previously been obtained by a call to PsGetNextProcess.
Enumeration may be terminated early by calling PsQuitNextProcess on the last non-NULL process
returned by PsGetNextProcess.
Processes may be referenced and used later safely.
For example, to enumerate all system processes in a loop use this code fragment:
for (Process = PsGetNextProcess (NULL);
Process != NULL;
Process = PsGetNextProcess (Process)) {
...
...
//
// Early terminating conditions are handled like this:
//
if (NeedToBreakOutEarly) {
PsQuitNextProcess (Process);
break;
}
}
Arguments:
Process - Process to get the next process from or NULL for the first process
Return Value:
PEPROCESS - Next process or NULL if no more processes available
--*/
{
PEPROCESS NewProcess = NULL;
PETHREAD CurrentThread;
PLIST_ENTRY ListEntry;
CurrentThread = PsGetCurrentThread ();
PspLockProcessList (CurrentThread);
for (ListEntry = (Process == NULL) ? PsActiveProcessHead.Flink : Process->ActiveProcessLinks.Flink;
ListEntry != &PsActiveProcessHead;
ListEntry = ListEntry->Flink) {
NewProcess = CONTAINING_RECORD (ListEntry, EPROCESS, ActiveProcessLinks);
//
// Processes are removed from this list during process objected deletion (object reference count goes
// to zero). To prevent double deletion of the process we need to do a safe reference here.
//
if (ObReferenceObjectSafe (NewProcess)) {
break;
}
NewProcess = NULL;
}
PspUnlockProcessList (CurrentThread);
if (Process != NULL) {
ObDereferenceObject (Process);
}
return NewProcess;
}
VOID
PsQuitNextProcess (
IN PEPROCESS Process
)
/*++
Routine Description:
This function is used to terminate early a process enumeration using PsGetNextProcess
Arguments:
Process - Non-NULL process previously obtained by a call to PsGetNextProcess.
Return Value:
None
--*/
{
ObDereferenceObject (Process);
}
PETHREAD
PsGetNextProcessThread (
IN PEPROCESS Process,
IN PETHREAD Thread
)
/*++
Routine Description:
This function is used to enumerate the threads in a process.
Arguments:
Process - Process to enumerate
Thread - Thread to start enumeration from. This must have been obtained from previous call to
PsGetNextProcessThread. If NULL enumeration starts at the first non-terminating thread in the process.
Return Value:
PETHREAD - Pointer to a non-terminated process thread or a NULL if there are non. This thread must be passed
either to another call to PsGetNextProcessThread or PsQuitNextProcessThread.
--*/
{
PLIST_ENTRY ListEntry;
PETHREAD NewThread, CurrentThread;
PAGED_CODE ();
CurrentThread = PsGetCurrentThread ();
PspLockProcessShared (Process, CurrentThread);
for (ListEntry = (Thread == NULL) ? Process->ThreadListHead.Flink : Thread->ThreadListEntry.Flink;
;
ListEntry = ListEntry->Flink) {
if (ListEntry != &Process->ThreadListHead) {
NewThread = CONTAINING_RECORD (ListEntry, ETHREAD, ThreadListEntry);
//
// Don't reference a thread thats in its delete routine
//
if (ObReferenceObjectSafe (NewThread)) {
break;
}
} else {
NewThread = NULL;
break;
}
}
PspUnlockProcessShared (Process, CurrentThread);
if (Thread != NULL) {
ObDereferenceObject (Thread);
}
return NewThread;
}
VOID
PsQuitNextProcessThread (
IN PETHREAD Thread
)
/*++
Routine Description:
This function quits thread enumeration early.
Arguments:
Thread - Thread obtained from a call to PsGetNextProcessThread
Return Value:
None.
--*/
{
ObDereferenceObject (Thread);
}
NTSTATUS
PsEnumProcessThreads (
IN PEPROCESS Process,
IN THREAD_ENUM_ROUTINE CallBack,
IN PVOID Context
)
/*++
Routine Description:
This function calls the callback routine for each active thread in the process.
Thread objects in the process of being deleted are skipped.
Returning anything but a success code from the callback routine terminates the enumeration at that point.
Thread may be referenced and used later safely.
Arguments:
CallBack - Routine to be called with its first parameter the enumerated process
Return Value:
NTSTATUS - Status of call
--*/
{
NTSTATUS Status;
PETHREAD Thread;
Status = STATUS_SUCCESS;
for (Thread = PsGetNextProcessThread (Process, NULL);
Thread != NULL;
Thread = PsGetNextProcessThread (Process, Thread)) {
Status = CallBack (Process, Thread, Context);
if (!NT_SUCCESS (Status)) {
PsQuitNextProcessThread (Thread);
break;
}
}
return Status;
}
PEJOB
PsGetNextJob (
IN PEJOB Job
)
/*++
Routine Description:
This function allows code to enumerate all the active jobs in the system.
The first job (if Job is NULL) or subsequent jobs (if Job not NULL) is returned on
each call.
If Job is not NULL then this job must have previously been obtained by a call to PsGetNextJob.
Enumeration may be terminated early by calling PsQuitNextJob on the last non-NULL job
returned by PsGetNextJob.
Jobs may be referenced and used later safely.
For example, to enumerate all system jobs in a loop use this code fragment:
for (Job = PsGetNextJob (NULL);
Job != NULL;
Job = PsGetNextJob (Job)) {
...
...
//
// Early terminating conditions are handled like this:
//
if (NeedToBreakOutEarly) {
PsQuitNextJob (Job);
break;
}
}
Arguments:
Job - Job from a previous call to PsGetNextJob or NULL for the first job in the system
Return Value:
PEJOB - Next job in the system or NULL if none available.
--*/
{
PEJOB NewJob = NULL;
PLIST_ENTRY ListEntry;
PETHREAD CurrentThread;
CurrentThread = PsGetCurrentThread ();
PspLockJobListShared (CurrentThread);
for (ListEntry = (Job == NULL) ? PspJobList.Flink : Job->JobLinks.Flink;
ListEntry != &PspJobList;
ListEntry = ListEntry->Flink) {
NewJob = CONTAINING_RECORD (ListEntry, EJOB, JobLinks);
//
// Jobs are removed from this list during job objected deletion (object reference count goes
// to zero). To prevent double deletion of the job we need to do a safe reference here.
//
if (ObReferenceObjectSafe (NewJob)) {
break;
}
NewJob = NULL;
}
PspUnlockJobListShared (CurrentThread);
if (Job != NULL) {
ObDereferenceObject (Job);
}
return NewJob;
}
VOID
PsQuitNextJob (
IN PEJOB Job
)
/*++
Routine Description:
This function is used to terminate early a job enumeration using PsGetNextJob
Arguments:
Job - Non-NULL job previously obtained by a call to PsGetNextJob.
Return Value:
None
--*/
{
ObDereferenceObject (Job);
}
PEPROCESS
PsGetNextJobProcess (
IN PEJOB Job,
IN PEPROCESS Process
)
/*++
Routine Description:
This function is used to enumerate the processes in a job.
Arguments:
Job - Job to Enumerate
Process - Process to start enumeration from. This must have been obtained from previous call to
PsGetNextJobProcess. If NULL enumeration starts at the first non-terminating process in the Job.
Return Value:
PEPROCESS - Pointer to a non-terminated process or a NULL if there are non. This process must be passed
either to another call to PsGetNextJobProcess or PsQuitNextJobProcess.
--*/
{
PLIST_ENTRY ListEntry;
PEPROCESS NewProcess;
PETHREAD CurrentThread;
PAGED_CODE ();
CurrentThread = PsGetCurrentThread ();
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
ExAcquireResourceExclusiveLite (&Job->JobLock, TRUE);
for (ListEntry = (Process == NULL) ? Job->ProcessListHead.Flink : Process->JobLinks.Flink;
;
ListEntry = ListEntry->Flink) {
if (ListEntry != &Job->ProcessListHead) {
NewProcess = CONTAINING_RECORD (ListEntry, EPROCESS, JobLinks);
//
// Don't reference a process thats in its delete routine
//
if (ObReferenceObjectSafe (NewProcess)) {
break;
}
} else {
NewProcess = NULL;
break;
}
}
ExReleaseResourceLite (&Job->JobLock);
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
if (Process != NULL) {
ObDereferenceObject (Process);
}
return NewProcess;
}
VOID
PsQuitNextJobProcess (
IN PEPROCESS Process
)
/*++
Routine Description:
This function quits job process enumeration early.
Arguments:
Process - Process obtained from a call to PsGetNextJobProcess
Return Value:
None.
--*/
{
ObDereferenceObject (Process);
}
PEPROCESS
PspGetNextJobProcess (
IN PEJOB Job,
IN PEPROCESS Process
)
/*++
Routine Description:
This function is used to enumerate the processes in a job with the job lock held.
Arguments:
Job - Job to Enumerate
Process - Process to start enumeration from. This must have been obtained from previous call to
PsGetNextJobProcess. If NULL enumeration starts at the first non-terminating process in the Job.
Return Value:
PEPROCESS - Pointer to a non-terminated process or a NULL if there are non. This process must be passed
either to another call to PsGetNextJobProcess or PsQuitNextJobProcess.
--*/
{
PLIST_ENTRY ListEntry;
PEPROCESS NewProcess;
PAGED_CODE ();
for (ListEntry = (Process == NULL) ? Job->ProcessListHead.Flink : Process->JobLinks.Flink;
;
ListEntry = ListEntry->Flink) {
if (ListEntry != &Job->ProcessListHead) {
NewProcess = CONTAINING_RECORD (ListEntry, EPROCESS, JobLinks);
//
// Don't reference a process thats in its delete routine
//
if (ObReferenceObjectSafe (NewProcess)) {
break;
}
} else {
NewProcess = NULL;
break;
}
}
if (Process != NULL) {
ObDereferenceObjectDeferDelete (Process);
}
return NewProcess;
}
VOID
PspQuitNextJobProcess (
IN PEPROCESS Process
)
/*++
Routine Description:
This function quits job process enumeration early.
Arguments:
Process - Process obtained from a call to PsGetNextJobProcess
Return Value:
None.
--*/
{
ObDereferenceObjectDeferDelete (Process);
}
NTSTATUS
NtGetNextProcess (
IN HANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN ULONG HandleAttributes,
IN ULONG Flags,
OUT PHANDLE NewProcessHandle
)
/*++
Routine Description:
This function gets the next for first process in the list of all processes in the system
Arguments:
ProcessHandle - Process obtained from a previous call to NtGetNextProcess or NULL for the first process
DesiredAccess - Access requested for process handle
HandleAttributes - Handle attributes requested.
Flags - Flags for the operation
NewProcessHandle - Pointer to a handle value that is returned on sucess
Return Value:
NTSTATUS - Status of operation.
--*/
{
KPROCESSOR_MODE PreviousMode;
PEPROCESS Process, NewProcess;
NTSTATUS Status;
ACCESS_STATE AccessState;
AUX_ACCESS_DATA AuxData;
HANDLE Handle;
PAGED_CODE ();
PreviousMode = KeGetPreviousMode ();
//
// Sanitize handle attributes
//
HandleAttributes = ObSanitizeHandleAttributes (HandleAttributes, PreviousMode);
//
// Validate pointer arguments
//
try {
if (PreviousMode != KernelMode) {
ProbeForWriteHandle (NewProcessHandle);
}
*NewProcessHandle = NULL;
} except (ExSystemExceptionFilter ()) {
return GetExceptionCode ();
}
//
// Check for inclusion of reserved flags and reject the call if present
//
if (Flags != 0) {
return STATUS_INVALID_PARAMETER;
}
if (ProcessHandle == NULL) {
Process = NULL;
} else {
Status = ObReferenceObjectByHandle (ProcessHandle,
0,
PsProcessType,
PreviousMode,
&Process,
NULL);
if (!NT_SUCCESS (Status)) {
return Status;
}
}
NewProcess = PsGetNextProcess (Process);
if (NewProcess == NULL) {
return STATUS_NO_MORE_ENTRIES;
}
Status = SeCreateAccessState (&AccessState,
&AuxData,
DesiredAccess,
&PsProcessType->TypeInfo.GenericMapping);
if (!NT_SUCCESS (Status)) {
ObDereferenceObject (NewProcess);
return Status;
}
if (SeSinglePrivilegeCheck (SeDebugPrivilege, PreviousMode)) {
if (AccessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) {
AccessState.PreviouslyGrantedAccess |= PROCESS_ALL_ACCESS;
} else {
AccessState.PreviouslyGrantedAccess |= AccessState.RemainingDesiredAccess;
}
AccessState.RemainingDesiredAccess = 0;
}
while (1) {
if (NewProcess->GrantedAccess != 0) {
Status = ObOpenObjectByPointer (NewProcess,
HandleAttributes,
&AccessState,
0,
PsProcessType,
PreviousMode,
&Handle);
if (NT_SUCCESS (Status)) {
try {
*NewProcessHandle = Handle;
} except (ExSystemExceptionFilter ()) {
Status = GetExceptionCode ();
}
break;
}
if (Status != STATUS_ACCESS_DENIED) {
break;
}
}
NewProcess = PsGetNextProcess (NewProcess);
if (NewProcess == NULL) {
Status = STATUS_NO_MORE_ENTRIES;
break;
}
}
SeDeleteAccessState (&AccessState);
if (NewProcess != NULL) {
ObDereferenceObject (NewProcess);
}
return Status;
}
NTSTATUS
NtGetNextThread (
IN HANDLE ProcessHandle,
IN HANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN ULONG HandleAttributes,
IN ULONG Flags,
OUT PHANDLE NewThreadHandle
)
/*++
Routine Description:
This function gets the next for first thread in a process
Arguments:
ProcessHandle - Process that is being enumerated
ThreadHandle - Last thread returned by the enumeration routine or NULL if the first is required
DesiredAccess - Access requested for thread handle
HandleAttributes - Handle attributes requested.
Flags - Flags for the operation
NewThreadHandle - Pointer to a handle value that is returned on sucess
Return Value:
NTSTATUS - Status of operation.
--*/
{
KPROCESSOR_MODE PreviousMode;
PEPROCESS Process;
PETHREAD Thread, NewThread;
NTSTATUS Status;
ACCESS_STATE AccessState;
AUX_ACCESS_DATA AuxData;
HANDLE Handle;
PAGED_CODE ();
PreviousMode = KeGetPreviousMode ();
//
// Sanitize handle attributes
//
HandleAttributes = ObSanitizeHandleAttributes (HandleAttributes, PreviousMode);
//
// Validate pointer arguments
//
try {
if (PreviousMode != KernelMode) {
ProbeForWriteHandle (NewThreadHandle);
}
*NewThreadHandle = NULL;
} except (ExSystemExceptionFilter ()) {
return GetExceptionCode ();
}
//
// Check for inclusion of reserved flags and reject the call if present
//
if (Flags != 0) {
return STATUS_INVALID_PARAMETER;
}
Status = ObReferenceObjectByHandle (ProcessHandle,
PROCESS_QUERY_INFORMATION,
PsProcessType,
PreviousMode,
&Process,
NULL);
if (!NT_SUCCESS (Status)) {
return Status;
}
if (ThreadHandle == NULL) {
Thread = NULL;
} else {
Status = ObReferenceObjectByHandle (ThreadHandle,
0,
PsProcessType,
PreviousMode,
&Thread,
NULL);
if (!NT_SUCCESS (Status)) {
ObDereferenceObject (Process);
return Status;
}
//
// Make sure
//
if (THREAD_TO_PROCESS (Thread) != Process) {
ObDereferenceObject (Thread);
ObDereferenceObject (Process);
return STATUS_INVALID_PARAMETER;
}
}
NewThread = PsGetNextProcessThread (Process, Thread);
if (NewThread == NULL) {
ObDereferenceObject (Process);
return STATUS_NO_MORE_ENTRIES;
}
Status = SeCreateAccessState (&AccessState,
&AuxData,
DesiredAccess,
&PsProcessType->TypeInfo.GenericMapping);
if (!NT_SUCCESS (Status)) {
ObDereferenceObject (Process);
ObDereferenceObject (NewThread);
return Status;
}
if (SeSinglePrivilegeCheck (SeDebugPrivilege, PreviousMode)) {
if (AccessState.RemainingDesiredAccess & MAXIMUM_ALLOWED) {
AccessState.PreviouslyGrantedAccess |= PROCESS_ALL_ACCESS;
} else {
AccessState.PreviouslyGrantedAccess |= AccessState.RemainingDesiredAccess;
}
AccessState.RemainingDesiredAccess = 0;
}
while (1) {
if (NewThread->GrantedAccess != 0) {
Status = ObOpenObjectByPointer (NewThread,
HandleAttributes,
&AccessState,
0,
PsThreadType,
PreviousMode,
&Handle);
if (NT_SUCCESS (Status)) {
try {
*NewThreadHandle = Handle;
} except (ExSystemExceptionFilter ()) {
Status = GetExceptionCode ();
}
break;
}
if (Status != STATUS_ACCESS_DENIED) {
break;
}
}
NewThread = PsGetNextProcessThread (Process, NewThread);
if (NewThread == NULL) {
Status = STATUS_NO_MORE_ENTRIES;
break;
}
}
SeDeleteAccessState (&AccessState);
ObDereferenceObject (Process);
if (NewThread != NULL) {
ObDereferenceObject (NewThread);
}
return Status;
}