代码之家  ›  专栏  ›  技术社区  ›  Jason R. Coombs

确定Windows进程是否具有创建符号链接的权限

  •  6
  • Jason R. Coombs  · 技术社区  · 15 年前

    我想通过编程确定当前用户(或进程)是否有权创建符号链接。在Windows(Vista和更高版本)中,如果没有secreatesymboliclinkpriville,则无法创建符号链接,默认情况下,只能将其分配给管理员。如果试图在没有此权限的情况下创建符号链接,则会发生Windows错误1314(客户端不持有所需的权限)。

    为了演示此限制,我创建了一个全新的Windows安装,作为初始管理员帐户登录(通过UAC进行限制),并且无法在主目录中创建symlink。

    以管理员身份运行命令提示符或禁用UAC后,该命令执行时不会出错。

    根据 This article ,“Every process executed for the user has a copy of the[access]token.”

    因此,我创建了一个python脚本来查询权限。为了方便和子孙后代,我在下面摘录了一段内容。

    脚本背后的思想是枚举所有特权,并确定进程是否具有所需的特权。不幸的是,我发现当前进程实际上不具有所需的特权,即使它可以创建符号链接。

    我怀疑问题在于,即使当前用户的权限没有明确包括该权限,但其组成员身份确实提供了该权限。

    简而言之,我如何确定给定进程是否具有创建符号链接的特权(而不尝试创建符号链接)?C或C++或Python中的一个例子是首选的,尽管使用Windows API的任何东西都是合适的。

    def get_process_token():
    令牌=wintypes.handle()
    令牌访问=0xf01ff
    res=openprocesstoken(getcurrentprocess(),token_all_access,token)
    如果不是res>0:raise runtimeerror(“无法获取进程令牌”)。
    返回令牌
    
    def get_privilege_information():
    #第一次调用零长度以确定我们需要的缓冲区大小
    
    返回_length=wintypes.dword()。
    PARAM=
    获取_process_token(),
    令牌\信息\类.tokenprivileges,
    没有,
    0,
    返回长度
    ]
    
    res=gettokeninformation(*params)
    
    #假设我们现在有必要的返回长度
    
    buffer=ctypes.create_string_buffer(返回长度.value)
    参数[2]=缓冲区
    params[3]=返回长度值
    
    res=gettokeninformation(*params)
    assert res>0,“第二次gettokeninformation时出错(%d)”%res
    
    privileges=ctypes.cast(buffer,ctypes.pointer(token_privileges))。内容
    返回特权
    
    privileges=get_privilege_information()。
    打印(“找到0特权”.格式(privileges.count))
    地图(打印,权限)
    < /代码> <创建一个没有secreatesymboliclinkpriville的符号链接,默认情况下,它只分配给管理员。如果试图在没有此权限的情况下创建符号链接,则会发生Windows错误1314(客户端不保留所需的权限)。

    为了演示这个限制,我创建了一个全新的Windows安装,作为初始管理员帐户登录(通过UAC进行限制),无法在主目录中创建symlink。

    Command Prompt demonstrates mklink fails due to insufficient privilege

    以管理员身份运行命令提示符或禁用UAC后,该命令将无错误地执行。

    根据this article“代表用户执行的每个进程都有一个[访问]令牌的副本”。

    所以我创造了a Python script to query for the permissions. 为了方便和子孙后代,我在下面摘录了一段。

    脚本背后的思想是枚举所有特权,并确定进程是否具有所需的特权。不幸的是,我发现当前进程实际上没有所需的特权,即使它可以创建符号链接。

    我怀疑问题在于,即使当前用户的权限没有明确包括该权限,但其组成员身份确实提供了该权限。

    简而言之,我如何确定给定进程是否具有创建符号链接的特权(而不尝试创建符号链接)?C或C++或Python中的一个例子是首选的,尽管使用Windows API的任何东西都是合适的。

    def get_process_token():
        token = wintypes.HANDLE()
        TOKEN_ALL_ACCESS = 0xf01ff
        res = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, token)
        if not res > 0: raise RuntimeError("Couldn't get process token")
        return token
    
    def get_privilege_information():
        # first call with zero length to determine what size buffer we need
    
        return_length = wintypes.DWORD()
        params = [
            get_process_token(),
            TOKEN_INFORMATION_CLASS.TokenPrivileges,
            None,
            0,
            return_length,
            ]
    
        res = GetTokenInformation(*params)
    
        # assume we now have the necessary length in return_length
    
        buffer = ctypes.create_string_buffer(return_length.value)
        params[2] = buffer
        params[3] = return_length.value
    
        res = GetTokenInformation(*params)
        assert res > 0, "Error in second GetTokenInformation (%d)" % res
    
        privileges = ctypes.cast(buffer, ctypes.POINTER(TOKEN_PRIVILEGES)).contents
        return privileges
    
    privileges = get_privilege_information()
    print("found {0} privileges".format(privileges.count))
    map(print, privileges)
    
    1 回复  |  直到 15 年前
        1
  •  4
  •   Jason R. Coombs    15 年前

    我找到了解决办法。下面的python代码是python 2.6或3.1下的一个功能完整的脚本,它演示了如何确定创建符号链接的特权。在管理员帐户下运行此操作将成功响应,在来宾帐户下运行此操作将失败响应。

    注意,脚本的前3/4大部分是API定义。这部小说从get_process_token()开始。

    from __future__ import print_function
    import ctypes
    from ctypes import wintypes
    
    GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess
    GetCurrentProcess.restype = wintypes.HANDLE
    OpenProcessToken = ctypes.windll.advapi32.OpenProcessToken
    OpenProcessToken.argtypes = (wintypes.HANDLE, wintypes.DWORD, ctypes.POINTER(wintypes.HANDLE))
    OpenProcessToken.restype = wintypes.BOOL
    
    class LUID(ctypes.Structure):
        _fields_ = [
            ('low_part', wintypes.DWORD),
            ('high_part', wintypes.LONG),
            ]
    
        def __eq__(self, other):
            return (
                self.high_part == other.high_part and
                self.low_part == other.low_part
                )
    
        def __ne__(self, other):
            return not (self==other)
    
    LookupPrivilegeValue = ctypes.windll.advapi32.LookupPrivilegeValueW
    LookupPrivilegeValue.argtypes = (
        wintypes.LPWSTR, # system name
        wintypes.LPWSTR, # name
        ctypes.POINTER(LUID),
        )
    LookupPrivilegeValue.restype = wintypes.BOOL
    
    class TOKEN_INFORMATION_CLASS:
        TokenUser = 1
        TokenGroups = 2
        TokenPrivileges = 3
        # ... see http://msdn.microsoft.com/en-us/library/aa379626%28VS.85%29.aspx
    
    SE_PRIVILEGE_ENABLED_BY_DEFAULT = (0x00000001)
    SE_PRIVILEGE_ENABLED            = (0x00000002)
    SE_PRIVILEGE_REMOVED            = (0x00000004)
    SE_PRIVILEGE_USED_FOR_ACCESS    = (0x80000000)
    
    class LUID_AND_ATTRIBUTES(ctypes.Structure):
        _fields_ = [
            ('LUID', LUID),
            ('attributes', wintypes.DWORD),
            ]
    
        def is_enabled(self):
            return bool(self.attributes & SE_PRIVILEGE_ENABLED)
    
        def enable(self):
            self.attributes |= SE_PRIVILEGE_ENABLED
    
        def get_name(self):
            size = wintypes.DWORD(10240)
            buf = ctypes.create_unicode_buffer(size.value)
            res = LookupPrivilegeName(None, self.LUID, buf, size)
            if res == 0: raise RuntimeError
            return buf[:size.value]
    
        def __str__(self):
            res = self.get_name()
            if self.is_enabled(): res += ' (enabled)'
            return res
    
    LookupPrivilegeName = ctypes.windll.advapi32.LookupPrivilegeNameW
    LookupPrivilegeName.argtypes = (
        wintypes.LPWSTR, # lpSystemName
        ctypes.POINTER(LUID), # lpLuid
        wintypes.LPWSTR, # lpName
        ctypes.POINTER(wintypes.DWORD), #cchName
        )
    LookupPrivilegeName.restype = wintypes.BOOL
    
    class TOKEN_PRIVILEGES(ctypes.Structure):
        _fields_ = [
            ('count', wintypes.DWORD),
            ('privileges', LUID_AND_ATTRIBUTES*0),
            ]
    
        def get_array(self):
            array_type = LUID_AND_ATTRIBUTES*self.count
            privileges = ctypes.cast(self.privileges, ctypes.POINTER(array_type)).contents
            return privileges
    
        def __iter__(self):
            return iter(self.get_array())
    
    PTOKEN_PRIVILEGES = ctypes.POINTER(TOKEN_PRIVILEGES)
    
    GetTokenInformation = ctypes.windll.advapi32.GetTokenInformation
    GetTokenInformation.argtypes = [
        wintypes.HANDLE, # TokenHandle
        ctypes.c_uint, # TOKEN_INFORMATION_CLASS value
        ctypes.c_void_p, # TokenInformation
        wintypes.DWORD, # TokenInformationLength
        ctypes.POINTER(wintypes.DWORD), # ReturnLength
        ]
    GetTokenInformation.restype = wintypes.BOOL
    
    # http://msdn.microsoft.com/en-us/library/aa375202%28VS.85%29.aspx
    AdjustTokenPrivileges = ctypes.windll.advapi32.AdjustTokenPrivileges
    AdjustTokenPrivileges.restype = wintypes.BOOL
    AdjustTokenPrivileges.argtypes = [
        wintypes.HANDLE,                # TokenHandle
        wintypes.BOOL,                  # DisableAllPrivileges
        PTOKEN_PRIVILEGES,              # NewState (optional)
        wintypes.DWORD,                 # BufferLength of PreviousState
        PTOKEN_PRIVILEGES,              # PreviousState (out, optional)
        ctypes.POINTER(wintypes.DWORD), # ReturnLength
        ]
    
    def get_process_token():
        """
        Get the current process token
        """
        token = wintypes.HANDLE()
        TOKEN_ALL_ACCESS = 0xf01ff
        res = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, token)
        if not res > 0: raise RuntimeError("Couldn't get process token")
        return token
    
    def get_symlink_luid():
        """
        Get the LUID for the SeCreateSymbolicLinkPrivilege
        """
        symlink_luid = LUID()
        res = LookupPrivilegeValue(None, "SeCreateSymbolicLinkPrivilege", symlink_luid)
        if not res > 0: raise RuntimeError("Couldn't lookup privilege value")
        return symlink_luid
    
    def get_privilege_information():
        """
        Get all privileges associated with the current process.
        """
        # first call with zero length to determine what size buffer we need
    
        return_length = wintypes.DWORD()
        params = [
            get_process_token(),
            TOKEN_INFORMATION_CLASS.TokenPrivileges,
            None,
            0,
            return_length,
            ]
    
        res = GetTokenInformation(*params)
    
        # assume we now have the necessary length in return_length
    
        buffer = ctypes.create_string_buffer(return_length.value)
        params[2] = buffer
        params[3] = return_length.value
    
        res = GetTokenInformation(*params)
        assert res > 0, "Error in second GetTokenInformation (%d)" % res
    
        privileges = ctypes.cast(buffer, ctypes.POINTER(TOKEN_PRIVILEGES)).contents
        return privileges
    
    def report_privilege_information():
        """
        Report all privilege information assigned to the current process.
        """
        privileges = get_privilege_information()
        print("found {0} privileges".format(privileges.count))
        tuple(map(print, privileges))
    
    def enable_symlink_privilege():
        """
        Try to assign the symlink privilege to the current process token.
        Return True if the assignment is successful.
        """
        # create a space in memory for a TOKEN_PRIVILEGES structure
        #  with one element
        size = ctypes.sizeof(TOKEN_PRIVILEGES)
        size += ctypes.sizeof(LUID_AND_ATTRIBUTES)
        buffer = ctypes.create_string_buffer(size)
        tp = ctypes.cast(buffer, ctypes.POINTER(TOKEN_PRIVILEGES)).contents
        tp.count = 1
        tp.get_array()[0].enable()
        tp.get_array()[0].LUID = get_symlink_luid()
        token = get_process_token()
        res = AdjustTokenPrivileges(token, False, tp, 0, None, None)
        if res == 0:
            raise RuntimeError("Error in AdjustTokenPrivileges")
    
        ERROR_NOT_ALL_ASSIGNED = 1300
        return ctypes.windll.kernel32.GetLastError() != ERROR_NOT_ALL_ASSIGNED
    
    def main():
        assigned = enable_symlink_privilege()
        msg = ['failure', 'success'][assigned]
    
        print("Symlink privilege assignment completed with {0}".format(msg))
    
    if __name__ == '__main__': main()