Link to home
Start Free TrialLog in
Avatar of Lars007
Lars007

asked on

AdjustTokenPrivileges fails with error 87 (invalid parameter)

Hello,

I am trying to load a registry hive from C# (which requires P/Invoke).  This code is based on a sample from MS, but for some reason AdjustTokenPrivileges always fails with GetLastError = 87 (=Invalid Parameter), but I cannot see why (see line 78 below):

public class RegistryHive
    {
        private struct LUID
        {
            public long LowPart;
            public long HighPart;
        }

        private struct LUID_AND_ATTRIBUTES
        {
            public LUID pLuid;
            public long Attributes;
        }

        private class TOKEN_PRIVILEGES
        {
            public long PrivilegeCount;
            public LUID_AND_ATTRIBUTES[] Privileges = new LUID_AND_ATTRIBUTES[2];
        }

        private const int TOKEN_ADJUST_PRIVLEGES = 0x20;
        private const int TOKEN_QUERY = 0x8;
        private const int SE_PRIVILEGE_ENABLED = 0x2;
        private const uint HKEY_USERS = 0x80000003;
        private const string SE_RESTORE_NAME = "SeRestorePrivilege";
        private const string SE_BACKUP_NAME = "SeBackupPrivilege";

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentProcess();

        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);

        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool AdjustTokenPrivileges(IntPtr TokenHandle,
            [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
            ref TOKEN_PRIVILEGES NewState,
            UInt32 Zero,
            IntPtr Null1,
            IntPtr Null2);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern long RegLoadKey(UInt32 hKey, String lpSubKey, String lpFile);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern long RegUnLoadKey(UInt32 hKey, string lpSubKey);

        private string _loadUnderKeyName;
        private IntPtr _myToken;
        private TOKEN_PRIVILEGES _tokenPrivileges = new TOKEN_PRIVILEGES();
        private LUID _restoreLuid;
        private LUID _backupLuid;

        public RegistryHive()
        {
            if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVLEGES | TOKEN_QUERY, out _myToken))
                Console.WriteLine("OpenProcess Error");

            if (!LookupPrivilegeValue(null, SE_RESTORE_NAME, out _restoreLuid))
                Console.WriteLine("LookupPrivilegeValue Error");

            if (!LookupPrivilegeValue(null, SE_BACKUP_NAME, out _backupLuid))
                Console.WriteLine("LookupPrivilegeValue Error");

            _tokenPrivileges.PrivilegeCount = 2;
            _tokenPrivileges.Privileges[0].pLuid = _restoreLuid;
            _tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
            _tokenPrivileges.Privileges[1].pLuid = _backupLuid;
            _tokenPrivileges.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;

            // ----> THIS ALWAYS FAILS WITH ERROR 87.  Why?  <----
            if (!AdjustTokenPrivileges(_myToken, false, ref _tokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero))
                Console.WriteLine("AdjustTokenPrivileges Error: " + Marshal.GetLastWin32Error()); 
        }

        public bool LoadHive(string filePath)
        {
            _loadUnderKeyName = "123test";
            long Retval = RegLoadKey(HKEY_USERS, _loadUnderKeyName, filePath);
            if (Retval != 0)
            {
                Console.WriteLine("RegLoadKey Error");
                return false;
            }
            return true;
        }
    }

Open in new window

 

Windows 7 (64), VS2008.  I have launched VS2008 as administrator (and added a manifest with requestedExecutionLevel = requireAdministrator), just in case.  

Any help on this would be appreciated!

ASKER CERTIFIED SOLUTION
Avatar of graye
graye
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Lars007
Lars007

ASKER

Thanks for the reply, but I tried changing the longs to ints, to no avail - same error.  

Other suggestions?
Well, here is a VB.Net example that I've got handy....
<StructLayout(LayoutKind.Sequential, Pack:=4)> _
    Private Structure LUID_AND_ATTRIBUTES
        Dim Luid As Long
        Dim Attributes As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential, Pack:=4)> _
    Private Structure TOKEN_PRIVILEGES
        Dim PrivilegeCount As Integer
        Dim Privilege1 As LUID_AND_ATTRIBUTES
        Dim Privilege2 As LUID_AND_ATTRIBUTES
    End Structure

    <DllImport("advapi32.dll")> _
    Private Function OpenProcessToken _
        (ByVal ProcessHandle As IntPtr, _
         ByVal DesiredAccess As Integer, _
         ByRef TokenHandle As IntPtr) _
        As Boolean
    End Function

    <DllImport("advapi32.dll")> _
    Private Function LookupPrivilegeValue _
        (ByVal lpSystemName As String, _
         ByVal lpName As String, _
         ByRef lpLuid As Long) _
         As Boolean
    End Function

    <DllImport("advapi32.dll")> _
    Private Function AdjustTokenPrivileges _
        (ByVal TokenHandle As IntPtr, _
         ByVal DisableAllPrivileges As Boolean, _
         ByRef NewState As TOKEN_PRIVILEGES, _
         ByVal BufferLength As Integer, _
         ByVal PreviousState As IntPtr, _
         ByVal ReturnLength As IntPtr) _
         As Boolean
    End Function

    Const TOKEN_QUERY As Integer = &H8
    Const TOKEN_ADJUST_PRIVILEGES As Integer = &H20
    Const SE_TAKE_OWNERSHIP_NAME As String = "SeTakeOwnershipPrivilege"
    Const SE_RESTORE_NAME As String = "SeRestorePrivilege"
    Const SE_PRIVILEGE_ENABLED As Integer = &H2

    Public Function SetPrivileges() As Boolean
        Dim hProc, hToken As IntPtr
        Dim luid_TakeOwnership, luid_Restore As Long
        Dim tp As New TOKEN_PRIVILEGES

        ' get the current process's token
        hProc = Process.GetCurrentProcess().Handle
        hToken = IntPtr.Zero
        If Not OpenProcessToken(hProc, TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, hToken) Then
            Return False
        End If

        ' get the LUIDs for the  privileges (provided they already exists)
        luid_TakeOwnership = 0
        If Not LookupPrivilegeValue(Nothing, SE_TAKE_OWNERSHIP_NAME, luid_TakeOwnership) Then
            Return False
        End If
        luid_Restore = 0
        If Not LookupPrivilegeValue(Nothing, SE_RESTORE_NAME, luid_Restore) Then
            Return False
        End If

        tp.PrivilegeCount = 2
        tp.Privilege1.Luid = luid_TakeOwnership
        tp.Privilege1.Attributes = SE_PRIVILEGE_ENABLED
        tp.Privilege2.Luid = luid_Restore
        tp.Privilege2.Attributes = SE_PRIVILEGE_ENABLED

        ' enable the privileges
        If Not AdjustTokenPrivileges(hToken, False, tp, 0, IntPtr.Zero, IntPtr.Zero) Then
            Return False
        End If

        Return True
    End Function

