Avoid dll pre-loading vulnerability by reloading dll, any better way? - c++

To avoid dll pre-loading I tried to use SetDllDirectory("") as it is mentioned in MSDN to remove current directory from search path.
The directory to be added to the search path. If this parameter is an
empty string (""), the call removes the current directory from the
default DLL search order. If this parameter is NULL, the function
restores the default search order.
Surprisingly it didn't work. I can see dlls are still getting loaded from current directory if dll present there. I also tired to set dll directory path as system 32 path, but still it picks dll from current directory. Lastly I decide to get all the modules loaded in my application and reloading them again. My code looks like this,
if(wcsstr(szModName,L"TestLibrary.dll"))
{
FreeLibrary(hMods[i]);
LoadLibrary("SomeRelavantPath\TestLibrary.dll");
}
Do you see any problem with my code?
#Edit:
My Complete POC code,
int main( void )
{
SetDllDirectory(L"");
LPWSTR s = new WCHAR[100];
GetDllDirectory(100,s);
HINSTANCE myDLL = LoadLibrary(L"TestLibrary.dll");
//myDLL returns non-null, there is file in current dir and not at any other location, it should have return null.
return 0;
}

Unloading and loading again with a new path is pointless from a security perspective, it is already too late because a evil .dll could execute code in its DllMain!
The best solution is to directly link to as few DLLs as possible and call SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32) before anything else in your WinMain function.

If the string specifies a full path, the function searches only that path for the module.
If the string specifies a relative path or a module name without a path, the function uses a standard search strategy to find the module; for more information, see the Remarks.
From LoadLibrary function # MSDN.
Calls to SetDllDirectory will do nothing against full paths.

Related

How to use `/DELAYLOAD` on a DLL from another DLL

I have a solution with two DLLs. The first one is the "main" DLL. It happens to be an ODBC driver, but I think that is not important for this question.
The second DLL contains all the UI logic for the first one. As the UI is not always needed, I want to use the /DELAYLOAD feature which explicitly says:
The delayed loading of a DLL can be specified during the build of
either a .EXE or .DLL project.
The main DLL's project correctly references the UI ones. If I don't use /DELAYLOAD, everythin works just fine. The two DLLs will be installed into the same directory, so I thought loading one DLL from within the other should be easy. But apparently, it's not.
As soon as the first function from the UI DLL is called, the application (any ODBC client in my case) crashes.
GetLastError() yields 126 which apparently means that the target DLL could not be found in any of the search paths.
And indeed, according to this answer LoadLibrary() does have a look into the directory of the calling executable, but not into the one of the currently executed DLL. I'm assuming /DELAYLOAD is also just using LoadLibrary() under the hood, is that correct?
If I copy the executable into the installation directory of my driver, it works just fine, which proves my assumption that it just doesn't look in the current DLL's directory.
Appart from that, I was also able to make it run by calling
LoadLibrary(L"C:\\absolute\\path\\to\\UI.dll");
just before the first function of the UI DLL is loaded.
I was also able to determine this path programmatically using
wchar_t buffer[512];
GetModuleFileName(hThisDLL, buffer, sizeof(buffer));
But then I would have to cover every single UI call with this logic. So I wouldn't see much advantage anymore that /DELAYLOAD has over the "old-school" way of using LoadLibrary() and GetProcAddress().
Question
Is there a simple way to make /DELAYLOAD find the target DLL from another DLL in the same directory?
There is. My suggestion would be to create a delay-load-failure hook function.
https://learn.microsoft.com/en-us/cpp/build/reference/failure-hooks?view=vs-2019
Basically, you write a function inside your main DLL that gets notified in the event of a delay load failure. In that function, when the given code indicates failure, you try manually calling LoadLibrary with of a path consisting of the folder in which your main DLL resides plus the name of the DLL that failed to load
How you get the your main DLL from within your main DLL is up to you. There are many ways.
Something like this:
FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
FARPROC fpRet = NULL;
switch (dliNotify)
{
case dliStartProcessing:
break;
case dliNotePreLoadLibrary:
break;
case dliNotePreGetProcAddress:
break;
case dliFailLoadLib:
{
std::string newPath = GetMyModulePath();
newPath += "\\";
newPath += pdli->szDll;
fpRet = reinterpret_cast<FARPROC>(::LoadLibrary(csDir));
}
break;
case dliFailGetProc:
break;
case dliNoteEndProcessing:
break;
default:
break;
}
return fpRet;
}
//
// Set access to our delay load hook.
//
PfnDliHook __pfnDliFailureHook2 = delayHook;

