Page 1 of 1

Vista's User Account Control (UAC)

#1 StCroixSkipper  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 10
  • View blog
  • Posts: 121
  • Joined: 23-December 08

Posted 04 March 2010 - 12:32 PM

Microsoft introduced 'User Account Control' in Windows Vista. The intent was to better secure user's systems by enabling users to run with standard user rights normally but be able to 'elevate' to Administrator rights if they were administrators and needed to access protected resources.

I've written several applications that require access to protected resources like enumerating the files and directories in the Master File Table or using the NTFS USN Journal or accessing WMI classes and objects that require admin rights.

My first attempts at writing applications that would automatically elevate was frustrating.

Facts:
You cannot elevate a running process. Process elevation happens at the moment a process is started. You cannot change its elevation level after it has been started.

Level Description
asInvoker Process does not require elevation
highestAvailable Requests the highest available privilege tokens of its parent
process.
requireAdministrator Requires elevation to full administrator privileges.

The uiAccess attribute determines whether the applicaton requires access to any protected ui elements such as system dialog boxes or higher level processes. Only signed applications may do this. That is why the value defaults to 'false'.

When you write a UAC enabled process in Visual Studio and select 'Debug>Start Debugging', Visual Studio does not start the application in a way that UAC elevation takes effect.

That said, here is a little console application that accesses the directory 'c:\Program Files' which we all know to be protected. If I try to access 'c:\program files' in a Vista machine I'll get 'Access Denied'.

I've simply created a 'WPF Application' and taken the defaults. In the xaml code, I've added a button that when clicked will attempt to enumerate the files below 'c:\Program Files'.

Here is the xaml code:
<Window x:Class="ElevateProcessProject.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <Button Name="_btnProgramFiles" Click="_btnProgramFiles_Click">
            <ListBox Name="_lbFiles"/>
        </Button>
    </StackPanel>
</Window>



The only thing I need to do to change the User Account Control level is to replace the 'requestedExecutionLevel' node and request 'requireAdministrator' level access. To do this, I right click on the project in Solution Explorer and select 'Add>New Item...> to get the 'Add New Item' dialog. The User Account Control level is specified in the application's manifest file. So select 'Application Manifest File' and click the 'Add' button.

This will add the app.manifest file to the project that looks like:
<?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>
    </security>
  </trustInfo>
</asmv1:assembly>



Replace the line '<requestedExecutionLevel level="asInvoker" uiAccess="false" />' with '<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />'. This is what your app.manifest file should look like.

<?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="requireAdministrator" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>
</asmv1:assembly>



Here is the C# code in the file Window1.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Security.Principal;
using System.Threading;

namespace ElevateProcessProject
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void _btnProgramFiles_Click(object sender, RoutedEventArgs e)
        {
            DirectoryInfo di = new DirectoryInfo(@"c:\Program Files");
            _lbFiles.ItemsSource = di.GetFiles("*.*", SearchOption.AllDirectories);
        }
    }
}



However, when you attempt to launch your program with Visual Studio's 'Debug>Start Debugging' you'll notice that you do not get the UAC evelation dialog and when your application runs, it will fail with an 'Access Denied' exception. It doesn't happen immediately, give it a few seconds. I get the error 'UnauthorizedAccessException was unhandled', 'Access to the path 'c:\Program Files\Google\CrashReports' is denied'.

Now close Visual Studio and restart it using the 'Run As Administrator' option. Right click on Visual Studio in the start menu and select the 'Run as administrator' menu item. Now open your project and try to run it. It will work but no UAC dialog will appear because Visual Studio is running under Admin credentials. When VS starts an application/process it starts it under the user credentials of its process or the user credentials under which it was started and bypasses the UAC code.

So I added another button to the UI which when clicked launches another copy of my exe file such that the UAC does get invoked.

Here is the new Window1.xaml file:
<Window x:Class="ElevateProcessProject.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <Button Name="_btnLaunchExe" Click="_btnLaunchExe_Click">
            Launch Another Copy of the exe
        </Button>
        <Button Name="_btnProgramFiles" Click="_btnProgramFiles_Click">
            <ListBox Name="_lbFiles"/>
        </Button>
    </StackPanel>
</Window>



and the new Window1.xaml.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Security.Principal;
using System.Threading;
using System.Diagnostics;

namespace ElevateProcessProject
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void _btnProgramFiles_Click(object sender, RoutedEventArgs e)
        {
            DirectoryInfo di = new DirectoryInfo(@"c:\Program Files");
            _lbFiles.ItemsSource = di.GetFiles("*.*", SearchOption.AllDirectories);
        }

        private void _btnLaunchExe_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                ProcessStartInfo proc = new ProcessStartInfo();
                proc.UseShellExecute = true;
                proc.WorkingDirectory = Directory.GetCurrentDirectory();
                proc.FileName = "ElevateProcessProject.exe";
                proc.Verb = "runas";

                Process.Start(proc);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }
    }
}



Notice, I also added a using statment for the System.Diagnostics namespace so I could reference the Process and ProcessStartInfo classes.

Now when you press the 'Launch Another Copy...' button, you get the UAC dialog.

Also note that if you simply use Windows Explorer to navigate to your project>bin>Debug directory and start ElevateProcessProject.exe by clicking on it, you also get the UAC dialog asking for permission to elevate your process.

Is This A Good Question/Topic? 0
  • +

Replies To: Vista's User Account Control (UAC)

#2 PsychoCoder  Icon User is offline

  • Google.Sucks.Init(true);
  • member icon

Reputation: 1639
  • View blog
  • Posts: 19,853
  • Joined: 26-July 07

Posted 05 March 2010 - 11:12 PM

I'm going to move this to the WPF & Silverlight Tutorial Section. I feel that is the most appropriate place
Was This Post Helpful? 0
  • +
  • -

#3 Guest_Armen*


Reputation:

Posted 02 April 2010 - 02:59 PM

Is it possible to get writing permission this way? I can't do it.
Was This Post Helpful? 0

#4 StCroixSkipper  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 10
  • View blog
  • Posts: 121
  • Joined: 23-December 08

Posted 05 April 2010 - 08:28 AM

I'm not sure what you mean by writing permisson. This elevates the permissions of your process. If you are an administrator, it elevates your permissions to 'Administrator'. I use it to get access to protected Win32 API's like DeviceIoControl. After you are elevated, you should be able to do anything an administrator can do.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1