OLD | NEW |
1 /* | 1 /* |
2 * $Id: process_info.c 778 2010-11-08 19:59:08Z g.rodola $ | 2 * $Id: process_info.c 1142 2011-10-05 18:45:49Z g.rodola $ |
| 3 * |
| 4 * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. |
| 5 * Use of this source code is governed by a BSD-style license that can be |
| 6 * found in the LICENSE file. |
3 * | 7 * |
4 * Helper functions related to fetching process information. Used by | 8 * Helper functions related to fetching process information. Used by |
5 * _psutil_mswindows module methods. | 9 * _psutil_mswindows module methods. |
6 */ | 10 */ |
7 | 11 |
8 #include <Python.h> | 12 #include <Python.h> |
9 #include <windows.h> | 13 #include <windows.h> |
10 #include <Psapi.h> | 14 #include <Psapi.h> |
11 #include <tlhelp32.h> | 15 #include <tlhelp32.h> |
12 | 16 |
13 #include "security.h" | 17 #include "security.h" |
14 #include "process_info.h" | 18 #include "process_info.h" |
15 | 19 #include "ntextapi.h" |
16 | 20 |
17 /* | 21 /* |
18 * NtQueryInformationProcess code taken from | 22 * NtQueryInformationProcess code taken from |
19 * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/ | 23 * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/ |
20 * typedefs needed to compile against ntdll functions not exposted in the API | 24 * typedefs needed to compile against ntdll functions not exposted in the API |
21 */ | 25 */ |
22 typedef LONG NTSTATUS; | 26 typedef LONG NTSTATUS; |
23 | 27 |
24 typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( | 28 typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( |
25 HANDLE ProcessHandle, | 29 HANDLE ProcessHandle, |
26 DWORD ProcessInformationClass, | 30 DWORD ProcessInformationClass, |
27 PVOID ProcessInformation, | 31 PVOID ProcessInformation, |
28 DWORD ProcessInformationLength, | 32 DWORD ProcessInformationLength, |
29 PDWORD ReturnLength | 33 PDWORD ReturnLength |
30 ); | 34 ); |
31 | 35 |
32 typedef struct _UNICODE_STRING | |
33 { | |
34 USHORT Length; | |
35 USHORT MaximumLength; | |
36 PWSTR Buffer; | |
37 } UNICODE_STRING, *PUNICODE_STRING; | |
38 | |
39 typedef struct _PROCESS_BASIC_INFORMATION | 36 typedef struct _PROCESS_BASIC_INFORMATION |
40 { | 37 { |
41 PVOID Reserved1; | 38 PVOID Reserved1; |
42 PVOID PebBaseAddress; | 39 PVOID PebBaseAddress; |
43 PVOID Reserved2[2]; | 40 PVOID Reserved2[2]; |
44 ULONG_PTR UniqueProcessId; | 41 ULONG_PTR UniqueProcessId; |
45 PVOID Reserved3; | 42 PVOID Reserved3; |
46 } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; | 43 } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; |
47 | 44 |
48 | 45 |
49 /* | 46 /* |
50 * Set OSError(errno=ESRCH, strerror="No such process") Python exception. | |
51 */ | |
52 PyObject * | |
53 NoSuchProcess(void) { | |
54 PyObject *exc; | |
55 char *msg = strerror(ESRCH); | |
56 exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg); | |
57 PyErr_SetObject(PyExc_OSError, exc); | |
58 Py_XDECREF(exc); | |
59 return NULL; | |
60 } | |
61 | |
62 /* | |
63 * Set OSError(errno=EACCES, strerror="No such process") Python exception. | |
64 */ | |
65 PyObject * | |
66 AccessDenied(void) { | |
67 PyObject *exc; | |
68 char *msg = strerror(EACCES); | |
69 exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg); | |
70 PyErr_SetObject(PyExc_OSError, exc); | |
71 Py_XDECREF(exc); | |
72 return NULL; | |
73 } | |
74 | |
75 | |
76 /* | |
77 * A wrapper around OpenProcess setting NSP exception if process | 47 * A wrapper around OpenProcess setting NSP exception if process |
78 * no longer exists. | 48 * no longer exists. |
79 * "pid" is the process pid, "dwDesiredAccess" is the first argument | 49 * "pid" is the process pid, "dwDesiredAccess" is the first argument |
80 * exptected by OpenProcess. | 50 * exptected by OpenProcess. |
81 * Return a process handle or NULL. | 51 * Return a process handle or NULL. |
82 */ | 52 */ |
83 HANDLE | 53 HANDLE |
84 handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) | 54 handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) |
85 { | 55 { |
86 HANDLE hProcess; | 56 HANDLE hProcess; |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
170 } while(enumReturnSz == procArraySz * sizeof(DWORD)); | 140 } while(enumReturnSz == procArraySz * sizeof(DWORD)); |
171 | 141 |
172 /* The number of elements is the returned size / size of each element */ | 142 /* The number of elements is the returned size / size of each element */ |
173 *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD); | 143 *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD); |
174 | 144 |
175 return procArray; | 145 return procArray; |
176 } | 146 } |
177 | 147 |
178 | 148 |
179 int | 149 int |
180 is_system_proc(DWORD pid) { | |
181 HANDLE hProcess; | |
182 | |
183 // Special case for PID 0 System Idle Process | |
184 // and PID 4 (SYSTEM) | |
185 if ((pid == 0) || (pid == 4)) { | |
186 return 1; | |
187 } | |
188 if (pid < 0) { | |
189 return 0; | |
190 } | |
191 | |
192 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, p
id); | |
193 if (NULL == hProcess) { | |
194 // invalid parameter is no such process | |
195 if (GetLastError() == ERROR_INVALID_PARAMETER) { | |
196 return 0; | |
197 } | |
198 | |
199 // access denied obviously means there's a process to deny access to... | |
200 if (GetLastError() == ERROR_ACCESS_DENIED) { | |
201 return 1; | |
202 } | |
203 | |
204 PyErr_SetFromWindowsErr(0); | |
205 return -1; | |
206 } | |
207 | |
208 return HasSystemPrivilege(hProcess); | |
209 } | |
210 | |
211 | |
212 int | |
213 pid_is_running(DWORD pid) | 150 pid_is_running(DWORD pid) |
214 { | 151 { |
215 HANDLE hProcess; | 152 HANDLE hProcess; |
216 DWORD exitCode; | 153 DWORD exitCode; |
217 | 154 |
218 // Special case for PID 0 System Idle Process | 155 // Special case for PID 0 System Idle Process |
219 if (pid == 0) { | 156 if (pid == 0) { |
220 return 1; | 157 return 1; |
221 } | 158 } |
222 | 159 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 | 200 |
264 int | 201 int |
265 pid_in_proclist(DWORD pid) | 202 pid_in_proclist(DWORD pid) |
266 { | 203 { |
267 DWORD *proclist = NULL; | 204 DWORD *proclist = NULL; |
268 DWORD numberOfReturnedPIDs; | 205 DWORD numberOfReturnedPIDs; |
269 DWORD i; | 206 DWORD i; |
270 | 207 |
271 proclist = get_pids(&numberOfReturnedPIDs); | 208 proclist = get_pids(&numberOfReturnedPIDs); |
272 if (NULL == proclist) { | 209 if (NULL == proclist) { |
273 return NULL; | 210 return -1; |
274 } | 211 } |
275 | 212 |
276 for (i = 0; i < numberOfReturnedPIDs; i++) { | 213 for (i = 0; i < numberOfReturnedPIDs; i++) { |
277 if (pid == proclist[i]) { | 214 if (pid == proclist[i]) { |
278 free(proclist); | 215 free(proclist); |
279 return 1; | 216 return 1; |
280 } | 217 } |
281 } | 218 } |
282 | 219 |
283 free(proclist); | 220 free(proclist); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 PyObject* | 270 PyObject* |
334 get_ppid(long pid) | 271 get_ppid(long pid) |
335 { | 272 { |
336 HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | 273 HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); |
337 PROCESSENTRY32 pe = { 0 }; | 274 PROCESSENTRY32 pe = { 0 }; |
338 pe.dwSize = sizeof(PROCESSENTRY32); | 275 pe.dwSize = sizeof(PROCESSENTRY32); |
339 | 276 |
340 if( Process32First(h, &pe)) { | 277 if( Process32First(h, &pe)) { |
341 do { | 278 do { |
342 if (pe.th32ProcessID == pid) { | 279 if (pe.th32ProcessID == pid) { |
343 //printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID); | 280 ////printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID); |
344 CloseHandle(h); | 281 CloseHandle(h); |
345 return Py_BuildValue("I", pe.th32ParentProcessID); | 282 return Py_BuildValue("I", pe.th32ParentProcessID); |
346 } | 283 } |
347 } while(Process32Next(h, &pe)); | 284 } while(Process32Next(h, &pe)); |
348 | 285 |
349 // the process was never found, set NoSuchProcess exception | 286 // the process was never found, set NoSuchProcess exception |
350 NoSuchProcess(); | 287 NoSuchProcess(); |
351 CloseHandle(h); | 288 CloseHandle(h); |
352 return NULL; | 289 return NULL; |
353 } | 290 } |
(...skipping 15 matching lines...) Expand all Loading... |
369 LPWSTR *szArglist; | 306 LPWSTR *szArglist; |
370 HANDLE hProcess; | 307 HANDLE hProcess; |
371 PVOID pebAddress; | 308 PVOID pebAddress; |
372 PVOID rtlUserProcParamsAddress; | 309 PVOID rtlUserProcParamsAddress; |
373 UNICODE_STRING commandLine; | 310 UNICODE_STRING commandLine; |
374 WCHAR *commandLineContents; | 311 WCHAR *commandLineContents; |
375 PyObject *arg = NULL; | 312 PyObject *arg = NULL; |
376 PyObject *arg_from_wchar = NULL; | 313 PyObject *arg_from_wchar = NULL; |
377 PyObject *argList = NULL; | 314 PyObject *argList = NULL; |
378 | 315 |
379 | |
380 hProcess = handle_from_pid(pid); | 316 hProcess = handle_from_pid(pid); |
381 if(hProcess == NULL) { | 317 if(hProcess == NULL) { |
382 return NULL; | 318 return NULL; |
383 } | 319 } |
384 | 320 |
385 pebAddress = GetPebAddress(hProcess); | 321 pebAddress = GetPebAddress(hProcess); |
386 | 322 |
387 /* get the address of ProcessParameters */ | 323 /* get the address of ProcessParameters */ |
388 #ifdef _WIN64 | 324 #ifdef _WIN64 |
389 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32, | 325 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32, |
390 &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) | 326 &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) |
391 #else | 327 #else |
392 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10, | 328 if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10, |
393 &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) | 329 &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) |
394 #endif | 330 #endif |
395 { | 331 { |
396 //printf("Could not read the address of ProcessParameters!\n"); | 332 ////printf("Could not read the address of ProcessParameters!\n"); |
397 PyErr_SetFromWindowsErr(0); | 333 PyErr_SetFromWindowsErr(0); |
398 CloseHandle(hProcess); | 334 CloseHandle(hProcess); |
399 return NULL; | 335 return NULL; |
400 } | 336 } |
401 | 337 |
402 /* read the CommandLine UNICODE_STRING structure */ | 338 /* read the CommandLine UNICODE_STRING structure */ |
403 #ifdef _WIN64 | 339 #ifdef _WIN64 |
404 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112, | 340 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112, |
405 &commandLine, sizeof(commandLine), NULL)) | 341 &commandLine, sizeof(commandLine), NULL)) |
406 #else | 342 #else |
407 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40, | 343 if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40, |
408 &commandLine, sizeof(commandLine), NULL)) | 344 &commandLine, sizeof(commandLine), NULL)) |
409 #endif | 345 #endif |
410 { | 346 { |
411 //printf("Could not read CommandLine!\n"); | 347 ////printf("Could not read CommandLine!\n"); |
| 348 CloseHandle(hProcess); |
412 PyErr_SetFromWindowsErr(0); | 349 PyErr_SetFromWindowsErr(0); |
413 CloseHandle(hProcess); | |
414 return NULL; | 350 return NULL; |
415 } | 351 } |
416 | 352 |
417 | 353 |
418 /* allocate memory to hold the command line */ | 354 /* allocate memory to hold the command line */ |
419 commandLineContents = (WCHAR *)malloc(commandLine.Length+1); | 355 commandLineContents = (WCHAR *)malloc(commandLine.Length+1); |
420 | 356 |
421 /* read the command line */ | 357 /* read the command line */ |
422 if (!ReadProcessMemory(hProcess, commandLine.Buffer, | 358 if (!ReadProcessMemory(hProcess, commandLine.Buffer, |
423 commandLineContents, commandLine.Length, NULL)) | 359 commandLineContents, commandLine.Length, NULL)) |
424 { | 360 { |
425 //printf("Could not read the command line string!\n"); | 361 ////printf("Could not read the command line string!\n"); |
| 362 CloseHandle(hProcess); |
426 PyErr_SetFromWindowsErr(0); | 363 PyErr_SetFromWindowsErr(0); |
427 CloseHandle(hProcess); | |
428 free(commandLineContents); | 364 free(commandLineContents); |
429 return NULL; | 365 return NULL; |
430 } | 366 } |
431 | 367 |
432 /* print the commandline */ | 368 /* print the commandline */ |
433 //printf("%.*S\n", commandLine.Length / 2, commandLineContents); | 369 ////printf("%.*S\n", commandLine.Length / 2, commandLineContents); |
434 | 370 |
435 // null-terminate the string to prevent wcslen from returning incorrect leng
th | 371 // null-terminate the string to prevent wcslen from returning incorrect leng
th |
436 // the length specifier is in characters, but commandLine.Length is in bytes | 372 // the length specifier is in characters, but commandLine.Length is in bytes |
437 commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0'; | 373 commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0'; |
438 | 374 |
439 // attemempt tp parse the command line using Win32 API, fall back on string | 375 // attemempt tp parse the command line using Win32 API, fall back on string |
440 // cmdline version otherwise | 376 // cmdline version otherwise |
441 szArglist = CommandLineToArgvW(commandLineContents, &nArgs); | 377 szArglist = CommandLineToArgvW(commandLineContents, &nArgs); |
442 if (NULL == szArglist) { | 378 if (NULL == szArglist) { |
443 // failed to parse arglist | 379 // failed to parse arglist |
444 // encode as a UTF8 Python string object from WCHAR string | 380 // encode as a UTF8 Python string object from WCHAR string |
445 arg_from_wchar = PyUnicode_FromWideChar(commandLineContents, | 381 arg_from_wchar = PyUnicode_FromWideChar(commandLineContents, |
446 commandLine.Length / 2); | 382 commandLine.Length / 2); |
447 #if PY_MAJOR_VERSION >= 3 | 383 #if PY_MAJOR_VERSION >= 3 |
448 argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar))
; | 384 argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar))
; |
449 #else | 385 #else |
450 argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar)); | 386 argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar)); |
451 #endif | 387 #endif |
452 } | 388 } |
453 else { | 389 else { |
454 // arglist parsed as array of UNICODE_STRING, so convert each to Python | 390 // arglist parsed as array of UNICODE_STRING, so convert each to Python |
455 // string object and add to arg list | 391 // string object and add to arg list |
456 argList = Py_BuildValue("[]"); | 392 argList = Py_BuildValue("[]"); |
457 for(i=0; i<nArgs; i++) { | 393 for(i=0; i<nArgs; i++) { |
458 //printf("%d: %.*S (%d characters)\n", i, wcslen(szArglist[i]), | 394 ////printf("%d: %.*S (%d characters)\n", i, wcslen(szArglist[i]), |
459 // szArglist[i], wcslen(szArglist[i])); | 395 // szArglist[i], wcslen(szArglist[i])); |
460 arg_from_wchar = PyUnicode_FromWideChar(szArglist[i], | 396 arg_from_wchar = PyUnicode_FromWideChar(szArglist[i], |
461 wcslen(szArglist[i]) | 397 wcslen(szArglist[i]) |
462 ); | 398 ); |
463 #if PY_MAJOR_VERSION >= 3 | 399 #if PY_MAJOR_VERSION >= 3 |
464 arg = PyUnicode_FromObject(arg_from_wchar); | 400 arg = PyUnicode_FromObject(arg_from_wchar); |
465 #else | 401 #else |
466 arg = PyUnicode_AsUTF8String(arg_from_wchar); | 402 arg = PyUnicode_AsUTF8String(arg_from_wchar); |
467 #endif | 403 #endif |
468 Py_XDECREF(arg_from_wchar); | 404 Py_XDECREF(arg_from_wchar); |
469 PyList_Append(argList, arg); | 405 PyList_Append(argList, arg); |
470 Py_XDECREF(arg); | 406 Py_XDECREF(arg); |
471 } | 407 } |
472 } | 408 } |
473 | 409 |
474 LocalFree(szArglist); | 410 LocalFree(szArglist); |
475 free(commandLineContents); | 411 free(commandLineContents); |
476 CloseHandle(hProcess); | 412 CloseHandle(hProcess); |
477 return argList; | 413 return argList; |
478 } | 414 } |
479 | 415 |
| 416 |
| 417 #define PH_FIRST_PROCESS(Processes) ((PSYSTEM_PROCESS_INFORMATION)(Processes)) |
| 418 |
| 419 #define PH_NEXT_PROCESS(Process) ( \ |
| 420 ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \ |
| 421 (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \ |
| 422 ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : \ |
| 423 NULL \ |
| 424 ) |
| 425 |
| 426 const STATUS_INFO_LENGTH_MISMATCH = 0xC0000004; |
| 427 const STATUS_BUFFER_TOO_SMALL = 0xC0000023L; |
| 428 |
| 429 /* |
| 430 * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure |
| 431 * fills the structure with process information. |
| 432 * On success return 1, else 0 with Python exception already set. |
| 433 */ |
| 434 int |
| 435 get_process_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess, PVOID *retB
uffer) |
| 436 { |
| 437 static ULONG initialBufferSize = 0x4000; |
| 438 NTSTATUS status; |
| 439 PVOID buffer; |
| 440 ULONG bufferSize; |
| 441 PSYSTEM_PROCESS_INFORMATION process; |
| 442 |
| 443 // get NtQuerySystemInformation |
| 444 typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG); |
| 445 NTQSI_PROC NtQuerySystemInformation; |
| 446 HINSTANCE hNtDll; |
| 447 hNtDll = LoadLibrary(TEXT("ntdll.dll")); |
| 448 NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress( |
| 449 hNtDll, "NtQuerySystemInformation"); |
| 450 |
| 451 bufferSize = initialBufferSize; |
| 452 buffer = malloc(bufferSize); |
| 453 |
| 454 while (TRUE) { |
| 455 status = NtQuerySystemInformation(SystemProcessInformation, buffer, |
| 456 bufferSize, &bufferSize); |
| 457 |
| 458 if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MI
SMATCH) |
| 459 { |
| 460 free(buffer); |
| 461 buffer = malloc(bufferSize); |
| 462 } |
| 463 else { |
| 464 break; |
| 465 } |
| 466 } |
| 467 |
| 468 if (status != 0) { |
| 469 PyErr_Format(PyExc_RuntimeError, "NtQuerySystemInformation() failed"); |
| 470 return 0; |
| 471 } |
| 472 |
| 473 if (bufferSize <= 0x20000) { |
| 474 initialBufferSize = bufferSize; |
| 475 } |
| 476 |
| 477 process = PH_FIRST_PROCESS(buffer); |
| 478 do { |
| 479 if (process->UniqueProcessId == (HANDLE)pid) { |
| 480 *retProcess = process; |
| 481 *retBuffer = buffer; |
| 482 return 1; |
| 483 } |
| 484 } while ( (process = PH_NEXT_PROCESS(process)) ); |
| 485 |
| 486 NoSuchProcess(); |
| 487 return 0; |
| 488 } |
| 489 |
| 490 |
OLD | NEW |