Introduction

When I began this project, I didn't think it would take me two weeks to complete. Well, here I am, finally, writing the article portion of the whole thing. Don't worry, I took really good notes. I am splitting this article into three major segments: The first segment is a broad overview for those who really don't care about the nitty-gritty details of UAC and just want to get to the good stuff. The second segment gets into the really nasty stuff...for those of you who enjoy information overload. The last segment covers UAC Permanent Links - load multiple elevated DLLs, execute multiple elevated functions, and start multiple processes elevated - all of that with just displaying one UAC dialog from a non-elevated process. And now, without further ado, the article.

The Big Picture

Ah. Vista. Many things have been said about it. Some good, some bad, but that doesn't really matter. Vista looks pretty, has new APIs, and has UAC (Microsoft TechNet, Wikipedia).

Ah. UAC. For the acronymically challenged, it stands for User Account Control. The bane of numerous existing software applications, and the whole reason I ended up creating the Elevate package. I initially created it for my own users, but figured the rest of you developers need it as well.

We would all like to think our applications automatically work under Vista. However, the rude awakening that follows simply plastering Vista on the end of Win95/98/Me/NT/2000/XP/2003 causes us to actually do something about it. If you are reading this, I can only assume you have run into one of several barriers to successful application deployment that UAC introduces.

The majority of this article covers how UAC operates and how CreateProcessElevated(), half of the Elevate package, is possible. The other half of the Elevate package is to solve the major annoyance of UAC for non-elevated applications: the constant requirement to go through the UAC dialog to perform all operations that can only be done within elevated processes. If you find yourself in this position but don't want to switch your whole application to only run as an elevated process, this article is for you as well.

A few Google searches on UAC usually turns up someone's blog discussing how to use ShellExecuteEx() with the undocumented "runas" verb to force a process to start elevated. Alternatively, a modified manifest file can result in the same thing when a user runs the executable. There is also an article here on CodeProject on how to bypass ShellExecuteEx() by using an NT Service to do the job, but it has various issues besides the obvious system security-related ones.

But if you are like me, ShellExecuteEx() is not an option, and I didn't want to bypass UAC either - Microsoft made UAC for a reason, and I want to play nice. I have a large library, and depend heavily on CreateProcess(). Unfortunately, Microsoft failed to make a CreateProcessElevated() API for people like me. Fortunately, I found a way to make a series of CreateProcess...Elevated() APIs with nearly complete functionality.

Before I get to CreateProcessElevated(), let's look at some code that uses ShellExecuteEx():

That is your basic, run-of-the-mill, let's force Vista to elevate this process, function call. As was stated before, the magic word here is "runas". As far as I can tell, the "runas" verb is completely undocumented in the MSDN Library. At least, I never found any reference to it during my research.

Let me take this time right now and say that, wherever possible, avoid using the "runas" verb and prefer manifest file modifications. Which brings me to, uh, manifest modifications.

If you have never used a manifest, you probably live in the console realm. Or a cave. Or both. I generally only use a manifest when I need to let the Windows loader know to load the latest Common Controls. Manifests are required to load Common Controls 6 and later (theme support) via what are called Side-by-Side Assemblies (SxS, you may have noticed the "assembly" directory in C:\WINDOWS and wondered what that is). This is Microsoft's solution to DLL Hell, and it generally works.

A manifest is a plain-ol' XML file. Let's look at your average manifest file with Common Controls 6 support:

Wrong answer! Apparently, a malformed v3 manifest file will cause Windows XP SP2 to crash. And not any ordinary crash either. A Blue Screen Of Death (BSOD), complete with reboot. Without actually taking CreateProcess() apart, this is probably due to an XML parser crash in kernel mode (only way I know of to BSOD XP is to crash the kernel in ring 0). Also, apparently v2 manifests are significantly more stable and yet still work. So, for maximum stability, the correct format for the manifest file should be:

requestedExecutionLevel's "level" option can be one of 'asInvoker', 'requireAdministrator', or 'highestAvailable'. I will cover the "uiAccess" option later on in this article.

For those who are COM/DCOM/whatever addicts and heavily invested in that technology, you're probably already way ahead of me, but I'll post some redundant moniker out-of-process elevation code for you:

I honestly have no idea what that code does. But apparently, it is magically delicious. I did spot the "Elevation:Administrator!new:" string in ShellExecuteEx(), so I can only assume the code does work. I try to avoid COM wherever and whenever possible. COM is kind of like the plague.

User Interface Integration

If you are going to do process elevation, you have got to do it right and do it stylishly as well. The Vista shield icon is plastered pretty much everywhere where there is an administrative task to be performed. Task Manager, for instance, won't show all running processes from all users until you click the button with the shield icon and go through the UAC elevation process.

However, unless you are designing a Vista-only application, you will want to do the whole LoadLibrary()/GetProcAddress() thing for SHGetStockIconInfo().

Alternatively, LoadIcon() with IDI_SHIELD can be used to get an HICON, but that apparently loads a "low-quality" shield icon. The new LoadIconMetric() API for Vista is a better solution because it loads a "high-quality" shield icon, which will apparently be used for high DPI displays.

UAC Weirdness

So far, I've just covered the most prominent portions of UAC, but UAC is way more than just displaying a dialog to the user. It is a way of life. Or something like that. And, as with most things in life, UAC can be downright weird.

Many of us have existing applications. Some of us even have badly written applications that write to the "Program Files" directory. And some applications are really badly behaved, and write both to the Windows directory and the HKLM registry key.

UAC treats misbehaving (non-elevated) applications as illegitimate children. There is something called the "virtual store" for each application that misbehaves. This consists of files and registry keys that are written to in places that are deemed bad to write for non-elevated processes. What happens when a write operation occurs is, the OS copies the original file to the user's virtual store and redirects all requests to that location instead. Thus, the application makes changes to the file/registry key in the virtual store, and not the actual location it was intending to make changes to. But, only for that user. Other users will see the original file or a copy in their virtual store.

But wait! It gets weirder. Let's say the application goes to delete a file it has modified in Program Files. Well, the OS redirects the request to the virtual store instead. Yet, even though the file was deleted, if the application goes back, it can see that the file still exists. Once removed from the virtual store, the OS allows the application to see the original file. However, if the application attempts to delete the file again, it will cause an error to occur. It makes sense, but it is weird.

UAC also causes a number of other minor issues to come up: Interactive Services (of the GUI variety) are completely hosed (i.e., creating windows from NT Services), administrative shares have major issues, various functions that take or create tokens (e.g., HANDLE hToken) are affected by the split token (e.g., LogonUser), and side-by-side isolation issues.

Which, of course, brings me to split tokens. Split tokens are just weird. When you log onto Windows Vista and later (obviously), you get a split token. Basically, the logon architecture (which changed...again...for Vista) takes your initially really powerful user token and creates a second token with all Administrator privileges stripped out. This second token is used to launch all applications essentially as what XP called a Limited User Account (LUA). When UAC prompts for elevation and you accept, the first token is used to create the process instead of the second token. Essentially, what UAC elevation prompts are asking is, "Do you really want me to use your super powerful administrative token to start this application?"

Another weird UAC thing is the elevation dialog. If you let it sit there for an extended period of time, it will automatically cancel itself. Found that out during my research.