Load dll function from other directory

in my new project, I got a problem about loading dll.
My file structure is like below:
D:\dll_directory\calculate.dll
D:\myproject\myproject.vcxproj
D:\running_place\myproject.exe
There're my code,
HMODULE h = LoadLibrary("D:\\dll_directory\\calculate.dll");
if(!h)
return GetLastError();
pfnInit = (PInit)GetProcAddress(h, "initialize");
if(!pfnInit())
{
// Fail
}
In my code of calculate.dll, I need to get the working directory so I use
int initialize() {
...
GetModuleFileName(...,myPath,...);
currentPath = ...; // get directory from path
filePath = currentPath + ...
if(!DoesExist(filePath))
return 0;
...
}
The problem is there. Because I loadlibrary dll from "D:\dll_directory\" in other directory, currentPath value is wrong.
In debugging, currentPath is "D:\myproject\debug..." and in executing, it's "D:\running_place\".
Only if place myproject.exe into the same place with calculate.dll, it shows correctly, currentPath = "D:\dll_directory\".
But I don't want to place dll manually because of many files and many places.
Additionally, I can not change code in dlls, it's stable.
Are there any ways to make it work?
Thanks in advance.
Updated: I modify my question to more clearer. Sr about this. It's my first time on stackoverflow.
In function Init of DLL, it need to run other files that are placed in the same place with it. The expected of currentPath is directory of DLL.
EXE in dir1 loads DLL in dir2, EXE calls function Init of DLL -> currentPath=dir1 (wrong).
Try to change DLL code with GetCurrentDirectory -> currentPath=dir1; still wrong.
Put EXE and DLL in dir2, EXE calls function Init of DLL -> currentPath=dir2 (expected).
You're indeed using the wrong function t get the current directory. GetCurrentDirectory returns the current directory (i.e. where .\thisFile.txt would be sought).
GetModuleFileName returns the name where the module came from. That's in general not the current working directory. That working directory can be pretty much anything, really. The variation you see between debug and release is just a small sample of the possible variation. Expect anything.
It also appears that you're looking for another file, other than the 3 mentioned in the question. It's unclear where that other file is located, so it's possible that this answer is incomplete. Is there a reason to expect that other file in the current working directory?
I find out these code that return the DLL current path.
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
WCHAR DllPath[MAX_PATH] = {0};
GetModuleFileName((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
Actually, I don't know what _ImageBase is and its cons/pros, am still investigating.
If ok, maybe I use it in DLL code.
The first argument of GetModuleFileName (which you left out) determines which path to get. From the observed behavior, we can now deduce that you passed NULL or 0, which means you get the filename of the EXE module.
You should have passed HMODULE h from your first snippet, the DLL module.
(This value is also available inside your DllMain, 1st argument. Save that 1st argument to a global variable in your DLL, so you can pass it to GetModuleFileName)

How to tell if a folder is a subfolder of a special Windows folder?

If I have a CSIDL (or its newer alternative KNOWNFOLDERID) for a special folder (for the sake of this example, let's assume My Documents folder) and a DOS folder path, is there any way to tell that the path refers to a subfolder within the special folder?
EDIT 1: I implemented the following method after #RemyLebeau's suggestion, but it always sets my nIsParent to 0, or not a parent. What am I missing there?
int nCSIDL = CSIDL_PERSONAL;
LPCTSTR pDosPath = L"C:\\Users\\UserName\\Documents\\Subfolder1\\File.txt";
int nIsParent = -1; //-1=error, 0=no, 1=yes
LPITEMIDLIST pidlDocuments = NULL;
if(SUCCEEDED(SHGetFolderLocation(NULL, nCSIDL, NULL, 0, &pidlDocuments)))
{
LPITEMIDLIST pidl = ILCreateFromPath(pDosPath);
if(pidl)
{
nIsParent = ILIsParent(pidlDocuments, pidl, FALSE) ? 1 : 0;
ILFree(pidl);
}
ILFree(pidlDocuments);
}
EDIT 2: As for his 2nd suggestion to use SHGetPathFromIDList and then PathRelativePathTo on both DOS paths, it won't work for the following: My Documents on my computer is redirected to "\\SRVR-A\Home\UserName\Documents", which is also the "R:\Documents" folder with drive R: mapped to that Home share. PathRelativePathTo fails on those paths.
EDIT 3: If I had a folder Test folder in My Documents I could do this using my mapped drive R::
subst S: "R:\Documents\Test folder"
Which will technically make folder "S:\Test folder" a parent of My Documents as well, which is "\\SRVR-A\Home\UserName\Documents\Test folder".
That is why I was looking for a Shell-only, or a single API solution.
Everything in the Shell is represented by the ITEMIDLIST structure, even file system paths. Retrieve the ITEMIDLIST of the special folder using SHGetFolderLocation() or SHGetKnownFolderIDList(), then retrieve the ITEMIDLIST of the DOS path using SHParseDisplayName() or ILCreateFromPath(), then use ILIsParent() to check if the special folder's ITEMIDLIST is a parent of the DOS path's ITEMIDLIST.
Alternatively, retrieve the special folder's path using SHGetFolderPath() or SHGetKnownFolderPath(), then use PathRelativePathTo to check if the DOS path can be represented as a relative subfolder of the special folder's path without using any ".." components.
Create a function that gets a full path, name of the special folder, and just call
strstr on the full path with the name of the special folder and if it does not return NULL then it is a subfolder.
As for an API for it, I'm not aware of something like that but it could be possible.

Can I use applicationDirPath() to access resources at a higher directory level?

In the application that I am developing (using C++ and Qt), I am using QApplication::applicationDirPath() to access some resources, with respect of the application's path.
As an example, since I want to open a HTML manual from the application, I act this way:
void MainWindow::on_actionHelp_triggered()
{
QString link = QApplication::applicationDirPath() + "/Guide/guide.html";
bool r = QDesktopServices::openUrl(QUrl::fromLocalFile(link));
}
This snippet works if the project's structure presents the path "ProjectName/bin/Release/Guide/guide.html" (since the .exe file is in "ProjectName/bin/Release/AppName.exe").
But what can I do to refer to a higher-directory-level resource? As an example, I wish my HTML file to be in "ProjectName/data/Guide/guide.html". But this way, it seems not possible to compose the path in the way I'm acting.
EDIT: After #olive's comment, I wish to clarify a thing:
"Why am I not using '../'?"
Because it won't work from Visual Studio, where I am massively launch the application to test it. From VS, in fact, I shall use "../data/Guide/guide.html", when "from the outside", I'd have to do "../../data/Guide/guide.html".
That's why (I think) QApplication::applicationDirPath() exists. However, I am not an expert, so don't blame me and correct any eventual mistake of mine, please!
Just use ... QApplication::applicationDirPath() + "/../../data/Guide/guide.html" is perfectly valid path!
Of course there is another problem. When the application is installed, the relative path will probably be different again. You either need to configure the paths in visual studio so that the relative path works both during development and after deployment, or you need to detect the layout.

C++ How do we make our application start on computer startup (and of course, after a user signed in)?

How do we make our application start on computer startup (and of course, after the user had signed in)?
And no, I am not making a virus.
Does registry editing sound like a good idea?
My OS is Windows 8.
However, I will try to make my application available for all possible Window OS.
The correct way to do this is simply to add a shortcut to your application's executable to the user's Startup folder. You do not need to (and should not) modify the registry.
Advanced users know how to do this manually already, but it may also be an option you want to provide as part of your installer and/or a configuration dialog in your application.
To do this from C++ code, you will need to do two things:
Retrieve the location of the current user's Startup folder.
This is accomplished by calling the SHGetKnownFolderPath function and specifying the KNOWNFOLDERID of the folder you're interested in. In this case, that would be FOLDERID_Startup.
Sample code:
std::wstring GetStartupFolderPath()
{
PWSTR pszPath;
HRESULT hr = SHGetKnownFolderPath(&FOLDERID_Startup,
0, // no special options required
NULL, // no access token required
&pszPath);
if (SUCCEEDED(hr))
{
// The function succeeded, so copy the returned path to a
// C++ string, free the memory allocated by the function,
// and return the path string.
std::wstring path(pszPath);
CoTaskMemFree(static_cast<LPVOID>(pszPath));
return path;
}
else
{
// The function failed, so handle the error.
// ...
// You might want to throw an exception, or just return an
// empty string here.
throw std::runtime_error("The SHGetKnownFolderPath function failed");
}
}
Note, however, that while SHGetKnownFolderPath is the recommended function to use, it is supported only by Windows Vista and later. If you need to support older versions of the operating system, you'll need to call it dynamically on newer versions where it is available, and otherwise fall back to calling the SHGetFolderPath function. This one takes a different type of identifier, a CSIDL value. The one you want is CSIDL_STARTUP.
Create a shortcut to your application's executable.
This is accomplished using a different set of Shell API functions. I won't bother writing up sample code here because it's all quite well explained on MSDN already: Shell Links
Now you just connect the dots: when you create the shortcut to your application's executable, specify the user's Startup folder as its destination path.
The only other thing to be aware of is that there are actually multiple Startup folders. Each user has one, which is the one we retrieved above using FOLDERID_Startup. About 99% of the time, that's the one you want. Putting a shortcut to your app there will cause it to be launched automatically when that user logs on.
However, there is also a global Startup folder that is shared by all users. This one is identified by FOLDERID_CommonStartup (or CSIDL_COMMON_STARTUP) and requires administrative privileges to add items to. That makes sense, of course, because whatever you put in there is going to launch automatically when any user logs on to the computer. Only administrators can affect global behavior like this. And chances are, your app doesn't need this anyway.
Start menu
Simplest solution is to place .lnk of .bat file into the Start Menu\On startup folder. This is easiest and not too sneaky against the user.
Registry:
Another solution is creating the key in the registry keys:
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run] //All users
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce] //All users once
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run] //Currend user
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce] //Current user once
This is not that transparent - so a bit more agressive against the user.
On windows you can put a shortcut to your application in the Startup folder, or you can implement it as a service.
And that "I am not making a virus" does make you sound guilty... Maybe it is a keylogger? ;)
There are a lot of ways, but they all depend on your OS. For windows take a look at the "Task Schedualer" under "Administrative tools" in the control panel.
Maybe something like this? Note, this code snippet is not written by me.
#include <windows.h>
void Reg() {
::HKEY Handle_Key = 0;
::DWORD Dispoition = 0;
::RegOpenKeyEx( HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
0,
KEY_ALL_ACCESS,
&Handle_Key );
const unsigned char Path[ MAX_PATH ] = "C:\\Windows\\YourProgramName.exe";
::RegSetValueEx( Handle_Key, "My Directory", 0, 1, Path, sizeof( unsigned char ) );
};
What do you guys think?