Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 

164 rindas
4.4 KiB

  1. import contextlib
  2. import ctypes
  3. import os
  4. from ctypes.wintypes import (
  5. BOOL,
  6. CHAR,
  7. DWORD,
  8. HANDLE,
  9. LONG,
  10. LPWSTR,
  11. MAX_PATH,
  12. PDWORD,
  13. ULONG,
  14. )
  15. from shellingham._core import SHELL_NAMES
  16. INVALID_HANDLE_VALUE = HANDLE(-1).value
  17. ERROR_NO_MORE_FILES = 18
  18. ERROR_INSUFFICIENT_BUFFER = 122
  19. TH32CS_SNAPPROCESS = 2
  20. PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
  21. kernel32 = ctypes.windll.kernel32
  22. def _check_handle(error_val=0):
  23. def check(ret, func, args):
  24. if ret == error_val:
  25. raise ctypes.WinError()
  26. return ret
  27. return check
  28. def _check_expected(expected):
  29. def check(ret, func, args):
  30. if ret:
  31. return True
  32. code = ctypes.GetLastError()
  33. if code == expected:
  34. return False
  35. raise ctypes.WinError(code)
  36. return check
  37. class ProcessEntry32(ctypes.Structure):
  38. _fields_ = (
  39. ("dwSize", DWORD),
  40. ("cntUsage", DWORD),
  41. ("th32ProcessID", DWORD),
  42. ("th32DefaultHeapID", ctypes.POINTER(ULONG)),
  43. ("th32ModuleID", DWORD),
  44. ("cntThreads", DWORD),
  45. ("th32ParentProcessID", DWORD),
  46. ("pcPriClassBase", LONG),
  47. ("dwFlags", DWORD),
  48. ("szExeFile", CHAR * MAX_PATH),
  49. )
  50. kernel32.CloseHandle.argtypes = [HANDLE]
  51. kernel32.CloseHandle.restype = BOOL
  52. kernel32.CreateToolhelp32Snapshot.argtypes = [DWORD, DWORD]
  53. kernel32.CreateToolhelp32Snapshot.restype = HANDLE
  54. kernel32.CreateToolhelp32Snapshot.errcheck = _check_handle( # type: ignore
  55. INVALID_HANDLE_VALUE,
  56. )
  57. kernel32.Process32First.argtypes = [HANDLE, ctypes.POINTER(ProcessEntry32)]
  58. kernel32.Process32First.restype = BOOL
  59. kernel32.Process32First.errcheck = _check_expected( # type: ignore
  60. ERROR_NO_MORE_FILES,
  61. )
  62. kernel32.Process32Next.argtypes = [HANDLE, ctypes.POINTER(ProcessEntry32)]
  63. kernel32.Process32Next.restype = BOOL
  64. kernel32.Process32Next.errcheck = _check_expected( # type: ignore
  65. ERROR_NO_MORE_FILES,
  66. )
  67. kernel32.GetCurrentProcessId.argtypes = []
  68. kernel32.GetCurrentProcessId.restype = DWORD
  69. kernel32.OpenProcess.argtypes = [DWORD, BOOL, DWORD]
  70. kernel32.OpenProcess.restype = HANDLE
  71. kernel32.OpenProcess.errcheck = _check_handle( # type: ignore
  72. INVALID_HANDLE_VALUE,
  73. )
  74. kernel32.QueryFullProcessImageNameW.argtypes = [HANDLE, DWORD, LPWSTR, PDWORD]
  75. kernel32.QueryFullProcessImageNameW.restype = BOOL
  76. kernel32.QueryFullProcessImageNameW.errcheck = _check_expected( # type: ignore
  77. ERROR_INSUFFICIENT_BUFFER,
  78. )
  79. @contextlib.contextmanager
  80. def _handle(f, *args, **kwargs):
  81. handle = f(*args, **kwargs)
  82. try:
  83. yield handle
  84. finally:
  85. kernel32.CloseHandle(handle)
  86. def _iter_processes():
  87. f = kernel32.CreateToolhelp32Snapshot
  88. with _handle(f, TH32CS_SNAPPROCESS, 0) as snap:
  89. entry = ProcessEntry32()
  90. entry.dwSize = ctypes.sizeof(entry)
  91. ret = kernel32.Process32First(snap, entry)
  92. while ret:
  93. yield entry
  94. ret = kernel32.Process32Next(snap, entry)
  95. def _get_full_path(proch):
  96. size = DWORD(MAX_PATH)
  97. while True:
  98. path_buff = ctypes.create_unicode_buffer("", size.value)
  99. if kernel32.QueryFullProcessImageNameW(proch, 0, path_buff, size):
  100. return path_buff.value
  101. size.value *= 2
  102. def get_shell(pid=None, max_depth=10):
  103. proc_map = {
  104. proc.th32ProcessID: (proc.th32ParentProcessID, proc.szExeFile)
  105. for proc in _iter_processes()
  106. }
  107. pid = pid or os.getpid()
  108. for _ in range(0, max_depth + 1):
  109. try:
  110. ppid, executable = proc_map[pid]
  111. except KeyError: # No such process? Give up.
  112. break
  113. # The executable name would be encoded with the current code page if
  114. # we're in ANSI mode (usually). Try to decode it into str/unicode,
  115. # replacing invalid characters to be safe (not thoeratically necessary,
  116. # I think). Note that we need to use 'mbcs' instead of encoding
  117. # settings from sys because this is from the Windows API, not Python
  118. # internals (which those settings reflect). (pypa/pipenv#3382)
  119. if isinstance(executable, bytes):
  120. executable = executable.decode("mbcs", "replace")
  121. name = executable.rpartition(".")[0].lower()
  122. if name not in SHELL_NAMES:
  123. pid = ppid
  124. continue
  125. key = PROCESS_QUERY_LIMITED_INFORMATION
  126. with _handle(kernel32.OpenProcess, key, 0, pid) as proch:
  127. return (name, _get_full_path(proch))
  128. return None