首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在纯python中在windows上获取父shell的路径?

如何在纯python中在windows上获取父shell的路径?
EN

Stack Overflow用户
提问于 2021-01-26 14:11:21
回答 1查看 272关注 0票数 1

下面将返回启动当前进程的shell &shell的完整路径。但是,它使用了一个充满c++扩展的python库。有时候,shell会启动一个shell,等等。我只是在寻找“最近的祖先”过程,即shell。

我如何用纯蟒蛇来做这件事呢?(也就是说,没有c++扩展,使用windll.kernel32等都可以-当然,在某个时候,要获取进程信息代码必须访问特定于平台的本地代码,只需要将其隐藏在python标准库中,而不是需要编译c++)

谢林厄姆现在不能这么做

代码语言:javascript
复制
from typing import Tuple, List

import os

import psutil

SHELL_NAMES = {
    'sh', 'bash', 'dash', 'ash',    # Bourne.
    'csh', 'tcsh',                  # C.
    'ksh', 'zsh', 'fish',           # Common alternatives.
    'cmd', 'powershell', 'pwsh',    # Microsoft.
    'elvish', 'xonsh',              # More exotic.
}

def find_shell_for_windows() -> Tuple[str,str]:
    names_paths:List[Tuple[str,str]]=[]
    current_process = psutil.Process(os.getppid())
    process_name, process_path = current_process.name(), current_process.exe()
    names_paths.append((process_name, process_path))
    for parent in current_process.parents():
        names_paths.append((parent.name(), parent.exe()))
    for n,p in names_paths:
        if n.lower() in SHELL_NAMES or n.lower().replace(".exe","") in SHELL_NAMES:
            return n,p
    return ["",""]

if __name__ == '__main__':
    print(find_shell_for_windows())
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-01-29 13:33:53

问题在于寻找一种方法来遍历祖先进程列表,并找到第一个可执行文件与硬编码的名称列表中的项相匹配的方法,希望它将是用户的“shell”。在我回答之前,以下是为什么这是一件非常愚蠢和无用的事情的原因:

  • 您正在寻找的可执行文件可能会被命名为您所期望的其他内容。在对这个问题的评论中,我给出了一个名为Bash可执行程序shab.exe的例子,提问者用著名医生的笑话(“好吧,那就不要那么做”)来反驳它,因为用户试图“隐藏”他们的外壳。但这可能会发生,即使没有任何“恶意”意图‘隐藏’在用户方面。例如,用户可能安装了几个版本的Bash,具有不同的可执行名称(bash-4.00.exegitbash.exe等),以便测试它们的脚本是否与所有脚本兼容。您要枚举代码中的每个可能的名称吗?或者你要把所有的可执行文件与sh的名字匹配起来,并祈祷没有任何假阳性?
  • 相反,仅仅因为可执行文件有您所期望的名称,并不意味着它将具有您所期望的行为.在Windows上更是如此,在Windows中,并没有真正标准化的可执行名称集,这些名称应该始终具有特定的、指定的行为。如果您不加选择地将cmdbashfishxonsh这样的程序以“shell”的名称合并在一起,则尤其如此:这些程序在各自的命令行和各自的脚本语言中接受不同的语法。除非您想要做的就是启动shell供用户交互,否则您将寻找特定类型的shell --无论是与POSIX兼容的shell,还是像cmd这样的DOS派生的shell,或者是其他完全的shell--以便利用其特定的行为。仅仅知道它是“一个外壳”并不能告诉你任何有用的东西。我们不要忘记,并不是所有的shell都是命令行,毕竟Windows Explorer是一个shell。
  • 即使可执行名称排列得很完美,硬编码列表也不可能是详尽无遗的。提问者的列表已经省略了tcmdtclsh。我听说有些疯狂的人把Python本身当作一个shell --你是谁来阻止他们的?如果出现一个新的shell,则必须将其作为另一个条目添加到列表中;让我们希望还有人记得它当时的位置。
  • 不过,为了论点,我们只对命令行shell感兴趣,而忽略了其他所有内容。如果脚本是从自己从Bash启动的Explorer进程启动的呢?脚本将忽略Explorer进程,选择Bash作为“shell”,尽管启动脚本的显然是Explorer而不是Bash。这是正确的还是理想的?我认为答案远非显而易见。

但是,如果以上所述并不能阻止你完成这一徒劳的任务(或者你可能想为了一个更明智的目的而做一些类似的事情),那么无论如何,你都可以完成这个毫无意义的事情:

代码语言:javascript
复制
import ctypes, ctypes.wintypes, contextlib

k32 = ctypes.windll.kernel32

INVALID_HANDLE_VALUE = ctypes.wintypes.HANDLE(-1).value
ERROR_NO_MORE_FILES = 18
ERROR_INSUFFICIENT_BUFFER = 122
TH32CS_SNAPPROCESS = 2
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000

def _check_handle(error_val=0):
    def check(ret, func, args):
        if ret == error_val:
            raise ctypes.WinError()
        return ret
    return check

