We had a customer in the lab recently and they were quite certain their application was Windows 7 ready. To their surprise, it had several issues in certain situations. In this post, I’ll talk about one of the situations they encountered as well as other stumbling blocks you might run into with UAC Virtualization.
For this particular application, the default install directory was a custom directory on the system drive (e.g. C:\MyApp). The application performed as expected in this scenario. However, Microsoft recommends that applications be installed in Program Files. This is secured by default to reduce elevation of privilege attacks and is a known location for enterprise inventory tools. Also, installing to program files is a client logo requirement.
We changed the install location to Program Files and the application eventually failed. Why?
Program Files is considered a “per machine” install location. It is protected by Mandatory Integrity Control and only allows users read and execute rights. Under UAC, processes will run as a limited standard user and are denied write access to Program Files. The application was designed to store various data files in the install directory. It also stored data in the registry under under the protected HKLM\Software key as well. Here’s where it starts to get confusing… Most of the files and registry keys appeared to be able to write but others were not.
There is a mitigation that is included in Vista and Windows 7 called UAC Virtualization. This mitigation is intended for this exact scenario -- legacy applications that write to protected per-machine locations. If a legacy (non-manifested) application tries to write to a protected directory such as Program Files or certain keys in the HKLM hive, the “access denied” error is captured and the write is redirected to the user’s UAC virtual store. This allows legacy applications that store state in protected directories and registry locations to continue to work in most cases.
So, why did it fail?
UAC virtualization’s goal is to virtualize data. It doesn’t virtualize various executable files (exe, bat, dll, etc.). It doesn’t virtualize files that the user is explicitly denied write access. This is what occurred for the application and why it failed.
The scenario of a few files not being virtualized is uncommon. Here are a few more common issues that can really make you scratch your head.
UAC Virtualization works by catching the access denied error writing to a “per machine” location and redirecting it to a “per user” location. This solves the problem of writing the data but this can change the behavior of your application. For example, if a game application is designed to store a high score file to Program Files, every user will get his/her own copy of the high score file. This isn’t the correct behavior for a high score file. There should be one file for all users.
If you right click an application shortcut and select “Run as Administrator”, this runs the application with the full admin token. The application now has write access to protected locations. Therefore, UAC virtualization is disabled and it will read and write from the “per machine” location instead of the virtualized “per user” location. Now, you have a situation where the application is using completely different files or may not work at all.
You may be saying “This is madness.” Remember, this is a mitigation that greatly improved the ability to have legacy applications that weren’t designed to run as standard user to continue to work. Without this mitigation, lots of existing applications that store state in “per machine” protected locations would fail with access denied errors.
Applications that target Vista or above should be designed to be run by standard users. This means that the application only writes data to “per user” locations in the file system or registry. Standard user was introduced in Windows XP. Vista introduced “standard user by default” with UAC. Running as standard user is here to stay but virtualization will eventually be removed in future OS releases.
Ideally, you want to design your application to only write to per user locations. Sometimes, that’s not possible or it doesn’t make sense. Here are a few suggestions on how you might change your application if it needs to store per machine data.
For files, you may want to write to the The programdata directory. It is intended for per machine configuration data. Check out Chris’ post on options for writing configuration data.
For the registry, you may want to create your own key HKLM\Software\MyCompany\MyApp and then set the appropriate permissions at install time. Read more about registry virtualization here.
Once you have your application ready to run as a standard user, you will want to disable the mitigation. This is done by adding an application manifest with the “trustInfo” section to your application. For more information on manifesting your application, look at my post on manifests.