The last weird thing about UAC is that once a process has been started as an elevated process, it becomes very difficult to start a process non-elevated from the elevated process. This becomes a major nuisance for authors of software installers. Us, software developer types, like to let users try out the software at the end of an installation so the user has the tendency to actually use the software and have a greater chance of buying it. There is an article that shows how to ride the Vista elevator by using a global hook and hooking Explorer.exe to create a process at a lower Integrity Level.

CreateProcess() Fails With ERROR_ELEVATION_REQUIRED

Those are the basics of UAC, all in one concise location. For many people, ShellExecuteEx(), modified manifests, switching to HKCU, and not writing to naughty places on the hard drive, are "good enough" solutions. However, some of you want to know more. And some of you need CreateProcess() and all the power it contains (and a few people might need ShellExecute()/ShellExecuteEx() with custom verbs).

Now, on to the Elevate package and CreateProcessElevated().

CreateProcess() fails miserably for processes in Vista that require elevation via their manifest file. It should be noted that the manifest file doesn't really say "you have to use the administrative token to start this process". Instead, it says, "you can't use a token with fewer than these rights to start this process". So, the error message returned from CreateProcess(), ERROR_ELEVATION_REQUIRED (740), is somewhat misleading.

Regardless, if you are reading this, I can only assume you are desperate for a CreateProcessElevated() solution. Which is what the Elevate package is for. The Elevate package (Elevate_BinariesAndDocs.zip) consists of two components plus comprehensive documentation: the Elevation API DLL (Elevate.dll) and the Elevation Transaction Coordinator (Elevate.exe). Both must be placed in the same directory for the elevation process to work properly. The Elevation API DLL exports the following functions:

You will note that the elevated APIs have very similar names to their non-elevated counterparts (e.g., CreateProcess() and CreateProcessElevated()). And this package includes both ANSI and Unicode versions too (A vs. W). You may note that IsUserAnAdmin() is already a function exported from Shell32.dll, but MSDN says it may go away. IsUserAnAdmin() currently defers to the Shell32.dll version, but can fall back to internal code if it does disappear.

Moving along. You probably have existing code that looks something like this:

which works great until you try to launch a process that requires elevation. To use the Elevate package, just drop it on the system, and LoadLibrary()/GetProcAddress() the CreateProcessElevatedA() function:

The "...ParameterList..." options are _nearly_ identical. The only time you have to change the parameter is when you use the STARTF_USESTDHANDLES flag in the STARTUPINFO structure you pass in. If you do redirection of the standard handles (stdin, stdout, stderr), things can get a bit funky. Read the documentation, but, in short, you will need to familiarize yourself with named pipes and read the rest of this article in its entirety.

Note that you must use LoadLibrary()/GetProcAddress(). The DLL will intentionally fail to load on any Windows OS prior to Vista.

ShellExecute...Elevated()?!

Some of you are probably raising eyebrows as to the need for a ShellExecuteElevated() set of APIs. There are two reasons to do this. The first reason is actually a special case problem that the "runas" verb introduces. The lpVerb/lpOperation parameter only allows one verb to be used at a time. So, if someone needs to force a process to run elevated (that would normally run non-elevated) and, for example, use the "print" verb, the regular ShellExecute()/ShellExecuteEx() won't cut it.

The second reason is because someone may specifically need to run a ShellExecute()/ShellExecuteEx() command from within an elevated environment. There is no way to do this either.

Now, I will admit the need for the ShellExecuteElevated() APIs is going to be rare, but I am including them for completeness.

Demo Application

For the lack of a better spot to put this, the demo application is an example of CreateProcessElevated() in action, in all of its glory. To use the demo, first download it, and extract it to some directory you know how to get to from the Command Prompt. Then, start a non-elevated Command Prompt, and go to the directory you extracted the files to. Type in "TestParent", and press Enter. What follows should look like this:

Ignoring the obvious reference to my knowledge of cool phrases, what happens is really quite impressive. Normally, an elevated console-based program started from a non-elevated console-based program would have a separate console window. In this case, TestChild.exe (elevated) and TestParent.exe (non-elevated) share the same console. Additionally, TestChild.exe's stderr is being routed to TestParent.exe. This is possible by very carefully sifting through tons and tons of documentation and some experimentation.

Also, while the program doesn't show it, TestChild.exe shares the same environment variables as TestParent.exe.

The Nitty Gritty

Before I can discuss how Elevate.dll and Elevate.exe work to make the demo even possible, I have to cover some of the nastier details of how UAC elevation works. Bear with me, it gets pretty in-depth.

When I started this project, I wanted to avoid using ShellExecuteEx(), so to do that, I had to figure out what made the function "tick". My first thought was, "Well, they have to call CreateProcess() and related kin somewhere along the line. So, there's some trick to the call, right?" My first stop was the Detours traceapi.dll file. I hooked it into a test process with the ShellExecuteEx() API, and...nada. Nothing. I thought maybe traceapi.dll was broken, so I wasted a day on figuring out that maybe ShellExecuteEx() wasn't using CreateProcess() at all.

So, the next step was to dig into the actual call with the Visual Studio Disassembler. I quickly realized I needed to run out to the symbol store and get the symbols for the Vista Shell32.dll and other related DLLs. I know Microsoft massages their symbols on the symbol server to trim out stuff they don't want people knowing. I was hoping UAC elevation wouldn't be part of that. Turns out, they didn't remove that information, or just simply forgot to do so. Either way, you can be eternally grateful the information was there.

Stepping through ShellExecuteEx() is quite confusing. In fact, I'm still not sure what some of the stuff does. If you are following along with a debugger, your call stack will eventually look like this:

Looks promising, right? Well, under a normal function, yeah, that's probably about where you would execute a command. In this case, however, ShellExecuteEx() is just getting warmed up to...start a new thread. Seriously. To start a new process, a new thread is started. It is sickening. So, we step through with the debugger inside the new thread until the call stack looks like this:

Now, that looks promising, right? Well, to get here was an abysmal mess that takes something like five hours of pressing F10 and F11. You go through a huge mess of COM objects. Yup, that's right. COM is now involved in starting a new process. And that means bringing in a mess of DLLs with it. But guess what? We ain't done yet.

Inside the Execute() method, a call to CExecuteApplication::_VerifyExecTrust() is made. This uses COM, and takes a LOT of CPU (along with a bunch of blatant and unnecessary replication, code-wise). I sort of gave up trying to figure out what it did. My general impression was that it most likely has something to do with the pop up dialogs received when launching an EXE downloaded from the Internet (IZoneIdentifier). Probably looks for the existence of a NTFS stream called 'Zone.Identifier' so it can pop up that nifty (and annoying) dialog you see for downloaded files before executing them.

I backed out of _VerifyExecTrust(), and went down the only remaining path. The call stack turned hilarious:

Gotta love the names given to the functions. It sort of eggs you on to see what's next. _DoExecute(), _Execute(), _TryApplication(), _DoCommand()... Okay, now we're going to create the process. Oh, wait, never mind, now we're going to create the process. Oh, sorry about that, _now_ we're going to create the process... The marketing engine has gotten into the source code. Poor developers. I feel sorry for you guys who have to work with that mess every single day.

At any rate, the function of interest is AicLaunchAdminProcess(). Once we get to AicpMsgWaitForCompletion(), it will have gone too far. It took me almost two days of scratching my head to figure out how entering a MsgWaitForMultipleObjects() could cause UAC to display the elevation dialog unchecked and then continue as if nothing had happened when Accept/Cancel was clicked.

