In this post I am sharing a case that I investigated recently, during the tests of my application, PE-sieve. It demonstrates how the shims applied by the operating system can disrupt Imports recovery.
Recently I had a new release of PE-sieve. One of the added features was a complete Import Table recovery. From now, if you run the PE-sieve with an option /imp 3
it will collect all the addresses that the scanned module imported from the DLLs in the memory, and construct a new Import Table out of them. It is a very useful feature that many PE dumpers have. It helps i.e. to deal with packed applications. Let’s take an example of UPX: it may compress the Import Table of the payload, and load it dynamically during unpacking.
PE-sieve offers also another, “milder” mode of the recovery (/imp 2
). In this case PE-sieve bases on the existing import table, and only reconstruct the erased elements. It can be used i.e. in the following case, when the Thunks were overwritten by the functions addresses during the imports loading:
PE-sieve is able to recognize the exports that are at those addresses, and fills their names back into the table:
I decided to test my application on some ready-made and well-known examples. I selected Anthracene’s unpacking series, available here.
The first sample (1dbfd12ad3ee39930578b949c6899d0a) looks pretty straight-forward. It is a simple application showing a MessageBox, packed with the help of UPX.
I run this example on one of my Virtual Machines (Windows 8 64 bit) and the imports got recovered flawlessly (video).
Later, I tried to do the same on a different machine, with another set of patches installed. For some reason, I could not reproduce the valid results. Import recovery “magically” stopped working – the received results were incomplete. When I tried to dump the payload with PE-sieve, using the option /imp 2
, all the functions imported from Kernel32.dll got recovered, but the function from User32: MessageBoxA did not.
First I assumed that it must be a bug in PE-sieve, but it turned out that other applications i.e. Scylla have the same problem: they cannot map this address to any of the exported functions.
I investigated the mentioned address under the debugger, and this is what I saw. The call to a MessageBoxA was proxied via apphelp:
The function used from the apphelp was not present in the export table, so it is logical that the applications recovering imports could not recognize it. So, it is not really a bug in the application – but a problem caused by some peculiar way in which this import was loaded.
Of course I wanted to understand what is the reason of such behavior. I suspected that there must be some shimming going on, but why did it happen? I checked other applications importing MessageBoxA, but each of them used this function directly from User32.dll, and apphelp.dll was not used as a proxy.
I started thinking that it may be related with the fact that my test case is a very old application.
The OS Version in the PE header is set to 4 (Win 95):
I made an experiment and”upgraded it”, just by changing the version number:
And it worked! After this simple change, the shim was no longer applied. The application used the import directly, and, as a result PE-sieve (and other applications) were able to properly recognize the function.
So, it turned out that the operating system applies this shim automatically for the backward compatibility with old applications.
Now when I think of it, it looks pretty obvious, but it was not so intuitive when I saw it for the first time, that’s why I decided to document this case. So, just a small heads-up: when the import recovery is not going right, first check if shims are not the reason! I hope you enjoyed my small writeup.