The Import Address Table is now write-protected, and what that means for rogue patching

Raymond Chen

For a few years now, the Import Address Table (IAT) has been write-protected.

The import address table is the part of the Windows module (executable or dynamic link library) which records the addresses of functions imported from other DLLs. For example, if your program calls Get­System­Info(), then the executable or DLL will have an entry in its import table that says, “I would like to be able to call the function Get­System­Info() from kernel32.dll.” When the module is loaded, the system goes and finds that function, obtains its address, and stores it in a table known as the Import Address Table (IAT). When the module needs to call the Get­System­Info() function, it does so by fetching the value from the Import Address Table and calling it.

The Import Address Table is therefore a table filled with function pointers. This makes it an attractive target for attackers looking to achieve remote code injection, since they can overwrite the entry in the Import Address Table (using a write-what-where vulnerability) and redirect a function call to a location of their choosing.

As a defense in depth measure, the Import Address Table is now write-protected. Once the loader has obtained all the function pointers, it write-protects the table to make it harder for an attacker to overwrite it.

This write protection isn’t permanent, however. For delay-loaded imports, the Import Address Table holds a pointer to a stub function, so that the first time the module tries to call the imported function, the call is sent to the stub. That stub function looks up the real function and then updates the Import Address Table entry to point to the real function. To perform this update, the delay-load library temporarily makes the Import Address Table read-write, updates the function pointers, and then restores it to read-only status. So there are still small windows of opportunity in which the Import Address Table is unprotected, but the hope is that these windows are quite small and provide enough of an obstacle that attackers won’t consider it a fruitful avenue of attack.

This security mitigation was in place in internal builds, and everything looked pretty good. Then the changes rolled out to the Windows Insider Program, and there were many reports of users not being able to sign into their account.

What’s going on?

The users that couldn’t sign in had installed Windows “enhancement tools” that inject themselves into Explorer and rewrite various parts of the operating system in order to implement their various enhancements. One of them accomplished its nefarious task by patching the Import Address Table in order to detour functions that Explorer used, so it could substitute its own alternative. And that enhancement tool assumed that the Import Address Table was permanently read-write.

What was happening is that users would sign in, Explorer would start up, and then the enhancement tool would inject itself into Explorer and try to patch the Import Address Table. This patch would run afoul of the fact that the Import Address Table is now read-only, so they would crash, taking Explorer down with it.

No Explorer means no desktop, no taskbar, no Start menu.

But we didn’t abandon this additional security measure to accommodate these nefarious programs. Users who have such enhancement tools generally understand that the programs are doing sketchy things. When these types of programs are discovered, we put them on the list of apps that are fundamentally incompatible, and what happens next depends on what kind of update introduces the incompatibility.

If the incompatibility is in a major update of Windows, then the program is placed in the compatibility report for that update, and when users attempt to upgrade, they will be warned of the incompatibility. If it’s a free program, then the upgrade installer advises the user that the program will be uninstalled as part of the upgrade. If it’s a paid program, then the upgrade installer will not force-uninstall the program and require users to do it themselves.

User who still miss the features of that enhancement software can go and reinstall it. And then Explorer will crash at logon. But this time, the blame is properly assigned to “the thing you installed most recently”, which is the enhancement software.

Basically, the upgrade process changed the order of operations. What used to be

Working system
Install Program X
Working system
Upgrade Windows
Broken system ⮭ blame this guy

is now

Working system
Install Program X
Working system
Uninstall Program X
Working system
Upgrade Windows
Working system
Install Program X
Broken system ⮭ blame this guy

On the other hand, things are not so easy if the incompatibility is with an update that comes as part of Patch Tuesday. The patch updater has no opportunity to ask the user questions or gain confirmation. That’s by design, because it would be bad to delay the install of a security update. Those things need to get installed as soon as practical. In this case, Windows also has to tweak itself so that the incompatible “system enhancement” program fails in a more benign way. The enhancement program doesn’t have to keep working, but it can’t crash the system either. This can be done by making some sort of harmless change that nevertheless throws off the enhancement program’s patch logic so that it gives up instead of applying a bad patch.

9 comments

Comments are closed. Login to edit/delete your existing comments

  • Márton Marczell 0

    How can the upgrade installer determine, about some unknown program, whether it’s free or paid?

    • Chris Iverson 0

      It can’t, and doesn’t.

      “When these types of programs are discovered, we put them on the list of apps that are fundamentally incompatible.”

      They find these programs in testing, and keep a list of them. That list can include whether or not it’s a free or paid program.

      “then the program is placed in the compatibility report for that update”

      The program isn’t flagged during the update. It’s flagged as part of the compatibility check BEFORE the upgrade. It’s flagged by checking the list they’ve built.

      If the program is on the list, and it’s a free program, it will be uninstalled. If it’s on the list, and it’s not a free program, the user will be advised of issues with the program, and recommend uninstallation.

      If the program isn’t on the list, it’s assumed to be good.

  • Dmitry 0

    What about those who are trying to gain style points by writing programs that are still compatible with Win9x by means of unicows.dll, but instead of diving into the world of LIB-files and their incompatibilities just perform runtime version checks and redirect Unicode-function imports (filled in with Win9x stubs by the loader in a normal way) to the supplementary DLL at program startup? Would some playing with page protection be sufficient? How long will it take before the next upgrade makes such temporal unprotection unavailable for the program itself and getting on the compatibility list becomes a paid premium feature for VIP vendors?

    • Daniel Roskams 0

      I am pretty sure you can just VirtualProtect the memory you want to access and then rewrite whatever you want. Generally speaking, you can change the page protections of any memory in your own process. At least that’s true on Windows 7; I couldn’t be bothered starting a VM to check whether that’s still true on 8/10/11. Probably not true for UWP apps.

    • Joshua Hudson 0

      You know, I was thinking something similar with an alternate delay load stub. Assuming the address table is writable due to a loader artifact this may need an application compatibility flag (like NX). However if it’s a change to the protect bits in the relocation section when compiling windows, no such thing is needed.

  • Dan Weiss 0

    I’ve used VirtualQuery to get prior permissions for the Import Tables, used VirtualProtect to make it writable, then used VirtualProtect to restore read-only again. Except this does not work reliably when Delay Loaders are running. When you restore the previous permissions, you cause an access violation when the delay loaders finish up.

    So I was wondering if there was any way to wait for delay loaders to finish, I can’t figure out which API call to use for that.

    • Henke37 0

      You can skip the initial query, the first call to VirtualProtect will return the previous permissions in an out param.

      As for knowing when the delay loaders are “finished”, you need to call every single delay loaded function to be sure.

    • Solomon Ucko 0

      I think the delay loaders internally use a reference count to determine how long the memory remains writeable.

  • GL 0

    I’d imagine a big press day when the major upgrade is released, and I can’t really blame the media this time — it is a fact that “Microsoft is uninstalling third-party programs when Windows is upgraded”, and possibly “who knows what conspiracy and what-not”!

Feedback usabilla icon