$theTitle=wp_title(" - ", false); if($theTitle != "") { ?>
About Virtualization, VDI, SBC, Application Compatibility and anything else I feel like
20 Jan // php the_time('Y') ?>
As you may know, you can enumerate processes of a specific Terminal Server or Citrix session using the NtQuerySystemInformation function.
On x86 system the code below works fine:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | var Status : NTSTATUS; RetLength : DWORD; SessionInfo : SYSTEM_SESSION_PROCESS_INFORMATION; CurrentProcess : PSystemProcesses; begin SessionInfo.SessionId := SessionId; SessionInfo.Buffer := nil; SessionInfo.SizeOfBuf := 0; Status := NtQuerySystemInformation(SystemSessionProcessesInformation, @SessionInfo, SizeOf(SessionInfo), @RetLength); while (Status = STATUS_INFO_LENGTH_MISMATCH) do begin SessionInfo.SizeOfBuf := RetLength; SessionInfo.Buffer := GetMemory(RetLength); try Status := NtQuerySystemInformation(SystemSessionProcessesInformation, @SessionInfo, SizeOf(SessionInfo), @RetLength); if NT_SUCCESS(Status) then begin CurrentProcess := SessionInfo.Buffer; while True do begin // Your code here if (CurrentProcess^.NextEntryDelta = 0) then Break; Cardinal(CurrentProcess) := Cardinal(CurrentProcess) + CurrentProcess^.NextEntryDelta; end; Exit; end; finally FreeMemory(SessionInfo.Buffer); end; end; SetLastError(RtlNtStatusToDosError(Status)); RaiseLastOSError; end; |
While this works fine on Windows XP and 2003 x86, it fails to work correctly on the x64 versions of Windows XP and 2003 (or maybe even higher).
The problem is that RetLength is always SizeOf(SYSTEM_SESSION_PROCESS_INFORMATION) and thus we are in an endless loop!
If you disassemble an a call to NtQuerySystemInformation, for example in the CreateToolhelp32Snapshot API, you will see that it just executes NtQuerySystemInformation and increases the buffer size in a loop.
Ok, let’s do it in the same way 🙂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | var Status : NTSTATUS; SessionInfo : SYSTEM_SESSION_PROCESS_INFORMATION; CurrentProcess : PSystemProcesses; begin SessionInfo.SessionId := SessionId; SessionInfo.Buffer := nil; SessionInfo.SizeOfBuf := 0; Status := STATUS_INFO_LENGTH_MISMATCH; while (Status = STATUS_INFO_LENGTH_MISMATCH) do begin SessionInfo.SizeOfBuf := SessionInfo.SizeOfBuf + MEMORY_DELTA_SIZE; SessionInfo.Buffer := GetMemory(SessionInfo.SizeOfBuf); try Status := NtQuerySystemInformation(SystemSessionProcessesInformation, @SessionInfo, SizeOf(SessionInfo), nil); if NT_SUCCESS(Status) then begin CurrentProcess := SessionInfo.Buffer; while True do begin // Your code here if (CurrentProcess^.NextEntryDelta = 0) then Exit; Cardinal(CurrentProcess) := Cardinal(CurrentProcess) + CurrentProcess^.NextEntryDelta; end; Exit; end; finally FreeMemory(SessionInfo.Buffer); end; end; SetLastError(RtlNtStatusToDosError(Status)); RaiseLastOSError; end; |
But this code doesn’t work as well. The reason is that you need to place the whole buffer in a contiguous memory area.
So finally the working code is :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | var Status : NTSTATUS; SessionInfo : PSYSTEM_SESSION_PROCESS_INFORMATION; CurrentProcess : PSystemProcesses; Size : DWORD; begin Size := 0; Status := STATUS_INFO_LENGTH_MISMATCH; while (Status = STATUS_INFO_LENGTH_MISMATCH) do begin Size := Size + MEMORY_DELTA_SIZE; SessionInfo := GetMemory(Size); try SessionInfo^.SessionId := SessionId; SessionInfo^.SizeOfBuf := Size - SizeOf(SessionInfo^); Cardinal(SessionInfo^.Buffer) := Cardinal(@SessionInfo^.Buffer) + SizeOf(SessionInfo^.Buffer); Status := NtQuerySystemInformation(SystemSessionProcessesInformation, SessionInfo, Size, nil); if NT_SUCCESS(Status) then begin CurrentProcess := SessionInfo^.Buffer; while True do begin // Your core here if (CurrentProcess^.NextEntryDelta = 0) then Exit; Cardinal(CurrentProcess) := Cardinal(CurrentProcess) + CurrentProcess^.NextEntryDelta; end; Exit; end; finally FreeMemory(SessionInfo); end; end; SetLastError(RtlNtStatusToDosError(Status)); RaiseLastOSError; end; |
One Response for "Enumerating Session Processes with NtQuerySystemInformation"
[…] 13) Getting a session process list on x64 may fail if you do not allocate buffers correctly – described here […]
Leave a reply