def _check_expected(expected):
    def check(ret, func, args):
        if ret:
            return True
        code = ctypes.GetLastError()
        if code == expected:
            return False
        raise ctypes.WinError(code)
    return check

class ProcessEntry32(ctypes.Structure):
    _fields_ = (
        ('dwSize'             , ctypes.wintypes.DWORD),
        ('cntUsage'           , ctypes.wintypes.DWORD),
        ('th32ProcessID'      , ctypes.wintypes.DWORD),
        ('th32DefaultHeapID'  , ctypes.POINTER(ctypes.wintypes.ULONG)),
        ('th32ModuleID'       , ctypes.wintypes.DWORD),
        ('cntThreads'         , ctypes.wintypes.DWORD),
        ('th32ParentProcessID', ctypes.wintypes.DWORD),
        ('pcPriClassBase'     , ctypes.wintypes.LONG),
        ('dwFlags'            , ctypes.wintypes.DWORD),
        ('szExeFile'          , ctypes.wintypes.CHAR * ctypes.wintypes.MAX_PATH),
    )

k32.CloseHandle.argtypes = \
    (ctypes.wintypes.HANDLE,)
k32.CloseHandle.restype = ctypes.wintypes.BOOL

k32.CreateToolhelp32Snapshot.argtypes = \
    (ctypes.wintypes.DWORD, ctypes.wintypes.DWORD)
k32.CreateToolhelp32Snapshot.restype = ctypes.wintypes.HANDLE
k32.CreateToolhelp32Snapshot.errcheck = _check_handle(INVALID_HANDLE_VALUE)

k32.Process32First.argtypes = \
    (ctypes.wintypes.HANDLE, ctypes.POINTER(ProcessEntry32))
k32.Process32First.restype = ctypes.wintypes.BOOL
k32.Process32First.errcheck = _check_expected(ERROR_NO_MORE_FILES)

k32.Process32Next.argtypes = \
    (ctypes.wintypes.HANDLE, ctypes.POINTER(ProcessEntry32))
k32.Process32Next.restype = ctypes.wintypes.BOOL
k32.Process32Next.errcheck = _check_expected(ERROR_NO_MORE_FILES)

k32.GetCurrentProcessId.argtypes = ()
k32.GetCurrentProcessId.restype = ctypes.wintypes.DWORD

k32.OpenProcess.argtypes = \
    (ctypes.wintypes.DWORD, ctypes.wintypes.BOOL, ctypes.wintypes.DWORD)
k32.OpenProcess.restype = ctypes.wintypes.HANDLE
k32.OpenProcess.errcheck = _check_handle(INVALID_HANDLE_VALUE)

k32.QueryFullProcessImageNameW.argtypes = \
    (ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.LPWSTR, ctypes.wintypes.PDWORD)
k32.QueryFullProcessImageNameW.restype = ctypes.wintypes.BOOL
k32.QueryFullProcessImageNameW.errcheck = _check_expected(ERROR_INSUFFICIENT_BUFFER)

@contextlib.contextmanager
def Win32Handle(handle):
    try:
        yield handle
    finally:
        k32.CloseHandle(handle)

def enum_processes():
    with Win32Handle(k32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) as snap:
        entry = ProcessEntry32()
        entry.dwSize = ctypes.sizeof(entry)
        ret = k32.Process32First(snap, entry)
        while ret:
            yield entry
            ret = k32.Process32Next(snap, entry)

def get_full_path(proch):
    size = ctypes.wintypes.DWORD(ctypes.wintypes.MAX_PATH)
    while True:
        path_buff = ctypes.create_unicode_buffer('', size.value)
        if k32.QueryFullProcessImageNameW(proch, 0, path_buff, size):
            return path_buff.value
        size.value *= 2

SHELLS = frozenset((
    b'sh.exe', b'bash.exe', b'dash.exe', b'ash.exe',
    b'csh.exe', b'tcsh.exe',
    b'ksh.exe', b'zsh.exe', b'fish.exe',
    b'cmd.exe', b'powershell.exe', b'pwsh.exe',
    b'elvish.exe', b'xonsh.exe',
))

def find_shell_for_windows():
    proc_map = {
        proc.th32ProcessID: (proc.th32ParentProcessID, proc.szExeFile)
        for proc in enum_processes()
    }

    pid = proc_map[k32.GetCurrentProcessId()][0]
    proc = proc_map[pid]
    while proc:
        ppid, name = proc
        if name in SHELLS:
            with Win32Handle(k32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid)) as proch:
                return get_full_path(proch)
        pid, proc = ppid, proc_map.get(ppid)

if __name__ = '__main__':
    print(find_shell_for_windows())

上面的操作完全按照要求,只使用ctypes,并且应该在Windows和更高版本上工作(在Windows7上使用Python3.8进行测试)。对于旧版本的Windows,一些Win32调用可能需要更改(最重要的是,QueryFullProcessImageNameW)。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65902925

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档