Background
I'm developing a C++ windows DLL module that exports a single function
__declspec(dllexport) void Run()
Motivation
I would like to implement some sort of accessing rights to my function. I wish to prevent unauthorized modules from activating my DLL proc.
I don't need a robust/bullet proof mechanism. I only like to "defend" this proc from other modules running under my own app.
Approach
Get the calling module name and decide based on the name if access is granted.
Question
Would this approach suffice?
if so, how do I get the name of the calling module?
if so, how do I get the name of the calling module?
get return address by call _ReturnAddress
get base address of the image that contains this return address -
RtlPcToFileHeader
finally call GetModuleFileName function
so code can be like this
HMODULE hmod;
if (RtlPcToFileHeader(_ReturnAddress(), (void**)&hmod))
{
WCHAR sz[MAX_PATH];
if (GetModuleFileName(hmod, sz, MAX_PATH))
{
DbgPrint("%p %S\n", hmod, sz);
}
}
about - are this work in XP ? yes, but with one note. _ReturnAddress is CL intrinsic - so not depended from os version (for say gcc exist __builtin_return_address (0) ) GetModuleFileName also very old api function and exist in win2000, xp, everywhere. about RtlPcToFileHeader
- it exported (and implemented) in ntdll.dll in all windows versions from xp to latest. also begin from win2003 it also exported from kernel32.dll but implementation here - simply jump to ntdll.RtlPcToFileHeader - so if want use this on xp also - link with ntdll.lib and place it before kernel32.lib in libs order or can get it it runtime by GetProcAddress(GetModuleHandle(L"ntdll"), "RtlPcToFileHeader");
or even if somebody afraid that RtlPcToFileHeader will be removed from ntdll (this is of course no) can use this
GetProcAddress(GetModuleHandle(g_xp ? L"ntdll" : L"kernel32"), "RtlPcToFileHeader");
Related
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;
I have a third-party console application. I need run it from my application but I cannot run it as a separate process (because I need to work with its dependencies: fill Import tables manually, setup hooks etc.). So probably I should call main function of this executable manually. Here is how I'm trying to do this:
Load this EXE using auto hMod = LoadLibrary("console_app.exe")
Fill Import table of this exe manually
Get entry point of this EXE and call it
And I'm stuck with the last step.
Here is how I'm trying to call entry point:
void runMain(HINSTANCE hInst)
{
typedef BOOL(WINAPI *PfnMain)(int, char*[]);
auto imageNtHeaders = ImageNtHeader(hInst);
auto pfnMain = (PfnMain)(DWORD_PTR)(imageNtHeaders->OptionalHeader.AddressOfEntryPoint + (DWORD_PTR)hInst);
char* args[] = { R"(<console_app_path>)", R"(arg1)", R"(arg2)" };
pfnMain(3, args);
}
It works. But it works as if there is no arguments.
Where am I wrong? How can I run an executable inside my process with arguments? Thanks.
UPDATE:
I've investigated how my particular third-party exe gets cmd arguments and found that:
It doesn't import GetCommandLine at all and do not call it
After call _initterm call argc and argv arguments are available through cs:argc and cs:argv (see pictures below)
CMD arguments that I pass to my main console app are transferred to
child EXE too.
Can you explain, please, what _initterm actually do and where CMD arguments are actually stored?
You're calling the entry point of the application, not int main(int, char**). Now you may have read that the entry point of a C++ program is int main(int, char**) but that's just a C++ perspective.
The Win32 perspective is different; the entry point is a int (*)(void);. The Visual Studio linker looks for int mainCRTStartup(void); and uses that, unless you specify another entry point with /ENTRY. The default implementation of mainCRTStartup calls GetCommandLine() to fill in argv[] before calling main(argc,argv). There are also other things in mainCRTStartup which you might want to happen: run global ctors, initialize the CRT state, ...
Of course, that's assuming the other program was compiled with Visual C++, but whatever language it's been written in, it must be calling GetCommandLine.
Now, for your problem, here's an interesting observation: GetCommandLine() returns a writeable pointer. You can overwrite the existing command line. Of course, if you control the import tables, you decide what GetCommandLine means. (Remember, as usual there are A and W variants).
One warning: the MSVCRT isn't designed to be initialized twice, neither the static version nor the DLL one. So practically speaking you can't use it, and that will hurt.
[edit]
Your update shows a call to _initterm. That's a MSVCRT function, as I already hinted. Specifically,
/***
*crtexe.c - Initialization for console EXE using CRT DLL
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
...
/*
* routine in DLL to do initialization (in this case, C++ constructors)
*/
extern int __cdecl _initterm_e(_PIFV *, _PIFV *);
extern void __cdecl _initterm(_PVFV *, _PVFV *);
The MSVCRT DLL calls GetCommandLine() on behalf of the EXE.
the entrypoint of executable (EP) have no arguments - so you and can not direct call it with arguments.
usual application got arguments by parsing command line. [w]mainCRTStartup do this - if you have console application linked to c/c++ runtime - this is real EP .
so if you Fill Import table of this exe manually - set exception for GetCommandLineA and GetCommandLineW functions - redirect it to self implementation and return your custom command line.
but if app used not static linked CRT it can import __getmainargs or __wgetmainargs or even _acmdln, or _wcmdln from msvcrt.dll - so already task become complex.
and you assume that relocs exits in EXE, you not handle TLS if it exist, you not handle application manifest, possible dl redirections, etc.
but I cannot run it as a separate process
this is not true. you can and must run it as separate process - this is the best solution.
exec your app by CreateProcess with CREATE_SUSPENDED flag. here you free easy set any CommandLine which you need. you not need manually and not fully correct load EXE but system do this task for you.
after process is created you need inject self DLL to it by using QueueUserAPC (but not CreateRemoteThread !!) and finally call ResumeThread
as result your DLL will be loaded and executed in first EXE thread, just before application EP - and here you can do all needed tasks
I've had plenty of success using EasyHook to hook system API routines (in C++) out of libraries. These libraries have always been flat and basically filled with globally callable routines. Here is a small sample using MessageBeep() out of the User32.dll library (minus setup code):
HMODULE hUser32 = GetModuleHandle ( L"User32" );
FARPROC TrampolineMethod; // This would have been set to my new replacement trampoline method.
TRACED_HOOK_HANDLE hHook = new HOOK_TRACE_INFO();
NTSTATUS status;
status = LhInstallHook(
GetProcAddress(hUser32, "MessageBeep"),
TrampolineMethod,
(PVOID)0x12345678,
hHook);
This is all works great. The problem is, I now have a need to hook methods out of a class, not just a global function. I don't really care about the object itself, I'm really more interested in examining the parameters of the method and that's it. I don't know how to syntactically identify the routine in the function name parameter for GetProcAddress(), and I'm not even sure if GetProcAddress() supports it. For example, I'd like to hook the Pen::SetColor() method out of the gdiplus.dll library:
HMODULE hGDIPlus = GetModuleHandle ( L"Gdiplus" );
FARPROC TrampolineMethod; // This would have been set to my new replacement trampoline method.
TRACED_HOOK_HANDLE hHook = new HOOK_TRACE_INFO();
NTSTATUS status;
status = LhInstallHook(
GetProcAddress(hGDIPlus, "Pen.SetColor"), // this is probably wrong or not possible here
TrampolineMethod,
(PVOID)0x12345678,
hHook);
This doesn't work of course and I don't think the GetProcAddress(hGDIPlus, "Pen.SetColor") is correct. How do I specify a member function of a class to GetProcAddress()? Is this even possible? Also, how would this look if I wanted to hook a constructor such as Pen::Pen()?
The Portable Executable (PE) format that Windows uses doesn't really supports exporting or importing objects or their methods, so that's not what GdiPlus (or any other DLL) uses internally. The object notation is probably an abstraction implemented in the import library for the DLL.
If you take a look at GdiPlus's export table with the Dependency Walker tool (Windows SDK), or similar, you will see
GdipGetPenColor
GdipSetPenColor
etc.
So it is basically no different than the legacy exports, like MessageBeep.
I'm trying to create a dll that contains a VCL data module - the idea being that various applications can all load the same dll and use the same database code.
The data module itself is tested ok as part of an application - I've copied the form over to my dll project.
So in the dll entry point method, I need to initialize the data module:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
//if I don't call this, I get an exception on initializing the data module
CoInitialize(NULL);
//initialize a standard VCL form; seems to works fine
//I'm not using Application->CreateForm as I don't want the form to appear straight away
if(!MyForm) MyForm = new TMyForm(Application);
//this doesn't work - the thread seems to hang in the TDataModule base constructor?
//I've also tried Application->CreateForm; same result
if(!MyDataModule) MyDataModule = new TMyDataModule(Application);
}
I've also seen something about how I need to call Application->Initialize before creating the form but this doesn't seem to make any difference.
Any ideas?
Thanks
You really should not be doing very much work in your DllEntryPoint() at all. Certainly not calling CoInitialize(), anyway. It is not the DLL's responsibility to call that when loaded. It is the calling app's responsibility before loading the DLL.
You should either:
export an additional function to initialize your DLL and then have the app it after loading the DLL (same for uninitialing the DLL before unloading it)
don't create your TForm/TDataModule until the first time the DLL actually needs them.
move your TForm/TDataModule into their own worker thread inside the DLL. In this case, you would then call CoIniitalize().
And in all cases, don't relay on the DLL's Application object to manage the lifetime of your TForm/TDataModule. Free them yourself instead before the DLL is unloaded.
Consider the following registry export:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries\000000000001]
;...
"ProtocolName"="#%SystemRoot%\\System32\\wshtcpip.dll,-60100"
The intention here appears to be for someone to load the DLL in question, and use some form of API retrieve the actual name. But I don't know what that API is :/
I'd like to avoid loading the DLL into my address space (and thus call DLL_PROCESS_ATTACH) if at all possible; can't really trust third party DLLs to be trustworthy.
RegLoadMUIString will do the necessary for you. Note however, that it was introduced in Vista so won't help if you need to support XP.
If you want to avoid code in the DLL running whilst you extract resources, use LoadLibraryEx passing LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE, or possibly LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE. Once you have done that, you can call LoadString to extract the MUI value.
This is going to help:
HMODULE hModule = LoadLibrary(_T("wshtcpip.dll")); // LoadLibraryEx is even better
TCHAR pszValue[1024] = { 0 };
INT nResult = LoadString(hModule, 60100, pszValue, _countof(pszValue));
LoadString will take care of downloading resource from MUI, if needed. LoadString uses thread locale, which you might want to override prior to the call.
Also: Loading Language Resources on MSDN.