【发布时间】:2016-04-18 00:18:47
【问题描述】:
场景
我想确定指定的进程是否启用了特定的privilege。
为了让这个问题更简单,示例目标进程将是当前进程,我将检查关闭本地系统的权限(之前启用了 AdjustTokenPrivileges 功能) .
然后,我发现PrivilegeCheck函数似乎可以确定是否在目标进程的访问令牌中启用了一组指定的权限。
更新
我认为我专注于错误的方向,因为似乎PrivilegeCheck 函数需要模拟,所以现在我正面临另一个无休止的试错阶段尝试 GetTokenInformation 函数,这似乎是实现此任务的正确函数。
问题
我遇到的问题是,当我尝试使用PrivilegeCheck函数时,它总是返回False(错误),并且权限的引用数组没有预期值(因为函数失败) .
更新
GetTokenInformation 函数也因 False 值而失败,返回此 win32 错误代码:122(HRESULT:-2147467259)留言:
传递给系统调用的数据区域太小
问题
我应该做些什么来修复我在代码上遇到的错误,以便能够检查进程的权限是否存在,然后,该权限是启用还是禁用?
使用PrivilegeCheck 或GetTokenInformation 函数,或任何其他可以确定特权状态的该死函数。
源代码
这是一个完整的可复制示例(连同下面的 p/invokes),我将在其中演示我如何测试 PrivilegeCheck 和 GetTokenInformation 方法,两者都失败了。
Dim pHandle As IntPtr = Process.GetCurrentProcess().Handle
Dim privilegeName As String = "SeShutdownPrivilege"
Dim tokenAccess As TokenAccess = (TokenAccess.AdjustPrivileges Or TokenAccess.Query Or TokenAccess.Duplicate)
Dim hToken As IntPtr
Dim hTokenDup As IntPtr
Try
' ****************************************************************************
' 1st Step: Enable the "SeShutdownPrivilege" privilege in the current process.
' ****************************************************************************
Dim win32Err As Integer
' Get the process token.
NativeMethods.OpenProcessToken(pHandle, tokenAccess, hToken)
' Set up a LuidAndAttributes structure containing the privilege to enable,
' getting the LUID that corresponds to the privilege.
Dim luAttr As New LuidAndAttributes
luAttr.Attributes = TokenPrivilegeAttributes.PrivilegeEnabled
NativeMethods.LookupPrivilegeValue(Nothing, privilegeName, luAttr.Luid)
' Set up a TokenPrivileges structure containing only the source privilege.
Dim newState As New TokenPrivileges
newState.PrivilegeCount = 1
newState.Privileges = New LuidAndAttributes() {luAttr}
' Set up a TokenPrivileges structure for the previous (modified) privileges.
Dim prevState As New TokenPrivileges
prevState = New TokenPrivileges
ReDim prevState.Privileges(CInt(newState.PrivilegeCount))
' Apply the TokenPrivileges structure to the source process token.
Dim bufferLength As Integer = Marshal.SizeOf(prevState)
Dim returnLength As IntPtr
If Not NativeMethods.AdjustTokenPrivileges(hToken, False, newState, bufferLength, prevState, returnLength) Then
win32Err = Marshal.GetLastWin32Error
MessageBox.Show("AdjustTokenPrivileges failed.")
Throw New Win32Exception(win32Err)
End If
' *********************************************************************
' Everything OK at this point,
' as AdjustTokenPrivileges dididn't failed, I assume the privilege Is enabled in the process.
'
' 2n Step: Check whether the privilege is enabled or not...
' *********************************************************************
' Set up a new one LuidAndAttributes structure containing the privilege to check,
' getting the LUID that corresponds to the privilege.
luAttr = New LuidAndAttributes
NativeMethods.LookupPrivilegeValue(Nothing, privilegeName, luAttr.Luid)
' *********************************************************************
' Trying PrivilegeCheck and Duplicatetoken methodology...
' *********************************************************************
NativeMethods.DuplicateToken(hToken, SecurityImpersonationLevel.SecurityImpersonation, hTokenDup)
win32Err = Marshal.GetLastWin32Error
If (hTokenDup <> IntPtr.Zero) Then
Dim result As Boolean
Dim pSet As New PrivilegeSet
pSet.Control = 0
pSet.PrivilegeCount = 1
pSet.Privileges = New LuidAndAttributes() {luAttr}
If Not NativeMethods.PrivilegeCheck(hToken, pSet, result) Then
win32Err = Marshal.GetLastWin32Error
MessageBox.Show("PrivilegeCheck using original access-token failed.")
' Ignore exception, to continue with the GetTokenInformation methodology.
' Throw New Win32Exception(win32Err)
Else
MessageBox.Show(String.Format("{0} (original token) state is: {1}",
privilegeName, pSet.Privileges(0).Attributes.ToString()))
If Not NativeMethods.PrivilegeCheck(hTokenDup, pSet, result) Then
win32Err = Marshal.GetLastWin32Error
MessageBox.Show("PrivilegeCheck using impersonated access-token failed.")
' Ignore exception, to continue with the GetTokenInformation methodology.
' Throw New Win32Exception(win32Err)
Else
MessageBox.Show(String.Format("{0} (impersonated token) state is: {1}",
privilegeName, pSet.Privileges(0).Attributes.ToString()))
End If
End If
Else
MessageBox.Show("DuplicateToken failed.")
' Ignore exception, to continue with the GetTokenInformation methodology.
' Throw New Win32Exception(win32Err)
End If
' *********************************************************************
' Trying GetTokenInformation methodology...
' *********************************************************************
Dim tkp As New TokenPrivileges
Dim tkpHandle As IntPtr
Dim tkInfoLength As Integer = 0
tkpHandle = Marshal.AllocHGlobal(Marshal.SizeOf(tkpHandle))
Marshal.StructureToPtr(tkp, tkpHandle, False)
NativeMethods.GetTokenInformation(hToken, TokenInformationClass.TokenPrivileges, IntPtr.Zero, tkInfoLength, tkInfoLength)
win32Err = Marshal.GetLastWin32Error
' If I understood, It is supposed to return 122,
' so I should ignore that error code?:
If (win32Err <> 122) Then
MessageBox.Show("GetTokenInformation failed in the attempt to get the TokenPrivileges's size.")
Throw New Win32Exception(win32Err)
Else
If Not NativeMethods.GetTokenInformation(hToken, TokenInformationClass.TokenPrivileges, tkpHandle, tkInfoLength, tkInfoLength) Then
win32Err = Marshal.GetLastWin32Error
MessageBox.Show("GetTokenInformation failed in the attempt to get the TokenPrivileges.")
Throw New Win32Exception(win32Err)
Else
Dim privilegeAttr As TokenPrivilegeAttributes = tkp.Privileges(0).Attributes
MessageBox.Show(String.Format("{0} state is: {1}", privilegeName, privilegeAttr.ToString()))
End If
End If
Catch ex As Win32Exception
MessageBox.Show(ex.NativeErrorCode & " " & ex.Message)
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
If (hTokenDup <> IntPtr.Zero) Then
NativeMethods.CloseHandle(hTokenDup)
End If
If (hToken <> IntPtr.Zero) Then
NativeMethods.CloseHandle(hToken)
End If
End Try
这些是相关的 winapi 定义(请注意评论的 MSDN 网址以引起兴趣):
' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379295%28v=vs.85%29.aspx
<DllImport("advapi32.dll", SetLastError:=True)>
Public Shared Function OpenProcessToken(ByVal processHandle As IntPtr,
ByVal desiredAccess As TokenAccess,
ByRef tokenHandle As IntPtr
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379180%28v=vs.85%29.aspx
<DllImport("Advapi32.dll", SetLastError:=True, CharSet:=CharSet.Auto, BestFitMapping:=False, ThrowOnUnmappableChar:=True)>
Public Shared Function LookupPrivilegeValue(ByVal lpSystemName As String,
ByVal lpName As String,
ByRef lpLuid As Luid
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
' http://msdn.microsoft.com/es-es/library/windows/desktop/aa375202%28v=vs.85%29.aspx
<DllImport("Advapi32.dll", SetLastError:=True)>
Public Shared Function AdjustTokenPrivileges(ByVal tokenHandle As IntPtr,
ByVal disableAllPrivileges As Boolean,
ByRef newState As TokenPrivileges,
ByVal bufferLength As Integer,
ByRef refPreviousState As TokenPrivileges,
ByRef refReturnLength As IntPtr
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379304%28v=vs.85%29.aspx
<DllImport("Advapi32.dll", SetLastError:=True)>
Public Shared Function PrivilegeCheck(ByVal token As IntPtr,
<[In], Out> ByRef privileges As PrivilegeSet,
ByRef refResult As Boolean
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
' https://msdn.microsoft.com/en-us/library/windows/desktop/aa446616%28v=vs.85%29.aspx
<DllImport("advapi32.dll", SetLastError:=True)>
Public Shared Function DuplicateToken(ByVal tokenHandle As IntPtr,
ByVal impersonationLevel As SecurityImpersonationLevel,
ByRef duplicateTokenHandle As IntPtr
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
' https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671%28v=vs.85%29.aspx
<DllImport("Advapi32.dll", SetLastError:=True)>
Public Shared Function GetTokenInformation(ByVal tokenHandle As IntPtr,
ByVal tokenInformationClass As TokenInformationClass,
ByVal tokenInformation As IntPtr,
ByVal tokenInformationLength As Integer,
ByRef refReturnLength As Integer
) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
' http://msdn.microsoft.com/en-us/library/windows/desktop/aa374905%28v=vs.85%29.aspx
<Flags>
Public Enum TokenAccess As UInteger
' THIS ENUMERATION IS PARTIALLY DEFINED.
' **************************************
TokenAdjustPrivileges = &H20UI
TokenQuery = &H8UI
End Enum
' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379630%28v=vs.85%29.aspx
<Flags>
Public Enum TokenPrivilegeAttributes As UInteger
PrivilegeDisabled = &H0UI
PrivilegeEnabledByDefault = &H1UI
PrivilegeEnabled = &H2UI
PrivilegeRemoved = &H4UI
PrivilegeUsedForAccess = &H80000000UI
End Enum
' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379572(v=vs.85).aspx
Public Enum SecurityImpersonationLevel As Integer
SecurityAnonymous = 0
SecurityIdentification = 1
SecurityImpersonation = 2
SecurityDelegation = 3
End Enum
' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379261%28v=vs.85%29.aspx
<StructLayout(LayoutKind.Sequential)>
Public Structure Luid
Public LowPart As UInteger
Public HighPart As Integer
End Structure
' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379263%28v=vs.85%29.aspx
<StructLayout(LayoutKind.Sequential)>
Public Structure LuidAndAttributes
Public Luid As Luid
Public Attributes As TokenPrivilegeAttributes
End Structure
' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379630%28v=vs.85%29.aspx
<StructLayout(LayoutKind.Sequential)>
Public Structure TokenPrivileges
Public PrivilegeCount As UInteger
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)>
Public Privileges As LuidAndAttributes()
End Structure
' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379307%28v=vs.85%29.aspx
<StructLayout(LayoutKind.Sequential)>
Public Structure PrivilegeSet
Public PrivilegeCount As UInteger
Public Control As UInteger
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)>
Public Privileges As LuidAndAttributes()
End Structure
【问题讨论】:
-
PrivilegeCheck 的文档说该令牌“必须是通过打开模拟客户端的线程的令牌获得的”,即它必须是一个模拟令牌。我不确定,但我似乎记得在某处读过这是一个严格的要求,如果给定一个主令牌,该功能真的会失败,这就是你正在做的。首先尝试使用 DuplicateToken 将主令牌转换为模拟令牌。
-
@Harry Johnston 感谢您注意到文档的信息,我遵循了您的建议,我确保
DuplicateToken函数返回的句柄/令牌不是零/空,但是,即使使用新的获得的令牌我仍然遇到我的问题中描述的相同问题,PrivilegeCheck函数失败返回 False。 -
无论如何我不确定我是否应该搞乱用户模拟,我不是这方面的专家,但我认为我并没有假装在那个级别做需要模拟的特定事情,因为我只想确定是否存在特权,并且它在本地系统上的目标进程和当前登录的用户上启用,所以如果
PrivilegeCheck需要模拟,那么我可能专注于错误的功能,我应该使用我需要什么比PrivilegeCheck更简单的功能?那会是什么功能?有什么想法吗?。 -
IMO,此时您可以做的最有用的事情是找到 PrivilegeCheck 返回的 Win32 错误代码 - IIRC,您需要在 P/Invoke 函数声明中添加一些内容,但我没有不记得细节。但是如果你想要一个替代方案,你总是可以使用 GetTokenInformation 并自己检查权限数组。 (我不确定比较两个 LUID 的正确方法,但出于向后兼容性的考虑,每个结构元素的简单比较应该是安全的。)
-
看起来 GetTokenInformation 的问题是缓冲区溢出。您告诉它缓冲区
tkpHandle的长度为tkInfoLength字节长,但除非我错误地读取了.NET 代码,否则您实际上只分配了Marshal.SizeOf(tkpHandle),即4 或8 个字节,具体取决于位数。您需要在第一次调用 GetTokenInformation 之后分配缓冲区,一旦您知道它需要多大。
标签: .net vb.net winapi process pinvoke