Hooked NtOpenFile fails when std::wstring is defined on the stack - c++

I have written a hooking dll using the mhook library. In a spezial case the NtOpenFile() fails when a std::wstring is defined as stack var. Defining it on the heap the code is working.
The code is working without problems except when a certain win32 application (lets call it nuisance.exe) tries to open an existing testfile (like c:\temp\anyfile.log) the access fails. Mostly STATUS_INVALID_ACL (0xC0000077) is returned then.
I have reduced my code line by line and finally found that the error happens when in a called function a std::wstring is defined (this example below). The error happens every time an on different OS's
NTSTATUS NtOpenFileApiHook::NtOpenFileHook(PHANDLE FileHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG ShareAccess,
ULONG OpenOptions
)
{
NTSTATUS Status = STATUS_SUCCESS;
// using this function the call will fail
AfterThis_NtOpenFile_WillFail();
// using this function INSTEAD the call will work
AfterThis_NtOpenFile_WillWork();
// calling the real NtOpenFile using a pointer
// nothing was changed hier, the original parameters are passed
Status = RealNtOpenFile(FileHandle, ...);
return Status;
}
int AfterThis_NtOpenFile_WillFail()
{
std::wstring String = L"";
return 0;
}
int AfterThis_NtOpenFile_WillWork()
{
std::wstring * pString = new std::wstring();
pString->assign(L"");
delete pString;
return 0;
}
I have fixed it this way for this call. But I'm afraid that other functions in other circumstainces could fail so I'm looking for the reason and (probably) for a solution.
Nuisance.exe is a C# application with default stacksize callling a win32 dll about which I know nothing.

If Nuisance.exe was a C++ application, I would imagine that it calls NtOpenFile in a way similar to this, allocating one of pointer parameters on overwritten stack:
POBJECT_ATTRIBUTES MakeObjectAttributes()
{
POBJECT_ATTRIBUTES oa = {...};
return &oa; // Pointer to stack variable - UB
}
...
NtOpenFile(..., MakeObjectAttributes(), ...)
STATUS_INVALID_ACL (0xC0000077) error might suggest that SecurityDescriptor within OBJECT_ATTRIBUTES is allocated this way.
Then it matters how much stack is used by AfterThis_NtOpenFile_WillFail, and it is more than AfterThis_NtOpenFile_WillWork, since std::wstring would be larger than just a couple of pointers due to small string optimization.
If the call chain is always the same, the corruption may be deterministic.
I don't know if code equivalent of returning address of temporary is possible in C#. But the DLL may be in C/C++ or similar language that allows dandling pointers.
To prove/disprove the role of stack, try to allocate other data on stack that has std::wstring size. More precise proof could be checking passed pointer to see if they point to stack area that is about to be overwritten.

Related

Win32 API calls throwing access violation

I've got a simple piece of C++ code I'm exporting from a DLL.
DWORD WINAPI MessageBoxThread(LPVOID lpParam)
{
MessageBox(0, L"Test", L"Test", 0);
return 0;
}
Here's how I'm calling it
typedef DWORD(*MessageBoxThread)(LPVOID);
int StartMessageBoxThread() {
MessageBoxThread ShowMessageBox;
HMODULE testModule = LoadLibrary(L"C:\\Users\\david\\COMServer.dll");
ShowMessageBox = (MessageBoxThread)GetProcAddress(testModule, "MessageBoxThread");
ShowMessageBox(NULL);
FreeLibrary(testModule);
return 0;
}
I get an exception thrown in KernelBase.dll on the ShowMessageBox() line, involved an access violation when writing to a memory location.
I can't understand what I'm doing wrong. Both Visual Studio projects are set to Unicode, and I know using the L prefix denotes wide strings.
I can debug and step through into my DLL, I see the address of my function, so I can't see anything wrong with the code calling the function.
typedef DWORD(*MessageBoxThread)(LPVOID);
The prototype does not match the definition in dll. By default calling convention here is __cdecl whereas WINAPI is __stdcall
typedef DWORD(WINAPI *MessageBoxThread)(LPVOID);
Specifically, at the called end, since the convention is __stdcall(callee clears the stack), the function pops argument off the stack. At the caller end, it sees that the convention is __cdecl(caller clears the stack) and it also pops the argument from the stack, eventually corrupting the stack.
In your StartMessageBoxThread() code, MessageBoxThread is declared incorrectly. Specifically, it is missing a calling convention, so it uses the compiler's default convention, which is typically __cdecl rather than __stdcall (what WINAPI maps to). Calling convention mismatches are a common cause of crashes, call stack corruption, etc.
Also, the code has no error checking at all.
Try this instead:
typedef DWORD (WINAPI *MessageBoxThread)(LPVOID);
int StartMessageBoxThread()
{
HMODULE testModule = LoadLibrary(L"C:\\Users\\david\\COMServer.dll");
if (testModule)
{
MessageBoxThread ShowMessageBox = (MessageBoxThread) GetProcAddress(testModule, "MessageBoxThread");
if (ShowMessageBox)
ShowMessageBox(NULL);
FreeLibrary(testModule);
}
return 0;
}

