Click here to Skip to main content
15,886,518 members
Articles / Programming Languages / Visual Basic

A Quick-Start Guide of Process Mandatory Level Checking and Self-elevation under UAC

Rate me:
Please Sign up or sign in to vote.
4.84/5 (63 votes)
23 Mar 2010Ms-PL5 min read 130.6K   5K   155  
This example demonstrates how to check the privilege level of the current process, and how to self-elevate the process by giving explicit consent with the Consent UI.
=============================================================================
          APPLICATION : CSUACSelfElevation Project Overview
=============================================================================

/////////////////////////////////////////////////////////////////////////////
Summary: 

User Account Control (UAC) is a new security component in Windows Vista and 
newer operating systems. With UAC fully enabled, interactive administrators 
normally run with least user privileges. This example demonstrates how to 
check the privilege level of the current process, and how to self-elevate 
the process by giving explicit consent with the Consent UI. 


/////////////////////////////////////////////////////////////////////////////
Prerequisite:

You must run this sample on Windows Vista or newer operating systems.


/////////////////////////////////////////////////////////////////////////////
Demo:

The following steps walk through a demonstration of the UAC sample.

Step1. After you successfully build the sample project in Visual Studio 2008, 
you will get an application: CSUACSelfElevation.exe. 

Step2. Run the application as a protected administrator on a Windows Vista or 
Windows 7 system with UAC fully enabled. The application should display the 
following content on the main dialog.

  IsUserInAdminGroup:     True
  IsRunAsAdmin:           False
  IsProcessElevated:      False
  Integrity Level:        Medium

There is a UAC shield icon on the Self-elevate button.

Step3. Click on the Self-elevate button. You will see a Consent UI.

  User Account Control
  ---------------------------------- 
  Do you want to allow the following program from an unknown publisher to 
  make changes to this computer?

Step4. Click Yes to approve the elevation. The original application will then 
be started and display the following content on the main dialog.

  IsUserInAdminGroup:     True
  IsRunAsAdmin:           True
  IsProcessElevated:      True
  Integrity Level:        High

The Self-elevate button on the dialog does not have the UAC shield icon this 
time. That is, the application is running as elevated administrator. The 
elevation succeeds. If you click on the Self-elevate button again, the 
application will tell you that it is running as administrator.

Step5. Close the application. 


/////////////////////////////////////////////////////////////////////////////
Creation:

Step1. Create a new Visual C# Windows Forms project named CSUACSelfElevation.

Step2. Add controls to the main form

  Type: Button
  ID: btnElevate
  Caption: "Self-elevate"
  
  Type: Label
  ID: lbInAdminGroup
  Use: Display whether the primary access token of the process belongs to 
  user account that is a member of the local Administrators group, even if it 
  currently is not elevated.
  
  Type: Label
  ID: lbIsRunAsAdmin
  Use: Display whether the application is run as administrator. 
  
  Type: Label
  ID: lbIsElevated
  Use: Display whether the process is elevated or not. Token elevation is 
  only available on Windows Vista and newer operating systems. The label 
  shows N/A on systems prior to Windows Vista.
 
  Type: Label
  ID: lbIntegrityLevel
  Use: Display the integrity level of the current process. Integrity level is 
  only available on Windows Vista and newer operating systems. The label 
  shows N/A on systems prior to Windows Vista.

Step3. Check and display the current process's "run as administrator" status, 
elevation information and integrity level when the application initializes 
the main dialog.

