1

When loading shared library given its name, systems searches for the actual file (eg .dll) in some directories, based on search order, or in cache.

How can I programmatically get the resolved path of DLL given its name, but without actually loading it? E.g. on Windows, for kernel32 or kernel32.dll it would probably return C:\windows\system32\kernel32.dll whereas given foo it could be C:\Program Files\my\app\foo.dll.

If that can't be done, is there another way to determinate whether certain library belongs to system? E.g. user32.dll or libc.so.6 are system libraries but avcodec-55.dll or myhelperslib.so are not.

I'm interested solutions that work on Windows, Linux and Mac OS.

mcpiroman
  • 175
  • 2
  • 11

1 Answers1

5

On Windows, LoadLibraryEx has the LOAD_LIBRARY_AS_DATAFILE flag which opens the DLL without performing the operations you refer to as "actually loading it".

This can be combined with any of the search order flags (Yeah, there is more than just one search order).

Unfortunately, you cannot use GetModuleFilename. Use GetMappedFileName instead.

The LoadLibraryEx documentation also says specifically not to use the SearchPath function to locate DLLs and not to use the DONT_RESOLVE_DLL_REFERENCES flag mentioned in comments.


For Linux, there's an existing tool ldd for which source code is available. It does actually load the shared libraries, but with a special environment variable LD_TRACE_LOADED_OBJECTS set that by convention causes them to skip doing anything. Because this is just a convention, beware that malicious files can perform actions when loaded by ldd CVE-2009-5064.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • How can I get memory address required by `GetMappedFileName` form handle returned by `LoadLibraryEx`? – mcpiroman May 04 '19 at 15:04
  • @mcpiroman: Apart from the low-order bits being used as flags, the `HMODULE` returned by `LoadLibraryEx` is the base address of the library. So you can just cast the HMODULE to a pointer, or you can mask the flag bits off first and cast, either will work. – Ben Voigt May 04 '19 at 15:11
  • `LOAD_LIBRARY_AS_DATAFILE` has a separate implementation that loads and maps the file via `CreateFileW`, etc, as opposed to using the loader function `LdrLoadDll`, so it does not special case "known DLLs" and API sets as the loader does and doesn't load the DLL as a module. Use `DONT_RESOLVE_DLL_REFERENCES` instead. And then free it via `FreeLibrary`. – Eryk Sun May 04 '19 at 22:30
  • @eryksun: Documentation says not to use `DONT_RESOLVE_DLL_REFERENCES`. If you know of ways that `LOAD_LIBRARY_AS_DATAFILE` doesn't match the normal DLL location logic, please file a bug with Microsoft. And not being loaded as a module is the whole point. – Ben Voigt May 04 '19 at 23:09
  • I know it says not to use it, but I doubt that support for the flag will ever go away in the API, especially since the way it's implemented is exactly the same as loading an EXE as a module. I think they deprecated it, understandably, because it's a problem if `FreeLibrary` isn't called, and because it's subject to race conditions if the module wasn't already loaded. So of course we have to use it cautiously. – Eryk Sun May 04 '19 at 23:39
  • A benefit of loading as a module is the simplicity of `GetModuleFileNameW` compared to `GetMappedFileNameW`, which returns the NT device name. The latter requires extra work to convert back to a DOS path. For example, we can prepend `"\\\\?\\GlobalRoot"` to the path, open a handle via `CreateFileW`, and call `GetFinalPathNameByHandleW`. – Eryk Sun May 04 '19 at 23:39
  • @eryksun: With `GetMappedFileName` you pass in the `HMODULE` you got from `LoadLibraryEx`, so you know it is giving you the path to that DLL. With `GetModuleFileName` you don't have that guarantee. – Ben Voigt May 04 '19 at 23:52
  • I don't follow. If we load with `DONT_RESOLVE_DLL_REFERENCES` we have an `HMODULE` that works with `GetModuleFileNameW`. If we load with `LOAD_LIBRARY_AS_DATAFILE` we don't actually have an `HMODULE` since it wasn't loaded as a module. We have the base address of the mapped file, and we can only use `GetMappedFileNameW`, which forces us to do extra work to convert the NT device path back to a DOS path that we can use. – Eryk Sun May 04 '19 at 23:57
  • Sorry, I'm too used to seeing chaining `GetModuleFileName(GetModuleHandle(filename), ...)`. You're right, you can skip the `GetModuleHandle` and just pass the `HMODULE`. – Ben Voigt May 04 '19 at 23:58