none
C# Code unable to restart Windows 2008 R2 x64 system RRS feed

  • Question

  • Hi all, 

    I have a 3.5 C# console application that its main purpose is to impersonate a specific Administrator user and restart a Windwows 2008 R2 x64 server.

    The code doesn't use the ProcessStart to call the shutdown function but makes directly calls to the kernel32.dll.

    The problem: when the Console app is run as administrator, it restarts the system without an issue. When it is not run as administrator, even though the user logged in is a Domain Administrator, and the application impersonates a user that is part of the Local Administrators the system restart code is executed but the OS fails to restart. The Console App ends on the Succeeded line but the system is not restarted.

    The code is as follows. Note that not all classes are present (UserAccount, Impersonator are missing, but they have been unit tested and work OK)

    namespace RebootTestApplication
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("About to impersonate");
                //The following is not secure but it is for illustration purposes
                UserAccounts.AddAdminUser();
                string pwd = UserAccounts.GetPwd();
                
              
                using (new Impersonator(UserAccounts.AccountName, ".", pwd))
                {
                    Console.WriteLine("User Impersonated");
                    Console.WriteLine("About to reboot, press key to continue");
    
                    Console.ReadLine();
                    RebootSystem();
                    Console.WriteLine("Reboot issued after impersonating User");
                    Console.ReadLine();
                }
                
    
            }
    
    
            [DllImport("kernel32.dll")]
            private static extern IntPtr GetCurrentProcess();
    
            [DllImport("advapi32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool OpenProcessToken(
                IntPtr ProcessHandle,
                UInt32 DesiredAccess,
                out IntPtr TokenHandle);
    
            private const UInt32 TOKEN_QUERY = 0x00000008;
            private const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x00000020;
    
            [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool LookupPrivilegeValue(
                string lpSystemName,
                string lpName,
                out UInt64 lpLuid);
    
            const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
    
            [DllImport("advapi32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle,
               [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
               ref TOKEN_PRIVILEGES NewState,
               UInt32 Zero,
               IntPtr Null1,
               IntPtr Null2);
    
            private const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
    
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            private struct TOKEN_PRIVILEGES
            {
                public UInt32 PrivilegeCount;
                public UInt64 Luid;
                public UInt32 Attributes;
            }
    
            [DllImport("kernel32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool CloseHandle(IntPtr hObject);
    
            [DllImport("user32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool ExitWindowsEx(
                UInt32 uFlags,
                UInt32 dwReason);
    
            private const UInt32 SHTDN_REASON_MAJOR_APPLICATION = 0x00040000;
            private const UInt32 SHTDN_REASON_MINOR_INSTALLATION = 0x00000002;
            private const UInt32 SHTDN_REASON_FLAG_PLANNED = 0x80000000;
    
            private const UInt32 EWX_REBOOT = 0x00000002;
            private const UInt32 EWX_FORCE = 0x00000004;
    
            public static void RebootSystem()
            {
                IntPtr hProc = IntPtr.Zero;
                IntPtr hToken = IntPtr.Zero;
                UInt64 luid_Restore = 0;
                TOKEN_PRIVILEGES tp;
    
                Console.WriteLine("System reboot");
    
                hProc = GetCurrentProcess();
    
                try
                {
                    if (!OpenProcessToken(hProc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken))
                    {
                        throw new Exception("(OpenProcessToken)");
                    }
    
                    if (!LookupPrivilegeValue(null, SE_SHUTDOWN_NAME, out luid_Restore))
                    {
                        throw new Exception("(LookupPrivilegeValue)");
                    }
    
                    tp.PrivilegeCount = 1;
                    tp.Luid = luid_Restore;
                    tp.Attributes = SE_PRIVILEGE_ENABLED;
    
                    if (!AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
                    {
                        throw new Exception("(AdjustTokenPrivileges)");
                    }
    
                    if (!ExitWindowsEx(EWX_REBOOT | EWX_FORCE,
                                       SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED))
                    {
                        throw new Exception("(ExitWindowsEx)");
                    }
                }
                catch (Exception ex)
                {
                    //log for me
                    //...
                    Console.WriteLine(ex.Message);
                    Console.WriteLine("Failed to restart system");
                }
                finally
                {
                    if (IntPtr.Zero != hProc)
                        CloseHandle(hProc);
    
                    if (IntPtr.Zero != hToken)
                        CloseHandle(hToken);
                }
                Console.WriteLine("Succeeded"); //this line should never be reached if the system restarts
            }
        }

    Any suggestions to modify this code is more than welcome. It successfully runs on client Windows OSs such as Windows 7, XP, Windows 10. The failure so far has only happened on Windows 2008 R2 x64 Standard.

    Thanks in advance.

    Friday, September 4, 2015 2:34 PM

Answers

  • Hello  LizetP,
    To further help you about this issue, I am trying to invoke someone experienced to help look into this thread, this may take some time and as soon as we get any result, we will post back.

    Regards,

    Kristin


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    • Marked as answer by LizetP Thursday, September 17, 2015 6:41 PM
    Tuesday, September 15, 2015 9:37 AM

All replies

  • Hi LizetP,

    Could you try by adding the user account into local machine's administrator user group. Hope this helps you.

    Thanks,

    Sabah Shariq

    Friday, September 4, 2015 2:55 PM
  • Sabah,

    The UserAccount class that is not shown on the code snippet already does that with the call to this method

    UserAccounts.AddAdminUser();

    That's already done and verified.

    The user that the code creates is added to the Local Administrators group before the impersonation.

    The problem still persists even when the impersonated user is part of the Local  Administrators group.



    LizetP


    • Edited by LizetP Friday, September 4, 2015 3:44 PM
    Friday, September 4, 2015 3:43 PM
  • Hi LizetP,

    >>I have a 3.5 C# console application that its main purpose is to impersonate a specific Administrator user and restart a Windows 2008 R2 x64 server.

    About impersonate a specific Administrator user, I would suggest you trying the following article's solution.

    A Complete Impersonation Demo in C#.NET

    If you don't want to run your console app as administrator or  impersonate users , you could consider modifying the manifest that gets embedded in the program.  This works on VS2008 and higher: Project + Add New Item, select "Application Manifest File".  Change the <requestedExecutionLevel> element to:

    <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />

    http://msdn.microsoft.com/en-us/library/windows/desktop/bb756929.aspx

    By the way, I've tested your code, without some code that impersonate a specific Administrator user, just call RebootSystem method, it works fine on my side. My local machine is win8.1+VS2013.

    Best regards,

    Kristin


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.



    • Edited by Kristin Xie Tuesday, September 8, 2015 2:26 AM
    Tuesday, September 8, 2015 2:24 AM
  • Hi Kristin Xie,

    Thank you for your prompt reply.

    The manifest of the C# Console application looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <assemblyIdentity version="1.0.0.0" name="RebootTestApplication" />
      <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
          <applicationRequestMinimum>
            <defaultAssemblyRequest permissionSetReference="Custom" />
            <PermissionSet ID="Custom" SameSite="site" Unrestricted="true" />
          </applicationRequestMinimum>
        </security>
      </trustInfo>
    </asmv1:assembly>

    The impersonation code is very similar to the CodeProject Article you pointed out.

    With the manifest as shown above, do you have the same results?

    Thanks in advance,


    LizetP

    Tuesday, September 8, 2015 3:05 AM
  • Hi Kristin Xie,

    Thank you for your prompt reply.

    The manifest of the C# Console application looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <assemblyIdentity version="1.0.0.0" name="RebootTestApplication" />
      <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
          <applicationRequestMinimum>
            <defaultAssemblyRequest permissionSetReference="Custom" />
            <PermissionSet ID="Custom" SameSite="site" Unrestricted="true" />
          </applicationRequestMinimum>
        </security>
      </trustInfo>
    </asmv1:assembly>

    The impersonation code is very similar to the CodeProject Article you pointed out.

    With the manifest as shown above, do you have the same results?

    Thanks in advance,


    LizetP

    >>The impersonation code is very similar to the CodeProject Article you pointed out.

    I don't know how you write the code, please try to compare yours and the solution in CodeProject Article.

    >>With the manifest as shown above, do you have the same results?

     Click your project --> Add New Item, select "Application Manifest File". Change the <requestedExecutionLevel> element to:

    <requestedExecutionLevel level="requireAdministrator" uiAccess="false/>

    The following is my app.manifest

    <?xml version="1.0" encoding="utf-8"?>
    <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
      <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
          <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
            <!-- UAC Manifest Options
                If you want to change the Windows User Account Control level replace the 
                requestedExecutionLevel node with one of the following.
    
            <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
            <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
            <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
    
                Specifying requestedExecutionLevel node will disable file and registry virtualization.
                If you want to utilize File and Registry Virtualization for backward 
                compatibility then delete the requestedExecutionLevel node.
            -->
            <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
          </requestedPrivileges>
        </security>
      </trustInfo>
    
      <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
        <application>
          <!-- A list of all Windows versions that this application is designed to work with. 
          Windows will automatically select the most compatible environment.-->
    
          <!-- If your application is designed to work with Windows Vista, uncomment the following supportedOS node-->
          <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"></supportedOS>-->
    
          <!-- If your application is designed to work with Windows 7, uncomment the following supportedOS node-->
          <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>-->
    
          <!-- If your application is designed to work with Windows 8, uncomment the following supportedOS node-->
          <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS>-->
    
          <!-- If your application is designed to work with Windows 8.1, uncomment the following supportedOS node-->
          <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>-->
    
        </application>
      </compatibility>
    
      <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
      <!-- <dependency>
        <dependentAssembly>
          <assemblyIdentity
              type="win32"
              name="Microsoft.Windows.Common-Controls"
              version="6.0.0.0"
              processorArchitecture="*"
              publicKeyToken="6595b64144ccf1df"
              language="*"
            />
        </dependentAssembly>
      </dependency>-->
    
    </asmv1:assembly>
    

    Best regards,

    Kristin


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Tuesday, September 8, 2015 3:19 AM
  • Hi again Kristin,

    Here's the Impersonator class. So far it has worked like a charm:

    using System;
    using System.Security.Principal;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    
    namespace RebootTestApplication
    {
        public class Impersonator : IDisposable
        {
            private WindowsImpersonationContext impersonationContext = null;
    
            public Impersonator(string user_name, string domain_name, string password)
            {
                ImpersonateValidUser(user_name, domain_name, password);
            }
    
            public void Dispose()
            {
                UndoImpersonation();
            }
    
            private void ImpersonateValidUser(string user_name, string domain, string password)
            {
                WindowsIdentity tempWindowsIdentity = null;
                IntPtr token = IntPtr.Zero;
                IntPtr tokenDuplicate = IntPtr.Zero;
    
                try
                {
                    if (!RevertToSelf() ||
                        /* Use LOGON32_LOGON_NETWORK instead of LOGON32_LOGON_INTERACTIVE to inherit elevation */
                        0 == LogonUser(user_name, domain, password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, ref token) ||
                        0 == DuplicateToken(token, SecurityImpersonation, ref tokenDuplicate))
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
    
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                }
                finally
                {
                    if (token != IntPtr.Zero)
                    {
                        CloseHandle(token);
                    }
    
                    if (tokenDuplicate != IntPtr.Zero)
                    {
                        CloseHandle(tokenDuplicate);
                    }
                }
            }
    
            private void UndoImpersonation()
            {
                if (impersonationContext != null)
                {
                    impersonationContext.Undo();
                }
            }
    
            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern int LogonUser(
                string lpszUserName,
                string lpszDomain,
                string lpszPassword,
                int dwLogonType,
                int dwLogonProvider,
                ref IntPtr phToken);
    
            [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern int DuplicateToken(
                IntPtr hToken,
                int impersonationLevel,
                ref IntPtr hNewToken);
    
            [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern bool RevertToSelf();
    
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            private static extern bool CloseHandle(
                IntPtr handle);
    
            private const int LOGON32_LOGON_INTERACTIVE = 2;
            private const int LOGON32_LOGON_NETWORK = 3;
            private const int LOGON32_PROVIDER_DEFAULT = 0;
    
            private const int SecurityImpersonation = 2;
        }
    }
    

    The application as it is now, has the manifest I copied on my previous post. I will give the new manifest you suggested a try and let you know.

    If you notice any problems with the Impersonator class, feel free to point them out. The issue is happening mostly on two types of OSs Windows 2008 R2 x64  and Windows 7 x64 SP1.

    Thank you,

    -LizetP


    LizetP

    Tuesday, September 8, 2015 3:33 AM
  • Hi LizetP,

    >>The application as it is now, has the manifest I copied on my previous post. I will give the new manifest you suggested a try and let you know.

    What's the problem now? Do you have any updates about change manifest file?

    I've checked that http://www.codeproject.com/Articles/124981/Impersonating-user-accessing-files-and-HKCU, yes, your code looks good.

    I suspected that even though your Admin unless you constantly tell it "RUN AS ADMIN" you actually will run as common user.

    Best regards,

    Kristin


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.


    • Edited by Kristin Xie Wednesday, September 9, 2015 7:28 AM
    Wednesday, September 9, 2015 7:28 AM
  • Hi Kristin Xie,

    The current manifest with the following

    <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
          </requestedPrivileges>

    did not did it for my application. When the application first ran, UAC kicked in and asked me for the credentials to run.

    I changed the manifest to have that line as:

    <requestedExecutionLevel level="requireAdministrator" uiAccess="true" />
          </requestedPrivileges>

    I'm currently testing the application with that manifest and digitally signing the assembly. It seems that if the assembly is not digitally signed the OS will still prompt for credentials (UAC).

    Do you know the what the uiAccess attribute sets?

    Is this suppressing the UAC popup?

    The main MSDN document I could find on the subject pertains to Vista, which is not one of the OSs I'm concerned about

    https://msdn.microsoft.com/en-us/library/ms742884(v=vs.110).aspx

    Thanks in advance.


    LizetP

    Friday, September 11, 2015 3:25 PM
  • Hello  LizetP,
    To further help you about this issue, I am trying to invoke someone experienced to help look into this thread, this may take some time and as soon as we get any result, we will post back.

    Regards,

    Kristin


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    • Marked as answer by LizetP Thursday, September 17, 2015 6:41 PM
    Tuesday, September 15, 2015 9:37 AM
  • Thank you Kristin Xie!

    I can provide most of the code that is involved on restarting the OS (Windows 2008 R2 x64 and Windows 7 x64 SP1), the impersonation class and the different manifests I've tried.

    Please let me know when this person from MSDN is available.

    My last resource would be opening a support ticket and trying to escalate this, but I prefer if someone with Code Access Security experience looks at this first.


    LizetP

    Tuesday, September 15, 2015 1:18 PM
  • Kristin,

    My team has decided to use a different restart code to solve this issue.

    If you could gather more information on the difference between the uiAccess attribute in the manifest and UAC; specifically for these OSs:

    Windows Server 2008 x64

     and

    Windows 7 x64 SP1

    that would be great.

    Thank you.


    LizetP

    Thursday, September 17, 2015 6:59 PM