Home > Mobile >  Trying to create a demo using RegenerateUserEnvironment throwing Access violation error
Trying to create a demo using RegenerateUserEnvironment throwing Access violation error

Time:12-26

I am trying to make use of RegenerateUserEnvironment method so that I can refresh environment variables without restarting the instance. However, it ends up with an access violation error. I am not sure but to me, a process updating its own memory should not cause an error.

Things I tried:

  1. Passing the actual token of current process. No chance.
  2. Duplicating current token with all possible permissions. No chance.
  3. Running as administrator, in case it is about SeDebugPrivilege or similar, but no chance.

Any ideas?

Code:

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using Microsoft.Win32;

namespace EnvVarControl
{
    internal static partial class Program
    {
        #region Pinvoke
        public const uint STANDARD_RIGHTS_READ = 0x00020000;
        public const uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
        public const uint TOKEN_ADJUST_DEFAULT = 0x0080;
        public const uint TOKEN_ADJUST_GROUPS = 0x0040;
        public const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
        public const uint TOKEN_ADJUST_SESSIONID = 0x0100;
        public const uint TOKEN_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
            TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
            TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
            TOKEN_ADJUST_SESSIONID;

        public const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
        public const uint TOKEN_DUPLICATE = 0x0002;
        public const uint TOKEN_IMPERSONATE = 0x0004;
        public const uint TOKEN_QUERY = 0x0008;
        public const uint TOKEN_QUERY_SOURCE = 0x0010;
        public const uint TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;

        public enum SECURITY_IMPERSONATION_LEVEL
        {
            SecurityAnonymous,
            SecurityIdentification,
            SecurityImpersonation,
            SecurityDelegation
        }

        public enum TOKEN_TYPE
        {
            TokenPrimary = 1,
            TokenImpersonation = 2
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public int Length;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }

        [DllImport("advapi32.dll", SetLastError = true, EntryPoint = "DuplicateTokenEx")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, ref SECURITY_ATTRIBUTES lpThreadAttributes, TOKEN_TYPE TokenType, SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, ref IntPtr DuplicateTokenHandle);

        [LibraryImport("shell32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static partial bool RegenerateUserEnvironment(IntPtr hToken,
                                                     [MarshalAs(UnmanagedType.Bool)] bool bSet);
        #endregion Pinvoke

        public static void Main()
        {
            // Subscribe to the UserPreferenceChanged event
            SystemEvents.UserPreferenceChanged  = OnUserPreferenceChanged;

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }

        private static void CheckPathBeforeAndAfterRegenerateUserEnvironment()
        {
            // Get the value of the PATH environment variable before calling RegenerateUserEnvironment
            var pathBefore = Environment.GetEnvironmentVariable("PATH");
            Console.WriteLine($"BEFORE: {pathBefore}\n");

            RefreshEnvironmentVariables();

            // Get the value of the PATH environment variable after calling RegenerateUserEnvironment
            var pathAfter = Environment.GetEnvironmentVariable("PATH");

            Console.WriteLine($"AFTER: {pathAfter}\n");

            // Compare the two values of the PATH environment variable
            if (pathBefore != pathAfter)
            {
                Console.WriteLine("Warning: The value of the PATH environment variable has changed!");
            }
            else
            {
                Console.WriteLine("No change.");
            }
        }

        private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
        {
            // Check if the environment variables have changed
            if (e.Category == UserPreferenceCategory.General)
            {
                // Check the value of the PATH environment variable before and after calling RegenerateUserEnvironment
                CheckPathBeforeAndAfterRegenerateUserEnvironment();
            }
        }

        private static void RefreshEnvironmentVariables()
        {
            // Open the process token and duplicate
            var token = WindowsIdentity.GetCurrent().AccessToken.DangerousGetHandle();
            var duplicatededToken = DuplicateToken(token);

            // Call the RegenerateUserEnvironment function to update the environment variables
            if (!RegenerateUserEnvironment(duplicatededToken, true))
            {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }
        }

        private static nint DuplicateToken(nint token)
        {
            var sa = new SECURITY_ATTRIBUTES
            {
                bInheritHandle = false
            };
            sa.Length = Marshal.SizeOf(sa);
            sa.lpSecurityDescriptor = 0;

            const TOKEN_TYPE TokenType = TOKEN_TYPE.TokenPrimary;
            const SECURITY_IMPERSONATION_LEVEL SecurityImpersonation = SECURITY_IMPERSONATION_LEVEL.SecurityIdentification;
            var DupedToken = new IntPtr(0);

            if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, ref sa, TokenType, SecurityImpersonation, ref DupedToken))
            {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }

            return DupedToken;
        }
    }
}

Error message:


Exception thrown at ... (shell32.dll) in EnvVarControl.exe: ...: Access violation writing location ...

Edit: Based on the very accurate answer, I paste the correct piece of code below:


using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace EnvVarControl
{
    internal static partial class Program
    {
        #region Pinvoke
        [LibraryImport("shell32.dll", SetLastError = true)]
        [UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static partial bool RegenerateUserEnvironment(out IntPtr pPrevEnv, [MarshalAs(UnmanagedType.Bool)] bool bSetCurrentEnv);
        #endregion Pinvoke

        public static void Main()
        {
            // Subscribe to the UserPreferenceChanged event
            SystemEvents.UserPreferenceChanged  = OnUserPreferenceChanged;

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }

        private static void CheckPathBeforeAndAfterRegenerateUserEnvironment()
        {
            // Get the value of the PATH environment variable before calling RegenerateUserEnvironment
            var pathBefore = Environment.GetEnvironmentVariable("PATH");
            Console.WriteLine($"BEFORE: {pathBefore}\n");

            RefreshEnvironmentVariables();

            // Get the value of the PATH environment variable after calling RegenerateUserEnvironment
            var pathAfter = Environment.GetEnvironmentVariable("PATH");

            Console.WriteLine($"AFTER: {pathAfter}\n");

            // Compare the two values of the PATH environment variable
            if (pathBefore != pathAfter)
            {
                Console.WriteLine("Warning: The value of the PATH environment variable has changed!");
            }
            else
            {
                Console.WriteLine("No change.");
            }
        }

        private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
        {
            // Check if the environment variables have changed
            if (e.Category == UserPreferenceCategory.General)
            {
                // Check the value of the PATH environment variable before and after calling RegenerateUserEnvironment
                CheckPathBeforeAndAfterRegenerateUserEnvironment();
            }
        }

        private static void RefreshEnvironmentVariables()
        {
            // Call the RegenerateUserEnvironment function to update the environment variables
            // Reference: https://stackoverflow.com/questions/74913727/trying-to-create-a-demo-using-regenerateuserenvironment-throwing-access-violatio/74914038#74914038
            if (!RegenerateUserEnvironment(out _, true))
            {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }
        }
    }
}

CodePudding user response:

RegenerateUserEnvironment does not need a token; the first parameter is a 2-level pointer, which will be set to the previous environmet block.

define RegenerateUserEnvironment as:

[DllImport("shell32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RegenerateUserEnvironment(out IntPtr pPrevEnv, [MarshalAs(UnmanagedType.Bool)] bool bSetCurrentEnv);

and call it like follows:

RegenerateUserEnvironment(out IntPtr pPrevEnv, true)
  • Related