Weird export forwarding thanks to Vista x64 SP1

Rating: 3 votes, 2.33 average.
After installing the SP1 for Vista x64, I noticed that ImpREC stopped working properly on some files using DefWindowProcA and DefWindowProcW from user32.dll.
These 2 APIs are forwarded as usual respectively to NtdllDefWindowProc_A and NtdllDefWindowProc_W from ntdll.dll but cannot be "unforwarded" back to user32.dll using the traditional method.

I'll explain how the loader usually resolves forwarded exports, the unforwarding method used by ImpREC and why it fails on those 2 particular cases.

Using the information provided by the Import Directory of the executable, the loader looks for a matching name or ordinal in the specified dll.
After a match is found, the corresponding entry from the AddressOfFunctions array is retrieved from the Export Directory and augmented by the ImageBase of the dll.
That value is then written to the IAT.

Sometimes, for compatibility reasons, an import can be forwarded to another one from a different module.
In that case, the AddressOfFunctions entry does not lead to code but to an ASCII string composed of the module name and the import name or ordinal.
.text:7DCA751E ; LRESULT __stdcall DefWindowProcA(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
.text:7DCA751E DefWindowProcA  db 'NTDLL.NtdllDefWindowProc_A',0
.text:7DCA7539 ; Exported entry 151. DefWindowProcW
.text:7DCA7539                 public DefWindowProcW
.text:7DCA7539 ; LRESULT __stdcall DefWindowProcW(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
.text:7DCA7539 DefWindowProcW  db 'NTDLL.NtdllDefWindowProc_W',0
The loader then restarts the whole process from the beginning, using the info from the forwarding string rather than the original info from the Import Directory of the executable.
The Entry Point of the newly-found function is written to the IAT instead.

ImpREC finds the IAT and goes through it to identify all the imports that it contains.
Some entries belong to ntdll.dll and need to be "unforwarded" to their original location.
DefWindowProcA should be unforwarded from NtdllDefWindowProc_A.


If everything goes well, the original import is found by "bruteforcing" the Entry Point of every function until a forwarding string is found: NTDLL.DefWindowProc_A at the Entry Point of DefWindowProcA in user32.dll and then comes in some guesswork.

Because an import could have been forwarded from another module, doesn't mean that it really was, there are some false-positives.
The guesswork is based on a very crude probability analysis based on the module name of the previous import and the module name of the next import.
EndDialog from user32.dll is almost never forwarded from shlwapi32.dll.

Something changed with Vista x64 SP1 through some modifications of wow64.dll since the content of the \SysWOW64 directory should be the same as the standard Vista 32-bit SP1.
Some hotfix is applied only during run-time by WoW64.



During execution, the AddressOfFunctions entries of DefWindowProcA and DefWindowProcW from user32.dll are modified.
The RVA based at the ImageBase becomes greater than the SizeOfImage and leads into the memory area of ntdll.dll rather than the usual forwarding string.
Instead of 0x0001751E, the AddressOfFunctions becomes 0x015C3D42 or 0x01793D42 or 0x01B83D42 for DefWindowProcA.

The same result is achieved but this method prevents the unforwarding of some imports through the traditional method since there is no forwarding string anymore.
It leads into code now:
ntdll.dll:77C43D42 ntdll_NtdllDefWindowProc_A:
ntdll.dll:77C43D42 jmp     ds:off_77CB6020
ntdll.dll:77CB6020 off_77CB6020 dd offset loc_7669C0E7     ; DATA XREF: ntdll.dll:ntdll_NtdllDefWindowProc_Ar
And jumps back to user32.dll:
user32.dll:7669C0E7 loc_7669C0E7:                           ; DATA XREF: ntdll.dll:off_77CB6020o
user32.dll:7669C0E7 push    10h
user32.dll:7669C0E9 push    offset unk_7669C158
user32.dll:7669C0EE call    near ptr unk_766BC240
user32.dll:7669C0F3 call    near ptr unk_766980D7
Unforwarding is still possible anyway since a side-effect could be identified: the Entry Point of both imports become identical.
GetProcAddress(DefWindowProcA) == GetProcAddress(NtdllDefWindowProc_A)
GetProcAddress(DefWindowProcW) == GetProcAddress(NtdllDefWindowProc_W)

To sum everything up:
It doesn't really matter, the average user of Vista x64 SP1 would never notice the difference.
Unpacking in a 32-bit VM instead is still more reliable than under WoW64.

This will be a part of my proposed upcoming ReCon talk.

Submit "Weird export forwarding thanks to Vista x64 SP1" to Digg Submit "Weird export forwarding thanks to Vista x64 SP1" to del.icio.us Submit "Weird export forwarding thanks to Vista x64 SP1" to StumbleUpon Submit "Weird export forwarding thanks to Vista x64 SP1" to Google