Turns out, not only is a new thread started and a half dozen COM objects instantiated, but RPC is brought into the mix as well. RPC (Remote Procedure Call for the acronymically challenged) is a pretty old technology, but mostly just used by Microsoft for NT Services. It combines with MIDL and allows you to, uh, call remote procedures. The target function could be on another computer or the same computer. Sort of one of those unexploited security holes because it is also an obscure technology that few know how to use.

And, this is where things get interesting. The RPC call is to a brand new NT Service in Vista called AppInfo (UUID "{201ef99a-7fa0-444c-9399-19ba84f12a1a}"). AppInfo is where the magic happens. But before I get to the magic, a short discussion on Sessions and Integrity Levels is in order.

Vista Sessions and Integrity Levels

Part of the changes in Vista and how UAC works is the introduction of what are known as Sessions. They are also occasionally referred to as "Secure Desktops". Vista has two Sessions, but could technically have many more than that. Session 0 and Session 1 are the official names. Session 0 is where all NT Services reside. Session 1 is where the "WinSta0\Default" desktop resides along with Explorer and user applications. Just to clarify, Window Stations are not Sessions.

It is important to note that some documentation on Vista UAC implicitly claims each Session is supposedly completely segmented from communication with other Sessions. This is not true. Most likely, the authors are referring to window messages or an early beta. Official Microsoft documentation, Impact of Session 0 Isolation on Services and Drivers in Windows Vista, says to use RPC and Named Pipes to communicate between processes running on different Sessions. RPC, Named Pipes, and sockets are all positively confirmed Interprocess Communication (IPC) mechanisms under Vista.

The other major change in Vista with UAC is what are known as Integrity Levels (ILs). There are four Integrity Levels: System (NT Services), High (Elevated processes), Medium (most user processes), Low (processes like Protected-mode IE). Every object created has an IL associated with it, and ILs are checked before DACLs. The process/thread IL is checked against the object IL before granting access to it. It is important to note that objects created by elevated and system processes, by default, have a Medium Integrity Level. (For the observant person, that last sentence is the key to why the Elevate package works.)

AppInfo and consent.exe

AppInfo is, obviously, the key to UAC elevation. ShellExecuteEx() forwards all elevation requests to the AppInfo NT Service via an RPC call. AppInfo then turns around and calls an executable in the SYSTEM context, called "consent.exe". As the name implies, this is the executable that brings up the dialog that the user consents to.

However, consent.exe is not your average, run-of-the-mill application. What you see when the dialog is active is not Session 1's WinSta0\Default. Nope. What you see is a desktop on Session 0. Hence the reason it is called a "secure desktop". consent.exe takes a snapshot of the screen, switches to the Session 0 desktop, plops the screenshot on the desktop, and displays the dialog. It only looks like Session 1's desktop. You can't click on anything but the dialog, because there literally isn't anything to click on. Once you accept/cancel, the desktop is switched again back to Session 1 and consent.exe exits.

AppInfo then takes the results from consent.exe and determines if it needs to start a new process (i.e., you accepted the elevation request). AppInfo then creates a process using the full administrative token (remember that split token thing?) of the logged in user on the Session 1 desktop with a High Integrity Level. If you fire up Task Manager, you can see that elevated processes indeed run as the current user. We know it also runs on the Session 1 desktop because GUI windows can be created, seen, and interacted with.

To create a process as the current user on a different desktop in a different Session is a seven stage process:

AppInfo goes and talks to the Local Security Authority to get the elevated token of the logged in user of Session 1.

AppInfo loads up a STARTUPINFOEX structure (new to Vista), and calls the brand new Vista API InitializeProcThreadAttributeList() with room for one attribute.

OpenProcess() is called to get a handle to the process that initiated the RPC call.

UpdateProcThreadAttribute() is called with PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, and uses the handle retrieved in step 3.

CreateProcessAsUser() is called with EXTENDED_STARTUPINFO_PRESENT and the results of steps 1 and 4.

DeleteProcThreadAttributeList() is called.

Results are gathered, and handles are cleaned up.

Once AppInfo succeeds in launching the process, it transfers some information back over the RPC interface to the application that called ShellExecuteEx(). ShellExecuteEx() meanders around a bit, and cleans up after itself, and eventually returns through the whole mess of function calls, closes the thread, and returns to the caller.

CreateProcessElevated() Without ShellExecuteEx()?

Once I had learned about roughly where in ShellExecuteEx() makes its RPC call to AppInfo and how AppInfo worked, I wanted to know if it was possible to create a fully-functional CreateProcessElevated() without making that insanely expensive call to ShellExecuteEx(). By fully functional, I meant I wanted complete control over the STARTUPINFO structure, with support for the STARTF_USESTDHANDLES flag.

This process took me about three days to complete, and was quite exhausting. I eventually narrowed everything down to a few lines of assembler inside AicLaunchAdminProcess():

RAiLaunchAdminProcess() takes a whole bunch of parameters, and then routes the whole thing behind the scenes through a very nasty MIDL call. You will note the lea (Load Effective Address) on [ebp+VarStartupInfo_Title]. For you non-assembler gurus, this is essentially passing the address of the address of a data block from Title to ShowWindow. So, this means only a limited amount of information from STARTUPINFO is actually passed onto the target process. This tells me that AppInfo doesn't handle very much information.

At this point, I chucked the idea of doing my own RPC thing with AppInfo out the window, cried for two seconds for wasting a couple days, and then went to figure out how to use ShellExecuteEx() to do what I wanted.

Routing via ShellExecuteEx()

I quickly decided to create a split DLL/EXE approach. My target audience was initially my own customer base, so it needed to be clean and easy to understand. The DLL would export functions that looked and felt a lot like Win32 APIs. The DLL would then package up each function's data and shuttle it across to the EXE, which would take that data, unpack it, and execute the correct function as an elevated process.

My initial approach was to shuttle data across using the lpParameters member of SHELLEXECUTEINFO. To my surprise, doing that caused an extremely meaningless error message to pop up: "The data area passed to a system call is too small." (Error code 122). It took a lot of experimentation, but apparently sending more than 2048 bytes (2K) of data in the lpParameters member causes that error message to occur.

My solution was to pass the process ID and thread ID to Elevate.exe from Elevate.dll, and use those bits of information to open a named pipe, and connect to a named pipe server running in Elevate.dll to send the information.

About Named Pipes

But I'm getting way ahead of myself there. I got the idea for using named pipes once I learned about Integrity Levels and the fact that objects created at higher ILs only have a Medium Integrity Level...the same level as non-elevated processes.

But pipes are HANDLE based. And, there's the "gotcha". Remember that whole roundabout mess to get a process elevated? Well, while HANDLEs are inheritable, the process that actually starts the elevated process is AppInfo, and not the process making the ShellExecuteEx() call. And, even if HANDLEs were passable, there is the issue of the Session 0 to Session 1 barrier. So, passing raw HANDLE values won't work.

Someone may point out that AppInfo's method of starting a process does actually inherit the handles of the original process and would include the Standard HANDLEs (stdin, stdout, stderr). However, while this is true and the HANDLEs are inheritable, AppInfo would have to populate the STARTUPINFO structure with HANDLEs that can't cross the Session 0 to Session 1 barrier.

