Access CurrentUser Registry Key for Impersonated User - Compatibility with .NET 3.5

[亡魂溺海] 提交于 2019-12-01 00:38:39

After days of research, and help from MSDN community to the same question, I've found the way to follow to accomplish my needs.

The initial suggestion was to use Win Api function RegOpenKeyEx (see P/Invoke website for infos and samples); but according to this MSDN article, I've found that

If your service or application impersonates different users, do not use this function with HKEY_CURRENT_USER. Instead, call the RegOpenCurrentUser function.

Finally, the way to go is RegOpenCurrentUser function. (unfortunately there's still no trace of this function on P/Invoke website, but you can find some infos on MSDN)

This is how I currently define it:

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenCurrentUser(int samDesired, out IntPtr phkResult);    

public enum RegistrySecurity
{
    KEY_ALL_ACCESS = 0xF003F,
    KEY_CREATE_LINK = 0x0020,
    KEY_CREATE_SUB_KEY = 0x0004,
    KEY_ENUMERATE_SUB_KEYS = 0x0008,
    KEY_EXECUTE = 0x20019,
    KEY_NOTIFY = 0x0010,
    KEY_QUERY_VALUE = 0x0001,
    KEY_READ = 0x20019,
    KEY_SET_VALUE = 0x0002,
KEY_WOW64_32KEY = 0x0200,
    KEY_WOW64_64KEY = 0x0100,
    KEY_WRITE = 0x20006,
}

public IntPtr GetImpersonateUserRegistryHandle(RegistrySecurity _access)
{
    IntPtr safeHandle = new IntPtr();
    int result = RegOpenCurrentUser((int)_access, out safeHandle);

    return safeHandle;
}

/// <summary>
/// Get a registry key from a pointer.
/// </summary>
/// <param name="hKey">Pointer to the registry key</param>
/// <param name="writable">Whether or not the key is writable.</param>
/// <param name="ownsHandle">Whether or not we own the handle.</param>
/// <returns>Registry key pointed to by the given pointer.</returns>
public RegistryKey _pointerToRegistryKey(IntPtr hKey, bool writable, bool ownsHandle)
{
    //Get the BindingFlags for private contructors
    System.Reflection.BindingFlags privateConstructors = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;

    //Get the Type for the SafeRegistryHandle
    Type safeRegistryHandleType =
            typeof(Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid).Assembly.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");

    //Get the array of types matching the args of the ctor we want
    Type[] safeRegistryHandleCtorTypes = new Type[] { typeof(IntPtr), typeof(bool) };

    //Get the constructorinfo for our object
    System.Reflection.ConstructorInfo safeRegistryHandleCtorInfo = safeRegistryHandleType.GetConstructor(
            privateConstructors, null, safeRegistryHandleCtorTypes, null);

    //Invoke the constructor, getting us a SafeRegistryHandle
    Object safeHandle = safeRegistryHandleCtorInfo.Invoke(new Object[] { hKey, ownsHandle });

    //Get the type of a RegistryKey
    Type registryKeyType = typeof(RegistryKey);

    //Get the array of types matching the args of the ctor we want
    Type[] registryKeyConstructorTypes = new Type[] { safeRegistryHandleType, typeof(bool) };

    //Get the constructorinfo for our object
    System.Reflection.ConstructorInfo registryKeyCtorInfo = registryKeyType.GetConstructor(
            privateConstructors, null, registryKeyConstructorTypes, null);

    //Invoke the constructor, getting us a RegistryKey
    RegistryKey resultKey = (RegistryKey)registryKeyCtorInfo.Invoke(new Object[] { safeHandle, writable });

    //return the resulting key
    return resultKey;
}

And this is how I use it to get registry:

IntPtr localRegistryHandle = GetImpersonateUserRegistryHandle(TestRegistryAccess.RegistrySecurity.KEY_ALL_ACCESS);

using(RegistryKey localRegistry = _pointerToRegistryKey(localRegistryHandle, true, true))
{
    // do something with local registry
}
vapcguy

Wrote an Impersonation class I posted here to answer the same kind of question: Impersonate admin account to edit registry key not working (C#)

To write to the key, you just do:

string userName = "domain\user";
string password = "whatever";
string KEY_STR = "SomeSubKey\\ASubKeyToThat";

WindowsImpersonationContext adminContext = Impersonation.getWic(userName, password);
if (adminContext != null)
{
    try
    {
       RegistryKey key = Registry.CurrentUser.OpenSubKey(KEY_STR, true);
       key.SetValue("State", 0x60001);
    }
    catch (Exception ex)
    {
        Console.Out.WriteLine("\nUnable to set registry value:\n\t" + ex.Message);
        Impersonation.endImpersonation();
        adminContext.Undo();
    }
    finally
    {
        Impersonation.endImpersonation();
        // The above line does this --            
        //if (tokenHandle != IntPtr.Zero) CloseHandle(tokenHandle);
        adminContext.Undo();
    }
}

No handles or other fancy functions, other than to get the WindowsImpersonationContext needed. Didn't repost that part because it looks like you already know how to get the WIC.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!