121 lines
4.1 KiB
Go
121 lines
4.1 KiB
Go
//go:build windows
|
|
|
|
package deploy
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
const serviceDiscoverScript = `
|
|
$ErrorActionPreference = 'SilentlyContinue'
|
|
$p = [ordered]@{ services = @(); hints = @() }
|
|
|
|
# ── Local services (T1007) — management + spread-relevant only ───────────────
|
|
$watch = @(
|
|
'CCMEXEC','CcmSetup','SmsAgent','WinRM','ssh','sshd','LanmanServer','Docker',
|
|
'com.docker.service','jenkins','Jenkins','gitlab-runner','GitLabRunner',
|
|
'OpenSSH SSH Server','cloudflared','gpsvc'
|
|
)
|
|
foreach ($n in $watch) {
|
|
try {
|
|
$s = Get-Service -Name $n -ErrorAction SilentlyContinue
|
|
if (-not $s) {
|
|
$s = Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $n -or $_.DisplayName -like "*$n*" } | Select-Object -First 1
|
|
}
|
|
if ($s) {
|
|
$st = if ($s.Status -eq 'Running') { 'running' } else { 'stopped' }
|
|
$p.services += [ordered]@{ name = $s.Name; status = $st }
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
# ── GPO / Intune passive indicators ───────────────────────────────────────────
|
|
try {
|
|
$cs = Get-CimInstance Win32_ComputerSystem
|
|
if ($cs.PartOfDomain) { $p.hints += 'domain_joined' }
|
|
} catch {}
|
|
if (Test-Path 'HKLM:\SOFTWARE\Microsoft\Enrollments') { $p.hints += 'intune_enrollment_key' }
|
|
if (Test-Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate') { $p.hints += 'wu_policy_key' }
|
|
try {
|
|
if ((Get-Service gpsvc -ErrorAction SilentlyContinue).Status -eq 'Running') { $p.hints += 'group_policy_client' }
|
|
} catch {}
|
|
|
|
# ── Docker socket / named pipe ────────────────────────────────────────────────
|
|
if (Test-Path '\\.\pipe\docker_engine') { $p.hints += 'docker_pipe' }
|
|
|
|
# ── Jenkins / GitLab runner filesystem hints ──────────────────────────────────
|
|
@(
|
|
'C:\Program Files\Jenkins',
|
|
'C:\GitLab-Runner',
|
|
'C:\gitlab-runner'
|
|
) | ForEach-Object { if (Test-Path $_) { $p.hints += ('runner_path:' + $_) } }
|
|
|
|
# ── Test-NetConnection — common ports on localhost (fast) ─────────────────────
|
|
$ports = @(22,445,3389,5985,5986,2375,8080,8443)
|
|
foreach ($port in $ports) {
|
|
try {
|
|
$r = Test-NetConnection -ComputerName 127.0.0.1 -Port $port -WarningAction SilentlyContinue -InformationLevel Quiet
|
|
if ($r) { $p.services += [ordered]@{ name = ('tcp/' + $port); port = $port; status = 'listening' } }
|
|
} catch {}
|
|
}
|
|
|
|
$p | ConvertTo-Json -Depth 4 -Compress
|
|
`
|
|
|
|
func probeLocalServices() []ServiceGraphEntry {
|
|
out, err := HiddenCombinedOutput(
|
|
"powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command",
|
|
serviceDiscoverScript,
|
|
)
|
|
if err != nil {
|
|
return fallbackWindowsLocalServices()
|
|
}
|
|
raw := strings.TrimSpace(string(out))
|
|
if idx := strings.LastIndex(raw, "{"); idx > 0 {
|
|
raw = raw[idx:]
|
|
}
|
|
var payload windowsDiscoverPayload
|
|
if err := json.Unmarshal([]byte(raw), &payload); err != nil {
|
|
return fallbackWindowsLocalServices()
|
|
}
|
|
entries := windowsRowsToEntries(payload.Services)
|
|
if dockerPipePresent() {
|
|
entries = append(entries, entryWithLane("docker", 0, "passive_hint"))
|
|
}
|
|
return dedupeEntries(entries)
|
|
}
|
|
|
|
func collectPassiveHints() []string {
|
|
out, err := HiddenCombinedOutput(
|
|
"powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command",
|
|
serviceDiscoverScript,
|
|
)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
raw := strings.TrimSpace(string(out))
|
|
if idx := strings.LastIndex(raw, "{"); idx > 0 {
|
|
raw = raw[idx:]
|
|
}
|
|
var payload windowsDiscoverPayload
|
|
if err := json.Unmarshal([]byte(raw), &payload); err != nil {
|
|
return nil
|
|
}
|
|
return payload.Hints
|
|
}
|
|
|
|
func fallbackWindowsLocalServices() []ServiceGraphEntry {
|
|
var entries []ServiceGraphEntry
|
|
if _, err := os.Stat(`\\.\pipe\docker_engine`); err == nil {
|
|
entries = append(entries, entryWithLane("docker", 0, "passive_hint"))
|
|
}
|
|
return entries
|
|
}
|
|
|
|
func dockerPipePresent() bool {
|
|
_, err := os.Stat(`\\.\pipe\docker_engine`)
|
|
return err == nil
|
|
}
|