Click here to Skip to main content
15,889,216 members
Articles / Programming Languages / C++
Article

Riding the Vista UAC elevator, up and down

Rate me:
Please Sign up or sign in to vote.
4.75/5 (35 votes)
27 Feb 20075 min read 355.8K   5.2K   96   74
How to control the execution level of Vista applications

Sample Image - VistaElevator.gif

Introduction

When developing applications for Windows Vista, one of the problems that often arises is how to programmatically control the execution level of a process. When the user starts an application, its elevation level is determined by the value of the requestedExecutionLevel attribute in its manifest, and Vista's User Account Control (UAC) takes appropriate actions depending on it (such as displaying the elevation prompt when needed, etc.) However, what if the application needs to start a new process with a different execution level than that of the application itself? For example:

  • An application that runs at the standard (non-elevated) level determines that an updated version of it is available for download. To be able to update itself, it needs to start a separate process that needs to be elevated in order to perform the upgrade properly. In this case a non-elevated process needs to start a new, elevated process.
  • Most of the installation utilities offer the user the option to run the application automatically at the end of the installation. The installation utility is executing at the elevated level, however, the application must be started at the standard, non-elevated level.

Microsoft has provided a relatively easy way to accomplish the first task (starting a process elevated), by specifying the "runas" verb when calling the ShellExecuteEx API. However, for some reason they have not offered a similarly easy way of going in the opposite direction: to start a non-elevated process from an elevated one. In this article I will show how to solve this and related problems.

Determining the current elevation level

First of all, how can an application determine its current elevation level? The file VistaTools.cxx included in the source code to this article contains two functions that give the answer to this question.

The first such function is GetElevationType() and it uses the Win32 API GetTokenInformation() to obtain the elevation type of the token of the current process. The possible values that it can return are:

  • TokenElevationTypeDefault - User is not using a "split" token. This value indicates that either UAC is disabled, or the process is started by a standard user (not a member of the Administrators group).
  • TokenElevationTypeFull - the process is running elevated.
  • TokenElevationTypeLimited - the process is not running elevated.

Note that the last two values can be returned only if both the UAC is enabled and the user is a member of the Administrator's group (that is, the user has a "split" token).

The second function is IsElevated() that also calls the GetTokenInformation() API, but requests the TokenElevation class of information. It can return one of the following:

  • S_OK - the current process is elevated. This value indicates that either UAC is enabled, and the process was elevated by the administrator, or that UAC is disabled and the process was started by a user who is a member of the Administrators group.
  • S_FALSE - the current process is not elevated (limited). This value indicates that either UAC is enabled, and the process was started normally, without the elevation, or that UAC is disabled and the process was started by a standard user.

Using these two functions, an application can determine the exact circumstances of its execution.

Starting an elevated process

If a non-elevated process needs to start an elevated one, all it has to do is call the ShellExecuteEx() API and supply the "runas" verb as one of its parameters. The source code of this article contains the function RunElevated() that does just that:

C++
BOOL
RunElevated(    HWND hwnd,
        LPCTSTR pszPath,
        LPCTSTR pszParameters = NULL,
        LPCTSTR pszDirectory = NULL )
{
    SHELLEXECUTEINFO shex;

    memset( &shex, 0, sizeof( shex) );

    shex.cbSize        = sizeof( SHELLEXECUTEINFO );
    shex.fMask        = 0;
    shex.hwnd        = hwnd;
    shex.lpVerb        = _T("runas");
    shex.lpFile        = pszPath;
    shex.lpParameters    = pszParameters;
    shex.lpDirectory    = pszDirectory;
    shex.nShow        = SW_NORMAL;

    return ::ShellExecuteEx( &shex );
}

Starting a non-elevated process from an elevated one

Going in the opposite direction (from an elevated process to a non-elevated one) turns out to be much more difficult. If the parent process is elevated, then any process it starts directly becomes elevated too, no matter which value of the requestedExecutionLevel attribute is specified in the application's manifest. For some reason, Microsoft has not provided an API to lower the execution level directly, so we had to come up with an indirect way of achieving the goal.

The trick is to use the built-in Task Scheduler of Windows Vista to set up a task to be executed at the low execution level, and request that the task should start as soon as it is registered with Task Scheduler. The net result is about the same as if the process was started directly.

The source code of this article contains the function RunAsStdUser() that does exactly that. It is based on the MSDN sample "Registration Trigger Example", and it involves calls to more than a dozen COM interfaces to communicate with the Task Scheduler and set up a task to run at the standard (non-elevated) level. I am not including the source code of the function here as it's rather boring; you can find it in the file VistaTools.cxx

Seeing it all in action

The demo application (VistaElevator) illustrates how to perform both the elevation and lowering of the execution level programmatically. When you run it, it displays a dialog box that shows the information about the execution level of the current process obtained by calling GetElevationType() and IsElevated() (see above). It also offers you two choices of how to restart it, elevated or not. Depending on your choice, VistaElevator calls either RunElevated() or RunAsStdUser() functions (see above) to restart itself at the requested execution level.

Note: Make sure you have the latest Windows SDK (see msdn.microsoft.com for more information) if you want to compile the source code on your own.