Create the following four helper functions:

    /// <summary>
    /// The function checks whether the primary access token of the process belongs 
    /// to user account that is a member of the local Administrators group, even if 
    /// it currently is not elevated.
    /// </summary>
    /// <returns>
    /// Returns true if the primary access token of the process belongs to user 
    /// account that is a member of the local Administrators group. Returns false 
    /// if the token does not.
    /// </returns>
    /// <exception cref="System.ComponentModel.Win32Exception">
    /// When any native Windows API call fails, the function throws a Win32Exception 
    /// with the last error code.
    /// </exception>
    internal bool IsUserInAdminGroup()

    /// <summary>
    /// The function checks whether the current process is run as administrator.
    /// In other words, it dictates whether the primary access token of the 
    /// process belongs to user account that is a member of the local 
    /// Administrators group and it is elevated.
    /// </summary>
    /// <returns>
    /// Returns true if the primary access token of the process belongs to user 
    /// account that is a member of the local Administrators group and it is 
    /// elevated. Returns false if the token does not.
    /// </returns>
    internal bool IsRunAsAdmin()
    
    /// <summary>
    /// The function gets the elevation information of the current process. It 
    /// dictates whether the process is elevated or not. Token elevation is only 
    /// available on Windows Vista and newer operating systems, thus 
    /// IsProcessElevated throws a C++ exception if it is called on systems prior 
    /// to Windows Vista. It is not appropriate to use this function to determine 
    /// whether a process is run as administartor.
    /// </summary>
    /// <returns>
    /// Returns true if the process is elevated. Returns false if it is not.
    /// </returns>
    /// <exception cref="System.ComponentModel.Win32Exception">
    /// When any native Windows API call fails, the function throws a Win32Exception 
    /// with the last error code.
    /// </exception>
    /// <remarks>
    /// TOKEN_INFORMATION_CLASS provides TokenElevationType to check the elevation 
    /// type (TokenElevationTypeDefault / TokenElevationTypeLimited / 
    /// TokenElevationTypeFull) of the process. It is different from TokenElevation 
    /// in that, when UAC is turned off, elevation type always returns 
    /// TokenElevationTypeDefault even though the process is elevated (Integrity 
    /// Level == High). In other words, it is not safe to say if the process is 
    /// elevated based on elevation type. Instead, we should use TokenElevation. 
    /// </remarks>
    internal bool IsProcessElevated()
    
    /// <summary>
    /// The function gets the integrity level of the current process. Integrity 
    /// level is only available on Windows Vista and newer operating systems, thus 
    /// GetProcessIntegrityLevel throws a C++ exception if it is called on systems 
    /// prior to Windows Vista.
    /// </summary>
    /// <returns>
    /// Returns the integrity level of the current process. It is usually one of 
    /// these values:
    /// 
    ///    SECURITY_MANDATORY_UNTRUSTED_RID - means untrusted level. It is used 
    ///    by processes started by the Anonymous group. Blocks most write access.
    ///    (SID: S-1-16-0x0)
    ///    
    ///    SECURITY_MANDATORY_LOW_RID - means low integrity level. It is used by
    ///    Protected Mode Internet Explorer. Blocks write acess to most objects 
    ///    (such as files and registry keys) on the system. (SID: S-1-16-0x1000)
    /// 
    ///    SECURITY_MANDATORY_MEDIUM_RID - means medium integrity level. It is 
    ///    used by normal applications being launched while UAC is enabled. 
    ///    (SID: S-1-16-0x2000)
    ///    
    ///    SECURITY_MANDATORY_HIGH_RID - means high integrity level. It is used 
    ///    by administrative applications launched through elevation when UAC is 
    ///    enabled, or normal applications if UAC is disabled and the user is an 
    ///    administrator. (SID: S-1-16-0x3000)
    ///    
    ///    SECURITY_MANDATORY_SYSTEM_RID - means system integrity level. It is 
    ///    used by services and other system-level applications (such as Wininit, 
    ///    Winlogon, Smss, etc.)  (SID: S-1-16-0x4000)
    /// 
    /// </returns>
    /// <exception cref="System.ComponentModel.Win32Exception">
    /// When any native Windows API call fails, the function throws a Win32Exception 
    /// with the last error code.
    /// </exception>
    internal int GetProcessIntegrityLevel()

Some of the methods need to P/Invoke some native Windows APIs. The P/Invoke 
signatures are defined in NativeMethod.cs.

