Failed to LoadLibrary from a subfolder on WindowsXP - c++

I want to load DLL from sub folder of my application folder. So I put my.dll with all dependant dlls to /myfolder
I takes me a while to figure out the correct way to load it:
m_handle = LoadLibraryEx(".\\myfolder\\my.dll", 0, LOAD_WITH_ALTERED_SEARCH_PATH);
It works very well on Windows 7 & and later. However it returns error 998 ERROR_NOACCESS “Invalid access to memory location.” On Windows XP.
However loading this dll from the application folder works very well on all versions of Windows
PS: I tried to use SetDllDirectory with absoulte and relative paths to myfolder, but still no success on Windows XP

MSDN has this to say about the LOAD_WITH_ALTERED_SEARCH_PATH flag:
If this value is used and lpFileName specifies a relative path, the
behavior is undefined.
If you don't need to use the alternative search strategy, you can just pass 0 as the flags parameter. If you really need this flag then you must call GetFullPathName first to get the absolute path.

Related

Get executable path with AssocQueryString in C++

Helo! I need to query application full path in C++, like "meshlab" -> "C:\Program Files\VCG\MeshLab\meshlab.exe"
This information is present in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths, but I don't want to work with registry directly, so I am using AssocQueryString like this:
#include "pch.h"
#include <iostream>
#include <Windows.h>
#include <Shlwapi.h>
int main()
{
char* executablePath = nullptr;
DWORD executablePathLen = 0;
std::string shortName = "mspaint";
HRESULT res = AssocQueryStringA(ASSOCF_OPEN_BYEXENAME,
ASSOCSTR_EXECUTABLE,
shortName.c_str(),
NULL,
executablePath,
&executablePathLen);
executablePath = new char[executablePathLen];
res = AssocQueryStringA(ASSOCF_OPEN_BYEXENAME,
ASSOCSTR_EXECUTABLE,
shortName.c_str(),
NULL,
executablePath,
&executablePathLen);
std::cout << executablePath; // prints: C:\Windows\system32\mspaint.exe
delete[] executablePath;
std::cin.get();
}
For mspaint it works as expected, but for meshlab it doesn't. HRESULT is ERROR_NO_ASSOCIATION
Any ideas what I missed?
UPDATE:
Also works well with foobar200 from C:\Program Files (x86)\foobar2000\foobar2000.exe
I suspect it must be related to 32/64 bit registry. I am using Windows 10 64 bit, and my application is 64 bit
I believe ASSOCF_OPEN_BYEXENAME:ASSOCSTR_EXECUTABLE searches HKEY_CLASSES_ROOT\Applications.
ShellExecuteEx searches the "normal directories" and the App Paths key:
Finding an Application Executable
When the ShellExecuteEx function is called with the name of an executable file in its lpFile parameter, there are several places where the function looks for the file. We recommend registering your application in the App Paths registry subkey. Doing so avoids the need for applications to modify the system PATH environment variable.
The file is sought in the following locations:
The current working directory.
The Windows directory only (no subdirectories are searched).
The Windows\System32 directory.
Directories listed in the PATH environment variable.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
(HKEY_CURRENT_USER is also used on recent versions of Windows)
The Assoc API is more focused on file associations.
If you are just going to execute the file then you should just use ShellExecuteEx. If you just want to find the fully qualified path you can use some helper functions to do some of the work for you:
FindExecutable seems promising but it has some compatibility workarounds and it will also find the executable used to open registered types (c:\foo\bar.txt might resolve to c:\Windows\Notepad.exe etc. because it uses ASSOCSTR_EXECUTABLE),
If you want to look for extensions like .exe and .bat automatically you can use PathResolve(..., PRF_REQUIREABSOLUTE|PRF_VERIFYEXISTS|PRF_TRYPROGRAMEXTENSIONS|PRF_DONTFINDLNK) but you must be careful that the search string does not contain \ nor :.
If you only want to look for a .exe you must manually append the extension if it is not present and then call PathFindOnPath(, NULL).
Neither of these functions looks in the App Paths key, they do not use the exact same search order as ShellExecuteEx (system32 before %windir%?) and they are most likely limited to MAX_PATH.
If those functions (or your own equivalent version) fail then you can check the App Paths key. When reading from the registry you must append .exe if there is no extension and use a helper function like SHQueryValueEx that will expand REG_EXPAND_SZ strings.

Is using an absolute path to load system DLLs with LoadLibrary() and GetModuleHandle() better to prevent DLL hijacking?