Open in new window

Avatar of Lars007

ASKER

The VB example gave me the same result, but I looked closer at the P/Invoke site you referred me to, and got AdjustTokenPrivileges to no longer return an error.

For some reason the TOKEN_PRIVILEGES caused the error when multi-privileges version.  When I changed to a single-privilege struct, the error went away:
Instead of this:
private class TOKEN_PRIVILEGES
{
    public UInt32 PrivilegeCount;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = ANYSIZE_ARRAY)]
    public LUID_AND_ATTRIBUTES[] Privileges = new LUID_AND_ATTRIBUTES[2];
}

I now use this (no attribute array):
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct TokPriv1Luid
{
    public int Count;
    public LUID Luid;
    public UInt32 Attr;
}

Open in new window


The only issue now is that RegLoadKey returns a non-zero value that is not a valid windows error message code (8589934613).  I will open a separate question for this.

Thanks.

Ps. Here is the full test code, in case it would help someone:
public class RegistryHive
    {
        [StructLayout(LayoutKind.Sequential)]
        private struct LUID
        {
            public uint LowPart;
            public int HighPart;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct LUID_AND_ATTRIBUTES
        {
            public LUID pLuid;
            public UInt32 Attributes;
        }

        private class TOKEN_PRIVILEGES
        {
            public UInt32 PrivilegeCount;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = ANYSIZE_ARRAY)]
            public LUID_AND_ATTRIBUTES[] Privileges = new LUID_AND_ATTRIBUTES[2];
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        private struct TokPriv1Luid
        {
            public int Count;
            public LUID Luid;
            public UInt32 Attr;
        }

        private const Int32 ANYSIZE_ARRAY = 1;
        private const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
        private const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
        private const UInt32 TOKEN_QUERY = 0x0008;

        private const uint HKEY_USERS = 0x80000003;
        private const string SE_RESTORE_NAME = "SeRestorePrivilege";
        private const string SE_BACKUP_NAME = "SeBackupPrivilege";

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentProcess();

        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);

        [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
        static extern bool AdjustTokenPrivileges(
            IntPtr htok,
            bool disableAllPrivileges,
            ref TokPriv1Luid newState,
            int len,
            IntPtr prev,
            IntPtr relen);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern long RegLoadKey(UInt32 hKey, String lpSubKey, String lpFile);

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern long RegUnLoadKey(UInt32 hKey, string lpSubKey);

        private string _loadUnderKeyName;
        private IntPtr _myToken;
        //private TOKEN_PRIVILEGES _tokenPrivileges = new TOKEN_PRIVILEGES();
        private TokPriv1Luid _tokenPrivileges = new TokPriv1Luid();

        private LUID _restoreLuid;
        private LUID _backupLuid;

        public RegistryHive()
        {
            if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out _myToken))
                Console.WriteLine("OpenProcess Error");

            if (!LookupPrivilegeValue(null, SE_RESTORE_NAME, out _restoreLuid))
                Console.WriteLine("LookupPrivilegeValue Error");

            if (!LookupPrivilegeValue(null, SE_BACKUP_NAME, out _backupLuid))
                Console.WriteLine("LookupPrivilegeValue Error");

            _tokenPrivileges.Attr = SE_PRIVILEGE_ENABLED;
            _tokenPrivileges.Luid = _restoreLuid;
            _tokenPrivileges.Count = 1;

            if (!AdjustTokenPrivileges(_myToken, false, ref _tokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero))
                Console.WriteLine("AdjustTokenPrivileges Error: " + Marshal.GetLastWin32Error());

            long retVal = RegLoadKey(HKEY_USERS, "123test", @"E:\temp\NTUSER.DAT");
            if (retVal != 0)
            {
                Console.WriteLine("RegLoadKey Error:" + retVal);
            }
        }
    }
}

Open in new window

Avatar of Lars007

ASKER

Your suggestion did not solve the problem, but your reference to the p/invoke site helped.