The solution lies in named pipes. Named pipes have, um, names. Sometimes ANSI, sometimes Unicode, but those names are strings. And, unlike HANDLEs, strings are passable via IPC. CreateNamedPipe() and CreateFile() with the same string equates to a HANDLE that points at the same pipe. And, since both objects have a Medium Integrity Level, it works too. For security reasons and for DACL reasons, the DLL calls CreateNamedPipe() and the EXE calls CreateFile() to connect to the named pipe.

How Elevate Works

Let's assume CreateProcessElevated() is being called. Elevate.dll is loaded up, and the function is called. Just before ShellExecuteEx() is called, one named pipe and three event objects are created. Elevate.dll starts Elevate.exe as an elevated process. Upon successful return (process started), Elevate.dll waits for Elevate.exe to finish initializing. Note that the event objects time out after 10 seconds and bail. This is so that the application doesn't hang.

Once the two processes are in sync with each other, Elevate.dll packs up the data sent to the function, and sends it as a packet of data to Elevate.exe. Elevate.exe receives the packet of data and unpacks it.

From here, Elevate.exe prepares to run the process. It attaches to the console of the process that started Elevate.exe. Also, I use the STARTF_USESTDHANDLES flag every so often, and Elevate.exe takes named pipe names and sets up the appropriate HANDLEs for stdin, stdout, and stderr along with building the rest of the STARTUPINFO structure. Attaching to a console gives access to that console's stdin, stdout, and stderr HANDLEs.

After that, the correct function is executed with correct parameters for everything. The only thing interesting to note is the use of the CREATE_SUSPENDED flag. This flag starts the process suspended. The reason for this is so that information about the process and main thread are sent back to Elevate.dll. Remember, raw HANDLEs can't be passed, but process and thread IDs can be. Also, keep in mind that it is possible for the started process to have such a short lifetime that it would exit before Elevate.dll could open appropriate HANDLEs for synchronization purposes.

Elevate.dll then receives the information about the process, processes it, and then fires the event to let Elevate.exe know that it has received and processed the information.

Now, you may be wondering, or have already wondered, what good those functions are. The term UAC Permanent Link is something I made up. Hey, there's nothing wrong with a little artistic license, right?

What exactly is a UAC Permanent Link? Suppose you have an application that you wrote and worked great on Windows XP, but required you to create a separate executable for Vista due to elevation issues. You plastered the shield icon wherever elevation would take place, and released the updated software.

Bam. Some users complained about the excessive UAC dialog use. However, you don't want to convert the whole application to require elevation to operate. This is where UAC Permanent Links come in handy.

Link_Create() instantiates a UAC Permanent Link. It launches Elevate.exe, which shows the UAC dialog. The user clicks "Allow", and Elevate.exe starts. The HANDLE that is returned from Link_Create() is then able to be passed to the other Link_...() functions. When the application is done with the UAC Permanent Link (e.g., at the end of the program), Link_Destroy() is called. Elevate.exe exits, and all resources are cleaned up.

The possibilities for this are endless. Once the UAC dialog has been displayed and allowed, your non-elevated program has free reign in the elevated process space.

CreateProcessElevated() and other exported functions actually instantiate a UAC Permanent Link, call Link_CreateProcess() or whatever is appropriate, and then call Link_Destroy().

Instead of starting processes, consider making a DLL. You can even display dialogs that return data to the non-elevated process. It does get somewhat complicated as all data sent/received has to be serialized and unserialized, but it may be worth saving the overhead of starting multiple processes, and two-way communication with the non-elevated application could be important as well. If you take this route, I recommend looking at the source code of Elevate to get a feel for how it works.

The DLL approach also works great if you need to load one or more API hook DLLs into the elevated process space.

The nice part about UAC Permanent Links is that this approach falls completely within Microsoft's defined parameters for how UAC is to be used.

Source Code

For those of you who download the source code, please note that the source code is just the Application Layer (Safe C++ terminology). The Base Library is not included. The source code is being made available in case someone finds a bug, they can point it out and maybe even fix it. I also included it so you could more or less follow along with this article.

More .manifest Issues

As stated earlier, an associated manifest file with an executable can have a requestedExecutionLevel's "level" of 'asInvoker', 'requireAdministrator', or 'highestAvailable'. 'asInvoker' means a non-elevated token or higher can start the process. 'requireAdministrator' means elevated or higher can start the process. 'highestAvailable' means the process will start as the highest level permitted by the user's split token.

I said earlier that I would discuss the 'uiAccess' option. 'uiAccess' is designed to grant a select set of non-elevated processes access to elevated process user interfaces. This is specifically designed for UI automation tasks (e.g., macro recorder and playback tools). Most of the time, you will want to specify the 'uiAccess' option as 'false'.

But what if you want to specify it as 'true'? Well, get ready to spend some money. You'll see recommendations for Verisign Code Signing Certificates, but they run ridiculous sums of green ($400/year). Alternatively, you can try one of these companies...just be sure they support Code Signing (and possibly time stamping - the more 'yes's, the better).

If you just need to try something out for personal use, you won't need to shell out any dough, but you will need the latest Authenticode tools and an EXE to sign that contains a manifest with uiAccess set to 'true'. If you have that set up, run the following commands from an elevated command prompt:

Note: If you sign your executable with a certificate from a trusted code signing certificate authority (CA), the orange UAC elevation dialog becomes a dull gray.

IShellExecuteHook vs. ShellExecuteWithHookElevated()

For those who use IShellExecuteHook DLLs, you are probably well aware, Microsoft has "deprecated" this COM interface for Vista, and is not currently offering an alternative. It is turned off, by default, because of problems with older hooks crashing the Vista shell. The interface can be turned on by setting:

An alternative to writing a IShellExecuteHook DLL is to hook ShellExecute(), ShellExecuteEx(), and IsUserAnAdmin(). When IsUserAnAdmin() is called by those functions, rewrite the ShellExecute() call to use the ShellExecute...Elevated() calls. This method is not perfect because various COM interfaces bypass the APIs to get at the elevation mechanism. You also have to be really careful to not get caught in an infinite loop (the Elevate package calls ShellExecuteEx()).

Another approach is to do a combination of the two. Write an IShellExecuteHook DLL and then also hook IsUserAnAdmin(). This has a better chance of catching everything that is about to do the whole UAC thing, but then you have to worry about if the COM interface will even work.

Share

About the Author

Been writing software for a really long time - something like 18 years. Started on the TI/99-4A, moved to the Tandy 1000, and somehow managed to skip all the lousy hardware/software jumps (286, 386, first Pentiums, Win95, etc.)

I now run a small software business called CubicleSoft with a few products you might be interested in. VerifyMyPC and MyUpdate Toolkit are the most popular. I'm also the author of a book called "Safe C++ Design Principles".

Comments and Discussions

Hello. Really inspired of your article. thx a lot, you are maybe the only one who do not suggest to add a manifest
I'd like to ask you. As I noticed after the elevation is done, the unstripped token is applied and any "unprotected" functions are available. So, I have a question - I need to elevate the right not only to another executable, but to the program itself to do some portions of actions. I want to call th elevation dialog, ask user to change the rights and then make a code with full administrative rights and all this within single exe. Is it possible?

