Tony Pulokas
Tony Pulokas

Reputation: 475

Windows API SysWOW redirection unexpected behavior

I have an old 32-bit installer program that installs some 32-bit dependency DLLs into the Windows system folder. I have discovered that it's failing to install some of the 32-bit DLLs on a 64-bit system because the SysWOW redirection is doing something that I don't understand.

The installer program has relied on Windows API function GetFileVersionInfo to indicate if the DLL is already present with a newer version number. However, I am now seeing a case where file MSVCR100.DLL is already present in the System32 folder, but not in the SysWOW64 folder. When GetFileVersionInfo is used to test C:\Windows\System32\MSVCR100.DLL, I expect it to redirect to C:\Windows\SysWOW64\MSVCR100.DLL. It seems that if the file doesn't exist in SysWOW64, then it looks in System32 as a fallback. Thus, the installer thinks MSVCR100.DLL is already there and fails to install it.

I created a C++ Win32 Console app to test this. The entirety of the code is:

int _tmain(int argc, _TCHAR* argv[])
{
    char sysDirName[64], sysWow64DirName[64];
    char fileName[256];

    strcpy_s(fileName, argv[1]);
    GetSystemDirectory((LPSTR)sysDirName, 256);
    GetSystemWow64Directory((LPSTR)sysWow64DirName, 256);

    test_file(sysDirName, fileName);
    test_file(sysWow64DirName, fileName);

    return 0;
}

void test_file(char *dir, char* fileName)
{
    char filePath[256];
    DWORD verInfoSize, tempDWORD;
    BOOL found;
    byte buff[8192];

    PathCombine((LPSTR)filePath, (LPSTR)dir, (LPSTR)fileName);
    verInfoSize = GetFileVersionInfoSize((LPSTR)filePath, &tempDWORD);
    found = GetFileVersionInfo((LPSTR)filePath, 0, verInfoSize, buff);
    if (found)
        printf("%s   --found\n", filePath);
    else
        printf("%s   --NOT found\n", filePath);
}

I tested it on 3 different 64-bit computers, including Win 10, Win 7, and I get the same results.

If MSVCR100.DLL is in SysWOW64 but not in System32, then my test shows redirection working as expected:

>testSysFile msvcr100.dll
C:\WINDOWS\system32\msvcr100.dll   --found
C:\WINDOWS\SysWOW64\msvcr100.dll   --found

If MSVCR100.DLL is in neither System32 nor SysWOW64, then the result is expected:

>testSysFile msvcr100.dll
C:\WINDOWS\system32\msvcr100.dll   --NOT found
C:\WINDOWS\SysWOW64\msvcr100.dll   --NOT found

If MSVCR100.DLL is in System32 but not SysWOW64, then the result shows something unexpected and unhelpful:

>testSysFile msvcr100.dll
C:\WINDOWS\system32\msvcr100.dll   --found
C:\WINDOWS\SysWOW64\msvcr100.dll   --NOT found

Web searching has shown me lots of information about the SysWOW redirection, but I couldn't find any documentation or discussion of this behavior. Is this really what I should expect? My tests are also showing that if I use API function GetSystemWow64Directory I can have a file path that doesn't rely on redirection. Would it be safe to just copy DLLs and register them at that path instead?

Upvotes: 2

Views: 549

Answers (1)

Anders
Anders

Reputation: 101656

GetFileVersionInfo* uses LoadLibraryEx to do its work by loading the file as a data file.

For some reason KERNELBASE!BasepLoadLibraryAsDataFile inside LoadLibraryW calls ntdll!RtlWow64EnableFsRedirectionEx to disable the redirection and if the requested file is inside "%WinDir%\System32" then it tries to load the file again, this time from the "real" system32 directory.

This is clearly by design and I can't think of a way around it that is not a giant hack. I assume they do this for compatibility reasons.

You can however detect it with something like this:

bool validFile = !(GetFileAttributes(filePath) & FILE_ATTRIBUTE_DIRECTORY);
bool falsePositive = gotversioninfo && !validFile;

Upvotes: 2

Related Questions