From ecb5ca90de22ee0576286dbc1ae420e63529ef24 Mon Sep 17 00:00:00 2001 From: dahall Date: Wed, 20 May 2020 12:27:58 -0600 Subject: [PATCH] Added NtQueryInformationProcess (#123) and supporting constants and structures along with unit test. --- PInvoke/NtDll/Winternl.cs | 438 +++++++++++++++++++++++++++++++ UnitTests/PInvoke/NtDll/NtDll.csproj | 8 + UnitTests/PInvoke/NtDll/WinternlTests.cs | 58 ++++ 3 files changed, 504 insertions(+) create mode 100644 PInvoke/NtDll/Winternl.cs create mode 100644 UnitTests/PInvoke/NtDll/WinternlTests.cs diff --git a/PInvoke/NtDll/Winternl.cs b/PInvoke/NtDll/Winternl.cs new file mode 100644 index 00000000..56321a6c --- /dev/null +++ b/PInvoke/NtDll/Winternl.cs @@ -0,0 +1,438 @@ +using System; +using System.Runtime.InteropServices; +using Vanara.InteropServices; + +namespace Vanara.PInvoke +{ + /// Platform invokable enumerated types, constants and functions from ntdll.h + public static partial class NtDll + { + /// The type of process information to be retrieved. + [PInvokeData("winternl.h", MSDNShortId = "0eae7899-c40b-4a5f-9e9c-adae021885e7")] + public enum PROCESSINFOCLASS + { + /// + /// Retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a + /// unique value used by the system to identify the specified process. + /// Use the CheckRemoteDebuggerPresent and GetProcessId functions to obtain this information. + /// + [CorrespondingType(typeof(PROCESS_BASIC_INFORMATION), CorrespondingAction.Get)] + ProcessBasicInformation = 0, + + /// + /// Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the + /// process is being run under the control of a ring 3 debugger. + /// Use the CheckRemoteDebuggerPresent or IsDebuggerPresent function. + /// + [CorrespondingType(typeof(IntPtr), CorrespondingAction.Get)] + ProcessDebugPort = 7, + + /// + /// Determines whether the process is running in the WOW64 environment (WOW64 is the x86 emulator that allows Win32-based + /// applications to run on 64-bit Windows). + /// Use the IsWow64Process2 function to obtain this information. + /// + [CorrespondingType(typeof(BOOL), CorrespondingAction.Get)] + ProcessWow64Information = 26, + + /// + /// Retrieves a UNICODE_STRING value containing the name of the image file for the process. + /// Use the QueryFullProcessImageName or GetProcessImageFileName function to obtain this information. + /// + [CorrespondingType(typeof(UNICODE_STRING), CorrespondingAction.Get)] + ProcessImageFileName = 27, + + /// + /// Retrieves a ULONG value indicating whether the process is considered critical. + /// + /// Note This value can be used starting in Windows XP with SP3. Starting in Windows 8.1, IsProcessCritical should be used instead. + /// + /// + [CorrespondingType(typeof(BOOL), CorrespondingAction.Get)] + ProcessBreakOnTermination = 29, + + /// + /// Retrieves a SUBSYSTEM_INFORMATION_TYPE value indicating the subsystem type of the process. The buffer pointed to by the + /// ProcessInformation parameter should be large enough to hold a single SUBSYSTEM_INFORMATION_TYPE enumeration. + /// + [CorrespondingType(typeof(SUBSYSTEM_INFORMATION_TYPE), CorrespondingAction.Get)] + ProcessSubsystemInformation = 75, + } + + /// + /// Indicates the type of subsystem for a process or thread. This enumeration is used in NtQueryInformationProcess and + /// NtQueryInformationThread calls. + /// + // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ne-ntddk-_subsystem_information_type typedef enum + // _SUBSYSTEM_INFORMATION_TYPE { SubsystemInformationTypeWin32, SubsystemInformationTypeWSL, MaxSubsystemInformationType } + // SUBSYSTEM_INFORMATION_TYPE, *PSUBSYSTEM_INFORMATION_TYPE; + [PInvokeData("ntddk.h", MSDNShortId = "B1E334BF-AAB3-410D-8D10-A750E8459E42")] + public enum SUBSYSTEM_INFORMATION_TYPE + { + /// The subsystem type for the process or thread is Win32. + SubsystemInformationTypeWin32, + + /// + /// The subsystem type for the process or thread is Windows Subsystem for Linux (WSL). For this process, these members of the + /// PS_CREATE_NOTIFY_INFO structure are set as follows: The preceding member values may be NULL. + /// + SubsystemInformationTypeWSL, + + /// Reserved. + MaxSubsystemInformationType, + } + + /// + /// + /// [ NtQueryInformationProcess may be altered or unavailable in future versions of Windows. Applications should use the + /// alternate functions listed in this topic.] + /// + /// Retrieves information about the specified process. + /// + /// A handle to the process for which information is to be retrieved. + /// + /// + /// The type of process information to be retrieved. This parameter can be one of the following values from the + /// PROCESSINFOCLASS enumeration. + /// + /// + /// + /// Value + /// Meaning + /// + /// + /// ProcessBasicInformation
0
+ /// + /// Retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a + /// unique value used by the system to identify the specified process. Use the CheckRemoteDebuggerPresent and GetProcessId functions + /// to obtain this information. + /// + ///
+ /// + /// ProcessDebugPort
7
+ /// + /// Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the process + /// is being run under the control of a ring 3 debugger. Use the CheckRemoteDebuggerPresent or IsDebuggerPresent function. + /// + ///
+ /// + /// ProcessWow64Information
26
+ /// + /// Determines whether the process is running in the WOW64 environment (WOW64 is the x86 emulator that allows Win32-based + /// applications to run on 64-bit Windows). Use the IsWow64Process2 function to obtain this information. + /// + ///
+ /// + /// ProcessImageFileName
27
+ /// + /// Retrieves a UNICODE_STRING value containing the name of the image file for the process. Use the QueryFullProcessImageName or + /// GetProcessImageFileName function to obtain this information. + /// + ///
+ /// + /// ProcessBreakOnTermination
29
+ /// Retrieves a ULONG value indicating whether the process is considered critical. + ///
+ /// + /// ProcessSubsystemInformation
75
+ /// + /// Retrieves a SUBSYSTEM_INFORMATION_TYPE value indicating the subsystem type of the process. The buffer pointed to by the + /// ProcessInformation parameter should be large enough to hold a single SUBSYSTEM_INFORMATION_TYPE enumeration. + /// + ///
+ ///
+ /// + /// + /// + /// A pointer to a buffer supplied by the calling application into which the function writes the requested information. The size of + /// the information written varies depending on the data type of the ProcessInformationClass parameter: + /// + /// PROCESS_BASIC_INFORMATION + /// + /// When the ProcessInformationClass parameter is ProcessBasicInformation, the buffer pointed to by the ProcessInformation + /// parameter should be large enough to hold a single PROCESS_BASIC_INFORMATION structure having the following layout: + /// + /// + /// + /// The UniqueProcessId member points to the system's unique identifier for this process. Use the GetProcessId function to + /// retrieve this information. + /// + /// The PebBaseAddress member points to a PEB structure. + /// The other members of this structure are reserved for internal use by the operating system. + /// ULONG_PTR + /// + /// When the ProcessInformationClass parameter is ProcessWow64Information, the buffer pointed to by the ProcessInformation + /// parameter should be large enough to hold a ULONG_PTR. If this value is nonzero, the process is running in a WOW64 + /// environment; otherwise, if the value is equal to zero, the process is not running in a WOW64 environment. + /// + /// Use the IsWow64Process2 function to determine whether a process is running in the WOW64 environment. + /// UNICODE_STRING + /// + /// When the ProcessInformationClass parameter is ProcessImageFileName, the buffer pointed to by the ProcessInformation + /// parameter should be large enough to hold a UNICODE_STRING structure as well as the string itself. The string stored in + /// the Buffer member is the name of the image file. + /// + /// + /// If the buffer is too small, the function fails with the STATUS_INFO_LENGTH_MISMATCH error code and the ReturnLength parameter is + /// set to the required buffer size. + /// + /// + /// The size of the buffer pointed to by the ProcessInformation parameter, in bytes. + /// + /// A pointer to a variable in which the function returns the size of the requested information. If the function was successful, + /// this is the size of the information written to the buffer pointed to by the ProcessInformation parameter, but if the buffer was + /// too small, this is the minimum size of buffer needed to receive the information successfully. + /// + /// + /// The function returns an NTSTATUS success or error code. + /// + /// The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available in the DDK, and are + /// described in the DDK documentation under Kernel-Mode Driver Architecture / Design Guide / Driver Programming Techniques / + /// Logging Errors. + /// + /// + /// + /// + /// The NtQueryInformationProcess function and the structures that it returns are internal to the operating system and + /// subject to change from one release of Windows to another. To maintain the compatibility of your application, it is better to use + /// public functions mentioned in the description of the ProcessInformationClass parameter instead. + /// + /// + /// If you do use NtQueryInformationProcess, access the function through run-time dynamic linking. This gives your code an + /// opportunity to respond gracefully if the function has been changed or removed from the operating system. Signature changes, + /// however, may not be detectable. + /// + /// + /// This function has no associated import library. You must use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess __kernel_entry NTSTATUS + // NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN + // ULONG ProcessInformationLength, OUT PULONG ReturnLength ); + [DllImport(Lib.NtDll, SetLastError = false, ExactSpelling = true)] + [PInvokeData("winternl.h", MSDNShortId = "0eae7899-c40b-4a5f-9e9c-adae021885e7")] + public static extern NTStatus NtQueryInformationProcess([In] HPROCESS ProcessHandle, PROCESSINFOCLASS ProcessInformationClass, [Out] IntPtr ProcessInformation, uint ProcessInformationLength, out uint ReturnLength); + + /// + /// Retrieves information about the specified process. + /// + /// The type of the structure to retrieve. + /// A handle to the process for which information is to be retrieved. + /// + /// + /// The type of process information to be retrieved. This parameter can be one of the following values from the + /// PROCESSINFOCLASS enumeration. + /// + /// + /// + /// Value + /// Meaning + /// + /// + /// ProcessBasicInformation
0
+ /// + /// Retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a + /// unique value used by the system to identify the specified process. Use the CheckRemoteDebuggerPresent and GetProcessId functions + /// to obtain this information. + /// + ///
+ /// + /// ProcessDebugPort
7
+ /// + /// Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the process + /// is being run under the control of a ring 3 debugger. Use the CheckRemoteDebuggerPresent or IsDebuggerPresent function. + /// + ///
+ /// + /// ProcessWow64Information
26
+ /// + /// Determines whether the process is running in the WOW64 environment (WOW64 is the x86 emulator that allows Win32-based + /// applications to run on 64-bit Windows). Use the IsWow64Process2 function to obtain this information. + /// + ///
+ /// + /// ProcessImageFileName
27
+ /// + /// Retrieves a UNICODE_STRING value containing the name of the image file for the process. Use the QueryFullProcessImageName or + /// GetProcessImageFileName function to obtain this information. + /// + ///
+ /// + /// ProcessBreakOnTermination
29
+ /// Retrieves a ULONG value indicating whether the process is considered critical. + ///
+ /// + /// ProcessSubsystemInformation
75
+ /// + /// Retrieves a SUBSYSTEM_INFORMATION_TYPE value indicating the subsystem type of the process. The buffer pointed to by the + /// ProcessInformation parameter should be large enough to hold a single SUBSYSTEM_INFORMATION_TYPE enumeration. + /// + ///
+ ///
+ /// + /// The structure and associated memory for any allocated sub-types. + /// Mismatch between requested type and class. + public static NtQueryResult NtQueryInformationProcess([In] HPROCESS ProcessHandle, PROCESSINFOCLASS ProcessInformationClass) where T : struct + { + if (!CorrespondingTypeAttribute.CanGet(ProcessInformationClass, typeof(T))) throw new ArgumentException("Mismatch between requested type and class."); + var mem = new NtQueryResult(); + var status = NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, mem, mem.Size, out var sz); + if (status.Succeeded) return mem; + if (status != NTStatus.STATUS_INFO_LENGTH_MISMATCH || sz == 0) throw status.GetException(); + mem.Size = sz; + NtQueryInformationProcess(ProcessHandle, ProcessInformationClass, mem, mem.Size, out _).ThrowIfFailed(); + return mem; + } + + /// + /// [This structure may be altered in future versions of Windows.] + /// Contains process information. + /// + /// The syntax for this structure on 64-bit Windows is as follows: + // https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb typedef struct _PEB { BYTE Reserved1[2]; BYTE + // BeingDebugged; BYTE Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID + // Reserved4[3]; PVOID AtlThunkSListPtr; PVOID Reserved5; ULONG Reserved6; PVOID Reserved7; ULONG Reserved8; ULONG + // AtlThunkSListPtr32; PVOID Reserved9[45]; BYTE Reserved10[96]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE + // Reserved11[128]; PVOID Reserved12[1]; ULONG SessionId; } PEB, *PPEB; + [PInvokeData("winternl.h", MSDNShortId = "836a6b82-d3e8-4de6-808d-5476dfb51356")] + [StructLayout(LayoutKind.Sequential)] + public struct PEB + { + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + private readonly byte[] Reserved_1; + + /// Indicates whether the specified process is currently being debugged. + public byte BeingDebugged; + + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + private readonly byte[] Reserved2; + + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + private readonly IntPtr[] Reserved3; + + /// A pointer to a PEB_LDR_DATA structure that contains information about the loaded modules for the process. + public IntPtr Ldr; + + /// + /// A pointer to an RTL_USER_PROCESS_PARAMETERS structure that contains process parameter information such as the command line. + /// + public IntPtr ProcessParameters; + + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + private readonly IntPtr[] Reserved4; + + private readonly IntPtr AtlThunkSListPtr; + + /// Reserved for internal use by the operating system. + private readonly IntPtr Reserved5; + + /// Reserved for internal use by the operating system. + private readonly uint Reserved6; + + /// Reserved for internal use by the operating system. + private readonly IntPtr Reserved7; + + /// Reserved for internal use by the operating system. + private readonly uint Reserved8; + + /// + private readonly uint AtlThunkSListPtr32; + + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 45)] + private readonly IntPtr[] Reserved9; + + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 96)] + private readonly byte[] Reserved10; + + /// + private readonly IntPtr PostProcessInitRoutine; + + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + private readonly byte[] Reserved11; + + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + private readonly IntPtr[] Reserved12; + + /// The Terminal Services session identifier associated with the current process. + public uint SessionId; + } + + /// Contains information for basic process information. + /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684280(v=vs.85).aspx + [StructLayout(LayoutKind.Sequential)] + public struct PROCESS_BASIC_INFORMATION + { + /// Reserved for internal use by the operating system. + private readonly IntPtr Reserved1; + + /// Pointer to a PEB structure. + public IntPtr PebBaseAddress; + + /// Reserved for internal use by the operating system. + private readonly IntPtr Reserved2_1; + + /// Reserved for internal use by the operating system. + private readonly IntPtr Reserved2_2; + + /// System's unique identifier for this process. + public IntPtr UniqueProcessId; + + /// Reserved for internal use by the operating system. + private readonly IntPtr Reserved3; + } + + /// + /// [This structure may be altered in future versions of Windows.] + /// Contains process parameter information. + /// + // https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-rtl_user_process_parameters typedef struct + // _RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; PVOID Reserved2[10]; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLine; + // } RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + [PInvokeData("winternl.h", MSDNShortId = "e736aefa-9945-4526-84d8-adb6e82b9991")] + [StructLayout(LayoutKind.Sequential)] + public struct RTL_USER_PROCESS_PARAMETERS + { + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + private readonly byte[] Reserved1; + + /// Reserved for internal use by the operating system. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + private readonly IntPtr[] Reserved2; + + /// The path of the image file for the process. + public UNICODE_STRING ImagePathName; + + /// The command-line string passed to the process. + public UNICODE_STRING CommandLine; + } + + /// + /// Represents the structure and associated memory returned by NtQueryXX functions. The memory associate with this structure + /// will be disposed when this variable goes out of scope or is disposed. + /// + /// The type of the retrieved structure. + public class NtQueryResult : SafeMemStruct where T : struct + { + internal NtQueryResult(uint sz = 0) : base(sz) + { + } + } + } +} \ No newline at end of file diff --git a/UnitTests/PInvoke/NtDll/NtDll.csproj b/UnitTests/PInvoke/NtDll/NtDll.csproj index f506606c..3362bba2 100644 --- a/UnitTests/PInvoke/NtDll/NtDll.csproj +++ b/UnitTests/PInvoke/NtDll/NtDll.csproj @@ -32,6 +32,7 @@ + @@ -41,6 +42,9 @@ 3.15.1 + + 4.5.4 + @@ -55,6 +59,10 @@ {a5e519e9-feba-4fe3-93a5-b8269bef72f4} Vanara.PInvoke.Shared + + {a96cff10-0967-429a-8700-4a86c97c5603} + Shared +