Thx for any answer and would be glad for any info on how to do it.

P.S. I do not want to make the program elevated forever. I wanna do it in only one place I need, and then re-launch itself non-elevated. Thank you.

Hi, first congrats on one of the most interesting article I've read on the subject. This certainly has enlighted me a lot.

Which leads me to this situation for which I wonder if this is related or not.

The problem context is this: we have an MFC42 application, no manifest in it, that runs fine in Vista. Call this TOOL.exe. We have built a DLL that provides an API to the TOOL.exe. Call this INTERFACE.dll.

INTERFACE.dll simply CreateProcess() to start the TOOL.exe.

Now, when building an MFC with VS2005 application, TEST.exe, that loads INTERFACE.DLL, all runs fine. This TEST.exe does not take advantage nor uses Visual Themes.

We then have another application, APPCLIENT.exe, which does take advantage of Visual Themes. This application loads a plugin DLL, which loads or INTERFACE.dll which CreateProcess() the TOOL.exe.

In this case, it fails loading the TOOL.exe (seems TOOL.exe receives an ExitProcess() of some sort).

However, when we change properties of the APPCLIENT.exe to disable Visual Themes, all runs fine.

I was wondering if this might be related to elevated process or not, or, if this is a known issue? I've been google search for weeks without any luck.

It does not matter if we use si.lpDesktop = "winsta0\\default"; or not either. The CREATE_SUSPENDED is normal: we do some Write/Read process memory (hence we use CreateProcess to get a process handle instead of using ShellExecute) and then a Resume thread on the process thread. It does not changes either with or without security descriptors than we init this way:

1 - I need this function to set values in registry. Do you have this function? If you have registry operations examples i really stay interested.
2 - I use c++builder 2007, so i don't know to make manifest files. Do you know?
Thanks!

The article as it currently stands completely confuses the levels of divisions used by Windows Vista. The UAC dialog is not displayed in Session 0 (almost nothing is), it is displayed on the "Secure Desktop" named WinSta0\WinLogon in the same session (session 1 or higher) as the WinSta0\Default containing the program that needs the elevation.

Windows Vista divides onscreen threads at 4 levels within each other, from outermost to innermost:

Sessions are the outermost layer and have numbers such as 0, 1, 2, ... Each session has its own copy of the parts of Windows that deals with the user interface, even the display driver! Sessions were introduced in "NT 4.0 Terminal Server Edition" to represent the "virtual computer" for each of the active remote terminal users. Services always run in Session 0. In NT 4.0 and Windows 2000, the display and keyboard physically connected to the computer was always connected to Session 0 too, and all other session numbers were thus remote terminals. In Windows XP and Windows 2003 this was still the default behaviour, but two features ("Fast User Switching" and "Remote Desktop Console Access") allowed this to be switched around, so the local display got connected to a different session number, while session 0 got connected to a remote terminal or to nothing at all. In Windows Vista, the local display is almost never connected to session 0, unless an old incompatible service tries to display a Window on WinSta0\Default in Session 0, in which case a special popup dialog allows the user to temporarily connect the display and keyboard to the almost empty desktop in session 0 to deal with it. The names of Events, Mutexes, Shared Memory, Named Pipes etc. have their own namespace for each session, plus one namespace for objects to be shared across sessions. If you pass a name such as "Local\\YourCompanyFoo" to the Win32 APIs, it means "\\DirectoryForYourSession\\YourCompanyFoo". If you pass "Global\\YourCompanyFoo", it means "\\DirectoryForSharedObjects\\YourCompanyFoo" "DirectoryForSharedObjects" may or may not be the same as "DirectoryForSession0", depending on the Windows version. If you pass simply "YourCompanyFoo", Windows starts guessing about your intentions, so only do that on NT 4.0-not-terminal-server or older. Security-wise, each token on Windows 2000 or later includes the applicable session number as a separate property. The easiest way to determine the current session id of your own process is to call GetProcessSessionId(). The session id of all processes can be seen in the process tab in Task Manager.

WinStations ("Window Stations") is a much less memory hungry feature originally intended to do what ended up being done with Sessions. WinStations have names such as WinSta0, WinSta-0x1-0x2 etc. Introduced in NT 3.51 (or maybe even NT 3.10), each Window Station logically represents a Display/Keyboard combination connected to the computer. In the real world, each Session contains only the following WinStations: WinSta0 is connected to the display and keyboard of the Session, almost all programs run there. Winsta-HexNumbers is connected to nothing at all and is used to run any non-interactive services, one WinSta-Xxxx for each user account. Each Window station has its own clipboard, global atoms table and global display graphics state. Security-wise, each Window Station has its own security descriptor, just like events, mutexes etc. do. You can get a handle to the WinStation of your own process by calling GetProcessWindowStation().

Desktops represent a logical screen layout with zero or more Windows on it. Desktops have names such as "Default" "WinLogon" "MyOwnName" (there is a documented API to create your own) and exists as items within a Window Station, resulting in full names such as "WinSta0\\Default". "Desktops" were introduced in NT 3.10. Window handles, window messages, "WindowHooks" etc. all live within a single desktop and cannot cross that boundary. Unlike Sessions and WinStations, each thread in a process may be connected to a different Desktop within the WinStation of its process, and this association can be changed on the fly in any thread which does not have any windows (not even hidden ones). Security-wise, each Desktop has its own security descriptor. You can get a handle to the Desktop of your own process by calling GetThreadDesktop(), for other tasks, OpenDesktop() and OpenInputDesktop() and CreateDesktop() are interesting APIs.

Integrity levels are new in Windows Vista. They take a small number of enumerated values known as "Low", "Medium", "High" and "System" and are stored in the token as an extra Sid. Any object with an ACL also has an associated integrity level and cross-integrity-level-ACL stored in a magic entry in the SACL (The one that normally specifies auditing logging rules). Various UI objects such as windows and the per-thread message queues are also associated with the integrity level of the process that created them. Integrity levels compensate for the (wrong) assumption made by all versions of Windows and Unix, that processes run under the same user logon need never be protected from one another ("there are no evil programs, only deliberately evil users") and also compensate for the fact that the user interface part of NT-based Windows had no security protection amongst programs on the same Desktop.

Thus what the UAC prompt does is:

Copy a screenshot of WinSta0\Default to WinSta0\WinLogon, but dim its colors.

Tell WinSta0 to switch the output to WinSta0\WinLogon.

Display the dialog and wait for an answer or timeout

Tell WinSta0 to switch back to WinSta0\Default

Tell WinLogon to launch the chosen program under the "High"-level token associated with the "Medium"-level token that requested the prompt.

All in the same session as the one the request came from.
To go the other way, processes running with enough privileges can get the other of a linked pair of tokens using GetTokenInformation() with the TokenLinkedToken argument.

This message is hasty and is not to be taken as serious, professional or legally binding.
I work with low level C/C++ in user and kernel mode, but also dabble in other areas.

That is an extensive explanation. When I wrote the article, I was still trying to make heads and tails of how all four layers worked. Clearly I need to go back and re-write that section of the article. Somehow Elevate worked.

I do have a couple questions: According to you and everything I've read, NT Services run in Session 0. AppInfo is an NT Service. So it runs in Session 0. Everything I've read indicates that Consent.exe just displays the dialog (and faded background) and AppInfo manages everything else.

