Home > Software engineering >  How to securely delay load PathAllocCanonicalize
How to securely delay load PathAllocCanonicalize

Time:05-21

I have a Windows desktop C application that currently uses ::PathCanonicalizeW. As you can see from the documentation, it was introduced in Windows 2000 and is located in shlwapi.dll. In order to support long paths on Win 10 , I need to start using ::PathAllocCanonicalize (or one of it's friends - ::PathCchCanonicalize or ::PathCchCanonicalizeEx).

This function was added in Windows 8, but I still need to support the older OS's. In order to support all OS's, I need to delay load ::PathAllocCanonicalize by calling ::LoadLibrary. The problem is that the documentation doesn't provide the DLL that includes this function.

After doing some searching, I found this documentation that includes all 3 of the new PathCanonicalize functions and it claims that they are in api-ms-win-core-path-l1-1-0.dll. After more searching, it appears that this is not a traditional DLL because there is no file anywhere in the OS with that name. This application has always loaded system libraries using the full path to the file in the system directory (typically C:\Windows\system32) to make sure that it's not loading malicious DLLs, but for this it will be impossible without a physical file to point to.

I have been able to test that calling ::LoadLibrary("api-ms-win-core-path-l1-1-0.dll") does work, but the fact that that documentation mentions UWP worries me. Is there any documentation for the supported way to delay load these kinds of functions in a desktop app? Is there a more secure way to load this DLL?

P.S. This app cannot be deployed with that DLL, and even if it were possible there's no point since any OS that doesn't have that function wouldn't have full support for long paths anyway. Dropping support for the older OS's is also completely out of the question. The function must be delay loaded.

CodePudding user response:

As pointed out by Hans, api-ms-win-core-path-l1-1-0 is known as an API set along with many others starting with api-ms-win-core. Based on the documentation there, it appears that the documentation for PathAllocCanonicalize is incomplete. It should list the API set on that page along with the DLL for desktop apps. Looking at the source on GitHub, it looks like there is a bug with that page and the other pathcch functions where that information is in the header but not rendered onto the page. That header lists api-ms-win-core-path-l1-1-0.dll and KernelBase.dll.

If for some reason I wanted to continue to load the API set instead of KernelBase.dll, ::LoadLibraryExW(L"api-ms-win-core-path-l1-1-0.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32) worked which would be just as secure as specifying the full path to a DLL in the system32 folder.

CodePudding user response:

You can delay load anything by using LoadLibrary and GetProcAddress.

What you need to do is know the path to the DLL and load it. In pseudocode:

if Windows8OrGreater()
    if Windows10OrGreater()
        path = GetMyExecutablePath()   "\"   "api-ms-win-core-path-l1-1-0.dll"
    else
        path = GetSystem32Path()   "\"   "api-ms-win-core-path-l1-1-0.dll"
    handle = LoadLibrary(path)
    pathAllocCanonicalize = GetProcAddress(handle, "PathAllocCanonicalize") /* A or W? I can't tell from your question */
else
    path = GetSystem32Path()   "\"   "shlwapi.dll"
    handle = LoadLibrary(path)
    pathCanonicalize = GetProcAddress(handle, "PathCanonicalize")
    pathAllocCanonicalize = your function that provides pathAllocCanonicalize from pathCanonicalize

It is possible that you will have to abandon this idea and always use the synthetic version. If you are using W versions of APIs, you can bypass the path length limit by using the \\?\ prefix and providing a full path to the Win32 APIs.

  • Related