diff options
author | EccoTheFlintstone <32797240+EccoTheFlintstone@users.noreply.github.com> | 2019-02-03 07:56:45 -0500 |
---|---|---|
committer | Giampaolo Rodola <g.rodola@gmail.com> | 2019-02-03 13:56:45 +0100 |
commit | a45365980d9ae9237caa61dc71d68ffd60c44acf (patch) | |
tree | 332de8b3560b2eb9523c03182c258c00b231deab | |
parent | af7e4b5fcac3e543664058adfe068ab881048832 (diff) | |
download | psutil-a45365980d9ae9237caa61dc71d68ffd60c44acf.tar.gz |
Starting from windows 8.1, get commandline content using NtQueryInformationProcess (see #1384) (#1398)
#1384, #1398: on windows 8.1, get cmdline() using NtQueryInformationProcess in case the original method fails with ACCESS_DENIED
-rw-r--r-- | psutil/arch/windows/process_info.c | 192 | ||||
-rw-r--r-- | psutil/arch/windows/security.c | 16 |
2 files changed, 183 insertions, 25 deletions
diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c index b79aeb3e..64888d60 100644 --- a/psutil/arch/windows/process_info.c +++ b/psutil/arch/windows/process_info.c @@ -162,6 +162,73 @@ const int STATUS_INFO_LENGTH_MISMATCH = 0xC0000004; const int STATUS_BUFFER_TOO_SMALL = 0xC0000023L; + +#define WINDOWS_UNINITIALIZED 0 +#define WINDOWS_XP 51 +#define WINDOWS_VISTA 60 +#define WINDOWS_7 61 +#define WINDOWS_8 62 +#define WINDOWS_81 63 +#define WINDOWS_10 100 + + +int get_windows_version() { + OSVERSIONINFO ver_info; + BOOL result; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + static int windows_version = WINDOWS_UNINITIALIZED; + // windows_version is static + // and equal to WINDOWS_UNINITIALIZED only on first call + if (windows_version == WINDOWS_UNINITIALIZED) { + memset(&ver_info, 0, sizeof(ver_info)); + ver_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + result = GetVersionEx(&ver_info); + if (result != FALSE) { + dwMajorVersion = ver_info.dwMajorVersion; + dwMinorVersion = ver_info.dwMinorVersion; + dwBuildNumber = ver_info.dwBuildNumber; + // Windows XP, Windows server 2003 + if (dwMajorVersion == 5 && dwMinorVersion == 1) { + windows_version = WINDOWS_XP; + } + // Windows Vista + else if (dwMajorVersion == 6 && dwMinorVersion == 0) { + windows_version = WINDOWS_VISTA; + } + // Windows 7, Windows Server 2008 R2 + else if (dwMajorVersion == 6 && dwMinorVersion == 1) { + windows_version = WINDOWS_7; + } + // Windows 8, Windows Server 2012 + else if (dwMajorVersion == 6 && dwMinorVersion == 2) { + windows_version = WINDOWS_8; + } + // Windows 8.1, Windows Server 2012 R2 + else if (dwMajorVersion == 6 && dwMinorVersion == 3) + { + windows_version = WINDOWS_81; + } + // Windows 10, Windows Server 2016 + else if (dwMajorVersion == 10) { + windows_version = WINDOWS_10; + } + } + } + return windows_version; +} + +_NtQueryInformationProcess psutil_NtQueryInformationProcess() { + static _NtQueryInformationProcess NtQueryInformationProcess = NULL; + if (NtQueryInformationProcess == NULL) { + NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress( + GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); + } + return NtQueryInformationProcess; +} + + // ==================================================================== // Process and PIDs utiilties. // ==================================================================== @@ -526,7 +593,7 @@ static int psutil_get_process_data(long pid, http://stackoverflow.com/a/14012919 http://www.drdobbs.com/embracing-64-bit-windows/184401966 */ - static _NtQueryInformationProcess NtQueryInformationProcess = NULL; + _NtQueryInformationProcess NtQueryInformationProcess = NULL; #ifndef _WIN64 static _NtQueryInformationProcess NtWow64QueryInformationProcess64 = NULL; static _NtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64 = NULL; @@ -548,10 +615,7 @@ static int psutil_get_process_data(long pid, if (hProcess == NULL) return -1; - if (NtQueryInformationProcess == NULL) { - NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress( - GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); - } + NtQueryInformationProcess = psutil_NtQueryInformationProcess(); #ifdef _WIN64 /* 64 bit case. Check if the target is a 32 bit process running in WoW64 @@ -791,10 +855,73 @@ error: return -1; } +int psutil_get_cmdline_data(long pid, WCHAR **pdata, SIZE_T *psize) { + HANDLE hProcess; + ULONG ret_length = 4096; + NTSTATUS status; + char * cmdline_buffer = NULL; + WCHAR * cmdline_buffer_wchar = NULL; + PUNICODE_STRING tmp = NULL; + DWORD string_size; + _NtQueryInformationProcess NtQueryInformationProcess = NULL; + int ret = -1; + + NtQueryInformationProcess = psutil_NtQueryInformationProcess(); + if (NtQueryInformationProcess == NULL) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + cmdline_buffer = calloc(ret_length, 1); + if (cmdline_buffer == NULL) { + PyErr_NoMemory(); + goto error; + } + + hProcess = psutil_handle_from_pid(pid, PROCESS_QUERY_LIMITED_INFORMATION); + if (hProcess == NULL) { + PyErr_SetFromWindowsErr(0); + goto error; + } + status = NtQueryInformationProcess( + hProcess, + 60, // ProcessCommandLineInformation + cmdline_buffer, + ret_length, + &ret_length + ); + if (!NT_SUCCESS(status)) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + tmp = (PUNICODE_STRING)cmdline_buffer; + string_size = wcslen(tmp->Buffer) + 1; + cmdline_buffer_wchar = (WCHAR *)calloc(string_size, sizeof(WCHAR)); + + if (cmdline_buffer_wchar == NULL) { + PyErr_NoMemory(); + goto error; + } + + wcscpy_s(cmdline_buffer_wchar, string_size, tmp->Buffer); + *pdata = cmdline_buffer_wchar; + *psize = string_size * sizeof(WCHAR); + ret = 0; + +error: + if (cmdline_buffer != NULL) + free(cmdline_buffer); + if (hProcess != NULL) + CloseHandle(hProcess); + + return ret; +} + /* - * returns a Python list representing the arguments for the process - * with given pid or NULL on error. - */ +* returns a Python list representing the arguments for the process +* with given pid or NULL on error. +*/ PyObject * psutil_get_cmdline(long pid) { PyObject *ret = NULL; @@ -804,10 +931,44 @@ psutil_get_cmdline(long pid) { PyObject *py_unicode = NULL; LPWSTR *szArglist = NULL; int nArgs, i; - - if (psutil_get_process_data(pid, KIND_CMDLINE, &data, &size) != 0) - goto out; - + int windows_version; + int func_ret; + + + windows_version = get_windows_version(); + + /* + by defaut, still use PEB (if command line params have been patched in + the PEB, we will get the actual ones) + Reading the PEB to get the command line parameters still seem to be + the best method if somebody has tampered with the parameters after + creating the process. + For instance, create a process as suspended, patch the command line + in its PEB and unfreeze it. + The process will use the "new" parameters whereas the system + (with NtQueryInformationProcess) will give you the "old" ones + (see here : https://blog.xpnsec.com/how-to-argue-like-cobalt-strike/) + */ + func_ret = psutil_get_process_data(pid, KIND_CMDLINE, &data, &size); + if (func_ret != 0) { + if ((GetLastError() == ERROR_ACCESS_DENIED) && + (windows_version >= WINDOWS_81)) + { + // reset that we had an error + // and retry with NtQueryInformationProcess + // (for protected processes) + PyErr_Clear(); + + func_ret = psutil_get_cmdline_data(pid, &data, &size); + if (func_ret != 0) { + goto out; + } + } + else { + goto out; + } + } + // attempt to parse the command line using Win32 API szArglist = CommandLineToArgvW(data, &nArgs); if (szArglist == NULL) { @@ -822,18 +983,18 @@ psutil_get_cmdline(long pid) { goto out; for (i = 0; i < nArgs; i++) { py_unicode = PyUnicode_FromWideChar(szArglist[i], - wcslen(szArglist[i])); + wcslen(szArglist[i])); if (py_unicode == NULL) goto out; PyList_SET_ITEM(py_retlist, i, py_unicode); py_unicode = NULL; } - ret = py_retlist; py_retlist = NULL; out: - LocalFree(szArglist); + if (szArglist != NULL) + LocalFree(szArglist); if (data != NULL) free(data); Py_XDECREF(py_unicode); @@ -960,3 +1121,4 @@ error: free(buffer); return 0; } + diff --git a/psutil/arch/windows/security.c b/psutil/arch/windows/security.c index 331d9622..d5f8f8d3 100644 --- a/psutil/arch/windows/security.c +++ b/psutil/arch/windows/security.c @@ -34,7 +34,7 @@ psutil_token_from_handle(HANDLE hProcess) { * constant, we pass through the TOKEN_PRIVILEGES constant. This value returns * an array of privileges that the account has in the environment. Iterating * through the array, we call the function LookupPrivilegeName looking for the - * string “SeTcbPrivilege. If the function returns this string, then this + * string “SeTcbPrivilege. If the function returns this string, then this * account has Local System privileges */ int @@ -131,7 +131,6 @@ psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) { ); if (GetLastError() != ERROR_SUCCESS) return FALSE; - // second pass. set privilege based on previous setting tpPrevious.PrivilegeCount = 1; tpPrevious.Privileges[0].Luid = luid; @@ -160,9 +159,8 @@ psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) { int psutil_set_se_debug() { HANDLE hToken; - if (! OpenThreadToken(GetCurrentThread(), + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - FALSE, &hToken) ) { if (GetLastError() == ERROR_NO_TOKEN) { @@ -170,9 +168,8 @@ psutil_set_se_debug() { CloseHandle(hToken); return 0; } - if (!OpenThreadToken(GetCurrentThread(), + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - FALSE, &hToken) ) { RevertToSelf(); @@ -198,17 +195,15 @@ psutil_set_se_debug() { int psutil_unset_se_debug() { HANDLE hToken; - if (! OpenThreadToken(GetCurrentThread(), + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - FALSE, &hToken) ) { if (GetLastError() == ERROR_NO_TOKEN) { if (! ImpersonateSelf(SecurityImpersonation)) return 0; - if (!OpenThreadToken(GetCurrentThread(), + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - FALSE, &hToken)) { return 0; @@ -223,3 +218,4 @@ psutil_unset_se_debug() { CloseHandle(hToken); return 1; } + |