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!WinMainShimsAndIat!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 4020ac004020ac 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!WinMainDWM_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)
But this application is shimmed. If we look at what _imp_GetVersionExW is pointing to, it's not GetVersionEx. It's:
0:000> dds 0040202800402028 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.
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've been talking a lot about shims lately here, but it'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).