Ele*_*ios 2 .net vb.net pinvoke winapi process
我想确定指定进程是否启用了特定权限。
为了使这个问题更容易,示例目标进程将是当前进程,我将检查关闭本地系统的权限(以前使用AdjustTokenPrivileges功能启用)。
Then, I found the PrivilegeCheck function that seems can determine whether a specified set of privileges are enabled in an access token of a target process.
I think that I focused on the wrong direction, because seems that the PrivilegeCheck function needs impersonation, so now I'm facing another neverending trial-and-error phase trying the GetTokenInformation function, which seems the proper function to realize this task.
The problem I have is that when I try to use the PrivilegeCheck function, it always returns False (error), and the by-reference array of privileges does not have the expected values (because the function failed).
The GetTokenInformation function fails too with a False value, returning this win32 error code: 122 (HRESULT: -2147467259) with message:
Data Area Passed to a System Call Is Too Small
What I should do to fix the errors I'm getting on my code to be able check whether a process's privilege exists, and then, whether the privilege is enabled or disabled?.
Using PrivilegeCheck or GetTokenInformation functions, or just any other damn function that could determine the privilege state.
This is a full copyable example (together with the p/invokes below) on where I'll demonstrate how I'm testing both the PrivilegeCheck and the GetTokenInformation methodologies, both fails.
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
Run Code Online (Sandbox Code Playgroud)
And these are the related winapi definitions (notice the commented MSDN urls for interest):
' 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
Run Code Online (Sandbox Code Playgroud)
我现在上传了我用来测试我的答案的完整代码,以防其他人需要一个工作示例。尤其是那里的样板并不完美(并且不打算完美),请不要以此来评判我。看到这个贴:http : //pastebin.com/gdxwWHRb
您错误地使用了非托管内存块tkpHandle和结构tkp。此外,您只是检查GetTokenInformation( tkp.Privileges(0))返回的第一个权限的属性- 相反,您必须检查所有并找到正确的。
如果我像这样更改您的代码,它对我有用:
' *********************************************************************
' Trying GetTokenInformation methodology...
' *********************************************************************
Dim tkp As New TokenPrivileges
Dim tkpHandle As IntPtr = IntPtr.Zero ' <<< will be set later
Dim tkInfoLength As Integer = 0
' Here we call GetTokenInformation the first time to receive the length of the data it would like to store.
NativeMethods.GetTokenInformation(hToken, TokenInformationClass.TokenPrivileges, IntPtr.Zero, tkInfoLength, tkInfoLength)
win32Err = Marshal.GetLastWin32Error
' Since the "current" length we pass is 0, we'll always get "error" 122, which is fine. We also get the required length returned.
If (win32Err <> 122) Then
MessageBox.Show("GetTokenInformation failed in the attempt to get the TokenPrivileges's size.")
Throw New Win32Exception(win32Err)
Else
Try
' Here we allocate memory for receiving the actual data. By now, tkInfoLength contains the size of the memory block we need to allocate.
tkpHandle = Marshal.AllocHGlobal(tkInfoLength)
' This time, we shouldn't get an error 122, because this time we already have set the correct buffer size. GetTokenInformation should now write the data into the memory block we just allocated.
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
' We will now ask PtrToStructure to read the raw data out of the memory block and convert it to a managed structure of type TokenPrivileges which we can use in our code. That's it!
tkp = Marshal.PtrToStructure(tkpHandle, GetType(TokenPrivileges))
' We have to iterate over all privileges listed in the TokenPrivileges structure to find the one we are looking for
Dim found As Boolean = False
For i As Integer = 0 To tkp.PrivilegeCount - 1
' There is a problem: Marshal.PtrToStructure can't marshal variable-length structures, but the array TokenPrivileges::Privileges has
' a variable length determined by the value of TokenPrivileges::PrivilegeCount! Since we don't know the size at compile time, the
' size of the array was hardcoded to 1, which means that we would only be able to access the first element of the array.
' To work around this, we calculate the raw memory offset pointing to the array element we need and load it separately into a
' LuidAndAttributes variable.
' The way this works is: The contents of the TokenPrivilege structure or stored in memory one after another, like this:
' PrivilegeCount (type: UInteger)
' Privileges(0) (type: LuidAndAttributes)
' Privileges(1) (type: LuidAndAttributes) << these and all further we normally can't access
' Privileges(2) (type: LuidAndAttributes)
' ...and so on.
' We are now calculating the offset into the structure for a specific array element. Let's use Privileges(2) as example:
' To get to it, we need to take the pointer to the beginning of the structure and add the sizes of all previous elements,
' which would be once the size of PrivilegeCount and then 2 times the size of a LuidAndAttributes structure.
Dim directPointer As New IntPtr(tkpHandle.ToInt64() + Len(tkp.PrivilegeCount) + i * Marshal.SizeOf(GetType(LuidAndAttributes)))
Dim luidAndAttributes As LuidAndAttributes = Marshal.PtrToStructure(directPointer, GetType(LuidAndAttributes))
' Get the privilege name. We first call LookupPrivilegeName with a zero size to get the real size we need, then reserve space, then get the actual data
' NOTE: The part below isn't actually necessary as commented Mark Hurd pointed out, because you already have the right privilege's LUID in luAttr.Luid.
' But I'll leave it here anyway in case somebody uses this piece of code without the part which sets the privilege.
' Another solution in this case would also be to run LookupPrivilegeValue to get the LUID to compare to (which is what led to luAttr.Luid as well).
Dim privNameLen As Integer = 0
Dim sb As New System.Text.StringBuilder()
NativeMethods.LookupPrivilegeName(Nothing, luidAndAttributes.Luid, sb, privNameLen)
sb.EnsureCapacity(privNameLen + 1)
If Not NativeMethods.LookupPrivilegeName(Nothing, luidAndAttributes.Luid, sb, privNameLen) Then
win32Err = Marshal.GetLastWin32Error
MessageBox.Show("LookupPrivilegeName failed.")
Throw New Win32Exception(win32Err)
End If
' Now that we have the name, we can check if it's the one we are looking for.
' NOTE: Refering to me comments above, this could be just: If luidAndAttributes.Luid = luAttr.Luid
If sb.ToString() = privilegeName Then
' Found! So we can finally get the status of the privilege!
found = True
MessageBox.Show(String.Format("{0} state is: {1}", privilegeName, luidAndAttributes.Attributes.ToString()))
Exit For
End If
Next
If Not found Then MessageBox.Show(String.Format("{0} not found in list of privileges!", privilegeName))
End If
Finally
' Make sure the memory block is freed again (even when an error occured)
If tkpHandle Then Marshal.FreeHGlobal(tkpHandle)
End Try
End If
Run Code Online (Sandbox Code Playgroud)
请参阅我在代码中的评论。基本上,流程是:
Marshal.PtrToStructure(not Marshal.StructureToPtr)将原始数据转换为 .NET 结构。这里还有另一个棘手的部分是如何找到正确的权限来检查由 返回的权限列表GetTokenInformation:
Marshal.PtrToStructure不能像TokenInformation结构内部那样编组可变长度数组。为了能够访问代码中第一个元素之后的元素,我们必须玩一些技巧并进行指针运算,以计算这些数组元素的内存偏移量,然后Marshal.PtrToStructure单独使用它们。有关详细信息,请参阅我在代码中的注释。LookupPrivilegeNamewhich 基本上与LookupPrivilegeValue. 请注意,我们再次使用零长度调用它以获得实际长度,然后保留空间,然后再次调用它。-注意:正如我在 EDIT2 中所写,如果您正在寻找特定的特权,这实际上并不是必需的。您只需要使用正确的 LUID LookupPrivilegeValue,然后将LUID与已知的LUID 进行比较。并且您已经在您的代码(在 中luAttr.Luid)的早些时候这样做了。请阅读代码中的其他注释!我是LookupPrivilegeName这样导入的:
<DllImport("Advapi32.dll", SetLastError:=True, CharSet:=CharSet.Auto, BestFitMapping:=False, ThrowOnUnmappableChar:=True)>
Public Shared Function LookupPrivilegeName(lpSystemName As String, ByRef lpLuid As Luid, lpName As System.Text.StringBuilder, ByRef cchName As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
Run Code Online (Sandbox Code Playgroud)
现在代码为我成功运行并显示一个消息框:
SeShutdownPrivilege 状态为:PrivilegeEnabled
编辑:我忽略了tkp.Privileges(0).Attributes硬编码的问题,而不是遍历所有权限以找到正确的权限。我在答案中添加了正确的解决方案。
EDIT2:正如评论者马克赫德正确指出的那样,实际上没有必要LookupPrivilegeName每次都打电话。在这个问题的原始代码中,特权的 LUID 是已知的,因为它早先使用LookupPrivilegeValue. 但是无论如何我都会在代码中保留名称查找,以防有人还需要枚举其他启用的权限 - 我只是添加了有关它的注释。
| 归档时间: |
|
| 查看次数: |
1857 次 |
| 最近记录: |