Understanding Shims

How shims work

Application experience and compatibility in Microsoft® Windows® operating systems is one of the fundamental pillars of its development, alongside performance, reliability, and manageability. To reduce deployment costs and accelerate adoption, Microsoft invests in deep technical solutions to ensure broad compatibility of existing software, driving compatibility into the engineering and release process.

The Microsoft Windows Application Compatibility Infrastructure (Shim Infrastructure) is one such powerful technical solution. As the Windows operating system evolves from version to version—changing to support new technology, incorporate bug fixes, and implement a modification in strategy—changes to the implementation of some functions may affect applications that depend on them. Because of the nature of software, modifying the function again to resolve this compatibility issue could break additional applications or require Windows to remain the same regardless of the improvement that the alternative implementation could offer. We can circumvent this possibility by placing branches directly in the source code for Windows, but doing so presents a long-term challenge for the serviceability and reliability of the Windows operating system. Using the Shim Infrastructure, however, you can target a specific application fix but only for a particular application (and typically, for particular versions of that application), with these fixes housed outside the core Windows functions and maintained separately.

The Shim Infrastructure implements a form of application programming interface (API) hooking. Specifically, it leverages the nature of linking to redirect API calls from Windows itself to alternative code—the shim itself. The Windows Portable Executable (PE) and Common Object File Format (COFF) Specification includes several headers, and the data directories in this header provide a layer of indirection between the application and the linked file. Calls to external binary files take place through the Import Address Table (IAT). Consequently, a call into Windows looks like Figure 1 to the system.

Figure 1   Application calling into Windows through the IAT

Specifically, you can modify the address of the Windows function resolved in the import table, and then replace it with a pointer to a function in the alternate shim code, as shown in Figure 2.

Figure 2   Application redirected to the shim prior to calling Windows

This indirection happens for statically linked .dll files when the application is loaded. You can also shim dynamically linked .dll files by hooking the GetProcAddress API.

Design implications for the shim infrastructure

You may find certain consequences of the Shim Infrastructure design relevant when determining your shimming strategy.

First, as shown in Figure 2. Application redirected to the shim prior to calling Windows, the code that runs inside a shim still sits outside Windows. Consequently, Windows holds shim code to the same security restrictions as the application code itself. In fact, to Windows, the shim code appears to be application code. As a result, you cannot use shims to bypass any security mechanisms present in Windows. For example, no shim is available to bypass the Windows 7 User Account Control (UAC) prompts while still running the application with elevated permissions. You can shim the application not to require administrator rights, or you can shim it to demand it, but in order to receive administrator rights with UAC enabled, the user will have to approve the elevation. The same is true for code that you write yourself.

Therefore, when evaluating the security implications of using shims in your enterprise, you are not opening any additional security vulnerability. In fact, using shims to avoid having to loosen security descriptors or make security policy more lax can frequently be the more secure choice. For example, without shims, you may be able to mitigate a compatibility issue by loosening the ACLs on a particular directory, but this decision has an effect on the entire system. Using shims, you may be able to redirect the file access to a per-user location for that application. As another example, an application may be explicitly checking for administrator rights. Without shims, you may have to grant the application administrator rights to pass this check. If the check is an unnecessary one, however, a shim could simply lie about whether the current user has administrator rights, allowing the check to succeed without exposing additional security surface area.

Second, because the Shim Infrastructure, in essence, injects additional code into the application before it calls into Windows, any mitigation you can use a shim to accomplish can be done by modifying the application code itself. At a minimum, the application could include code similar to what Windows implements in the shim immediately prior to calling into Windows APIs.

Finally, because shims run as user-mode code inside a user-mode application process, you cannot use a shim to fix kernel-mode code. For example, you cannot use shims to resolve compatibility issues with device drivers or with other kernel-mode code. (For example, some antivirus, firewall, and antispyware code runs in kernel mode.)