In many cases to load some newer API one would use a construct as such:
(FARPROC&)pfnZwQueryVirtualMemory = ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "ZwQueryVirtualMemory");
But then, considering a chance of Dll hijacking, is it better to specify a DLL's absolute path, as such. Or is it just an overkill?
WCHAR buff[MAX_PATH];
buff[0] = 0;
if(::GetSystemDirectory(buff, MAX_PATH) &&
::PathAddBackslash(buff) &&
SUCCEEDED(::StringCchCat(buff, MAX_PATH, L"ntdll.dll")))
{
(FARPROC&)pfnZwQueryVirtualMemory = ::GetProcAddress(::GetModuleHandle(buff), "ZwQueryVirtualMemory");
}
else
{
//Something went wrong
pfnZwQueryVirtualMemory = NULL;
}
The problem with the latter method is that it doesn't always work (for instance with Comctl32.dll.)
You don't have to do anything special for ntdll.dll and kernel32.dll because they are going to be loaded before you get the chance to do anything, they are also on the known-dlls list.
The dll hijacking issues often include auxiliary libraries. Take version.dll for example, it is no longer on the known-dlls list so explicitly linking to it is problematic, it needs to be loaded dynamically.
The best solution is a combination of 3 things:
Call SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32) if it is available (Win8+ and updated Win7).
Call LoadLibrary with the full path (GetSystemDirectory) before calling GetModuleHandle.
Don't explicitly link to anything other than kernel32, user32, gdi32, shlwapi, shell32, ole32, comdlg32 and comctl32.
If SetDefaultDllDirectories is not available then it is really hard to protect yourself if you don't control the application directory because various Windows functions will delay-load dlls like shcore.dll without full paths (especially the shell APIs). SetDllDirectory("") helps against the current/working directory but there is no good application directory workaround for unpatched pre-Win8 systems, you just have to test with Process Monitor and manually load the problematic libraries early in WinMain.
The application directory is a problem because some users just put everything in the downloads folder and run it from there. This means you might end up with a malicious dll in your application directory.

Should path search order be identical for ::ShellExecute and static CFile::GetStatus() on relative path .EXE name?

From within my MFC App, I am doing something like
CFileStatus fs;
if (CFile::GetStatus("MyOtherProg.exe", fs))
{
// found the file
::ShellExecute(NULL, NULL, "MyOtherProg.exe", NULL, NULL, SW_SHOW);
}
but the full path to the file found in the static CFile::GetStatus is NOT the one being executed by ::ShellExecute (it has the same name, but is running a different version of "MyOtherProg.exe", in a different folder).
I have tried this on different PCs with the same O/S (Win7 64 bit), with different sets of "MyOtherProg.exe" in various folders. Neither PC's ShellExecute runs the same one found in the CFile::GetStatus. One PC always ends up running version 3, another PC always ends up running version 2 (why isn't THAT consistent?).
Note 1: Across the 2 PCs, at least 3 versions are "installed", but NONE of them are installed in the PATH. Some DO have SHORTCUTS on the Desktop, if that is some undocumented feature.
Note 2: ALL of them are different versions of "MyOtherProg.exe", but installed in different directories.
Note 3: The full path of the one that ends up being run via ::ShellExecute is the same one found when I just type "MyOtherProg.exe" in the Search edit field below the Task Bar's START menu. But WHY is the one found by CFile::GetStatus NOT the SAME one? And why on one PC it's version 2, but on another PC its version 3?
Note 4: On both PCs, the resulting search list of the RUN command "MyOtherProg.exe" only shows 1 version (although on one PC, 3 versions are installed, and on the other PC 4 different versions are installed with a fifth one being a "debug" build). The PC with 3 versions always ONLY lists version 2, the PC with 5 versions always ONLY lists version 3.
CFile::GetStatus expands the relative path with the working directory.
ShellExecute looks for the file using the process creation search order. This is described in the CreateProcess documentation. As well as the paths listed there, the per-application path is searched.
In short this means that CFile::GetStatus alone is simply not suitable for the task you have in mind. You'd have to re-create the shell search.
But why would you do that? The logical thing to is to call ShellExecute directly and check for errors. Let it perform the search, because it knows the rules. Since ShellExecute has deficient error reporting you should rather call ShellExecuteEx.
On the other hand, if you only want to search in the working directory as you do with your current call to CFile::GetStatus then use an absolute rather than relative path. I doubt that this is what you want to do but mention it for completeness.

dll loading error using LoadLibraryA