An update: Vista Elevator 2.0

  • 2007-Feb-27: I have posted an updated version 2.0 of this sample application at http://www.tweak-uac.com. The main improvement is the new function RunNonElevated() that does not rely on Task Scheduler and works well for both the administrators and standard users. Check it out!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
When not busy entertaining my two cats, I run my micro-ISV business at www.winability.com

Comments and Discussions

 
AnswerRe: how to rise the elevator without restarting de app ? Pin
Mario M.5-Jun-07 9:34
Mario M.5-Jun-07 9:34 
GeneralRe: how to rise the elevator without restarting de app ? Pin
danielj2330-Jun-07 23:23
danielj2330-Jun-07 23:23 
GeneralAnother to run app at elevated mode by default... Pin
John Tan Jin Kiat4-Jan-07 15:42
John Tan Jin Kiat4-Jan-07 15:42 
GeneralRe: Another to run app at elevated mode by default... Pin
Andrei Belogortseff5-Jan-07 4:37
Andrei Belogortseff5-Jan-07 4:37 
GeneralRe: Another to run app at elevated mode by default... Pin
John Tan Jin Kiat5-Jan-07 21:47
John Tan Jin Kiat5-Jan-07 21:47 
GeneralRe: Another to run app at elevated mode by default... Pin
Andrei Belogortseff6-Jan-07 6:41
Andrei Belogortseff6-Jan-07 6:41 
GeneralRe: Another to run app at elevated mode by default... Pin
Michael Dunn27-Feb-07 13:14
sitebuilderMichael Dunn27-Feb-07 13:14 
GeneralRe: Another to run app at elevated mode by default... Pin
Andrei Belogortseff28-Feb-07 8:05
Andrei Belogortseff28-Feb-07 8:05 
> When an app is launched from the Startup group or the various Run registry keys, it will never generate a UAC prompt. If the app requires elevation, it will not run at all.

That's right. However, there are other ways to start an application elevated automatically at user's logon without displaying the UAC prompt: for example, by installing a service that would monitor WTS notifications, and whenever a logon notification received, it would call CreateProcessAsUser() to launch an elevated application in the user's context. The application would start elevated and no UAC prompt would be displayed!

Which begs the question: does UAC increase the security of the system by preventing elevated application from running automatically at user's logon? I dare to say that in this case UAC is actually making the system _less_secure_. Why? Because now everyone who wants to launch its application elevated will write a service to do that. Soon enough, the computers will be filled with such services, one per each vendor, wasting the CPU cycles and memory, doing nothing but just waiting for the logon notifications. And since the services are executed in a much more privileged context, they open great opportunities for the bad guys to exploit their security defects (such as buffer overruns). If a virus manages to inject itself into a badly written service's process, it gets access to the system as the SYSTEM user (which in some ways is even more powerful than the Administrator account!).

If UAC were to allow some mechanism for allowing the applications to start elevated automatically, there would be no need for the dummy services, and that would actually make Vista more secure.

My 2c.


Andrei Belogortseff
http://www.winability.com

GeneralRe: Another to run app at elevated mode by default... Pin
nnm30-May-07 11:23
nnm30-May-07 11:23 
Generalyou need to leverage Task scheduler to go down... Pin
Msftone2-Jan-07 1:33
Msftone2-Jan-07 1:33 
GeneralRe: you need to leverage Task scheduler to go down... Pin
Andrei Belogortseff2-Jan-07 6:21
Andrei Belogortseff2-Jan-07 6:21 
GeneralStarting a non-elevated process from an elevated one Pin
anfilat20-Dec-06 5:12
anfilat20-Dec-06 5:12 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Andrei Belogortseff20-Dec-06 7:14
Andrei Belogortseff20-Dec-06 7:14 
GeneralRe: Starting a non-elevated process from an elevated one [modified] Pin
Toby Opferman20-Dec-06 17:06
Toby Opferman20-Dec-06 17:06 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Andrei Belogortseff20-Dec-06 18:26
Andrei Belogortseff20-Dec-06 18:26 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Toby Opferman20-Dec-06 18:30
Toby Opferman20-Dec-06 18:30 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Toby Opferman20-Dec-06 18:56
Toby Opferman20-Dec-06 18:56 
GeneralRe: Starting a non-elevated process from an elevated one [modified] Pin
Toby Opferman20-Dec-06 19:04
Toby Opferman20-Dec-06 19:04 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Andrei Belogortseff21-Dec-06 6:42
Andrei Belogortseff21-Dec-06 6:42 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Toby Opferman21-Dec-06 8:21
Toby Opferman21-Dec-06 8:21 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Toby Opferman21-Dec-06 8:28
Toby Opferman21-Dec-06 8:28 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Andrei Belogortseff21-Dec-06 8:29
Andrei Belogortseff21-Dec-06 8:29 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Toby Opferman21-Dec-06 8:35
Toby Opferman21-Dec-06 8:35 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Toby Opferman21-Dec-06 8:55
Toby Opferman21-Dec-06 8:55 
GeneralRe: Starting a non-elevated process from an elevated one Pin
Andrei Belogortseff21-Dec-06 13:52
Andrei Belogortseff21-Dec-06 13:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.