How to determine whether a process's privilege exists and its enabled/disabled?
<h2>SCENARIO</h2>

<hr>

<p>I would like to determine whether the specified process has enabled a specific <strong><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716%28v=vs.85%29.aspx" rel="nofollow">privilege</a></strong>. </p>

<p>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 <strong><a href="http://msdn.microsoft.com/es-es/library/windows/desktop/aa375202%28v=vs.85%29.aspx" rel="nofollow">AdjustTokenPrivileges</a></strong> function).</p>

<p>Then, I found the <strong><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa379304%28v=vs.85%29.aspx" rel="nofollow">PrivilegeCheck</a></strong> function that seems can determine whether a specified set of privileges are enabled in an access token of a target process. </p>

<h2>UPDATE</h2>

<p>I think that I focused on the wrong direction, because seems that the <code>PrivilegeCheck</code> function needs impersonation, so now I'm facing another neverending <em>trial-and-error</em> phase trying the <strong><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx" rel="nofollow">GetTokenInformation</a></strong> function, which seems the proper function to realize this task.</p>

<h2>PROBLEM</h2>

<hr>

<p>The problem I have is that when I try to use the <code>PrivilegeCheck</code> function, it always returns <code>False</code> (error), and the by-reference array of privileges does not have the expected values (because the function failed). </p>

<h2>UPDATE</h2>

<p>The <code>GetTokenInformation</code> function fails too with a <strong>False</strong> value, returning this win32 error code: <strong>122</strong> (HRESULT: <strong>-2147467259</strong>) with message:</p>

<blockquote>
  <p>Data Area Passed to a System Call Is Too Small</p>
</blockquote>

<h2>QUESTION</h2>

<hr>

<p>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?.</p>

<p>Using <code>PrivilegeCheck</code> or <code>GetTokenInformation</code> functions, or just any other damn function that could determine the privilege state.</p>

<h2>SOURCE-CODE</h2>

<hr>

<p>This is a full copyable example (together with the p/invokes below) on where I'll demonstrate how I'm testing both the <code>PrivilegeCheck</code> and the <code>GetTokenInformation</code> methodologies, both fails.</p>

<pre><code>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 &lt;&gt; 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 &lt;&gt; 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 &amp; " " &amp; ex.Message)

Catch ex As Exception
    MessageBox.Show(ex.Message)

Finally
    If (hTokenDup &lt;&gt; IntPtr.Zero) Then
        NativeMethods.CloseHandle(hTokenDup)
    End If

    If (hToken &lt;&gt; IntPtr.Zero) Then
        NativeMethods.CloseHandle(hToken)
    End If

End Try
</code></pre>

<p>And these are the related winapi definitions (notice the commented MSDN urls for interest):</p>

<pre><code>' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379295%28v=vs.85%29.aspx
&lt;DllImport("advapi32.dll", SetLastError:=True)&gt;
Public Shared Function OpenProcessToken(ByVal processHandle As IntPtr,
                                        ByVal desiredAccess As TokenAccess,
                                        ByRef tokenHandle As IntPtr
) As &lt;MarshalAs(UnmanagedType.Bool)&gt; Boolean
End Function

' http://msdn.microsoft.com/en-us/library/windows/desktop/aa379180%28v=vs.85%29.aspx
&lt;DllImport("Advapi32.dll", SetLastError:=True, CharSet:=CharSet.Auto, BestFitMapping:=False, ThrowOnUnmappableChar:=True)&gt;
Public Shared Function LookupPrivilegeValue(ByVal lpSystemName As String,
                                            ByVal lpName As String,
                                            ByRef lpLuid As Luid
) As &lt;MarshalAs(UnmanagedType.Bool)&gt; Boolean
End Function

' http://msdn.microsoft.com/es-es/library/windows/desktop/aa375202%28v=vs.85%29.aspx
&lt;DllImport("Advapi32.dll", SetLastError:=True)&gt;
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 &lt;MarshalAs(UnmanagedType.Bool)&gt; Boolean
End Function

' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379304%28v=vs.85%29.aspx
&lt;DllImport("Advapi32.dll", SetLastError:=True)&gt;
Public Shared Function PrivilegeCheck(ByVal token As IntPtr,
                          &lt;[In], Out&gt; ByRef privileges As PrivilegeSet,
                                      ByRef refResult As Boolean
) As &lt;MarshalAs(UnmanagedType.Bool)&gt; Boolean
End Function

' https://msdn.microsoft.com/en-us/library/windows/desktop/aa446616%28v=vs.85%29.aspx
&lt;DllImport("advapi32.dll", SetLastError:=True)&gt;
Public Shared Function DuplicateToken(ByVal tokenHandle As IntPtr,
                                      ByVal impersonationLevel As SecurityImpersonationLevel,
                                      ByRef duplicateTokenHandle As IntPtr
) As &lt;MarshalAs(UnmanagedType.Bool)&gt; Boolean
End Function

' https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671%28v=vs.85%29.aspx
&lt;DllImport("Advapi32.dll", SetLastError:=True)&gt;
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 &lt;MarshalAs(UnmanagedType.Bool)&gt; Boolean
End Function

' http://msdn.microsoft.com/en-us/library/windows/desktop/aa374905%28v=vs.85%29.aspx
&lt;Flags&gt;
Public Enum TokenAccess As UInteger
    ' THIS ENUMERATION IS PARTIALLY DEFINED.
    ' **************************************
    TokenAdjustPrivileges = &amp;H20UI
    TokenQuery = &amp;H8UI
End Enum

' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379630%28v=vs.85%29.aspx
&lt;Flags&gt;
Public Enum TokenPrivilegeAttributes As UInteger
    PrivilegeDisabled = &amp;H0UI
    PrivilegeEnabledByDefault = &amp;H1UI
    PrivilegeEnabled = &amp;H2UI
    PrivilegeRemoved = &amp;H4UI
    PrivilegeUsedForAccess = &amp;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
&lt;StructLayout(LayoutKind.Sequential)&gt;
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
&lt;StructLayout(LayoutKind.Sequential)&gt;
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
&lt;StructLayout(LayoutKind.Sequential)&gt;
Public Structure TokenPrivileges
    Public PrivilegeCount As UInteger

    &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)&gt;
    Public Privileges As LuidAndAttributes()
End Structure

' https://msdn.microsoft.com/en-us/library/windows/desktop/aa379307%28v=vs.85%29.aspx
&lt;StructLayout(LayoutKind.Sequential)&gt;
Public Structure PrivilegeSet
    Public PrivilegeCount As UInteger
    Public Control As UInteger

    &lt;MarshalAs(UnmanagedType.ByValArray, SizeConst:=1)&gt;
    Public Privileges As LuidAndAttributes()
End Structure
</code></pre>