#define TEB_HARD_ERROR_MODE_OFFSET 0x16B0
#endif
-/*
- * These psapi functions are available in kernel32.dll library with K32 prefix
- * on Windows 7 and higher systems. On older Windows systems these functions are
- * available in psapi.dll libary without K32 prefix. So resolve pointers to
- * these functions dynamically at runtime from the available system library.
- * Function GetProcessImageFileNameW() is not available on Windows 2000 and
- * older systems.
- */
-typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
-typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
-typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
-
/*
* These aclapi function is available in advapi.dll library on Windows 2000
* and higher systems.
typedef DWORD (WINAPI *GetThreadErrorModeProt)(VOID);
typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
+/*
+ * This NtQuerySystemInformation() function and SystemProcessInformation class
+ * is available in all Windows NT based systems. But their definitions are in
+ * any standard WinAPI header file.
+ */
+#ifndef NTSTATUS
+#define NTSTATUS LONG
+#endif
+#ifndef STATUS_INFO_LENGTH_MISMATCH
+#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004)
+#endif
+#ifndef SYSTEM_INFORMATION_CLASS
+#define SYSTEM_INFORMATION_CLASS DWORD
+#endif
+#ifndef SystemProcessInformation
+#define SystemProcessInformation 5
+#endif
+typedef struct {
+ ULONG NextEntryOffset;
+ ULONG NumberOfThreads;
+ LARGE_INTEGER Reserved[6];
+ struct {
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+ } ImageName;
+ LONG BasePriority;
+ HANDLE UniqueProcessId;
+} MY_SYSTEM_PROCESS_INFORMATION;
+typedef NTSTATUS (NTAPI *NtQuerySystemInformationProt)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
+
static DWORD
format_message_from_system(DWORD win32_error_id, DWORD lang_id, LPSTR buffer, DWORD size)
* optionally also with vm read right.
*/
static HANDLE
-open_process_for_query(DWORD pid, BOOL with_vm_read)
+open_process_for_query(DWORD pid)
{
BOOL revert_only_privilege;
LUID luid_debug_privilege;
else
process_right = PROCESS_QUERY_INFORMATION;
- if (with_vm_read)
- process_right |= PROCESS_VM_READ;
-
process = OpenProcess(process_right, FALSE, pid);
if (process)
return process;
}
/*
- * Check if process image path name (wide string) matches exe file name
- * (7-bit ASCII string). Do case-insensitive string comparison. Process
- * image path name can be in any namespace format (DOS, Win32, UNC, ...).
+ * Check if the non-nul-term wide string of the process image file name
+ * (base name with extension) matches the narrow nul-term string of the
+ * exe file name (base name with extension). Do case-insensitive string
+ * comparison because process image name is uppercase on older Windows
+ * versions.
*/
static BOOL
-check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
+check_process_name(LPCWSTR image_name, DWORD image_name_byte_length, LPCSTR exe_file)
{
- DWORD exe_file_length;
+ DWORD exe_file_length = strlen(exe_file);
WCHAR c1;
UCHAR c2;
DWORD i;
- exe_file_length = 0;
- while (exe_file[exe_file_length] != '\0')
- exe_file_length++;
-
- /* Path must have backslash before exe file name. */
- if (exe_file_length >= path_length ||
- path[path_length-exe_file_length-1] != L'\\')
+ if (image_name_byte_length / sizeof(WCHAR) != exe_file_length)
return FALSE;
for (i = 0; i < exe_file_length; i++)
{
- c1 = path[path_length-exe_file_length+i];
+ c1 = image_name[i];
c2 = exe_file[i];
- /*
- * Input string for comparison is 7-bit ASCII and file name part
- * of path must not contain backslash as it is path separator.
- */
- if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
- return FALSE;
if (c1 >= L'a' && c1 <= L'z')
c1 -= L'a' - L'A';
if (c2 >= 'a' && c2 <= 'z')
c2 -= 'a' - 'A';
- if (c1 != c2)
+ if (c1 != c2 || c2 >= 0x80) /* exe_file must be 7-bit ASCII */
return FALSE;
}
HANDLE
win32_find_and_open_process_for_query(LPCSTR exe_file)
{
- GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
- GetModuleFileNameExWProt MyGetModuleFileNameExW;
- EnumProcessesProt MyEnumProcesses;
- HMODULE kernel32, psapi;
- UINT prev_error_mode;
- DWORD partial_retry;
- BOOL found_process;
- DWORD size, length;
- DWORD *processes;
+ HMODULE ntdll;
+ NtQuerySystemInformationProt MyNtQuerySystemInformation;
+ MY_SYSTEM_PROCESS_INFORMATION *info;
+ NTSTATUS status;
+ BYTE *buffer;
+ DWORD size;
HANDLE process;
- LPWSTR path;
- DWORD error;
- DWORD count;
- DWORD i;
- psapi = NULL;
- kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
- if (!kernel32)
+ /* Ntdll.dll is loaded into every process on all NT systems. */
+ ntdll = GetModuleHandle(TEXT("ntdll.dll"));
+ if (!ntdll)
+ return NULL;
+
+ MyNtQuerySystemInformation = (LPVOID)GetProcAddress(ntdll, "NtQuerySystemInformation");
+ if (!MyNtQuerySystemInformation)
return NULL;
/*
- * On Windows 7 and higher systems these functions are available in
- * kernel32.dll library with K32 prefix.
+ * Retrieve information about all processes in system via
+ * NtQuerySystemInformation(SystemProcessInformation) call.
+ * This method returns both process id and process name.
+ * It is supported on all Windows NT based systems.
*/
- MyGetModuleFileNameExW = NULL;
- MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(void(*)(void))GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
- MyEnumProcesses = (EnumProcessesProt)(void(*)(void))GetProcAddress(kernel32, "K32EnumProcesses");
- if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
- {
- /*
- * On older NT-based systems these functions are available in
- * psapi.dll library without K32 prefix.
- */
- prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX, TRUE);
- psapi = LoadLibrary(TEXT("psapi.dll"));
- win32_change_error_mode(prev_error_mode, FALSE);
-
- if (!psapi)
- return NULL;
-
- /*
- * Function GetProcessImageFileNameW() is available in
- * Windows XP and higher systems. On older versions is
- * available function GetModuleFileNameExW().
- */
- MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(void(*)(void))GetProcAddress(psapi, "GetProcessImageFileNameW");
- MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(void(*)(void))GetProcAddress(psapi, "GetModuleFileNameExW");
- MyEnumProcesses = (EnumProcessesProt)(void(*)(void))GetProcAddress(psapi, "EnumProcesses");
- if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
- {
- FreeLibrary(psapi);
- return NULL;
- }
- }
-
- /* Make initial buffer size for 1024 processes. */
- size = 1024 * sizeof(*processes);
-
+ size = 4096;
retry:
- processes = (DWORD *)LocalAlloc(LPTR, size);
- if (!processes)
+ buffer = (BYTE *)LocalAlloc(LPTR, size);
+ if (!buffer)
+ return NULL;
+ status = MyNtQuerySystemInformation(SystemProcessInformation, buffer, size, NULL);
+ if (status == STATUS_INFO_LENGTH_MISMATCH)
{
- if (psapi)
- FreeLibrary(psapi);
- return NULL;
+ LocalFree(buffer);
+ size *= 2;
+ goto retry;
}
-
- if (!MyEnumProcesses(processes, size, &length))
+ if (status < 0)
{
- LocalFree(processes);
- if (psapi)
- FreeLibrary(psapi);
+ LocalFree(buffer);
return NULL;
}
- else if (size == length)
- {
- /*
- * There is no indication given when the buffer is too small to
- * store all process identifiers. Therefore if returned length
- * is same as buffer size there can be more processes. Call
- * again with larger buffer.
- */
- LocalFree(processes);
- size *= 2;
- goto retry;
- }
process = NULL;
- count = length / sizeof(*processes);
-
- for (i = 0; i < count; i++)
+ info = (MY_SYSTEM_PROCESS_INFORMATION *)buffer;
+ while (1)
{
- /* Skip System Idle Process. */
- if (processes[i] == 0)
- continue;
-
/*
- * Function GetModuleFileNameExW() requires additional
- * PROCESS_VM_READ right as opposite to function
- * GetProcessImageFileNameW() which does not need it.
+ * ImageName is just file base name with extension, not the full path.
+ * For inaccessible and system processes it can be an empty string.
+ * Note that ImageName.Length is length without nul term in bytes
+ * and ImageName.Buffer is the wide string.
*/
- process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
- if (!process)
- continue;
-
- /*
- * Set initial buffer size to 256 (wide) characters.
- * Final path length on the modern NT-based systems can be also larger.
- */
- size = 256;
- found_process = FALSE;
- partial_retry = 0;
-
-retry_path:
- path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
- if (!path)
- goto end_path;
-
- if (MyGetProcessImageFileNameW)
- length = MyGetProcessImageFileNameW(process, path, size);
- else
- length = MyGetModuleFileNameExW(process, NULL, path, size);
-
- error = GetLastError();
-
- /*
- * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
- * when remote process is in the middle of updating its module table.
- * Sleep 10 ms and try again, max 10 attempts.
- */
- if (!MyGetProcessImageFileNameW)
- {
- if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
- {
- Sleep(10);
- goto retry_path;
- }
- partial_retry = 0;
- }
-
- /*
- * When buffer is too small then function GetModuleFileNameEx() returns
- * its size argument on older systems (Windows XP) or its size minus
- * argument one on new systems (Windows 10) without signalling any error.
- * Function GetProcessImageFileNameW() on the other hand returns zero
- * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
- * cases call function again with larger buffer.
- */
-
- if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
- goto end_path;
-
- if ((MyGetProcessImageFileNameW && length == 0) ||
- (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
+ if (check_process_name(info->ImageName.Buffer, info->ImageName.Length, exe_file))
{
- LocalFree(path);
- size *= 2;
- goto retry_path;
- }
-
- if (length && check_process_name(path, length, exe_file))
- found_process = TRUE;
-
-end_path:
- if (path)
- {
- LocalFree(path);
- path = NULL;
+ process = open_process_for_query((DWORD)info->UniqueProcessId);
+ break;
}
- if (found_process)
+ if (info->NextEntryOffset == 0)
break;
- CloseHandle(process);
- process = NULL;
+ info = (MY_SYSTEM_PROCESS_INFORMATION *)((BYTE *)info + info->NextEntryOffset);
}
- LocalFree(processes);
-
- if (psapi)
- FreeLibrary(psapi);
-
+ LocalFree(buffer);
return process;
}