|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionWhen 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 PictureAh. 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 A few Google searches on UAC usually turns up someone's blog discussing how to use But if you are like me, Before I get to SHELLEXECUTEINFOA TempInfo = {0};
TempInfo.cbSize = sizeof(SHELLEXECUTEINFOA);
TempInfo.fMask = 0;
TempInfo.hwnd = NULL;
TempInfo.lpVerb = "runas";
TempInfo.lpFile = "C:\\TEMP\\UAC\\Test.exe";
TempInfo.lpParameters = "";
TempInfo.lpDirectory = "C:\\TEMP\\UAC\\";
TempInfo.nShow = SW_NORMAL;
::ShellExecuteExA(&TempInfo);
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: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
The Internet tells us that, for administrative privileges under Vista, modify the above file to be: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<v3:trustInfo xmlns:v3="urn:schemas-microsoft-com:asm.v3">
<v3:security>
<v3:requestedPrivileges>
<v3:requestedExecutionLevel level="requireAdministrator" />
</v3:requestedPrivileges>
</v3:security>
</v3:trustInfo>
</assembly>
<BZZZZZZZT!> 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 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="requireAdministrator"
uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
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: HRESULT CreateElevatedComObject(HWND hwnd,
REFCLSID rclsid,
REFIID riid,
__out void ** ppv)
{
BIND_OPTS3 bo;
WCHAR wszCLSID[50];
WCHAR wszMonikerName[300];
StringFromGUID2(rclsid, wszCLSID, cntof(wszCLSID));
HRESULT hr = StringCchPrintf(wszMonikerName,
cntof(wszMonikerName)),
L"Elevation:Administrator!new:%s",
wszCLSID);
if (FAILED(hr))
return hr;
memset(&bo, 0, sizeof(bo));
bo.cbStruct = sizeof(bo);
bo.hwnd = hwnd;
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
return CoGetObject(wszMonikerName, &bo, riid, ppv);
}
// cntof() is defined as:
#define cntof(a) (sizeof(a)/sizeof(a[0]))
I honestly have no idea what that code does. But apparently, it is magically delicious. I did spot the "Elevation:Administrator!new:" string in User Interface IntegrationIf 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. Fortunately, Microsoft makes it easy to plant the shield icon onto dialog buttons: GetDlgItem(IDC_SOMEBUTTONID)->SendMessage(BCM_SETSHIELD, 0, TRUE);
// OR use the new macro:
Button_SetElevationRequiredState(ButtonHWnd, fRequired);
Unfortunately, however, putting the shield icon anywhere else is a pain. You can call the new Vista-only function HICON ShieldIcon;
SHSTOCKICONINFO sii = {0};
sii.cbSize = sizeof(sii);
SHGetStockIconInfo(SIID_SHIELD, SHGFI_ICON | SHGFI_SMALLICON, &sii);
ShieldIcon = sii.hIcon;
However, unless you are designing a Vista-only application, you will want to do the whole Alternatively, UAC WeirdnessSo 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_REQUIREDThose are the basics of UAC, all in one concise location. For many people, Now, on to the Elevate package and
Regardless, if you are reading this, I can only assume you are desperate for a Link_Create()
Link_CreateAsUser()
Link_CreateWithLogon()
Link_CreateWithToken()
Link_Destroy()
Link_CreateProcessA()
Link_CreateProcessW()
Link_ShellExecuteExA()
Link_ShellExecuteExW()
Link_ShellExecuteA()
Link_ShellExecuteW()
Link_LoadLibraryA()
Link_LoadLibraryW()
Link_SendData()
Link_GetData()
Link_SendFinalize()
CreateProcessElevatedA()
CreateProcessElevatedW()
CreateProcessAsUserElevatedA()
CreateProcessAsUserElevatedW()
CreateProcessWithLogonElevatedW()
CreateProcessWithTokenElevatedW()
SH_RegCreateKeyExElevatedA()
SH_RegCreateKeyExElevatedW()
SH_RegOpenKeyExElevatedA()
SH_RegOpenKeyExElevatedW()
SH_RegCloseKeyElevated()
ShellExecuteElevatedA()
ShellExecuteElevatedW()
ShellExecuteExElevatedA()
ShellExecuteExElevatedW()
IsUserAnAdmin()
You will note that the elevated APIs have very similar names to their non-elevated counterparts (e.g., Moving along. You probably have existing code that looks something like this: Result = CreateProcess(...ParameterList...);
if (!Result) return FALSE;
...Do something with process like WaitForSingleObject()...
::CloseHandle()'s;
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 Result = CreateProcess(...ParameterList...); // Existing line of code.
if (!Result && GetLastError() == ERROR_ELEVATION_REQUIRED)
{
HMODULE LibHandle = LoadLibrary("Elevate.dll");
if (LibHandle != NULL)
{
DLL_CreateProcessElevated =
(typecast)GetProcAddress("CreateProcessElevatedA");
if (DLL_CreateProcessElevated)
{
...Custom handle changes here *...
Result = DLL_CreateProcessElevated(...ParameterList...);
...Custom handle connections here *...
}
FreeLibrary(LibHandle);
}
}
if (!Result) return FALSE;
// Continue as usual with existing code...
The "...ParameterList..." options are _nearly_ identical. The only time you have to change the parameter is when you use the Note that you must use ShellExecute...Elevated()?!Some of you are probably raising eyebrows as to the need for a The second reason is because someone may specifically need to run a Now, I will admit the need for the Demo ApplicationFor the lack of a better spot to put this, the demo application is an example of
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 GrittyBefore 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 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 shell32.dll!CShellExecute::ExecuteNormal() + 0x7a
shell32.dll!ShellExecuteNormal() + 0x33
shell32.dll!_ShellExecuteExW@4() + 0x42
shell32.dll!_ShellExecuteExA@4() + 0x4a
ShellExecuteTest.exe!main() Line 18 + 0xc
ShellExecuteTest.exe!mainCRTStartup() Line 259 + 0x19
kernel32.dll!@BaseThreadInitThunk@12() + 0x12
ntdll.dll!__RtlUserThreadStart@8() + 0x27
Looks promising, right? Well, under a normal function, yeah, that's probably about where you would execute a command. In this case, however, shell32.dll!CExecuteApplication::Execute() + 0x22
shell32.dll!CExecuteAssociation::_DoCommand() + 0x5b
shell32.dll!CExecuteAssociation::_TryApplication() + 0x32
shell32.dll!CExecuteAssociation::Execute() + 0x30
shell32.dll!CShellExecute::_ExecuteAssoc() + 0x82
shell32.dll!CShellExecute::_DoExecute() + 0x4c
shell32.dll!CShellExecute::s_ExecuteThreadProc() + 0x25
shlwapi.dll!WrapperThreadProc() + 0x98
kernel32.dll!@BaseThreadInitThunk@12() + 0x12
ntdll.dll!__RtlUserThreadStart@8() + 0x27
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 I backed out of shell32.dll!AicpMsgWaitForCompletion() + 0x36
shell32.dll!AicpAsyncFinishCall() + 0x2c
shell32.dll!AicLaunchAdminProcess() + 0x2ee
shell32.dll!_SHCreateProcess() + 0x59d0
shell32.dll!CExecuteApplication::_CreateProcess() + 0xac
shell32.dll!CExecuteApplication::_TryCreateProcess() + 0x2e
shell32.dll!CExecuteApplication::_DoApplication() + 0x3c
shell32.dll!CExecuteApplication::Execute() + 0x33
shell32.dll!CExecuteAssociation::_DoCommand() + 0x5b
shell32.dll!CExecuteAssociation::_TryApplication() + 0x32
shell32.dll!CExecuteAssociation::Execute() + 0x30
shell32.dll!CShellExecute::_ExecuteAssoc() + 0x82
shell32.dll!CShellExecute::_DoExecute() + 0x4c
shell32.dll!CShellExecute::s_ExecuteThreadProc() + 0x25
shlwapi.dll!WrapperThreadProc() + 0x98
kernel32.dll!@BaseThreadInitThunk@12() + 0x12
ntdll.dll!__RtlUserThreadStart@8() + 0x27
Gotta love the names given to the functions. It sort of eggs you on to see what's next. At any rate, the function of interest is 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 LevelsPart 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.exeAppInfo is, obviously, the key to UAC elevation. 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:
Once AppInfo succeeds in launching the process, it transfers some information back over the RPC interface to the application that called CreateProcessElevated() Without ShellExecuteEx()?Once I had learned about roughly where in 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 mov edi, [ebp+VarStartupInfo]
mov eax, [edi+_STARTUPINFOW.lpTitle]
mov [ebp+VarStartupInfo_Title], eax
mov eax, [edi+_STARTUPINFOW.dwX]
mov [ebp+VarStartupInfo_X], eax
mov eax, [edi+_STARTUPINFOW.dwY]
mov [ebp+VarStartupInfo_Y], eax
mov eax, [edi+_STARTUPINFOW.dwXSize]
mov [ebp+VarStartupInfo_XSize], eax
mov eax, [edi+_STARTUPINFOW.dwYSize]
mov [ebp+VarStartupInfo_YSize], eax
mov eax, [edi+_STARTUPINFOW.dwXCountChars]
mov [ebp+VarStartupInfo_XCountChars], eax
mov eax, [edi+_STARTUPINFOW.dwYCountChars]
mov [ebp+VarStartupInfo_YCountChars], eax
mov eax, [edi+_STARTUPINFOW.dwFillAttribute]
mov [ebp+VarStartupInfo_FillAttr], eax
mov eax, [edi+_STARTUPINFOW.dwFlags]
mov [ebp+VarStartupInfo_Flags], eax
mov cx, [edi+_STARTUPINFOW.wShowWindow]
mov [ebp+VarStartupInfo_ShowWindow], cx
...
push eax
push ebx
push 0FFFFFFFFh
push [ebp+hwnd]
lea eax, [ebp+VarStartupInfo_Title]
push eax
push [ebp+hMemToWinSta0_Desktop]
push [ebp+VarExpandedCurrDir]
push [ebp+ArgCreationFlags]
push [ebp+arg_8] ; Probably bInheritHandles
push [ebp+VarExpandedCommandLine]
push [ebp+VarExpandedApplicationName]
push StaticBindingHandle
lea eax, [ebp+pAsync]
push eax
call _RAiLaunchAdminProcess@52
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 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 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 PipesBut 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 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 The solution lies in named pipes. Named pipes have, um, names. Sometimes ANSI, sometimes Unicode, but those names are strings. And, unlike The MSDN Library confirms that starting a process using named pipes for redirection is possible. How Elevate WorksLet's assume 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 After that, the correct function is executed with correct parameters for everything. The only thing interesting to note is the use of the 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. Elevate.exe resumes the process (if applicable), and exits. Elevate.dll returns, and the caller resumes normal operations. UAC Permanent LinksThe Elevation API DLL (Elevate.dll) exports the following functions: Link_Create()
Link_CreateAsUser()
Link_CreateWithLogon()
Link_CreateWithToken()
Link_Destroy()
Link_CreateProcessA()
Link_CreateProcessW()
Link_ShellExecuteExA()
Link_ShellExecuteExW()
Link_ShellExecuteA()
Link_ShellExecuteW()
Link_LoadLibraryA()
Link_LoadLibraryW()
Link_SendData()
Link_GetData()
Link_SendFinalize()
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.
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.
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 CodeFor 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 IssuesAs stated earlier, an associated manifest file with an executable can have a I said earlier that I would discuss the ' 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). According to the comments, Tucows.com offers Comodo Code Signing certificates for $75/year. Very nice. 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 1) Create a trusted root certificate:
Browse to the folder that you wish to contain a copy of the certificate.
In the command shell, execute the following commands:
makecert -r -pe -n "CN=Test Certificate" -ss PrivateCertStore testcert.cer
certmgr.exe -add testcert.cer -s -r localMachine root
2) Sign your file:
SignTool sign /v /s PrivateCertStore /n "Test Certificate"
/t http://timestamp.verisign.com/scripts/timestamp.dll
YourApp.exe
Where YourApp.exe is your application.
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 [HKLM or HKCU]\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer EnableShellExecuteHooks=1 (DWORD) An alternative to writing a Another approach is to do a combination of the two. Write an Additional References
History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||