1) I made the assumption that AppInfo would start Consent.exe within the same Session. Getting a screenshot of something in Session 1 baffled me. So, just to confirm, does AppInfo, which resides in Session 0, start Consent.exe in Session 1? If so, that definitely explains a number of things.

2) Referring to the order of operations of "what the UAC prompt does": How does one copy a screenshot between desktops _before_ switching? Shouldn't it be: BitBlt() on "WinSta0\Default", fade content, switch output, display full screen window containing faded content, display dialog, get user response, switch output, launch program?

Thanks for the GetTokenInformation() tip. That will help to clarify a few bits of the 7-step process - probably will make it 8 or 9 steps.

Consent.exe is started in the session of the user (beginning with 1). It is a SYSTEM process.
1. start consent.exe in Session 1
consent.exe does
2.get a screenshot
3.call SwitchDesktop to winsta0\WinLogon on Session 1 and not Session 0 as you stated in your article. Desktops are secured by default (if you set the DACL correct and disallow hooks).
3.call SetThreadDesktop
4.Show Prompt
5.call SwitchDesktop and SetThreadDesktop to get back to default desktop
And now there are at least 2 possible solutions.
I. Let the AppInfo service handle the request. It can simply call CreateProcessAsUser and set the Token SessionID.
II. Consent.exe can also create the process since it is SYSTEM user and has TCB privilege.
It calls
1. WTSQueryUserToken to get the users token (using session ID).
2. GetTokenInformation for the twin token
3. CreateProcessAsUser for the rest
Of course there must be a way to return the result of the operation.

I never work on Vista and so my question will sound stupid.
When you say "UAC elevated", it seems the admin user's token privileges are elevated enough for the installation when he/she agrees on a consent dialog.

Is it possible to write a program so that the standard (or limited) users can run the install packages without a credential dialog?

When an Administrator is logged in they, by default, have many privileges stripped out of their security token. The full administrator token is only used when the elevation dialog is consented to. Limited users can install packages by entering the Administrator login credentials when prompted by the UAC dialog.

I know. I should have refined my question. You said "no way around this on a default setup". Does that mean there is a way while UAC is enabled? Will your library work for that case?
If not, is your library mainly to suppress the subsequent UAC consent dialogs for the users who are members of administrators?
Sorry; I do not intend to be rude. I just want to understand UAC and your library. TIA.

It seems to me that there is some sort of lack of communication here - probably on my behalf (I have that tendency). The Elevate package is designed to be integrated into existing software. The background for this package is that I use the CreateProcess() API call extensively within my own software. Microsoft, however, failed to include a CreateProcessElevated() API (or equivalent) into Windows and their ShellExecuteEx() solution would just not cut it. So I made the Elevate package that exports the APIs I need to use. Behind the scenes, the Elevate package uses the Microsoft-approved method of elevation to execute a near-feature-complete CreateProcess() call from an elevated process (Elevate.exe).

In other words, this is not a magical library that you just install somewhere and then it works to bypass UAC. It requires specific code within the application to get it to work. And it still uses the Microsoft-recommended solution to invoke an elevated application (i.e. it invokes the UAC dialog through approved means).

Now what I meant by "a default setup" is that UAC is turned on by default but can be turned off by the Administrator of the system. However, those settings only affect the Administrator account - Limited Users would still have to use Administrator credentials to elevate a process. (Actually, the medium IL Administrator token, by default, basically has the equivalent privileges of a Limited User).

With the latest version of the Elevate package, I introduce UAC Permanent Links. These are a fairly complex beast to describe and are probably what confused you. They can be used to only show the UAC dialog one time per application run. They still follow the Microsoft-approved approach to using UAC. Again, this depends on the application utilizing either the Elevate package or a similar approach within their code.

The best way to see how UAC works in conjunction with the Elevate package is to use Vista and try out the demo application at the top of the article.

If you are wanting to bypass UAC altogether, you probably need to look at NT Services - those processes run at the highest IL the OS offers. Installing a NT Service requires Administrator privileges. However, having user code bypass UAC (e.g. via a NT Service) to start processes at ILs above medium is not the Microsoft-approved approach to proper Windows application design.

The Elevate package has gone through a drastic re-write. I've created what I like to call "UAC Permanent Links". One of the major gripes about UAC is the fact that every elevated operation has to go through the elevation dialog. UAC Permanent Links are the answer to that - the non-elevated application only needs to display the dialog one time and then a Permanent Link is established. A side-effect of this approach is a much simpler DLL.

im building a windows application, i have a frame inside my window i want 2run another application inside this frame (several old program i have done previously)my application is like a toolkit.So i want to run those applications(*.exe) inside this frame. The problem is When the application is on the computer (dont need 2specify the path) it work, but when i specify the path it run separately:

Sorry - I don't know C# (or any other .NET language) and this isn't a general help forum. If it were UAC related, I might have some suggestions but I don't know what is going on and have never seen anyone attempt to run external executables inside frame windows - sounds pretty hacky to me. Try asking your question in the C# forum here on CodeProject.

First of all, what an awesome in-depth explanation of elevation! Great work Thomas!

Going back to the topic, is this possible? I have this Outlook COM Add-in client that can un-install itself within Outlook and needs to remove HKLM hive entries. Since Outlook is running in a non-elevated mode which renders the COM client to run in that mode as well, these hive entries doesn't get removed.

At the moment, the only way I can think of removing these hive entries is to create a separate un-install program. Are there any workaround that you can suggest to do the un-install by the Add-in itself?

Doesn't regsvr32 require elevation to properly register/unregister COM objects from the CLSID section of the HKCR\classes hive? The UnregisterServer function seems like it would have to run in an elevated environment. Even if regsvr32 isn't doing elevation, you should be able to force it to do so with the "runas" verb. Seems like an ideal spot to do uninstallation sorts of things. I haven't touched COM stuff in a while. I tend to avoid it - procrastination definitely plays a part in that.

I recently ran into the UAC problem myself - when I prepared my code to run on Windows 2008. In this I have a special process, which needs to execute another process under another dedicated system account. Up until now I have used CreateProcessWithLogon to execute this process - but on Win2k8 I keep getting "The requested operation requires elevation".

Ok - I therefore added a manifest file to the EXE - with "requireAdministrator" applied - however this didn't solve the problem.

I therefore turned to your Elevate DLL - however it also result in "The requested operation requires elevation" - even though the credentials are correct. The UAC prompts correctly regarding the "Elevate.exe" process - but 'CreateProcessWithLogonElevatedW' then returns "The requested operation requires elevation".

My small sample is just a win32 console program - which tries to execute my dedicated EXE under another user account. Running the console program with "Run as Administrator" also result in this error.

