mIRC Home    About    Download    Register    News    Help

Print Thread
#183614 21/08/07 09:37 AM
Joined: Oct 2003
Posts: 3,918
A
argv0 Offline OP
Hoopy frood
OP Offline
Hoopy frood
A
Joined: Oct 2003
Posts: 3,918
There is a bug in the LoadDll API that causes mIRC to crash if you send a SendMessage() command from within LoadDll() to mIRC which in turn calls any function in the DLL.

How to Reproduce

The following is the source to the DLL I used to reproduce the bug. Compile it and run /dll mcrash.dll initial_call to call a seemingly normal function. The LoadDll() function should force mIRC to run another function in the DLL before calling your initial_call function.

You should see a message box for "Inside crash()" popup first-- this is because LoadDll() calls it before your initial_call is fully executed. mIRC does not crash here- it executes the /dll call from SendMessage perfectly fine. It is upon returning from the function that it crashes.

The crash is an Access Violation, seemingly due to a jmp to unitialized address space, perhaps the stack was corrupted by the second call, overwriting the return address?

mcrash.cpp:
Code:
#include <windows.h>
 
typedef struct {
    DWORD  mVersion;
    HWND   mHwnd;
    BOOL   mKeep;
} LOADINFO;

#define WM_MCOMMAND        (WM_USER+200)
#define WM_MEVALUATE        (WM_USER+201)

static void SendToMirc(HWND mWnd, char *command) {
    HANDLE handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, "mIRC999");
    char *data = (char *)MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    strcpy_s(data, 4096, command);
    SendMessage(mWnd, WM_MCOMMAND, 1, 999);   
    UnmapViewOfFile(data);
    CloseHandle(handle);    
}

int __declspec(dllexport) __stdcall initial_call(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause) {
    MessageBoxA(NULL, "Inside initial_call()", "MCRASH", MB_OK);
    return 1;
}

int __declspec(dllexport) __stdcall crash(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause) {
    MessageBoxA(NULL, "Inside crash()", "MCRASH", MB_OK);
    return 1;
}

void __declspec(dllexport) __stdcall LoadDll(LOADINFO *t) {
    SendToMirc(t->mHwnd, "/dll mcrash.dll crash ."); 
    t->mKeep = FALSE;
}


Conclusion

My theory is that the call stack is somehow becoming corrupt upon returning from the second DLL call inside LoadDll(), specifically corrupting the return address. This would explain the program counter ending up in random address spaces. There is no reason the above code should have corrupted the stack on its own.

The crash is probably related to the fact that the DLL is not fully loaded at the time of the call, however, because the LoadDll function is part of mIRC's DLL API, it should be mIRC's responsibility to properly initialize the DLL before handing off code control to the user.

Sidenote

There is also a side-"bug" that was noticed, but calling a DLL without the .dll extension will cause LoadLibrary to re-load the DLL (as if it was never loaded). This is not a bug in itself, since this is a feature of LoadLibrary, though mIRC should do some checking to make sure a DLL is not loaded twice in this fashion.

One interesting observation was that if you run the above code by /dll mcrash initial_call it will not crash. This is because the LoadDll function calls on /dll mcrash.dll, which will re-load the library, calling LoadDll twice-- however, in this case, mIRC spits out an interesting warning:

Code:
* /dll: no such routine 'crash'


This may be related to the above bug.

Last edited by argv0; 21/08/07 09:54 AM.

- argv[0] on EFnet #mIRC
- "Life is a pointer to an integer without a cast"
argv0 #183778 22/08/07 02:33 PM
Joined: Dec 2002
Posts: 5,426
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,426
I was able to reproduce this issue. The reason for the crash is that when you make the second call, mIRC notices that the DLL is already loaded, calls it, and then immediately unloads it because the keep flag is false. When it returns to continue processing the first dll call, the dll has been unloaded.

The LoadDLL function should really only perform initialization of settings and should return the keep value to mIRC so that it can be processed when mIRC first loads the DLL and initializes its own internal settings.

That said, I'll add an extra check to prevent mIRC from unloading the DLL in this particular situation.


Link Copied to Clipboard