2

I've recently written an application that impersonate user account, get a handle to CURRENT_USER registry key (using PInvoke "LoadUserProfile" to retrieve ProfileInfo.hProfile object) and create registry key using RegistryKey.FromHandle.

Reference code:

using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(hToken))
{
    using (SafeRegistryHandle safeHandle = new SafeRegistryHandle(hProfile, true))
    {
        using (RegistryKey impersonatedUserHkcu = RegistryKey.FromHandle(safeHandle, RegistryView.Default))
        {
            // Do something with registry
        }
    }
}

This piece of code works good (run in Windows 7), but made use of objects/methods supported only from .NET 4.0 and greater (SafeRegistryHandle, RegistryKey.FromHandle(), RegistryView enum).

Now, I need to make this application compatible with .NET 3.5, for use it in a machine with Windows XP and no possibilities to install .NET Framework 4.0.

Are there any equivalent objects I can use with .NET 3.5 to accomplish the same result? (that is, make modification to registry key for impersonated user). Or does exist some kind of source codes of the only-.NET 4 objects?

Spaceman
  • 547
  • 8
  • 25

2 Answers2

5

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
}
Spaceman
  • 547
  • 8
  • 25
  • This is missing how you get your `WindowsImpersonationContext` and where you log in your user and send in the credentials. A complete example would have been nice. If you can use `RegistryKey`, I don't know why you needed a function to return a key. Just do `RegistryKey key = Registry.CurrentUser.OpenSubKey(KEY_STR, true);`, where `KEY_STR` is your path. You didn't need all this, and `SafeRegistryHandle` and all. Overkill, even for .NET 3.5. – vapcguy Feb 22 '17 at 22:48
  • @vapcguy , I have an app running as a Service and it when trying to use Registry.CurrentUser it would just read/write as if I was using Registry.LocalMachine. I will try his answer although you are right, he is missing some of the code – TBD Jan 06 '22 at 20:26
  • @TBD You should be able to have the service run the code directly, and use the service account to run the code without even needing impersonation. But if you really have to do it from the app, the app needs its own way to log the user in. When I did it that way, I stored the login params in an INI file and read them into the app using the app's code. Then established the WIC using those parameters. Then `Registry.CurrentUser` should be under that WIC if it's inside the `using` statement that uses that WIC. – vapcguy Jan 14 '22 at 18:51
0

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.

Community
  • 1
  • 1
vapcguy
  • 7,097
  • 1
  • 56
  • 52