How to Audit and Identify Who Disabled a Windows Service: Forensic Investigation Guide for Sysadmins


1 views

When critical Windows services mysteriously get disabled, system administrators need forensic tools to trace the modification. The key data sources include:

  • Windows Security Event Log (Event ID 7040)
  • System Event Log (Event ID 7036)
  • PowerShell command history (if applicable)
  • Registry modification timestamps

The most reliable method is querying Windows Event Logs with precise filtering. Here's the PowerShell command to extract service change events:

Get-WinEvent -LogName System | 
Where-Object {$_.Id -eq 7040 -or $_.Id -eq 7036} | 
Select-Object TimeCreated, Id, Message | 
Format-List

For more targeted results showing disabled services specifically:

Get-WinEvent -FilterHashtable @{
    LogName='System'
    ID=7040
    StartTime=(Get-Date).AddDays(-7)
} | Where-Object {$_.Message -match 'disabled'} 

If the events aren't logged, you may need to enable advanced auditing policies first:

auditpol /set /subcategory:"Security System Extension" /success:enable /failure:enable
auditpol /set /subcategory:"Other System Events" /success:enable /failure:enable

Services configuration is stored in HKLM\SYSTEM\CurrentControlSet\Services. You can check registry last modified times:

Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\YourServiceName | 
Select-Object PSPath, PSParentPath, PSChildName, PSDrive, 
PSProvider, Property, @{N='Modified';E={$_.PSPath | Get-Item | Select-Object -ExpandProperty LastWriteTime}}

Here's a complete PowerShell script that logs service configuration changes:

# ServiceChangeAudit.ps1
$services = Get-Service | Where-Object {$_.StartType -eq 'Disabled'}
$output = @()

foreach ($service in $services) {
    $regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$($service.Name)"
    $regItem = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue
    
    if ($regItem) {
        $lastModified = (Get-Item $regPath).LastWriteTime
        $output += [PSCustomObject]@{
            ServiceName = $service.Name
            DisplayName = $service.DisplayName
            CurrentStatus = $service.Status
            StartType = $service.StartType
            LastModified = $lastModified
            RegistryPath = $regPath
        }
    }
}

$output | Export-Csv -Path "C:\Audit\DisabledServices_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation

For domain environments, check Security logs for Event ID 4663 (filesystem access) filtered on the registry hive:

Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4663]] and *[EventData[Data[@Name='ObjectName'] and 
(Data='\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services')]]" | 
Select-Object TimeCreated, @{n='User';e={$_.Properties[1].Value}}, 
@{n='Process';e={$_.Properties[5].Value}}, 
@{n='AccessMask';e={$_.Properties[8].Value}} | 
Format-Table -AutoSize

To prevent unauthorized service modifications:

  1. Implement proper ACLs on service registry keys
  2. Enable detailed service change auditing
  3. Regularly export service configurations as baselines
  4. Monitor with SIEM solutions for real-time alerts

For historical tracking, WMI permanent event consumers can log service changes:

$query = "SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Service'"
$action = {Param($target) Write-EventLog -LogName System -Source "Service Monitor" -EventId 9999 -EntryType Information -Message "Service change detected: $($target.PreviousInstance.StartType) → $($target.TargetInstance.StartType) by $($env:USERNAME)"}

Register-WmiEvent -Query $query -Action $action -SourceIdentifier "ServiceChangeMonitor" -MaxTriggerCount 0

When critical Windows services mysteriously change from Automatic to Disabled, administrators need forensic tools to identify the responsible party. This investigation involves multiple Windows logging subsystems and sometimes requires custom PowerShell scripting.

The most reliable approach checks Windows Security logs for Event ID 7040 ("Service configuration changed"). However, this requires proper audit policies:

# Verify audit policy is configured
auditpol /get /subcategory:"Security System Extension"

# Enable if not configured (requires admin)
auditpol /set /subcategory:"Security System Extension" /success:enable /failure:enable

Sample log entry structure:

Event ID: 7040
Task Category: Service Control Manager
Modified Service: W32Time (Windows Time)
Original Start Type: Automatic
New Start Type: Disabled
User: DOMAIN\jsmith
Process: C:\Windows\System32\services.exe

If changes were made via PowerShell, check command history:

# Get PowerShell console history
Get-Content (Get-PSReadlineOption).HistorySavePath

# Check for Set-Service commands
Get-WinEvent -LogName "Windows PowerShell" | 
    Where-Object {$_.Message -like "*Set-Service*"} |
    Select-Object TimeCreated, Message

Enable debug logging for Service Control Manager (SCM):

# Create registry key
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control" 
    -Name "SvcHostSplitDisable" 
    -Value 0 
    -PropertyType DWORD -Force

# Set logging level
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Tracing" 
    -Name "ServiceControlManager" 
    -Value 1

Logs will appear in %SystemRoot%\System32\LogFiles\Scm with detailed change records including timestamps and process IDs.

For ongoing monitoring, implement a real-time watcher:

$query = @"
<QueryList>
  <Query Id="0" Path="Security">
    <Select Path="Security">
      *[System[EventID=7040]]
    </Select>
  </Query>
</QueryList>
"@

Register-WinEvent -Query $query -Action {
    $event = $args[0]
    $user = $event.Properties[6].Value
    $service = $event.Properties[0].Value
    $changeType = $event.Properties[3].Value
    Write-Host "[$(Get-Date)] $user modified $service: $changeType"
}

In domain environments, consider these additional sources:

  • SIEM system logs (if forwarding Windows events)
  • AD audit logs for privileged account usage
  • Windows Task Scheduler history for automated scripts