The App Compat Guy

Chris Jackson's Semantic Consonance

Windows Vista Shim Internals Basics: How Shims Work to Address Compatibility Issues (and What are the Ramifications?)

Windows Vista Shim Internals Basics: How Shims Work to Address Compatibility Issues (and What are the Ramifications?)

Rate This
  • Comments 47

I want to talk a little bit today about shims, specifically addressing how they work to address compatibility issues, and what the security ramifications are when you use a shim to address a compatibility issue.

Merriam Webster defines a shim as:

"a thin often tapered piece of material (as wood, metal, or stone) used to fill in space between things (as for support, leveling, or adjustment of fit)"

(Wow, I feel like an 8th grader beginning a term paper, using a definition like that!)

But the name shim comes exactly from this definition - we jam a thin piece of code between things - specifically, between the application's code and Windows code.

This works specifically because we implement an Import Address Table (IAT) to link to DLLs - specifically, to Windows DLLs.

Rather than talk about this, I figured I would just show this to you in a debugger. I began with the following source code:

#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
    MessageBox(NULL, TEXT("Hello world."), TEXT("Hello"), MB_OK);
    return 0;
}

Nothing too fancy. In fact, it's pretty much identical to what you'll find at the beginning of Charles Petzold's Programming Windows book. Let's see how this looks in assembler:

0:000> uf ShimsAndIat!WinMain
ShimsAndIat!WinMain [c:\...\shimsandiat\shim.c @ 5]:
    5 00401000 6a00                push    0
    6 00401002 68f4204000      push    offset ShimsAndIat!`string' (004020f4)
    6 00401007 68fc204000      push    offset ShimsAndIat!`string' (004020fc)
    6 0040100c 6a00                push    0
    6 0040100e ff15ac204000   call    dword ptr [ShimsAndIat!_imp__MessageBoxA (004020ac)]
    7 00401014 33c0                xor     eax,eax
    8 00401016 c21000             ret     10h

When we call into Windows to invoke the MessageBoxA function, we don't jump there directly. We jump to a local symbol: _imp__MessageBoxA. If we then take a look at what we find at this address, we'll find our pointer to the function of interest:

0:000> dds 4020ac
004020ac  76ff56df USER32!MessageBoxA

Because we are jumping to the location pointed to by this pointer, we can see how easy it is to inject some new code: we simply need to replace the contents of the pointer (at 0x004020ac) with a new value - one that points to our shim code! The application we are trying to fix thinks it is calling in to Windows, but the shim code runs instead, and this shim code may call into Windows.

Let's take a look at a shimmed up application.

In this application, we are making a call to GetVersionEx. The application has a bug - it is checking for equality of the version of Windows to 5.1 (Windows XP). Here is the portion of the unassembled function doing this comparison:

0:000> uf DWM_Compositing_Rendering_Demo!WinMain
DWM_Compositing_Rendering_Demo!WinMain:

... (disassembly elided for clarity)


   10 0040103d ff1528204000    call    dword ptr [DWM_Compositing_Rendering_Demo!_imp__GetVersionExW (00402028)]
   11 00401043 837c240805       cmp     dword ptr [esp+8],5
   11 00401048 753e                 jne     DWM_Compositing_Rendering_Demo!WinMain+0x88 (00401088) 
   11 0040104a 837c240c01      cmp     dword ptr [esp+0Ch],1
   11 0040104f 7537                 jne     DWM_Compositing_Rendering_Demo!WinMain+0x88 (00401088)

... (disassembly elided for clarity)

But this application is shimmed. If we look at what _imp_GetVersionExW is pointing to, it's not GetVersionEx. It's:

0:000> dds 00402028
00402028  6cd54c66 AcLayers!NS_WinXPSP2VersionLie::APIHook_GetVersionExW

Yep - it's been redirected to a version lie, which causes this check to succeed. (If only every developer's keyboard included a > symbol...)

The shim itself doesn't need to call into Windows, but most do. They have to in many cases to do anything interesting. Because, as you can see, from the perspective of Windows, shim code is no different than application code. It's just arbitrary code that happens to be calling in to Windows to do some work for it. It can't circumvent any protections, it can't do anything less securely - shim code can only do what your application's code could do. No more, no less.

That's why there are no shims for "don't give me a UAC prompt for this application" - because your application can't do it either.

It also means that anything we can fix using a shim you could fix using code. After all, we did it with some code sitting between your application and Windows, you could just write similar code directly in your application and not have to use a shim at all.

  • Its interesting and informative. just looking into what shim is...

  • In an earlier post , I began having a conversation around how shims work. I wanted to supplement some

  • Short answers.

  • Has it been your experience that applications for which shims are provided tend not to be updated by the vendor?

    Presumably the compatibility team does report these issues to the original vendor if they are still around and contactable, but I wonder how many vendors simply think "Well, they've fixed it for us so we don't need to do anything!" thus requiring these shims to stay present for longer than would otherwise be necessary.

  • Short answers.

  • When we shim something, we shim it either for a specific version or up to and including a specific version. So, a vendor that has a compat issue isn't going to get the shim on the next version unless they don't update the version number.

    Existing copies of older versions don't go away, so we want to keep shimming those. New versions won't get the shim any longer, so this works out pretty well. Of course, we can't force anybody to release a new version of something unless they have new features and value they want to add to justify the cost of developing a new version.

  • Is there any kind of way to find out if a specific app has any shims applied to it (either on XP or on Vista)?

    And if so, what those shims are doing?

  • Using Compatibility Administrator from the Application Compatibility Toolkit, you can view all of the applications we shim in the system shim database. Just expand the Applications node in the tree on the left.

    You can also use the search functionality, point it at the directory containing the binary you are curious about, and it will determine which of the .exes will pick up which shims.

    As for what they shims are doing - it depends on the shim. In general, they're keeping the application working...

  • I was reading the 'Basics on how shims work', and all I could think about was the old linux/unix trick of using LD_PRELOAD to intercept library calls in dynamically linked applications (used to great effect to bypass timebombed applications). It...

  • How do we patch managed code like C#? Many of the .net framework BCL calls ultimately uses Win32 calls, if we need to patch such calls, can we apply shim to .net framework DLLs? Those are strongly named /signed.

  • Yes, you can shim managed code - we grab it at the Win32 API level. But you apply it to exes, not to DLLs. There is no requirement for them to be strong named or signed.

  • I've been talking a lot about shims lately here, but it's been rather lopsidedly technical. Using shims

  • I&#39;ve been talking a lot about shims lately here, but it&#39;s been rather lopsidedly technical. Using

  • Hi,

    Is it possible to check what type of shim is being applied programatically?

  • ananda84,

    I'm not sure I understand your intent. If you want to see which shims will be applied, Compatibility Administrator's query tool will help you. Point it at a directory, and we'll tell you what's being shimmed.

    If you want to know what shims are applied to the program you are actually writing, my advice would be not to do that. We're shimming you up for a reason - if you try to foil us, we have to be sneakier. (See: ShimViaEAT.) But, for copy protection and cheater-protection (games), you can monitor the Import Address Table for changes. We see that quite a bit, typically in programs that are also actively avoiding debuggers (because our job is apparently too easy otherwise).

Page 1 of 4 (47 items) 1234