How to resolve access violation writing location when calling dll method

I'm using GetProcAddress to gain access to a standard Isapi Filter DLL method - the GetFilterVersion method which takes a pointer to a HTTP_FILTER_VERSION structure.
https://msdn.microsoft.com/en-us/library/ms525822(v=vs.90).aspx
https://msdn.microsoft.com/en-us/library/ms525465(v=vs.90).aspx
I've tested the code against a working Isapi filter that I've written and it works fine. I debug the code against an Isapi filter from a vendor (I don't have access to the source code or anything beyond the dll itself) and I get the exception, "access violation writing location". What could be the issue? (Both Isapi filters work in IIS.)
//Attempted to define function ptr several ways
typedef BOOL(__cdecl * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL( * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL(WINAPI * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
void arbitraryMethod()
{
HINSTANCE hDLL; // Handle to DLL
TRIRIGAISAPIV lpfnDllFunc2; // Function pointer
DWORD lastError;
BOOL uReturnVal2;
hDLL = LoadLibrary(L"iisWASPlugin_http.dll"); //vendor's dll
//hDLL = LoadLibrary(L"KLTWebIsapi.dll //my dll
if (hDLL != NULL)
{
lpfnDllFunc2 = (TRIRIGAISAPIV)GetProcAddress(hDLL, "GetFilterVersion");
if (!lpfnDllFunc2)
{
lastError = GetLastError();
// handle the error
FreeLibrary(hDLL);
//return 1;
}
else
{
HTTP_FILTER_VERSION pVer = { 6 };
//Call the function via pointer; Works with my dll, fails with vendor's
uReturnVal2 = lpfnDllFunc2(&pVer);
//................ HELP!!!!!!!!!!!!!
}
}
}
One issue that I see is that your function pointer declaration is incorrect.
According to the Microsoft documentation, GetFilterVersion is prototyped as:
BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer);
The WINAPI is a Windows macro that is actually defined as __stdcall, thus you are declaring the function pointer incorrectly when you used __cdecl.
What does WINAPI mean?
Thus, your declaration should be:
typedef BOOL(__stdcall * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
It could be that there are actually some additional structure fields filled by the custom filter.
You can try to increase the size of the structure to see if that will work, like for example:
struct HTTP_FILTER_VERSION_EXTRA {
HTTP_FILTER_VERSION v;
char[1024] extra;
};
HTTP_FILTER_VERSION_EXTRA ver;
ver.v.dwServerFilterVersion = 6;
uReturnVal2 = lpfnDllFunc2(&ver.v);
It is sometimes the case with the WinAPI structures that they allow versioning, so adding fields is possible. If the function doesn't then check (or doesn't know) the actual structure version, it might try to use an extended one which might be different than the one supplied - if the size of the supplied struct is then lesser than the structure version the func tries to use, bad things can happen.
Also check if the DLL is 64-bit or 32-bit. You cannot use 64-bit DLL by 32-bit app and vice versa (but I expect that would already fail during the LoadLibrary call).

Why I can call StringFromCLSID even without the calling of CoInitializeEx before?

I am learning COM through C++. From MSDN:
Applications are required to use CoInitializeEx before they make any
other COM library calls except for memory allocation functions.
The memory allocation functions is CoTaskMemAlloc and CoTaskMemFree in my opinion.
But I see, my "Hello World" works fine with and without the CoInitializeEx and CoUninitialize functions calling. In my code I use the StringFromCLSID function which is declared in the combaseapi.h header. So, it is a COM function in my opinion. My code:
/* entry_point.cpp */
#include "Tools.h"
#include <objbase.h>
int main(){
HRESULT hr = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (FAILED(hr)){
trace("Can't initialize COM for using in the current thread.");
keep_window_opened();
return 1;
}
// {D434CF7D-2CDD-457A-A4EF-5822D629CE83}
static const CLSID clsid =
{ 0xd434cf7d, 0x2cdd, 0x457a, {
0xa4, 0xef, 0x58, 0x22, 0xd6, 0x29, 0xce, 0x83 } };
const size_t SIZE = 39;
wchar_t* wch = nullptr;
hr = ::StringFromCLSID(clsid, &wch);
if (FAILED(hr)){
trace("Can't convert CLSID to wchar_t array.");
}
else{
trace("CLSID converted to wchar_t array.");
char mch[SIZE];
size_t count = 0;
int result = ::wcstombs_s(&count, mch, wch, SIZE);
if (result){
trace("Can't convert wchar_t array to char array.");
}
else{
trace(mch);
}
::CoTaskMemFree(wch);
}
::CoUninitialize();
keep_window_opened();
return 0;
}
If I remove the calls of CoInitializeEx and CoUninitialize functions, then my code works still. I expected it will not work...
Why StringFromCLSID work even without the calling of CoInitializeEx before?
Thank you.
StringFromCLSID is basically a printout of GUID value (bytes) into string, then formatting it nicely with hyphens and braces. There is nothing else involved and hence COM initialization is not really needed for this call to succeed.
You have to do CoInitialize/CoInitializeEx to be safe, but not doing it you don't necessarily hit a problem right away.
Why StringFromCLSID work even without the calling of CoInitializeEx before?
The key information is stated right in the documentation.
CoInitializeEx function:
You need to initialize the COM library on a thread before you call any of the library functions except ... the memory allocation functions. Otherwise, the COM function will return CO_E_NOTINITIALIZED.
StringFromCLSID function:
StringFromCLSID calls the StringFromGUID2 function to convert a globally unique identifier (GUID) into a string of printable characters.
The caller is responsible for freeing the memory allocated for the string by calling the CoTaskMemFree function.
StringFromCLSID() returns a dynamically allocated string. We can infer from the highlighted sentence above that the memory is allocated using CoTaskMemAlloc() - which is explicitly documentated as not requiring CoInitializeEx().
StringFromGUID2() formats GUID data into a caller-specified memory block. Formatting a string does not require COM functionality. The wsprintfW(), StringCbPrintfW(), or other equivalent function would suffice. So CoInitializeEx() should not be required for StringFromGUID2(), even though this is not explicitly documented. I think it would be pretty short-sighted for Microsoft to not use one of their many available string formatting functions to implement StringFromGUID2(). So I think it should be safe to say that CoInitializeEx() is not a requirement for this (unless Microsoft says otherwise).
The GUID structure simply contains a few numbers and bytes. Declaring and using a GUID is not dependent on the COM library. You can freely use a GUID in your code all you want without touching COM at all - unless you want to generate a new GUID, in which case the CoInitializeEx() requirement for CoCreateGUID() is blurry as CoCreateGUID() is in the COM library but is explicitly documented as simply calling UuidCreate(), which is in the RPC library instead.
So that is why you can call StringFromCLSID() without calling CoInitializeEx() first. A GUID on its own does not require COM initialization. The string is being allocated with a memory function that does not require COM initialization. And the string is being formatted in a manner that most likely does not require COM initialization.

iTunes COM interface - Cannot add song to library

I'm trying to add a simple .mp3 file to the iTunes library but my program keeps crashing when I call AddFile(). However, when I call get_Tracks() it returns a valid pointer, so I suppose the pointer to IITLibraryPlaylist is valid. What am I doing wrong?
IiTunes* p_iTunes;
IITLibraryPlaylist* p_Library;
IITOperationStatus* status;
IITTrackCollection* iTrackCollection;
CoInitialize(0);
if (FAILED(CoCreateInstance(CLSID_iTunesApp, NULL, CLSCTX_LOCAL_SERVER, IID_IiTunes, (PVOID *)&p_iTunes))){
p_iTunes->Release();
CoUninitialize();
}
else{
p_iTunes->get_LibraryPlaylist(&p_Library);
p_Library->get_Tracks(&iTrackCollection); // This works, so I suppose p_Library is valid..
long trackCount = 0;
iTrackCollection->get_Count(&trackCount);
p_Library->AddFile(L"C:\\asd\asd.mp3",&status); // crashes here
}
The problem is you pass WCHAR* instead of properly allocated BSTR and that leads to undefined behavior.
You should first allocate a BSTR using SysAllocString() (don't forget to release the string later) or better yet use a wrapper class like ATL::CComBSTR or _bstr_t for managing BSTR lifetime.

Win API wrapper classes for handles

Writing a wrapper class for a handle that only gets passed by value is relatively easy. I am trying to determine what the best way is to encapsulate handles that need to be passed by address.
For example, writing a wrapper for something like SC_HANDLE that gets passed by value to QueryServiceConfig() is not that difficult. One can either implement a member function like .GetHandle() or implement operator().
The problem (for me at least) is API functions like RegOpenKeyEx() that wants the address of a HKEY.
I've read that overloading operator & is generally a bad idea. What is the recommended way to keep encapsulation (or as much of it as possible) and especially resource collection while allowing API functions access?
You can always add another layer of indirection to avoid the awful overloading of operator& and ugly Attach or Detach and return a pre-wrapped instance from there.
If you can use C++0x in VS2010 or gcc, or have other ways of accessing std::unique_ptr<>, then you can do this (error checking omitted for brevity):
struct hkey_deleter
{
void operator()(HKEY hkey)
{
::RegCloseKey(hkey);
}
};
typedef std::unique_ptr<HKEY__, hkey_deleter> regkey;
regkey MyRegOpenKeyEx(HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions, REGSAM samDesired)
{
HKEY hOpenedKey = NULL;
::RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, &hOpenedKey);
return regkey(hOpenedKey);
}
void SomewhereElse()
{
...
regkey r = MyRegOpenKeyEx(HKEY_CLASSES_ROOT, nullptr, 0, KEY_READ);
...
}
The hkey_deleter will make sure that the registry key gets closed when the scope is exited or regkey::reset() is called.