在Windows下,创建服务EXE需要特殊格式,这里分享记录一下,方便后面复制调试。

服务约定格式

一个最小的服务至少需要三个函数:

  • A Main Entry point (like any application),StartServiceCtrlDispatcher启动一个服务的线程,用ServiceTable维护一个类似的结构,提供关于这个服务的详细信息
int main(int argc, TCHAR* argv[])
{
    SERVICE_TABLE_ENTRY ServiceTable[] =
    {
        { const_cast<LPWSTR>(SERVICE_NAME), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
        { NULL, NULL }
    };

    if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
    {
        DWORD dwError = GetLastError();
        std::cerr << "StartServiceCtrlDispatcher error: " << dwError << std::endl;
        return dwError;
    }

    return 0;
}
  • A Service Entry point 服务入口,不难看出服务实际上是可以接受参数的
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
	//注册服务
    g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);

    if (g_StatusHandle == NULL)
    {
        exit(GetLastError());
    }

    ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
    g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwServiceSpecificExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;
    //设置服务状态,目前是SERVICE_START_PENDING

    if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugStringW(L"My Sample Service: ServiceMain: SetServiceStatus returned error");
    }

    g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    //如果事件创建失败,就直接退出程序
    if (g_ServiceStopEvent == NULL)
    {
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwWin32ExitCode = GetLastError();
        g_ServiceStatus.dwCheckPoint = 1;

        if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            OutputDebugStringW(L"My Sample Service: ServiceMain: SetServiceStatus returned error");
        }
        exit(0);
    }

    g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;
	//事件成功的话,就代表服务正在运行了
    if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugStringW(L"My Sample Service: ServiceMain: SetServiceStatus returned error");
    }
	//创建一个线程,等待这个线程完成,ServiceWorkerThread函数写我们的恶意代码
    HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(g_ServiceStopEvent);
    
    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 3;

    if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugStringW(L"My Sample Service: ServiceMain: SetServiceStatus returned error");
    }

EXIT:
    return;
}
  • A Service Control Handler 服务控制器,根据CtrlCode来接受SCM的控制请求,一旦CtrlCode等于SERVICE_CONTROL_STOP,就调用SetServiceStatus API设置服务状态为SERVICE_STOP_PENDING
VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode)
{
    switch (CtrlCode)
    {
    case SERVICE_CONTROL_STOP:

        if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
            break;

        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 4;

        if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            OutputDebugStringW(L"My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error");
        }
        SetEvent(g_ServiceStopEvent);
        break;

    default:
        break;
    }
}

测试代码

#include <windows.h>
#include <string>
#include <iostream>


SERVICE_STATUS        g_ServiceStatus = { 0 };
SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
HANDLE                g_ServiceStopEvent = INVALID_HANDLE_VALUE;

VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
VOID WINAPI ServiceCtrlHandler(DWORD);
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);

const wchar_t SERVICE_NAME[] = L"My Sample Service";

bool CreateAndWriteFile(const std::wstring& filePath, const std::string& data);

int main(int argc, TCHAR* argv[])
{
    SERVICE_TABLE_ENTRY ServiceTable[] =
    {
        { const_cast<LPWSTR>(SERVICE_NAME), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
        { NULL, NULL }
    };

    if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
    {
        DWORD dwError = GetLastError();
        std::cerr << "StartServiceCtrlDispatcher error: " << dwError << std::endl;
        return dwError;
    }

    return 0;
}

VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
    g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);

    if (g_StatusHandle == NULL)
    {
        exit(GetLastError());
    }

    ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus));
    g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwServiceSpecificExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;

    if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugStringW(L"My Sample Service: ServiceMain: SetServiceStatus returned error");
    }

    g_ServiceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (g_ServiceStopEvent == NULL)
    {
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwWin32ExitCode = GetLastError();
        g_ServiceStatus.dwCheckPoint = 1;

        if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            OutputDebugStringW(L"My Sample Service: ServiceMain: SetServiceStatus returned error");
        }
        exit(0);
    }

    g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
    g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 0;

    if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugStringW(L"My Sample Service: ServiceMain: SetServiceStatus returned error");
    }

    HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL);

    WaitForSingleObject(hThread, INFINITE);

    CloseHandle(g_ServiceStopEvent);

    g_ServiceStatus.dwControlsAccepted = 0;
    g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    g_ServiceStatus.dwWin32ExitCode = 0;
    g_ServiceStatus.dwCheckPoint = 3;

    if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
    {
        OutputDebugStringW(L"My Sample Service: ServiceMain: SetServiceStatus returned error");
    }

EXIT:
    return;
}

VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode)
{
    switch (CtrlCode)
    {
    case SERVICE_CONTROL_STOP:

        if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
            break;

        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 4;

        if (SetServiceStatus(g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            OutputDebugStringW(L"My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error");
        }
        SetEvent(g_ServiceStopEvent);
        break;

    default:
        break;
    }
}

bool CreateAndWriteFile(const std::wstring& filePath, const std::string& data) {
    HANDLE hFile = CreateFile(
        filePath.c_str(),
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );

    if (hFile == INVALID_HANDLE_VALUE) {
        return false;
    }

    DWORD bytesWritten;

    if (!WriteFile(hFile, data.c_str(), data.size(), &bytesWritten, NULL)) {
        CloseHandle(hFile);
        return false;
    }

    CloseHandle(hFile);
    return true;
}

DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
    std::wstring filePath = L"1.txt";
    std::string data = "111";
    //使用相对路径,保存在C:\Windows\System32目录下,推荐使用temp目录
    //C:\\Windows\\Temp\\1.txt,测试是否工作,重启之后不知道还有没有,得测试分析一下
    if (CreateAndWriteFile(filePath, data)) {
    }
    else {
    }
    while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
    {
        Sleep(3000);
    }
    return ERROR_SUCCESS;
}

链接到静态库

编译服务需要链接 Kernel32.lib and Advapi32.lib 否则无法找到外部解析

位置在这里:

编译运行

创建服务,并开启开机启动:

sc create "endlessparadox" binPath= C:\SampleService.exe start= auto

“Specifies a service that automatically starts each time the computer is restarted and runs even if no one logs on to the computer.” 微软原话,适合无登录的情况,以System权限运行。如果是用户登录就以低权限运行,具体情况得具体分析,不过服务有模拟权限,直接getsystem就行了。

启动服务:

sc start "endlessparadox" 

停止服务:

sc stop "endlessparadox" 

重启,一切工作正常。

最后

被windows的数据类型搞的有点烦了,后面得重新复习一下。补一个最近推的美少女:有村路美,贫乳萝莉+天才少女,真的太香了:

应该是今年推的TOP3的萝莉美少女了

参考:

https://www.codeproject.com/Articles/499465/Simple-Windows-Service-in-Cplusplus https://learn.microsoft.com/en-us/windows/win32/services/the-complete-service-sample