I can add, that in addition to being member of the "Administrators" group in the domain - the dedicated user account is member of some other groups which a 'typical' administrator account isn't member of - why running the process as an administrator isn't sufficient in my case (and no, making the administrator account member of these groups also isn't an option).

I don't know if this is a general problem - or it has something to do with the updates made in Win2k8 / Vista SP1 (the core of Vista SP1 is apparently identical with the Win2k8 core).

One should be able to use the normal CreateProcessWithLogin to launch wscript with the above script as argument - and voila, the script is run under the desired user account - which in turn starts the desired EXE file.

I am now not sure about what I said before about your method working. Are you _SURE_ that the process is elevated under the new user and not the user with the Administrator token? That is, does Task Manager definitively show that the elevated process is running under the user you want it to run under? The script might run under the desired user but that doesn't necessarily guarantee the elevated EXE is running under the same user. Step 1 of the 7 stage elevation process doesn't indicate which logged in user's token it will ask LSA for.

And now that I'm awake and can think straight, I'm thinking your way of starting a process as another user may actually be correct. It could be that the security token for the user is being created at the Medium Integrity Level. You'll recall from the article that all objects have a default Integrity Level of Medium, which is why Elevate works. So the user token is created at the Medium IL and then the process attempts to create another process with that token at the High IL and can't because the user token is a Medium IL token, so it issues an "elevation" error because the token is a Medium IL. Why CreateProcess() works is because it just reuses the existing elevated token. So the workaround is to create another process as the user at the Medium IL, then do the elevation thing, which gets a High IL user token from LSA, and then starts the process using that token. So, that would result in a process under the target user.

I just updated the article. I'm pretty sure I've fixed the bug. The solution is very similar to the one you came up with. Elevate.exe is launched with an extra parameter ("link") as the target user. Elevate.exe then turns around and ShellExecuteEx()'s itself to elevate itself (minus the extra parameter). This deals with the medium IL issue and ends up with a correctly elevated process as the target user - a UAC Permanent Link is returned to the caller. It gets tricky to explain, so if you want to see the details, look at the available source code.

If you could confirm that the updated Elevate package works for your situation, that would be excellent.

So, from what I understand, Elevate.exe runs successfully but the function call CreateProcessWithLogonW() fails saying something about elevation*? Any chance I could take a look at your "small sample" (without the username, password, etc.)?

* It could be possible I messed up the code somewhere. I didn't test every last bit of functionality and I don't know exactly how CreateProcessWithLogon() is supposed to function as I've never had a need for it. Lame excuse and slightly embarrassing . I tried to make the function in Elevate operate as closely to the MSDN specification as possible. Feel free to look at the source and point out anything horribly wrong with it.

Keep in mind that there is one Integrity Level beyond "High" (Elevated processes) and that is the "System" (NT Services) Integrity Level. Based solely on your other message that, you had success with an external process running under a different user, it might be the case that High Integrity Level processes cannot create transition processes between users without using a System Integrity Level transition mechanism BUT, and this is where it gets weird, a Medium Integrity Level _can_ switch. It could be a security precaution but I can't think of a good reason off the top of my head why it would be that. It is more likely a legacy application issue. Imagine what would happen to Microsoft's phone lines if CreateProcessWithLogon() returned "needs elevation" under both non-elevated and elevated processes?

What do you think - does that make sense? If so, I've got my work cut out for me to correctly handle several of the functions in Elevate. But it has been about 8 months since I released Elevate that someone has used that function. Perhaps people don't need it or have found some other way around it (perhaps using something similar to your script that worked).

I was planning on working on the next version of my forum software package, but this seems far more interesting and I seriously need a break from PHP. If I wrote one line of code in PHP this weekend, I'd probably hurl. Okay, I've convinced myself.

I have a frondose question. I had been made a install program.
The public file is only one file that is can execute self unzip pack, the pack's head is my self writed program that preside over release all install file, I attach a manifest file in it's resource, so the head can run by administrator. In those instll file, have a program is 'setup.exe',I attach a manifest file in it's resource also.

when the pack run, it release all file in current user's(the user is in admin group in local machine) temp path. late, the pack call 'CreateProcess()' to run 'setup.exe' for wait setup end.

but when the setup.exe start, The Vista show a message box,The box display message is:"5". I don't know why in this way, how to solve the problem?

And if the pack hide self main window before run 'setup.exe' , and then the 'setup.exe' can not was showed(very oddness!), and then The Vista Kill the setup.exe's process and the pack's process(very very oddness!!!)

Hello,
gr8!!! You did the gr8 think.
Please help . I am working on MFC Dialog based application. I need to give UAC support to my application. For that i have created the manifest file but when i add this file in project and run the application then my Xp machine restarted. Please tell me the way to embedded this manifest file with my mfc dialog base application. my manifest file is<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0"
processorArchitecture="X86"
name="VistaProblem"
type="win32"/>

Read your article and used "run as" in installer and added manifests to a application executables that I am working on. Am able to get the elevation prompt on launch of the application. I am facing some problems though:

1) The application (win32) I am working on uses drag and drop. It a legacy application and I still use an old IDE for its development. The problem is that elevation has worked but drag and drop does not work any more. I read that since explorer and the desktop work at a lower priviledge level (medium) my application cannot receive any messages related to drag and drop. How do I resolve this? I have tried adding drag and drop messages to the application message filter using an API Changewindowmessagefilter(). This caused the application to crash.

2) I require to map a drive for my application to use. If I use Explorer to map the drive then my application does not recieve any messages regarding the added network drive. If I map using my application then only does my application becomes aware of the network drive.

3) All folders in a drive are set to read only so I cant send files to these drives as I get write protection status on checking the drive before copying.

According to that article, what you want to do is impossible. I'm thinking it is still possible to do what you want. However, while I remember very little about drag-and-drop, I do seem to recall it involved clipboard formats and targets and sources and what-not. Quickly looking over a tutorial is bringing back memories.

I can fully understand why you can't cross the elevation barrier via normal means. However, there are actually a couple options worth exploring:

1) It might be possible if there is a window in a non-elevated process that can be dragged to. Then, from the non-elevated process, you pass the data to the elevated process via a named pipe server/client pair (similar to how Elevate works).

2) Don't run your process elevated except for those moments and specific functions that require elevation. You may or may not be able to do this, but this is the way Microsoft intended apps. to run under Vista. When you need to run something elevated, start up a new, separate process, perform the task, and then exit.

As to the other problems, that's going to be somewhat complicated. I recommend running a non-elevated process to perform the drive mapping. Your elevated app. should communicate with the non-elevated process and vice versa via a named pipe server/client pair. As you already know, Explorer broadcasts messages - you can capture those messages in the non-elevated process and then pass them on to the elevated process, which could be friendly to any other elevated apps. and do its own broadcast.

The uiAccess route. Something I read today indirectly hinted that drag-and-drop might be successful with uiAccess. The uiAccess approach, as per the article, requires your executable to be code-signed. Note that this might not work, but it is worth looking into to avoid doing a giant hacky workaround.

Would I be right in thinking that RegCreateKeyExElevated() allows an unelevated process to create a registry key in a (transient) elevated process? Or at least in some way creates a registry key while temporarily running with elevated permissions? And if so, is the resulting HKEY available to the original, unelevated process?

If so, this is very cool. And if so, would it be possible to add some elevated file I/O API's to your package (CreateFile, DeleteFile, ...). If this were done, it would be possible to write an installer that could run entirely asInvoker, which means in turn that you could launch your app at the end.

I am flabbergasted that M$ did not forsee that programs might want to unelevate themselves. They need to wake up, the dozy so-and-so's.

Actually, as per the source code, it creates a "fake" HKEY and is fairly limited. I had to do that to accommodate the ShellExecuteExElevated() functions. When the lpExecInfo->fMask parameter is SEE_MASK_CLASSKEY and hkeyClass contains a valid HKEY, something happens. I'm feeling slightly lazy right now to look up what exactly happens. To make it easy to pass that information along to the target process' ShellExecuteEx() call, I made fake functions.

