diff options
-rw-r--r-- | psutil/_psutil_windows.c | 8 | ||||
-rw-r--r-- | psutil/_pswindows.py | 8 | ||||
-rw-r--r-- | psutil/arch/windows/ntextapi.h | 90 | ||||
-rw-r--r-- | psutil/arch/windows/process_handles.c | 819 | ||||
-rw-r--r-- | psutil/arch/windows/process_handles.h | 103 |
5 files changed, 619 insertions, 409 deletions
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index a45124e7..b05db820 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -2500,14 +2500,6 @@ error: return NULL; } - -#ifdef UNICODE -#define WTSOpenServer WTSOpenServerW -#else -#define WTSOpenServer WTSOpenServerA -#endif - - /* * Return a Python dict of tuples for disk I/O information */ diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 2ca96106..af77e247 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -444,10 +444,10 @@ class Process(object): # Convert the first part in the corresponding drive letter # (e.g. "C:\") by using Windows's QueryDosDevice() raw_file_names = cext.proc_open_files(self.pid) - for file in raw_file_names: - file = _convert_raw_path(file) - if isfile_strict(file) and file not in retlist: - ntuple = _common.popenfile(file, -1) + for _file in raw_file_names: + _file = _convert_raw_path(_file) + if isfile_strict(_file) and _file not in retlist: + ntuple = _common.popenfile(_file, -1) retlist.append(ntuple) return retlist diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index d1aa62df..d10432a3 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -3,9 +3,9 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ - #if !defined(__NTEXTAPI_H__) #define __NTEXTAPI_H__ +#include <winternl.h> typedef enum _KTHREAD_STATE { Initialized, @@ -66,23 +66,6 @@ typedef struct _CLIENT_ID { HANDLE UniqueThread; } CLIENT_ID, *PCLIENT_ID; - -typedef struct _UNICODE_STRING { - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING, *PUNICODE_STRING; - -typedef struct _SYSTEM_TIMEOFDAY_INFORMATION { - LARGE_INTEGER BootTime; - LARGE_INTEGER CurrentTime; - LARGE_INTEGER TimeZoneBias; - ULONG TimeZoneId; - ULONG Reserved; - ULONGLONG BootTimeBias; - ULONGLONG SleepTimeBias; -} SYSTEM_TIMEOFDAY_INFORMATION, *PSYSTEM_TIMEOFDAY_INFORMATION; - typedef struct _SYSTEM_THREAD_INFORMATION { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; @@ -111,7 +94,7 @@ typedef struct _SYSTEM_EXTENDED_THREAD_INFORMATION { ULONG_PTR Reserved4; } SYSTEM_EXTENDED_THREAD_INFORMATION, *PSYSTEM_EXTENDED_THREAD_INFORMATION; -typedef struct _SYSTEM_PROCESS_INFORMATION { +typedef struct _SYSTEM_PROCESS_INFORMATION2 { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER SpareLi1; @@ -146,31 +129,10 @@ typedef struct _SYSTEM_PROCESS_INFORMATION { LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; SYSTEM_THREAD_INFORMATION Threads[1]; -} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; - - -// structures and enums from winternl.h (not available under mingw) -typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { - LARGE_INTEGER IdleTime; - LARGE_INTEGER KernelTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER Reserved1[2]; - ULONG Reserved2; -} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, - *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; +} SYSTEM_PROCESS_INFORMATION2, *PSYSTEM_PROCESS_INFORMATION2; - -typedef enum _SYSTEM_INFORMATION_CLASS { - SystemBasicInformation = 0, - SystemPerformanceInformation = 2, - SystemTimeOfDayInformation = 3, - SystemProcessInformation = 5, - SystemProcessorPerformanceInformation = 8, - SystemInterruptInformation = 23, - SystemExceptionInformation = 33, - SystemRegistryQuotaInformation = 37, - SystemLookasideInformation = 45 -} SYSTEM_INFORMATION_CLASS; +#define SYSTEM_PROCESS_INFORMATION SYSTEM_PROCESS_INFORMATION2 +#define PSYSTEM_PROCESS_INFORMATION PSYSTEM_PROCESS_INFORMATION2 // ================================================ @@ -189,32 +151,9 @@ typedef struct _WINSTATION_INFO { FILETIME CurrentTime; } WINSTATION_INFO, *PWINSTATION_INFO; -typedef enum _WINSTATIONINFOCLASS { - WinStationInformation = 8 -} WINSTATIONINFOCLASS; - typedef BOOLEAN (WINAPI * PWINSTATIONQUERYINFORMATIONW) (HANDLE,ULONG,WINSTATIONINFOCLASS,PVOID,ULONG,PULONG); -typedef struct _WINSTATIONINFORMATIONW { - BYTE Reserved2[70]; - ULONG LogonId; - BYTE Reserved3[1140]; -} WINSTATIONINFORMATIONW, *PWINSTATIONINFORMATIONW; - -// mingw support: -// http://www.koders.com/c/fid7C02CAE627C526914CDEB427405B51DF393A5EFA.aspx -#ifndef _INC_WTSAPI -typedef struct _WTS_CLIENT_ADDRESS { - DWORD AddressFamily; // AF_INET, AF_IPX, AF_NETBIOS, AF_UNSPEC - BYTE Address[20]; // client network address -} WTS_CLIENT_ADDRESS, * PWTS_CLIENT_ADDRESS; - -HANDLE WINAPI WTSOpenServerA(IN LPSTR pServerName); - -VOID WINAPI WTSCloseServer(IN HANDLE hServer); -#endif - /* * NtQueryInformationProcess code taken from @@ -238,16 +177,9 @@ typedef NTSTATUS (NTAPI *_NtSetInformationProcess)( DWORD ProcessInformationLength ); -typedef struct _PROCESS_BASIC_INFORMATION { - PVOID Reserved1; - PVOID PebBaseAddress; - PVOID Reserved2[2]; - ULONG_PTR UniqueProcessId; - PVOID Reserved3; -} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; -typedef enum _PROCESSINFOCLASS { - ProcessBasicInformation, +typedef enum _PROCESSINFOCLASS2 { + _ProcessBasicInformation, ProcessQuotaLimits, ProcessIoCounters, ProcessVmCounters, @@ -273,7 +205,7 @@ typedef enum _PROCESSINFOCLASS { ProcessDeviceMap, ProcessSessionInformation, ProcessForegroundInformation, - ProcessWow64Information, + _ProcessWow64Information, /* added after XP+ */ ProcessImageFileName, ProcessLUIDDeviceMapsEnabled, @@ -287,6 +219,10 @@ typedef enum _PROCESSINFOCLASS { ProcessCookie, ProcessImageInformation, MaxProcessInfoClass -} PROCESSINFOCLASS; +} PROCESSINFOCLASS2; + +#define PROCESSINFOCLASS PROCESSINFOCLASS2 +#define ProcessBasicInformation _ProcessBasicInformation +#define ProcessWow64Information _ProcessWow64Information #endif // __NTEXTAPI_H__ diff --git a/psutil/arch/windows/process_handles.c b/psutil/arch/windows/process_handles.c index 6b0a4194..2840b613 100644 --- a/psutil/arch/windows/process_handles.c +++ b/psutil/arch/windows/process_handles.c @@ -4,380 +4,559 @@ * found in the LICENSE file. * */ +#include "process_handles.h" -#ifndef UNICODE -#define UNICODE -#endif +static _NtQuerySystemInformation __NtQuerySystemInformation = NULL; +static _NtQueryObject __NtQueryObject = NULL; -#include <Python.h> -#include <stdio.h> -#include <windows.h> -#include <strsafe.h> -#include "process_handles.h" +CRITICAL_SECTION g_cs; +BOOL g_initialized = FALSE; +NTSTATUS g_status; +HANDLE g_hFile = NULL; +HANDLE g_hEvtStart = NULL; +HANDLE g_hEvtFinish = NULL; +HANDLE g_hThread = NULL; +PUNICODE_STRING g_pNameBuffer = NULL; +ULONG g_dwSize = 0; +ULONG g_dwLength = 0; +PVOID g_fiber = NULL; -#ifndef NT_SUCCESS -#define NT_SUCCESS(x) ((x) >= 0) -#endif -#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 +PVOID +GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) +{ + return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); +} -#include <winternl.h> -#define ObjectBasicInformation 0 -#define ObjectNameInformation 1 -#define ObjectTypeInformation 2 +PyObject * +psutil_get_open_files(long dwPid, HANDLE hProcess) +{ + OSVERSIONINFO osvi; -#define HANDLE_TYPE_FILE 28 + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + // Threaded version only works for Vista+ + if (osvi.dwMajorVersion >= 6) + return psutil_get_open_files_ntqueryobject(dwPid, hProcess); + else + return psutil_get_open_files_getmappedfilename(dwPid, hProcess); +} -typedef LONG NTSTATUS; +VOID +psutil_get_open_files_init(BOOL threaded) +{ + if (g_initialized == TRUE) + return; -typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( - ULONG SystemInformationClass, - PVOID SystemInformation, - ULONG SystemInformationLength, - PULONG ReturnLength -); + // Resolve the Windows API calls + __NtQuerySystemInformation = + GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); + __NtQueryObject = GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); + + // Create events for signalling work between threads + if (threaded == TRUE) + { + g_hEvtStart = CreateEvent(NULL, FALSE, FALSE, NULL); + g_hEvtFinish = CreateEvent(NULL, FALSE, FALSE, NULL); + InitializeCriticalSection(&g_cs); + } + + g_initialized = TRUE; +} -typedef NTSTATUS (NTAPI *_NtDuplicateObject)( - HANDLE SourceProcessHandle, - HANDLE SourceHandle, - HANDLE TargetProcessHandle, - PHANDLE TargetHandle, - ACCESS_MASK DesiredAccess, - ULONG Attributes, - ULONG Options -); +PyObject * +psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess) +{ + NTSTATUS status; + PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; + DWORD dwInfoSize = 0x10000; + DWORD dwRet = 0; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL; + DWORD i = 0; + BOOLEAN error = FALSE; + PyObject* pyListFiles = NULL; + PyObject* pyFilePath = NULL; + DWORD dwWait = 0; + + if (g_initialized == FALSE) + psutil_get_open_files_init(TRUE); + + // Due to the use of global variables, ensure only 1 call + // to psutil_get_open_files() is running + EnterCriticalSection(&g_cs); + + if (__NtQuerySystemInformation == NULL || + __NtQueryObject == NULL || + g_hEvtStart == NULL || + g_hEvtFinish == NULL) + + { + PyErr_SetFromWindowsErr(0); + error = TRUE; + goto cleanup; + } -typedef NTSTATUS (NTAPI *_NtQueryObject)( - HANDLE ObjectHandle, - ULONG ObjectInformationClass, - PVOID ObjectInformation, - ULONG ObjectInformationLength, - PULONG ReturnLength -); + // Py_BuildValue raises an exception if NULL is returned + pyListFiles = PyList_New(0); + if (pyListFiles == NULL) + { + error = TRUE; + goto cleanup; + } + do + { + if (pHandleInfo != NULL) + { + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; + } -// Undocumented FILE_INFORMATION_CLASS: FileNameInformation -const SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64; + // NtQuerySystemInformation won't give us the correct buffer size, + // so we guess by doubling the buffer size. + dwInfoSize *= 2; + pHandleInfo = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + dwInfoSize); -typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX -{ - PVOID Object; - HANDLE UniqueProcessId; - HANDLE HandleValue; - ULONG GrantedAccess; - USHORT CreatorBackTraceIndex; - USHORT ObjectTypeIndex; - ULONG HandleAttributes; - ULONG Reserved; -} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; - -typedef struct _SYSTEM_HANDLE_INFORMATION_EX -{ - ULONG_PTR NumberOfHandles; - ULONG_PTR Reserved; - SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; -} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; - - -typedef enum _POOL_TYPE { - NonPagedPool, - PagedPool, - NonPagedPoolMustSucceed, - DontUseThisType, - NonPagedPoolCacheAligned, - PagedPoolCacheAligned, - NonPagedPoolCacheAlignedMustS -} POOL_TYPE, *PPOOL_TYPE; - -typedef struct _OBJECT_TYPE_INFORMATION { - UNICODE_STRING Name; - ULONG TotalNumberOfObjects; - ULONG TotalNumberOfHandles; - ULONG TotalPagedPoolUsage; - ULONG TotalNonPagedPoolUsage; - ULONG TotalNamePoolUsage; - ULONG TotalHandleTableUsage; - ULONG HighWaterNumberOfObjects; - ULONG HighWaterNumberOfHandles; - ULONG HighWaterPagedPoolUsage; - ULONG HighWaterNonPagedPoolUsage; - ULONG HighWaterNamePoolUsage; - ULONG HighWaterHandleTableUsage; - ULONG InvalidAttributes; - GENERIC_MAPPING GenericMapping; - ULONG ValidAccess; - BOOLEAN SecurityRequired; - BOOLEAN MaintainHandleCount; - USHORT MaintainTypeList; - POOL_TYPE PoolType; - ULONG PagedPoolUsage; - ULONG NonPagedPoolUsage; -} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + if (pHandleInfo == NULL) + { + PyErr_NoMemory(); + error = TRUE; + goto cleanup; + } + } while ((status = __NtQuerySystemInformation( + SystemExtendedHandleInformation, + pHandleInfo, + dwInfoSize, + &dwRet)) == STATUS_INFO_LENGTH_MISMATCH); + // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH + if (!NT_SUCCESS(status)) + { + PyErr_SetFromWindowsErr(HRESULT_FROM_NT(status)); + error = TRUE; + goto cleanup; + } -PVOID -GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) -{ - return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); + for (i = 0; i < pHandleInfo->NumberOfHandles; i++) + { + hHandle = &pHandleInfo->Handles[i]; + + // Check if this hHandle belongs to the PID the user specified. + if (hHandle->UniqueProcessId != (HANDLE)dwPid || + hHandle->ObjectTypeIndex != HANDLE_TYPE_FILE) + goto loop_cleanup; + + if (!DuplicateHandle(hProcess, + hHandle->HandleValue, + GetCurrentProcess(), + &g_hFile, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) + { + /* + printf("[%d] DuplicateHandle (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + goto loop_cleanup; + } + + // Guess buffer size is MAX_PATH + 1 + g_dwLength = (MAX_PATH+1) * sizeof(WCHAR); + + do + { + // Release any previously allocated buffer + if (g_pNameBuffer != NULL) + { + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + } + + // NtQueryObject puts the required buffer size in g_dwLength + // WinXP edge case puts g_dwLength == 0, just skip this handle + if (g_dwLength == 0) + goto loop_cleanup; + + g_dwSize = g_dwLength; + if (g_dwSize > 0) + { + g_pNameBuffer = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + g_dwSize); + + if (g_pNameBuffer == NULL) + goto loop_cleanup; + } + + dwWait = psutil_NtQueryObject(); + + // If the call does not return, skip this handle + if (dwWait != WAIT_OBJECT_0) + goto loop_cleanup; + + } while (g_status == STATUS_INFO_LENGTH_MISMATCH); + + // NtQueryObject stopped returning STATUS_INFO_LENGTH_MISMATCH + if (!NT_SUCCESS(g_status)) + goto loop_cleanup; + + // Convert to PyUnicode and append it to the return list + if (g_pNameBuffer->Length > 0) + { + /* + printf("[%d] Filename (%#x) %#d bytes: %S\n", + dwPid, + hHandle->HandleValue, + g_pNameBuffer->Length, + g_pNameBuffer->Buffer); + */ + + pyFilePath = PyUnicode_FromWideChar(g_pNameBuffer->Buffer, + g_pNameBuffer->Length/2); + if (pyFilePath == NULL) + { + /* + printf("[%d] PyUnicode_FromWideChar (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + error = TRUE; + goto loop_cleanup; + } + + if (PyList_Append(pyListFiles, pyFilePath)) + { + /* + printf("[%d] PyList_Append (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + error = TRUE; + goto loop_cleanup; + } + } + +loop_cleanup: + Py_XDECREF(pyFilePath); + pyFilePath = NULL; + + if (g_pNameBuffer != NULL) + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + g_dwLength = 0; + + if (g_hFile != NULL) + CloseHandle(g_hFile); + g_hFile = NULL; + } + +cleanup: + if (g_pNameBuffer != NULL) + HeapFree(GetProcessHeap(), 0, g_pNameBuffer); + g_pNameBuffer = NULL; + g_dwSize = 0; + g_dwLength = 0; + + if (g_hFile != NULL) + CloseHandle(g_hFile); + g_hFile = NULL; + + if (pHandleInfo != NULL) + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; + + if (error) + { + Py_XDECREF(pyListFiles); + pyListFiles = NULL; + } + + LeaveCriticalSection(&g_cs); + + return pyListFiles; } -void PrintError(LPTSTR lpszFunction) +DWORD +psutil_NtQueryObject() { - // Retrieve the system error message for the last-error code - - LPVOID lpMsgBuf; - LPVOID lpDisplayBuf; - DWORD dw = GetLastError(); - - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - dw, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, NULL ); - - // Display the error message - lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, - (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); - StringCchPrintf((LPTSTR)lpDisplayBuf, - LocalSize(lpDisplayBuf) / sizeof(TCHAR), - TEXT("%s failed with error %d: %s"), - lpszFunction, dw, lpMsgBuf); - - wprintf(lpDisplayBuf); - LocalFree(lpMsgBuf); - LocalFree(GetLastError); + DWORD dwWait = 0; + + if (g_hThread == NULL) + g_hThread = CreateThread(NULL, + 0, + (LPTHREAD_START_ROUTINE)psutil_NtQueryObjectThread, + NULL, + 0, + NULL); + if (g_hThread == NULL) + return GetLastError(); + + // Signal the worker thread to start + SetEvent(g_hEvtStart); + + // Wait for the worker thread to finish + dwWait = WaitForSingleObject(g_hEvtFinish, NTQO_TIMEOUT); + + // If the thread hangs, kill it and cleanup + if (dwWait == WAIT_TIMEOUT) + { + SuspendThread(g_hThread); + TerminateThread(g_hThread, 1); + WaitForSingleObject(g_hThread, INFINITE); + CloseHandle(g_hThread); + + // Cleanup Fiber + if (g_fiber != NULL) + DeleteFiber(g_fiber); + g_fiber = NULL; + + g_hThread = NULL; + } + + return dwWait; } -PyObject * -psutil_get_open_files(long pid, HANDLE processHandle) +void +psutil_NtQueryObjectThread() { - _NtQuerySystemInformation NtQuerySystemInformation = - GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); + // Prevent the thread stack from leaking when this + // thread gets terminated due to NTQueryObject hanging + g_fiber = ConvertThreadToFiber(NULL); - _NtQueryObject NtQueryObject = - GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); - - NTSTATUS status; - PSYSTEM_HANDLE_INFORMATION_EX handleInfo; - ULONG handleInfoSize = 0x10000; - ULONG i; - ULONG fileNameLength; - PyObject *filesList = Py_BuildValue("[]"); - PyObject *arg = NULL; - PyObject *fileFromWchar = NULL; - DWORD nReturn = 0; - - if (filesList == NULL) - return NULL; - - handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)HeapAlloc(GetProcessHeap(), 0, - handleInfoSize); - - if (handleInfo == NULL) { - Py_DECREF(filesList); - PyErr_NoMemory(); - return NULL; + // Loop infinitely waiting for work + while (TRUE) + { + WaitForSingleObject(g_hEvtStart, INFINITE); + + g_status = __NtQueryObject(g_hFile, + ObjectNameInformation, + g_pNameBuffer, + g_dwSize, + &g_dwLength); + SetEvent(g_hEvtFinish); } +} - // NtQuerySystemInformation won't give us the correct buffer size, - // so we guess by doubling the buffer size. - while ((status = NtQuerySystemInformation( - SystemExtendedHandleInformation, - handleInfo, - handleInfoSize, - &nReturn - )) == STATUS_INFO_LENGTH_MISMATCH) +PyObject * +psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess) +{ + NTSTATUS status; + PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = NULL; + DWORD dwInfoSize = 0x10000; + DWORD dwRet = 0; + PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX hHandle = NULL; + HANDLE hFile = NULL; + HANDLE hMap = NULL; + DWORD i = 0; + BOOLEAN error = FALSE; + PyObject* pyListFiles = NULL; + PyObject* pyFilePath = NULL; + ULONG dwSize = 0; + LPVOID pMem = NULL; + TCHAR pszFilename[MAX_PATH+1]; + + if (g_initialized == FALSE) + psutil_get_open_files_init(FALSE); + + if (__NtQuerySystemInformation == NULL || __NtQueryObject == NULL) { - handleInfoSize *=2; - HeapFree(GetProcessHeap(), 0, handleInfo); - handleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)HeapAlloc( - GetProcessHeap(), 0, handleInfoSize); + PyErr_SetFromWindowsErr(0); + error = TRUE; + goto cleanup; } - // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH - if (!NT_SUCCESS(status)) { - Py_DECREF(filesList); - HeapFree(GetProcessHeap(), 0, handleInfo); - return NULL; + // Py_BuildValue raises an exception if NULL is returned + pyListFiles = PyList_New(0); + if (pyListFiles == NULL) + { + error = TRUE; + goto cleanup; } - for (i = 0; i < handleInfo->NumberOfHandles; i++) { - PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX handle = &handleInfo->Handles[i]; - HANDLE dupHandle = NULL; - HANDLE mapHandle = NULL; - POBJECT_TYPE_INFORMATION objectTypeInfo = NULL; - PVOID objectNameInfo; - UNICODE_STRING objectName; - ULONG returnLength; - DWORD error = 0; - fileFromWchar = NULL; - arg = NULL; - - // Check if this handle belongs to the PID the user specified. - if (handle->UniqueProcessId != (HANDLE)pid || - handle->ObjectTypeIndex != HANDLE_TYPE_FILE) + do + { + if (pHandleInfo != NULL) { - continue; + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; } - // Skip handles with the following access codes as the next call - // to NtDuplicateObject() or NtQueryObject() might hang forever. - if ((handle->GrantedAccess == 0x0012019f) - || (handle->GrantedAccess == 0x001a019f) - || (handle->GrantedAccess == 0x00120189) - || (handle->GrantedAccess == 0x00100000)) { - continue; + // NtQuerySystemInformation won't give us the correct buffer size, + // so we guess by doubling the buffer size. + dwInfoSize *= 2; + pHandleInfo = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + dwInfoSize); + + if (pHandleInfo == NULL) + { + PyErr_NoMemory(); + error = TRUE; + goto cleanup; } + } while ((status = __NtQuerySystemInformation( + SystemExtendedHandleInformation, + pHandleInfo, + dwInfoSize, + &dwRet)) == STATUS_INFO_LENGTH_MISMATCH); + + // NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH + if (!NT_SUCCESS(status)) + { + PyErr_SetFromWindowsErr(HRESULT_FROM_NT(status)); + error = TRUE; + goto cleanup; + } + + for (i = 0; i < pHandleInfo->NumberOfHandles; i++) + { + hHandle = &pHandleInfo->Handles[i]; + + // Check if this hHandle belongs to the PID the user specified. + if (hHandle->UniqueProcessId != (HANDLE)dwPid || + hHandle->ObjectTypeIndex != HANDLE_TYPE_FILE) + goto loop_cleanup; - if (!DuplicateHandle(processHandle, - handle->HandleValue, + if (!DuplicateHandle(hProcess, + hHandle->HandleValue, GetCurrentProcess(), - &dupHandle, + &hFile, 0, TRUE, DUPLICATE_SAME_ACCESS)) - { - //printf("[%#x] Error: %d \n", handle->HandleValue, GetLastError()); - continue; - } - - mapHandle = CreateFileMapping(dupHandle, - NULL, - PAGE_READONLY, - 0, - 0, - NULL); - if (mapHandle == NULL) { - error = GetLastError(); - if (error == ERROR_INVALID_HANDLE || error == ERROR_BAD_EXE_FORMAT) { - CloseHandle(dupHandle); - //printf("CreateFileMapping Error: %d\n", error); - continue; - } - } - CloseHandle(mapHandle); - - // Query the object type. - objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); - if (!NT_SUCCESS(NtQueryObject( - dupHandle, - ObjectTypeInformation, - objectTypeInfo, - 0x1000, - NULL - ))) { - free(objectTypeInfo); - CloseHandle(dupHandle); - continue; + /* + printf("[%d] DuplicateHandle (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + goto loop_cleanup; } - objectNameInfo = malloc(0x1000); - if (!NT_SUCCESS(NtQueryObject( - dupHandle, - ObjectNameInformation, - objectNameInfo, - 0x1000, - &returnLength - ))) + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMap == NULL) { - // Reallocate the buffer and try again. - objectNameInfo = realloc(objectNameInfo, returnLength); - if (!NT_SUCCESS(NtQueryObject( - dupHandle, - ObjectNameInformation, - objectNameInfo, - returnLength, - NULL - ))) - { - // We have the type name, so just display that. - /* - printf( - "[%#x] %.*S: (could not get name)\n", - handle->HandleValue, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer - ); - */ - free(objectTypeInfo); - free(objectNameInfo); - CloseHandle(dupHandle); - continue; + /* + printf("[%d] CreateFileMapping (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + goto loop_cleanup; + } - } + pMem = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 1); + + if (pMem == NULL) + { + /* + printf("[%d] MapViewOfFile (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + goto loop_cleanup; } - // Cast our buffer into an UNICODE_STRING. - objectName = *(PUNICODE_STRING)objectNameInfo; + dwSize = GetMappedFileName(GetCurrentProcess(), pMem, pszFilename, MAX_PATH); + if (dwSize == 0) + { + /* + printf("[%d] GetMappedFileName (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); + */ + goto loop_cleanup; + } - // Print the information! - if (objectName.Length) + pszFilename[dwSize] = '\0'; + /* + printf("[%d] Filename (%#x) %#d bytes: %S\n", + dwPid, + hHandle->HandleValue, + dwSize, + pszFilename); + */ + + pyFilePath = PyUnicode_FromWideChar(pszFilename, dwSize); + if (pyFilePath == NULL) { - // The object has a name. Make sure it is a file otherwise - // ignore it - fileNameLength = objectName.Length / 2; - if (wcscmp(objectTypeInfo->Name.Buffer, L"File") == 0) { - // printf("%.*S\n", objectName.Length / 2, objectName.Buffer); - fileFromWchar = PyUnicode_FromWideChar(objectName.Buffer, - fileNameLength); - if (fileFromWchar == NULL) - goto error_py_fun; -#if PY_MAJOR_VERSION >= 3 - arg = Py_BuildValue("N", - PyUnicode_AsUTF8String(fileFromWchar)); -#else - arg = Py_BuildValue("N", - PyUnicode_FromObject(fileFromWchar)); -#endif - if (!arg) - goto error_py_fun; - Py_XDECREF(fileFromWchar); - fileFromWchar = NULL; - if (PyList_Append(filesList, arg)) - goto error_py_fun; - Py_XDECREF(arg); - } /* - printf( - "[%#x] %.*S: %.*S\n", - handle->Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer, - objectName.Length / 2, - objectName.Buffer - ); + printf("[%d] PyUnicode_FromStringAndSize (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); */ + error = TRUE; + goto loop_cleanup; } - else + + if (PyList_Append(pyListFiles, pyFilePath)) { - // Print something else. /* - printf( - "[%#x] %.*S: (unnamed)\n", - handle->Handle, - objectTypeInfo->Name.Length / 2, - objectTypeInfo->Name.Buffer - ); + printf("[%d] PyList_Append (%#x): %#x \n", + dwPid, + hHandle->HandleValue, + GetLastError()); */ - ;; + error = TRUE; + goto loop_cleanup; } - free(objectTypeInfo); - free(objectNameInfo); - CloseHandle(dupHandle); + +loop_cleanup: + Py_XDECREF(pyFilePath); + pyFilePath = NULL; + + if (pMem != NULL) + UnmapViewOfFile(pMem); + pMem = NULL; + + if (hMap != NULL) + CloseHandle(hMap); + hMap = NULL; + + if (hFile != NULL) + CloseHandle(hFile); + hFile = NULL; + + dwSize = 0; } - HeapFree(GetProcessHeap(), 0, handleInfo); - CloseHandle(processHandle); - return filesList; - -error_py_fun: - Py_XDECREF(arg); - Py_XDECREF(fileFromWchar); - Py_DECREF(filesList); - return NULL; + +cleanup: + if (pMem != NULL) + UnmapViewOfFile(pMem); + pMem = NULL; + + if (hMap != NULL) + CloseHandle(hMap); + hMap = NULL; + + if (hFile != NULL) + CloseHandle(hFile); + hFile = NULL; + + if (pHandleInfo != NULL) + HeapFree(GetProcessHeap(), 0, pHandleInfo); + pHandleInfo = NULL; + + if (error) + { + Py_XDECREF(pyListFiles); + pyListFiles = NULL; + } + + return pyListFiles; } diff --git a/psutil/arch/windows/process_handles.h b/psutil/arch/windows/process_handles.h index 342ce8fd..4cf4023e 100644 --- a/psutil/arch/windows/process_handles.h +++ b/psutil/arch/windows/process_handles.h @@ -4,7 +4,110 @@ * found in the LICENSE file. */ +#ifndef __PROCESS_HANDLES_H__ +#define __PROCESS_HANDLES_H__ + +#ifndef UNICODE +#define UNICODE +#endif + #include <Python.h> +#include <stdio.h> #include <windows.h> +#include <strsafe.h> +#include <winternl.h> +#include <psapi.h> + + +#ifndef NT_SUCCESS +#define NT_SUCCESS(x) ((x) >= 0) +#endif + +#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 +#define ObjectBasicInformation 0 +#define ObjectNameInformation 1 +#define ObjectTypeInformation 2 +#define HANDLE_TYPE_FILE 28 +#define NTQO_TIMEOUT 100 + +typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength +); + +typedef NTSTATUS (NTAPI *_NtQueryObject)( + HANDLE ObjectHandle, + ULONG ObjectInformationClass, + PVOID ObjectInformation, + ULONG ObjectInformationLength, + PULONG ReturnLength +); +// Undocumented FILE_INFORMATION_CLASS: FileNameInformation +static const SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)64; + +typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX +{ + PVOID Object; + HANDLE UniqueProcessId; + HANDLE HandleValue; + ULONG GrantedAccess; + USHORT CreatorBackTraceIndex; + USHORT ObjectTypeIndex; + ULONG HandleAttributes; + ULONG Reserved; +} SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX +{ + ULONG_PTR NumberOfHandles; + ULONG_PTR Reserved; + SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1]; +} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX; + +typedef enum _POOL_TYPE { + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + DontUseThisType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS +} POOL_TYPE, *PPOOL_TYPE; + +typedef struct _OBJECT_TYPE_INFORMATION { + UNICODE_STRING Name; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccess; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + USHORT MaintainTypeList; + POOL_TYPE PoolType; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + +PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName); +VOID psutil_get_open_files_init(BOOL threaded); PyObject* psutil_get_open_files(long pid, HANDLE processHandle); +PyObject* psutil_get_open_files_ntqueryobject(long dwPid, HANDLE hProcess); +PyObject* psutil_get_open_files_getmappedfilename(long dwPid, HANDLE hProcess); +DWORD psutil_NtQueryObject(void); +void psutil_NtQueryObjectThread(void); + +#endif // __PROCESS_HANDLES_H__ |