Compiling a service DLL (a Windows DLL implementing a service to be executed inside a shared svchost.exe process) with MinGW on Kali, is almost the same as compiling a DLL.
A service DLL implements a service and must export a ServiceMain function. To prevent name mangling of the exported function, the ServiceMain definition needs to be ‘extern “C”‘:
extern “C” VOID WINAPI ServiceMain(DWORD dwArgc, LPCWSTR* lpszArgv)
And we need a DEF file:
LIBRARY SvcHostDemo EXPORTS ServiceMain
This DEF file can be passed as argument to the MinGW compiler.
To create a 32-bit DLL:
i686-w64-mingw32-gcc -shared -municode -o SvcHostDemo.dll SvcHostDemo.def SvcHostDemo.cpp
To create a 64-bit DLL:
x86_64-w64-mingw32-gcc -shared -municode -o SvcHostDemo.dll SvcHostDemo.def SvcHostDemo.cpp
Option -municode is needed because I use TCHARs and want to compile a UNICODE executable.
Here is the code of my service template:
#define _UNICODE #include <windows.h> #include <stdio.h> #include <tchar.h> #include <strsafe.h> SERVICE_STATUS_HANDLE g_serviceStatusHandle = nullptr; HANDLE g_hSvcStopEvent = NULL; SERVICE_STATUS g_serviceStatus = {SERVICE_WIN32_SHARE_PROCESS, SERVICE_START_PENDING, SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE}; BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { OutputDebugString(L"DllMain"); switch (dwReason) { case DLL_PROCESS_ATTACH: OutputDebugString(L"DLL_PROCESS_ATTACH"); break; case DLL_THREAD_ATTACH: OutputDebugString(L"DLL_THREAD_ATTACH"); break; case DLL_THREAD_DETACH: OutputDebugString(L"DLL_THREAD_DETACH"); break; case DLL_PROCESS_DETACH: OutputDebugString(L"DLL_PROCESS_DETACH"); break; } return TRUE; } DWORD WINAPI HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext) { switch (dwControl) { case SERVICE_CONTROL_STOP: OutputDebugString(L"HandlerEx SERVICE_CONTROL_STOP"); g_serviceStatus.dwCurrentState = SERVICE_STOPPED; SetEvent(g_hSvcStopEvent); break; case SERVICE_CONTROL_SHUTDOWN: OutputDebugString(L"HandlerEx SERVICE_CONTROL_SHUTDOWN"); g_serviceStatus.dwCurrentState = SERVICE_STOPPED; SetEvent(g_hSvcStopEvent); break; case SERVICE_CONTROL_PAUSE: OutputDebugString(L"HandlerEx SERVICE_CONTROL_PAUSE"); g_serviceStatus.dwCurrentState = SERVICE_PAUSED; break; case SERVICE_CONTROL_CONTINUE: OutputDebugString(L"HandlerEx SERVICE_CONTROL_CONTINUE"); g_serviceStatus.dwCurrentState = SERVICE_RUNNING; break; case SERVICE_CONTROL_INTERROGATE: OutputDebugString(L"HandlerEx SERVICE_CONTROL_INTERROGATE"); break; default: OutputDebugString(L"HandlerEx default"); break; } SetServiceStatus(g_serviceStatusHandle, &g_serviceStatus); return NO_ERROR; } DWORD WINAPI Worker(LPVOID lpParam) { while (TRUE) { if (g_serviceStatus.dwCurrentState == SERVICE_RUNNING) OutputDebugString(L"Working ..."); Sleep(1000); } } extern "C" VOID WINAPI ServiceMain(DWORD dwArgc, LPCWSTR* lpszArgv) { TCHAR szOutput[256]; DWORD dwIter; OutputDebugString(L"Begin ServiceMain"); StringCchPrintf(szOutput, 256, L"dwArgc = %d", dwArgc); OutputDebugString(szOutput); for (dwIter=0; dwIter<dwArgc; dwIter++) OutputDebugString(lpszArgv[dwIter]); if (dwArgc > 0) g_serviceStatusHandle = RegisterServiceCtrlHandlerExW(lpszArgv[0], HandlerEx, nullptr); else g_serviceStatusHandle = RegisterServiceCtrlHandlerExW(L"SvcHostDemo", HandlerEx, nullptr); if (!g_serviceStatusHandle) { OutputDebugString(L"Error 1 ServiceMain"); return; } g_hSvcStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (g_hSvcStopEvent == NULL) { OutputDebugString(L"Error 2 ServiceMain"); return; } g_serviceStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(g_serviceStatusHandle, &g_serviceStatus); OutputDebugString(L"Starting Worker ServiceMain"); if (NULL == CreateThread(NULL, 0, Worker, NULL, 0, NULL)) { OutputDebugString(L"Error 3 ServiceMain"); return; } OutputDebugString(L"Waiting ServiceMain"); WaitForSingleObject(g_hSvcStopEvent, INFINITE); OutputDebugString(L"End ServiceMain"); }