You know what though? That idea is really cool and is, in theory, entirely doable. I will definitely give it some thought.

Great article. You really earned your spurs there. It's a total mess, isn't it (UAC I mean, not your article).

The issue of not being able to 'unelevate' your process once you have elevated it has been occupying my mind as well. When I get time, I am considering modifying my installer to run asInvoker and then executing an elevated app from within it to do the grunt work. Then, when installation is complete, the unelevated 'installer shim' can launch the installed application. I don't know of a better way to do it. Injecting remote code into Explorer.exe is not an attractive option.

Also, you can get Comodo code signing certificates from Tucows for $75 per year (click on the 'Submit Software' link at the bottom of their home page). I think you have to set up a developer account, but that is free.

And, you can prevent an HKLM registry key from being virtualised by adding an ACL to it granting access to the 'Users' group (DOMAIN_ALIAS_RID_USERS). It's hard work but it can be done. This trick probably works for folders in Program Files as well. Just refrain from doing this to anybody's keys / folders except your own

Thank paul,I have a frondose question. I had been made a install program.
The public file is only one file that is can execute self unzip pack, the pack's head is my self writed program that preside over release all install file, I attach a manifest file in it's resource, so the head can run by administrator. In those instll file, have a program is 'setup.exe',I attach a manifest file in it's resource also.

when the pack run, it release all file in current user's(the user is in admin group in local machine) temp path. late, the pack call 'CreateProcess()' to run 'setup.exe' for wait setup end.

but when the setup.exe start, The Vista show a message box,The box display message is:"5". I don't know why in this way, how to solve the problem?

And if the pack hide self main window before run 'setup.exe' , and then the 'setup.exe' can not was showed(very oddness!), and then The Vista Kill the setup.exe's process and the pack's process(very very oddness!!!)

I can't answer questions on the elevation DLL I'm afraid as I'm not the author, but Thomas might still be monitoring this thread so keep your fingers crossed.

One thing you might try though is to use ShellExecute instead of CreateProcess, as only ShellExecute will elevate a process that requires it. And error 5 means 'Access Denied', if that helps at all. Maybe you are trying to unzip a file into a folder to which you don't have write access.

Incidentally people, I now run my installer program from a little 'shim' program which runs asInvoker and then run an elevated process from there to do the actual installation. This allows me to launch my app at the end (from the shim) without running it elevated. Maybe a simple diagram will help:

Your guess about what is wrong seems pretty logical. When confusing/weird stuff happens involving the file system or registry, I usually turn to Microsoft/SysInternals tools such as Process Monitor. That is always a good starting point for tracking down the cause of a problem.

Thanks for the compliment and the useful tips. Might use a couple of them.

As to the "installer shim", why not just use an extra command-line option that indicates to the installer that it is running elevated? Then your installer executable can loop back on itself and the one instance can wait on the other instance to complete. If you don't have to do anything fancy that goes beyond ShellExecuteEx(), this might be an option. But the code could get ugly...

Great article. Very interesting.
I am trying to the source code coming with the article.
I am using visual studio 2005.
I do not see how to what is "CoreLib.h".
Would it be possible to add this header file to the source code you provided ?
Thank you very much.
Best regards.

Read the article section on the source code. I specifically stated that you can't compile it and its purpose is to just follow along with the article. I've only included the application layer. The library layer (the part that is not included) is code on which my livelihood depends.

Even without the library layer, you should be able to follow along with the article. Just envision what the code behind the scenes looks like. Consider this a good exercise for building up a useful skill: Being able to look at any given piece of software from a black box perspective and guessing how the author(s) did it.

We've been working on a signed Java applet that downloads an EXE that needs to be run as the user (not admin). The IE dialog asking for permission to go up to medium integrity is not displayed when our app is in the low integrity temp directory. It silently executes the app at low integrity (ignoring our manifest asking for highestAvailable). IE only displays the elevation prompt when we execute apps from other directories. Oddly, IE also does not seem to display the elevation-to-medium prompt for limited users regardless of the app's location. I'm hoping that in your experiments you've dealt with the lack of docs on this type of elevation. Any help appreciated! (Oh, and thanks for the awesome article!)

First, let me bow my head in utter shame and say that I have not done any Protected Mode IE programming. Being a signed Java applet, you would think that the JVM would automatically handle the scenario. Java probably complicates things. However, I would make sure you are running the absolute latest JVM - I've noticed Sun has done several updates over the past few months...could be some IE7-specific changes in there. But don't leave! I'm not done yet.

Basically, from what I gather, it requires a "broker process" to elevate. But you can also use a specific registry key to register your process with IE for automatic elevation - but don't forget to call IERefreshElevationPolicy().

I find it interesting that IE apparently _hooks_ APIs to provide "compatibility support" with older extensions (it is interesting because Microsoft has told us that hooking APIs is bad and yet they do it anyway). There are also apparently new APIs for IE7[^]. Something else to look at.

Keep in mind that this is all speculation and you've probably run into some/most of this already. I've not done any Protected Mode IE7 development. Also, you could also be experiencing a bug in IE7 (e.g. IE7 could be ignoring the manifest).

Thanks so much for all the help! I really think it is Microsoft, however, that should be bowing their collective heads in shame for this mess.

Some updates: I was incorrect about limited user accounts behaving differently than admin users - Vista was in a tizzy and started behaving again after a reboot (thanks heavens!). That said, there is more to the sad and long story...

Both IE7 and Vista have some undocumented behaviors when it comes to low integrity. For starters, our original problem: IE does not bother to ask for medium integrity if the EXE is in the low integrity temp (TempLow) folder, regardless of its manifest.

Workaround: run wscript.exe and have it run a jscript file (again in TempLow). Since wscript.exe is not in TempLow, IE graces us with its elevate-to-medium dialog. It is less than informative since it is asking the user if it is OK to run wscript.exe, but that's the best we can do. So, our jscript file is called at medium integrity and it can run our helper app, but...

This is when we encountered another undocumented behavior of Vista: our helper app still ran with low integrity! We copied it to another folder (out of TempLow), and then jscript runs it properly at medium integrity!

So, at last were are running at medium integrity! But WshShell.Run (in our jscript) invoked the "are you sure you want to execute that exe file?" dialog. We tried WshShell.Exec, but when jscript/wscript exited, it killed our app. Exec is for piped/redirected output, so this is not unreasonable.

To workaround that we had our jscript use WshShell.Exec("cmd.exe /c " + app). Now the jscript could wait for cmd to exit and then wscript could go away, leaving just our helper app running. At medium integrity.

There's a very funny (bad?) taste in my mouth after all this hacking, but at least it works. For now. I suspect Microsoft will blacklist (via Elevation Policy) the use of wscript/cscript soon enough and we'll be back to the drawing board. We'll be submitting bugs on the disregard of the manifest; the lack of a way to request a partial elevation more explicitly, like ShellExecute(...., "runas",...) except maybe "runasuser"; and the poor documentation on apps in TempLow being always forced to low integrity.

Hopefully this information will help other poor victims (er, developers) who have to deal with Vista's woefully incomplete security model.

Again, thanks for all your assistance. The links are all stored away in my bookmarks. I'll be looking for any new articles you post - your work is very impressive! Great job!