SCENARIO
I would like to determine whether the specified process has enabled a specific privilege.
To make things easier for this question, the example target process will be the current process, and I'll check for the right to shutdown the local system (previouslly enabled with AdjustTokenPrivileges function).
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.
UPDATE
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.
PROBLEM
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).
UPDATE
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
QUESTION
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.
SOURCE-CODE
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
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
1 Answers
Answers 1
You are incorrectly using the unmanaged memory block tkpHandle
and the structure tkp
.
If I change your code like this, it works for me:
' ********************************************************************* ' 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)) Dim privilegeAttr As TokenPrivilegeAttributes = tkp.Privileges(0).Attributes MessageBox.Show(String.Format("{0} state is: {1}", privilegeName, privilegeAttr.ToString())) 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
See my comments in the code. Basically, the flow is:
- Call GetTokenInformation with a null pointer and a zero size in order to receive the actually required buffer size (so error 122 is normal here).
- Allocate memory according to the buffer size we received.
- Call GetTokenInformation again, with the real pointer to the memory block and the real size in order to receive the actual data.
- Convert the raw data into a .NET structure using
Marshal.PtrToStructure
(notMarshal.StructureToPtr
). - Free the memory block. (I added a Try/Finally block to make sure the memory is never leaked.)
Now the code successfully runs for me and shows a message box:
SeShutdownPrivilege state is: PrivilegeEnabled
0 comments:
Post a Comment