In the constructor of the main form, check and display the "run as 
administrator" status, the elevation information, and the integrity level of 
the current process.

    // Get and display whether the primary access token of the process belongs 
    // to user account that is a member of the local Administrators group even 
    // if it currently is not elevated (IsUserInAdminGroup).
    try
    {
        bool fInAdminGroup = IsUserInAdminGroup();
        this.lbInAdminGroup.Text = fInAdminGroup.ToString();
    }
    catch (Exception ex)
    {
        this.lbInAdminGroup.Text = "N/A";
        MessageBox.Show(ex.Message, "An error occurred in IsUserInAdminGroup",
            MessageBoxButtons.OK, MessageBoxIcon.Error);
    }

    // Get and display whether the process is run as administrator or not 
    // (IsRunAsAdmin).
    try
    {
        bool fIsRunAsAdmin = IsRunAsAdmin();
        this.lbIsRunAsAdmin.Text = fIsRunAsAdmin.ToString();
    }
    catch (Exception ex)
    {
        this.lbIsRunAsAdmin.Text = "N/A";
        MessageBox.Show(ex.Message, "An error occurred in IsRunAsAdmin",
            MessageBoxButtons.OK, MessageBoxIcon.Error);
    }


    // Get and display the process elevation information (IsProcessElevated) 
    // and integrity level (GetProcessIntegrityLevel). The information is not 
    // available on operating systems prior to Windows Vista.
    if (Environment.OSVersion.Version.Major >= 6)
    {
        // Running Windows Vista or later (major version >= 6). 

        try
        {
            // Get and display the process elevation information.
            bool fIsElevated = IsProcessElevated();
            this.lbIsElevated.Text = fIsElevated.ToString();

            // Update the Self-elevate button to show the UAC shield icon on 
            // the UI if the process is not elevated.
            this.btnElevate.FlatStyle = FlatStyle.System;
            NativeMethod.SendMessage(btnElevate.Handle, 
                NativeMethod.BCM_SETSHIELD, 0, 
                fIsElevated ? IntPtr.Zero : (IntPtr)1);
        }
        catch (Exception ex)
        {
            this.lbIsElevated.Text = "N/A";
            MessageBox.Show(ex.Message, "An error occurred in IsProcessElevated",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        try
        {
            // Get and display the process integrity level.
            int IL = GetProcessIntegrityLevel();
            switch (IL)
            {
            case NativeMethod.SECURITY_MANDATORY_UNTRUSTED_RID:
                this.lbIntegrityLevel.Text = "Untrusted"; break;
            case NativeMethod.SECURITY_MANDATORY_LOW_RID:
                this.lbIntegrityLevel.Text = "Low"; break;
            case NativeMethod.SECURITY_MANDATORY_MEDIUM_RID:
                this.lbIntegrityLevel.Text = "Medium"; break;
            case NativeMethod.SECURITY_MANDATORY_HIGH_RID:
                this.lbIntegrityLevel.Text = "High"; break;
            case NativeMethod.SECURITY_MANDATORY_SYSTEM_RID:
                this.lbIntegrityLevel.Text = "System"; break;
            default:
                this.lbIntegrityLevel.Text = "Unknown"; break;
            }
        }
        catch (Exception ex)
        {
            this.lbIntegrityLevel.Text = "N/A";
            MessageBox.Show(ex.Message, "An error occurred in GetProcessIntegrityLevel",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
    else
    {
        this.lbIsElevated.Text = "N/A";
        this.lbIntegrityLevel.Text = "N/A";
    }

Step4. Handle the click event of the Self-elevate button. When user clicks 
the button, elevate the process by restarting itself with 
ProcessStartInfo.UseShellExecute = true and ProcessStartInfo.Verb = "runas" 
if the process is not run as administrator.

    private void btnElevate_Click(object sender, EventArgs e)
    {
        // Elevate the process if it is not run as administrator.
        if (!IsRunAsAdmin())
        {
            // Launch itself as administrator
            ProcessStartInfo proc = new ProcessStartInfo();
            proc.UseShellExecute = true;
            proc.WorkingDirectory = Environment.CurrentDirectory;
            proc.FileName = Application.ExecutablePath;
            proc.Verb = "runas";

            try
            {
                Process.Start(proc);
            }
            catch
            {
                // The user refused to allow privileges elevation.
                // Do nothing and return directly ...
                return;
            }

            Application.Exit();  // Quit itself
        }
        else
        {
            MessageBox.Show("The process is running as administrator", "UAC");
        }
    }

Step7. Automatically elevate the process when it's started up.

If your application always requires administrative privileges, such as during 
an installation step, the operating system can automatically prompt the user 
for privileges elevation each time your application is invoked. 

If a specific kind of resource (RT_MANIFEST) is found embedded within the 
application executable, the system looks for the <trustInfo> section and 
parses its contents. Here is an example of this section in the manifest file:

    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
       <security>
          <requestedPrivileges>
             <requestedExecutionLevel
                level="requireAdministrator"
             />
          </requestedPrivileges>
       </security>
    </trustInfo>

Three different values are possible for the level attribute

  a) requireAdministrator 
  The application must be started with Administrator privileges; it won't run 
  otherwise.

  b) highestAvailable 
  The application is started with the highest possible privileges.
  If the user is logged on with an Administrator account, an elevation prompt 
  appears. If the user is a Standard User, the application is started 
  (without any elevation prompt) with these standard privileges.

  c) asInvoker 
  The application is started with the same privileges as the calling 
  application.

To configure the elevation level in this Visual C# Windows Forms project, 
open the project's properties, turn to the Security tab, check the checkbox 
"Enable ClickOnce Security Settings", check "This is a fulltrust application" 
and close the application Properies page. This creates an app.manifest file 
and configures the project to embed the manifest. You can open the 
"app.manifest" file from Solution Explorer by expanding the Properies folder. 
The file has the following content by default.

    <?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" />

                If you want to utilize File and Registry Virtualization for backward 
                compatibility then delete the requestedExecutionLevel node.
            -->
            <requestedExecutionLevel level="asInvoker" uiAccess="false" />
          </requestedPrivileges>
          <applicationRequestMinimum>
            <PermissionSet class="System.Security.PermissionSet" version="1" 
            Unrestricted="true" ID="Custom" SameSite="site" />
            <defaultAssemblyRequest permissionSetReference="Custom" />
          </applicationRequestMinimum>
        </security>
      </trustInfo>
    </asmv1:assembly>

Here we are focusing on the line:

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

You can change it to be 

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

to require the application always be started with Administrator privileges.


/////////////////////////////////////////////////////////////////////////////
References:

MSDN: User Account Control
http://msdn.microsoft.com/en-us/library/aa511445.aspx

MSDN: Windows Vista Application Development Requirements for User Account 
Control Compatibility
http://msdn.microsoft.com/en-us/library/bb530410.aspx


/////////////////////////////////////////////////////////////////////////////

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
China China
Microsoft All-In-One Code Framework delineates the framework and skeleton of Microsoft development techniques through typical sample codes in three popular programming languages (Visual C#, VB.NET, Visual C++). Each sample is elaborately selected, composed, and documented to demonstrate one frequently-asked, tested or used coding scenario based on our support experience in MSDN newsgroups and forums. If you are a software developer, you can fill the skeleton with blood, muscle and soul. If you are a software tester or a support engineer like us, you may extend the sample codes a little to fit your specific test scenario or refer your customer to this project if the customer's question coincides with what we collected.
http://cfx.codeplex.com/

Comments and Discussions