NTSTATUS
ExpGetProcessInformation (
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG Length OPTIONAL,
IN PULONG SessionId OPTIONAL,
IN BOOLEAN ExtendedInformation
)
{
KLOCK_QUEUE_HANDLE LockHandle;
PEPROCESS Process = NULL;
PETHREAD Thread;
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
PVOID ThreadInfo;
ULONG ThreadInfoSize;
PLIST_ENTRY NextThread;
PVOID MappedAddress;
PVOID LockVariable;
ULONG ProcessSessionId;
ULONG TotalSize = 0;
ULONG NextEntryOffset = 0;
PUCHAR Src;
PWCHAR SrcW;
PWSTR Dst;
ULONG n, nc;
NTSTATUS status = STATUS_SUCCESS, status1;
PUNICODE_STRING pImageFileName;
if (ARGUMENT_PRESENT(Length)) {
*Length = 0;
}
if (SystemInformationLength > 0) {
status1 = ExLockUserBuffer (SystemInformation,
SystemInformationLength,
KeGetPreviousMode(),
IoWriteAccess,
&MappedAddress,
&LockVariable);
if (!NT_SUCCESS(status1)) {
return status1;
}
} else {
//
// This indicates the caller just wants to know the size of the
// buffer to allocate but is not prepared to accept any data content
// in this instance.
//
MappedAddress = NULL;
LockVariable = NULL;
}
if (ExtendedInformation) {
ThreadInfoSize = sizeof(SYSTEM_EXTENDED_THREAD_INFORMATION);
} else {
ThreadInfoSize = sizeof(SYSTEM_THREAD_INFORMATION);
}
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) MappedAddress;
try {
//
// Do the idle process first then all the other processes.
//
for (Process = PsIdleProcess;
Process != NULL;
Process = PsGetNextProcess ((Process == PsIdleProcess) ? NULL : Process)) {
//
// If the process is marked as exiting, the executive process has
// no active threads, the kernel process has no threads, and the
// kernel process has been signaled, then skip the process.
//
// N.B. It is safe to examine the kernel thread list without a
// lock since no list pointers are dereferenced.
//
if (((Process->Flags & PS_PROCESS_FLAGS_PROCESS_EXITING) != 0) &&
(Process->Pcb.Header.SignalState != 0) &&
(Process->ActiveThreads == 0) &&
(IsListEmpty(&Process->Pcb.ThreadListHead) == TRUE)) {
continue;
}
if (ARGUMENT_PRESENT(SessionId) && (Process == PsIdleProcess)) {
continue;
}
ProcessSessionId = MmGetSessionId (Process);
if ((ARGUMENT_PRESENT(SessionId)) &&
(ProcessSessionId != *SessionId)) {
continue;
}
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
((PUCHAR)MappedAddress + TotalSize);
NextEntryOffset = sizeof(SYSTEM_PROCESS_INFORMATION);
TotalSize += sizeof(SYSTEM_PROCESS_INFORMATION);
if (TotalSize > SystemInformationLength) {
status = STATUS_INFO_LENGTH_MISMATCH;
if (ARGUMENT_PRESENT(Length) == FALSE) {
leave;
}
} else {
//
// Get information for each process.
//
ExpCopyProcessInfo (ProcessInfo, Process, ExtendedInformation);
ProcessInfo->NumberOfThreads = 0;
ProcessInfo->NextEntryOffset = 0;
//
// Store the Remote Terminal SessionId
//
ProcessInfo->SessionId = ProcessSessionId;
ProcessInfo->ImageName.Buffer = NULL;
ProcessInfo->ImageName.Length = 0;
ProcessInfo->ImageName.MaximumLength = 0;
if (Process == PsIdleProcess) {
//
// Since Idle process and system process share the same
// object table, zero out idle processes handle count to
// reduce confusion
//
// Idle Process always has SessionId 0
//
ProcessInfo->HandleCount = 0;
ProcessInfo->SessionId = 0;
}
}
//
// Raise IRQL to SYNCH_LEVEL, acquire the kernel process lock, and
// get information for each thread.
//
ThreadInfo = (PVOID)(ProcessInfo + 1);
KeAcquireInStackQueuedSpinLockRaiseToSynch(&Process->Pcb.ProcessLock,
&LockHandle);
NextThread = Process->Pcb.ThreadListHead.Flink;
while (NextThread != &Process->Pcb.ThreadListHead) {
NextEntryOffset += ThreadInfoSize;
TotalSize += ThreadInfoSize;
if (TotalSize > SystemInformationLength) {
status = STATUS_INFO_LENGTH_MISMATCH;
if (ARGUMENT_PRESENT(Length) == FALSE) {
KeReleaseInStackQueuedSpinLock(&LockHandle);
leave;
}
} else {
Thread = (PETHREAD)(CONTAINING_RECORD(NextThread,
KTHREAD,
ThreadListEntry));
//
// Lock dispatcher database to get atomic view of thread
// attributes.
//
KiLockDispatcherDatabaseAtSynchLevel();
ExpCopyThreadInfo (ThreadInfo, Thread, ExtendedInformation);
KiUnlockDispatcherDatabaseFromSynchLevel();
ProcessInfo->NumberOfThreads += 1;
ThreadInfo = (PCHAR) ThreadInfo + ThreadInfoSize;
}
NextThread = NextThread->Flink;
}
//
// Unlock kernel process lock and lower IRQL to its previous value.
//
KeReleaseInStackQueuedSpinLock(&LockHandle);
//
// Get the image name.
//
if (Process != PsIdleProcess) {
//
// Try to use the real image name if we can that not limited to 16 characters
//
Dst = (PWSTR)(ThreadInfo);
status1 = SeLocateProcessImageName (Process, &pImageFileName);
if (NT_SUCCESS (status1)) {
n = pImageFileName->Length;
if (n == 0) {
ExFreePool (pImageFileName);
}
} else {
n = 0;
}
if (n) {
SrcW = pImageFileName->Buffer + n / sizeof (WCHAR);
while (SrcW != pImageFileName->Buffer) {
if (*--SrcW == L'\\') {
SrcW = SrcW + 1;
break;
}
}
nc = n - (ULONG)(SrcW - pImageFileName->Buffer) * sizeof (WCHAR);
n = ROUND_UP (nc + 1, sizeof(LARGE_INTEGER));
TotalSize += n;
NextEntryOffset += n;
if (TotalSize > SystemInformationLength) {
status = STATUS_INFO_LENGTH_MISMATCH;
if (ARGUMENT_PRESENT(Length) == FALSE) {
ExFreePool (pImageFileName);
leave;
}
} else {
RtlCopyMemory (Dst, SrcW, nc);
Dst += nc / sizeof (WCHAR);
*Dst++ = L'\0';
}
ExFreePool (pImageFileName);
} else {
Src = Process->ImageFileName;
n = (ULONG) strlen ((PCHAR)Src);
if (n != 0) {
n = ROUND_UP( ((n + 1) * sizeof( WCHAR )), sizeof(LARGE_INTEGER) );
TotalSize += n;
NextEntryOffset += n;
if (TotalSize > SystemInformationLength) {
status = STATUS_INFO_LENGTH_MISMATCH;
if (ARGUMENT_PRESENT(Length) == FALSE) {
leave;
}
} else {
WCHAR c;
while (1) {
c = (WCHAR)*Src++;
*Dst++ = c;
if (c == L'\0') {
break;
}
}
}
}
}
if (NT_SUCCESS (status)) {
ProcessInfo->ImageName.Length = (USHORT)((PCHAR)Dst -
(PCHAR)ThreadInfo - sizeof( UNICODE_NULL ));
ProcessInfo->ImageName.MaximumLength = (USHORT)n;
//
// Set the image name to point into the user's memory.
//
ProcessInfo->ImageName.Buffer = (PWSTR)
((PCHAR)SystemInformation +
((PCHAR)(ThreadInfo) - (PCHAR)MappedAddress));
}
}
//
// Point to next process.
//
if (NT_SUCCESS (status)) {
ProcessInfo->NextEntryOffset = NextEntryOffset;
}
}
if (NT_SUCCESS(status)) {
ProcessInfo->NextEntryOffset = 0;
}
if (ARGUMENT_PRESENT(Length)) {
*Length = TotalSize;
}
} finally {
if ((Process != NULL) && (Process != PsIdleProcess)) {
PsQuitNextProcess (Process);
}
if (MappedAddress != NULL) {
ExUnlockUserBuffer (LockVariable);
}
}
return status;
}