Thread: Adding controls when DLL hooking

  1. #1
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401

    Adding controls when DLL hooking

    I've DLL hooked calc.exe, and I'm trying to created a new button, but it's not working. CreateWindowEx() returns a handle, but I can't see anything. When I check Spy++, I can't find the button window anywhere. Does anyone know what's going on?

    Code:
    BOOL WINAPI DllMain(HANDLE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
    {
    	if (fdwReason==DLL_PROCESS_ATTACH)
    	{
    		HWND hFind=NULL;
    		DWORD thisProcessId=GetCurrentProcessId();
    		DWORD wndProcessId;
    
    		do
    		{
    			hFind=FindWindowEx(NULL, hFind,NULL, "Calculator");
    
    			GetWindowThreadProcessId(hFind, &wndProcessId);
    		} while (thisProcessId!=wndProcessId);
    
    		lpCalculator=(WNDPROC)SetWindowLong(hFind, GWL_WNDPROC, (LONG)WndProc);
    		RECT rc;
    		GetWindowRect(hFind,&rc);
    		MoveWindow(hFind,rc.left,rc.top,(rc.right-rc.left)+500, rc.bottom-rc.top,TRUE);
    		hButton=CreateWindowEx(WS_EX_CLIENTEDGE,"BUTTON","YO",
    								WS_CHILD| WS_VISIBLE|BS_DEFPUSHBUTTON,
    									500,5,100,100,hFind,(HMENU)1020,
    									(HINSTANCE)GetWindowLong(hFind, GWL_HINSTANCE),NULL);
    		if (!hButton)
    		{
    			MessageBox(NULL,"couldn't create button","yo", MB_OK);
    		}
    		else
    		{
    			MessageBox(NULL,"created button","yo", MB_OK);
    			ShowWindow(hButton,TRUE);
    		}
    		
    		bButtonVisible=TRUE;
    	}
    
    	return TRUE;
    }
    It is very fustrating!
    Last edited by bennyandthejets; 10-18-2003 at 10:20 PM.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  2. #2
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    The button is visible while the MessageBox is still around. The problem is the button is being created in the new thread. When DllMain returns and the thread exits, all windows owned by the thread are automatically destroyed. I'll have a look on the web to find the solution.

  3. #3
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Thanks, that's the same conclusion I came to after debugging it for a while. The DLL module is still attached to the process but the DllMain entry point function was part of a new thread, which exited when the function returned.

    How about placing the creation code in a window procedure? If I subclassed the top-level window, and the new window procedure is part of my DLL, then the code should be executed in the context of the initial thread. Am I correct?

    I'll try it out and post my results.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  4. #4
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    OK. This works but it is about as stable as a paper airplane in a cyclone(aka hurricane & typhoon). There seems to be very little about this on the web. Changing any line is likely to cause a crash.

    Code:
    #include <windows.h>
    #include <stdio.h>
    
    WNDPROC lpCalculator;
    HWND hButton;
    BOOL bButtonVisible;
    HANDLE hEvent;
    
    
    // ================================================
    LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam,  LPARAM lParam) {
    
    	if (uMsg == WM_DESTROY) {
    		SetEvent(hEvent);
    	}
    
    	if (uMsg == WM_COMMAND &&
    	    LOWORD(wParam) == 1020 &&
    	    HIWORD(wParam) == BN_CLICKED) {
    		MessageBox(NULL, "I got clicked", NULL, 0);
    	}
    
    	return CallWindowProc(lpCalculator, hwnd, uMsg, wParam, lParam);
    }
    
    
    // ================================================
    void DoEvents(void) {
    
    	MSG Msg;
    
    	while ( PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) ) {
    		TranslateMessage(&Msg);
    		DispatchMessage(&Msg);
    	}
    }
    
    
    // ===============================================
    BOOL WINAPI LibMain(HANDLE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
    {
    
    	if (fdwReason==DLL_PROCESS_ATTACH) {
    
    		HWND hFind = NULL;
    		DWORD wndProcessId;
    		RECT rc;
    		DWORD thisProcessId=GetCurrentProcessId();
    
    		do
    		{
    			hFind=FindWindowEx(NULL, hFind,NULL, "Calculator");
    
    			GetWindowThreadProcessId(hFind, &wndProcessId);
    		} while (thisProcessId!=wndProcessId);
    
    		hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    
    		lpCalculator = (WNDPROC) SetWindowLongPtr(hFind, GWL_WNDPROC, (LONG_PTR) WndProc);
    
    		GetWindowRect(hFind,&rc);
    		MoveWindow(hFind, rc.left, rc.top, (rc.right-rc.left) + 500, rc.bottom - rc.top, TRUE);
    
    		hButton = CreateWindowEx(0, "BUTTON", "MYBUT",
    				         WS_CHILD | WS_VISIBLE,
    				         500,5,100,100,hFind,(HMENU)1020,
    				         (HINSTANCE) GetWindowLong(hFind, GWL_HINSTANCE), NULL);
    
    		if (!hButton) MessageBox(NULL,"couldn't create button","yo", MB_OK);
    		else MessageBox(NULL,"created button","yo", MB_OK);
    
    		bButtonVisible=TRUE;
    
    		while (MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLEVENTS) != WAIT_OBJECT_0) {
    			DoEvents();
    		}
    	}
    
    	return TRUE;
    }

  5. #5
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    It does appear to be quite tricky. Looking at the code I'm guessing the LoadLibrary thread created by my external program won't terminate until this process terminates. I'd prefer to eliminate this behavior, but maybe it's too hard.

    I tried something else, creating a button in the window procedure:

    Code:
    LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
    {
    	switch (msg)
    	{
    	case WM_TIMER:
    		
    		switch (wParam)
    		{
    		case ID_HOOK:
    			if (bButtonVisible)
    			{
    				SetWindowText(hwnd,"false");
    				bButtonVisible=FALSE;
    			}
    			else
    			{
    				SetWindowText(hwnd,"true");
    				bButtonVisible=TRUE;
    			}
    			return 0;
    		
    		default:
    			break;
    		}
    
    		break;
    
    	case WM_COMMAND:
    		switch (LOWORD(wParam))
    		{
    		case 104:
    			{
    				HWND hButton2=CreateWindow("BUTTON","button",
    											WS_CHILD|WS_VISIBLE,
    											0,0,20,100,hwnd,(HMENU)1070,
    											(HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),NULL);
    			}
    			break;
    
    		default:
    			break;
    		}
    		break;
    
    	default:
    		break;
    	}
    	
    	return CallWindowProc(lpCalculator,hwnd,msg,wParam,lParam);
    }
    It sort of works, but there's some painting issues. I believe the button is painted when WM_PAINT is called for the top-level window, but when another button is clicked then released, it disappears. There may be some painting conflicts between things. Although, I don't see what the problem is, because all I'm doing is creating a new child window, and this time it's from the same thread as everything else. Argggg!
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  6. #6
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Okay, the problem in my last post was because clicking that button kept trying to create the same button again and again. I added in some code to prevent that, and now it's fine.

    Code:
    LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
    {
    	switch (msg)
    	{
    	case WM_TIMER:
    		
    		switch (wParam)
    		{
    		case ID_HOOK:
    			if (bButtonVisible)
    			{
    				SetWindowText(hwnd,"false");
    				bButtonVisible=FALSE;
    			}
    			else
    			{
    				SetWindowText(hwnd,"true");
    				bButtonVisible=TRUE;
    			}
    			return 0;
    		
    		default:
    			break;
    		}
    
    		break;
    
    	case WM_COMMAND:
    		switch (LOWORD(wParam))
    		{
    		case 104:
    			{
    				if (button2)
    					break;
    
    				HWND hButton2=CreateWindow("BUTTON","button",
    											WS_CHILD|WS_VISIBLE,
    											0,0,20,100,hwnd,(HMENU)1070,
    											(HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),NULL);
    				UpdateWindow(hwnd);
    				button2=TRUE;
    			}
    			break;
    
    		default:
    			break;
    		}
    		break;
    
    	default:
    		break;
    	}
    	
    	return CallWindowProc(lpCalculator,hwnd,msg,wParam,lParam);
    }
    Does that all make sense?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  7. #7
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    I checked the thread ID in the window procedure, it's the correct thread. So, any windows to be created must be done in the window procedure, and not in the DLL entry point.

    I have a question: when I use CreateRemoteThread() with LoadLibraryA() in my other program, what does it do? Does it create a new thread, start the execution at LoadLibrary(), load the DLL, and then run the DLL entry point code in the context of this new thread? If all that is true, it would sure explain a lot. Man this stuff is confusing!

    I was thinking about something: what if I send a user message to the window from the DLL entry point function? Then, in the handler for that message, I do everything that needs to be done from the original thread? That would mean that everything is initialized as soon as the DLL is loaded, rather than after I click a button. One problem though, how do I know the target process isn't using that same user message? It's pretty unlikely, but still possible, that I could mess around with something they are also doing. What are your thoughts on this?
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  8. #8
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Putting the CreateWindow in the window procedure seems to be the way to go(I wish I'd thought of that instead of hacking around).
    I have a question: when I use CreateRemoteThread() with LoadLibraryA() in my other program, what does it do? Does it reate a new thread, start the execution at LoadLibrary(), load the DLL, and then run the DLL entry point code in the context of this new thread?
    Yes, I think so. The thread completes as soon as LoadLibraryA returns, which is right after DllMain returns. However, the dll is still loaded so its code(such as the window procedure) is still available. Currently, the dll is unloaded implicitly when the process completes. It can be done explicitly by using CreateRemoteThread with FreeLibrary.
    I was thinking about something: what if I send a user message to the window from the DLL entry point function? Then, in the handler for that message, I do everything that needs to be done from the original thread?
    Sounds good.
    One problem though, how do I know the target process isn't using that same user message?
    One can use RegisterWindowMessage to get a unique message number. This can be stored in a global variable for use between DllMain and WndProc.

    You seem to have all this pretty well figured out. You should post a working sample when it is complete as there is not much on the web. Most sites just mention that it can be done and then don't provide any code.
    Last edited by anonytmouse; 10-19-2003 at 02:00 PM.

  9. #9
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    OK, I used your ideas and it worked like a charm.
    Code:
    WNDPROC lpCalculator;
    HWND hButton;
    BOOL bButtonVisible;
    UINT g_uMsgInit;
    
    
    // ==============================================
    LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam,  LPARAM lParam) {
    
    	if (uMsg == g_uMsgInit) {
    		RECT rc;
    
    		GetWindowRect(hwnd,&rc);
    		MoveWindow(hwnd, rc.left, rc.top, (rc.right-rc.left) + 150, rc.bottom - rc.top, TRUE);
    
    		hButton = CreateWindowEx(0, "BUTTON", "Hello!", WS_CHILD | WS_VISIBLE,
    			                 500, 5, 100, 100, hwnd, (HMENU)1020,
    			                 (HINSTANCE) GetWindowLongPtr(hwnd, GWL_HINSTANCE), NULL);
    
    		bButtonVisible = TRUE;
    
    		return 0;
    	}
    
    	if (uMsg == WM_COMMAND &&
    	    LOWORD(wParam) == 1020 &&
    	    HIWORD(wParam) == BN_CLICKED) {
    		MessageBox(NULL, "I got clicked", "Hello!", 0);
    	}
    
    	return CallWindowProc(lpCalculator, hwnd, uMsg, wParam, lParam);
    }
    
    // ==============================================
    BOOL WINAPI LibMain(HANDLE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
    
    	if (fdwReason==DLL_PROCESS_ATTACH) {
    
    		HWND hFind = NULL;
    		DWORD dwCurrentProcessId;
    		DWORD dwWndProcessId;
    
    		dwCurrentProcessId = GetCurrentProcessId();
    
    		// Find target window...
    		do {
    			hFind = FindWindowEx(NULL, hFind, NULL, TEXT("Calculator"));
    
    			GetWindowThreadProcessId(hFind, &dwWndProcessId);
    		} while (dwCurrentProcessId != dwWndProcessId);
    
    		// Get unique window message id...
    		g_uMsgInit = RegisterWindowMessage(TEXT("l{4f3&vbEes'x.,kg][o.'[]0-9"));
    
    		// Subclass target window...
    		lpCalculator = (WNDPROC) SetWindowLongPtr(hFind, GWL_WNDPROC, (LONG_PTR) WndProc);
    
    		// Post init message to window...
    		PostMessage(hFind, g_uMsgInit, 0, 0);
    	}
    
    	return TRUE;
    }
    Last edited by anonytmouse; 10-19-2003 at 02:56 PM.

  10. #10
    mustang benny bennyandthejets's Avatar
    Join Date
    Jul 2002
    Posts
    1,401
    Yep, we've got it all figured out.

    Code:
    g_uMsgInit = RegisterWindowMessage(TEXT("l{4f3&vbEes'x.,kg][o.'[]0-9"));
    Are those random, to prevent doubling up? I could live with that, I suppose there's really no way to avoid it. Unless you can enumerate all existing registered window messages? Probably not necessary, but it would make it that last little bit complete.

    Thanks for all the help. I'll chuck together a whole workspace, with documentation, post it, and maybe a moderator can move it to the FAQ board. Seems pretty worthy.
    [email protected]
    Microsoft Visual Studio .NET 2003 Enterprise Architect
    Windows XP Pro

    Code Tags
    Programming FAQ
    Tutorials

  11. #11
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Yep, if you pass the same string to RegisterWindowMessage, you get the same id.
    However, the number of strings that can be produced with twenty five or so random characters means that an accidental match is essentially impossible.
    To put it another way 40^25 gives you a roughly one in 1.125e+40 chance of hitting a match.
    To put that in context one's chance of dying in a road accident in the next hour is about one in 87600000(10000*365*24) (higher in US, lower in Europe).
    Even a catastrophic asteroid strike or nuclear war is many millions of times more likely to occur.

    Therefore, it's not worth worrying about.

    P.S Didn't mean to depress anyone ;-)

    EDIT: I'm about 80% done adding a toolbar to notepad using this code. I will post it when done.
    Last edited by anonytmouse; 10-19-2003 at 08:48 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Dynamic adding of Controls
    By WDT in forum C# Programming
    Replies: 5
    Last Post: 04-22-2009, 07:59 AM
  2. dll communicating between each other
    By cloudy in forum C++ Programming
    Replies: 5
    Last Post: 06-17-2005, 02:20 AM
  3. confused about adding controls to main window
    By terracota in forum Windows Programming
    Replies: 4
    Last Post: 11-24-2004, 12:35 PM
  4. I need help on adding tooltips to controls
    By Templario in forum Windows Programming
    Replies: 6
    Last Post: 01-03-2003, 03:47 PM
  5. Tab Controls - API
    By -KEN- in forum Windows Programming
    Replies: 7
    Last Post: 06-02-2002, 09:44 AM