We are trying to load a dll-library from inside a 64-bit dll using LoadLibraryA function. It returns 126 error - mod not found. The dll file path given to the function is correct, we are sure.
We have tried a dummy dll to test and it worked, it is loaded.
We also tried adding the dll (which is a dependcy of the first dll that we want to load) to the dummy dll. It also worked. So the problem seems not about the dependency dlls, but the original dll that we want to load in the first place.
We also tried converting the dl to 64-bit, and tried that, still no good.
We also checked the dependencies with Dependency Walker. Everything is OK.
The operating system that we are using is Windows 8, 64bit. If it makes any difference..
Does anyone have any idea about this poblem?
EDIT:
We also tried this code:
hModule = LoadLibraryW(L"C:\\temp\\dllToLoad.dll");
and received this error code:
"First-chance exception at 0x00000000 in C_Test_TSMPPKCS11.exe: 0xC0000005: Access violation at location 0x0000000000000000."
EDIT 2:
The code that we used in the first place is:
hModule = LoadLibraryA((char*)aDLLFile);
EDIT 3:
We are using complete path to load the dll. In order to test this we tried this code:
FILE *fp;
int size = 0;
fp=fopen("C:\\temp\\dllToLoad.dll", "r");
size = fgetc(fp);
printf("size:%d\n",size);
fclose(fp);
There was no problem, we received the file size which is 77.
We also tried converting the dl to 64-bit, and tried that, still no good.
There's no way you can load a 32-bit dll as executable code into a 64-bit process (and since you're using LoadLibraryA() that's all you can be trying to do).
Assuming the dll that you are trying to load and the process that you're loading it into are the same type then are you passing a complete path to LoadLibraryA() or a relative path or just a dll name? If you're not using a complete path then consider using LoadLibraryEx() if possible as this gives you much more control over the search path used. If you are using a complete path try opening the file using normal file operations if you fail to load the dll, does this work? If that works then try LoadLibraryEX() with LOAD_LIBRARY_AS_DATAFILE and see if that will load the dll as a simple data file (which proves that it's finding the file).
Run up Sysinternal's ProcMon and watch the code open the DLL, that may show you dependent DLL load failures.

CreateProcess STATUS_DLL_NOT_FOUND - which dll?

I have a process which calls CreateProcess. It appears that CreateProcess returns nonzero indicating success. However, the HANDLE to the process then gets immediately set, indicating the process has exited. When I call GetExitCodeProcess, STATUS_DLL_NOT_FOUND is then returned.
I understand that a DLL is missing. I even know exactly which one. However, what I don't understand is how to figure that out programmatically.
I noticed that Windows will present a dialog saying that the process failed to start because it couldn't find the specified DLL (screenshot: http://www.mediafire.com/view/?kd9ddq0e2dlvlb9 ). In the dialog, Windows specifies which DLL is missing. However, I find no way to get that information myself programmatically.
If a process fails to start and would return STATUS_DLL_NOT_FOUND, how do I programmatically retrieve the library name to which the target process was linked which couldn't be found? That way I can automatically record in an error report what DLL appears to be missing or corrupt in a given installation.
CreateProcess returns 0 indicating success.
CreateProcess() returns a BOOL, where 0 is FALSE, aka failure not success.
If a process fails to start and would return STATUS_DLL_NOT_FOUND, how do I programmatically retrieve the library name to which the target process was linked which couldn't be found?
Unfortunately, there is no API for that. Your only option would be to manually access and enumerate the executable's IMPORTS table to find out what DLLs it uses, and then recursively access and enumerate their IMPORTS tables, manually checking every DLL reference you find to see whether that DLL file exists on the OS's search path or not.
If the dll is statically linked you can walk the iat and see if the dll exists. If the dll is dynamically loaded then starting the process suspended and hooking LoadLibrary (or instead of hooking emulate a debugger) is the only way I see.
The best way is to use loader snaps. Basically you use gflags.exe (which is included with windbg) to enable loader snaps; then, run the process with the debugger attached. Loader snaps will enable the loader to print out dbg messages of the process and it will print the failures.
gflags.exe -i yourcode.exe +sls
windbg yourcode.exe
I know this is not a "programmatic" way to find out the problem, but what the loader does is complicated, and you don't really want to be redoing its logic to find the failure. That is why loader snaps were invented.
The very hard way would be: Parsing the .EXE and .DLL files and create the dependency tree of .DLL files.
I don't think there is a way to get a list of DLL files that are missing: When Windows finds one missing DLL file it stops loading so if one DLL file is missing you won't find out if more DLL files are missing.
Another problem you could have is that old DLL versions could have missing "exports" (functions). This is even harder to detect than the dependency tree.
Just since this is somehow the top stackoverflow result on Google for "STATUS_DLL_NOT_FOUND". How to trace and solve any random occurence:
Download SysInternals procmon64.exe (or just the entire set). After startup immediately hit the looking glass 'Stop capture' button (Ctrl+E). And 'Clear' (Ctrl+X).
Set filters for:
'Process name' is to whatever the mentioned process name was (for me it was 'build-script-build.exe') [Add]
'Result' is not 'SUCCESS' [Add]
'Path' ends with '.dll' [Add] [OK]
Start capture again (Ctrl+E).
Run the thing that had a problem again (for me: build cargo). Google for the last listed DLL file.
For me that was VCRUNTIME140.dll, so I installed the VC++ 2015 to 2019 redistributable.
ProcMon is kind of like unix strace.