Add files via upload

This commit is contained in:
Daniel Card
2026-07-07 06:33:19 +01:00
committed by GitHub
parent 2a473ce80b
commit db9c9f600a
3 changed files with 302 additions and 37 deletions
+150 -17
View File
@@ -42,8 +42,9 @@
.\Manage-WindowsTelemetry.ps1 -UserMode # per-user items only, no admin .\Manage-WindowsTelemetry.ps1 -UserMode # per-user items only, no admin
.\Manage-WindowsTelemetry.ps1 -UserMode -DisableAll # harden your user profile .\Manage-WindowsTelemetry.ps1 -UserMode -DisableAll # harden your user profile
.\Manage-WindowsTelemetry.ps1 -Report # print status and exit .\Manage-WindowsTelemetry.ps1 -Report # print status and exit
.\Manage-WindowsTelemetry.ps1 -DisableAll # turn telemetry OFF (keeps [SEC] ON) .\Manage-WindowsTelemetry.ps1 -DisableAll # turn telemetry OFF (keeps [SEC]/[NET] ON)
.\Manage-WindowsTelemetry.ps1 -DisableAll -IncludeSecurity # also disable SmartScreen .\Manage-WindowsTelemetry.ps1 -DisableAll -IncludeSecurity # also disable SmartScreen
.\Manage-WindowsTelemetry.ps1 -GoDark # MAX: disable everything incl [SEC]+[NET]
.\Manage-WindowsTelemetry.ps1 -EnableAll # restore Windows default ON .\Manage-WindowsTelemetry.ps1 -EnableAll # restore Windows default ON
.\Manage-WindowsTelemetry.ps1 -Csv .\out.csv # export status and exit .\Manage-WindowsTelemetry.ps1 -Csv .\out.csv # export status and exit
.\Manage-WindowsTelemetry.ps1 -Report -Csv .\status.csv .\Manage-WindowsTelemetry.ps1 -Report -Csv .\status.csv
@@ -58,6 +59,7 @@ param(
[switch]$DisableAll, [switch]$DisableAll,
[switch]$EnableAll, [switch]$EnableAll,
[switch]$IncludeSecurity, # also disable [SEC] SmartScreen features in bulk actions [switch]$IncludeSecurity, # also disable [SEC] SmartScreen features in bulk actions
[switch]$GoDark, # maximum: disable EVERYTHING incl [SEC] and [NET] callbacks
[switch]$UserMode, # only per-user (HKCU) items; no admin needed [switch]$UserMode, # only per-user (HKCU) items; no admin needed
[string]$Csv [string]$Csv
) )
@@ -127,7 +129,9 @@ function New-RegControl {
[ValidateSet('On','Off')][string]$Default = 'On', [ValidateSet('On','Off')][string]$Default = 'On',
[string]$RegType = 'DWord', [string]$Note = '', [string]$RegType = 'DWord', [string]$Note = '',
[switch]$RemoveOnEnable, [switch]$RemoveOnEnable,
[switch]$Security # marks a control whose "Disabled" state REDUCES protection [switch]$Security, # marks a control whose "Disabled" state REDUCES protection
[switch]$GoDark, # ambient MS callback; only disabled by -GoDark or individually
[switch]$Critical # breaks important functionality; per-item "confirm close" required
) )
[pscustomobject]@{ [pscustomobject]@{
Type = 'Reg' Type = 'Reg'
@@ -142,6 +146,8 @@ function New-RegControl {
RegType = $RegType RegType = $RegType
RemoveOnEnable = [bool]$RemoveOnEnable RemoveOnEnable = [bool]$RemoveOnEnable
Security = [bool]$Security Security = [bool]$Security
GoDark = [bool]$GoDark
Critical = [bool]$Critical
AdminReq = ($Hive -eq 'HKLM') AdminReq = ($Hive -eq 'HKLM')
} }
} }
@@ -150,16 +156,19 @@ function New-ServiceControl {
param( param(
[string]$Name, [string]$ServiceName, [string]$Name, [string]$ServiceName,
[ValidateSet('Automatic','Manual')][string]$DefaultStartupType = 'Automatic', [ValidateSet('Automatic','Manual')][string]$DefaultStartupType = 'Automatic',
[string]$Note = '' [string]$Note = '', [string]$Category = 'Service',
[switch]$Critical
) )
[pscustomobject]@{ [pscustomobject]@{
Type = 'Service' Type = 'Service'
Name = $Name Name = $Name
Category = 'Service' Category = $Category
Note = $Note Note = $Note
ServiceName = $ServiceName ServiceName = $ServiceName
DefaultStartupType = $DefaultStartupType DefaultStartupType = $DefaultStartupType
Security = $false Security = $false
GoDark = $false
Critical = [bool]$Critical
AdminReq = $true AdminReq = $true
} }
} }
@@ -174,6 +183,8 @@ function New-TaskControl {
Note = $Note Note = $Note
Tasks = $Tasks Tasks = $Tasks
Security = $false Security = $false
GoDark = $false
Critical = $false
AdminReq = $true AdminReq = $true
} }
} }
@@ -501,6 +512,55 @@ function Get-Controls {
-ValueName 'ServiceEnabled' -OnValue 1 -OffValue 0 -Default On -RemoveOnEnable -Security ` -ValueName 'ServiceEnabled' -OnValue 1 -OffValue 0 -Default On -RemoveOnEnable -Security `
-Note 'SECURITY: Win11 password/phishing protection service') ) -Note 'SECURITY: Win11 password/phishing protection service') )
# --- Defender cloud (MAPS) - SECURITY, off reduces AV (Tamper Protection may block) ---
[void]$c.Add( (New-RegControl -Name 'Defender Cloud (MAPS)' -Category 'Defender Cloud' `
-Hive HKLM -Path 'SOFTWARE\Policies\Microsoft\Windows Defender\Spynet' `
-ValueName 'SpynetReporting' -OnValue 2 -OffValue 0 -Default On -RemoveOnEnable -Security `
-Note 'SECURITY: real-time cloud lookups. Tamper Protection may block changes.') )
[void]$c.Add( (New-RegControl -Name 'Defender Sample Submission' -Category 'Defender Cloud' `
-Hive HKLM -Path 'SOFTWARE\Policies\Microsoft\Windows Defender\Spynet' `
-ValueName 'SubmitSamplesConsent' -OnValue 1 -OffValue 2 -Default On -RemoveOnEnable -Security `
-Note 'SECURITY/PRIVACY: sends files to Microsoft. Tamper Protection may block changes.') )
# --- Ambient Microsoft callbacks ([NET]) - functional connections, go-dark only ---
[void]$c.Add( (New-RegControl -Name 'NCSI Active Probe' -Category 'Connectivity' `
-Hive HKLM -Path 'SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet' `
-ValueName 'EnableActiveProbing' -OnValue 1 -OffValue 0 -Default On -GoDark `
-Note 'Probes msftconnecttest.com. Off breaks captive-portal / internet indicator.') )
[void]$c.Add( (New-RegControl -Name 'Root Certificate Auto-Update' -Category 'Connectivity' `
-Hive HKLM -Path 'SOFTWARE\Policies\Microsoft\SystemCertificates\AuthRoot' `
-ValueName 'DisableRootAutoUpdate' -OnValue 0 -OffValue 1 -Default On -RemoveOnEnable -GoDark `
-Note 'Off stops new/rotated trusted-root CAs downloading - can break HTTPS over time.') )
[void]$c.Add( (New-RegControl -Name 'Store App Auto-Update' -Category 'Connectivity' `
-Hive HKLM -Path 'SOFTWARE\Policies\Microsoft\WindowsStore' `
-ValueName 'AutoDownload' -OnValue 4 -OffValue 2 -Default On -RemoveOnEnable -GoDark `
-Note 'Off stops Store apps auto-updating (including their security fixes).') )
[void]$c.Add( (New-RegControl -Name 'Font Streaming' -Category 'Connectivity' `
-Hive HKLM -Path 'SOFTWARE\Policies\Microsoft\Windows\System' `
-ValueName 'EnableFontProviders' -OnValue 1 -OffValue 0 -Default On -RemoveOnEnable -GoDark `
-Note 'Off stops on-demand font downloads from Microsoft.') )
[void]$c.Add( (New-RegControl -Name 'Windows Media DRM Online' -Category 'Connectivity' `
-Hive HKLM -Path 'SOFTWARE\Policies\Microsoft\WMDRM' `
-ValueName 'DisableOnline' -OnValue 0 -OffValue 1 -Default On -RemoveOnEnable -GoDark `
-Note 'Off stops Media DRM contacting Microsoft for licenses/individualization.') )
# --- Critical ([CRIT]) - breaks important functionality; per-item confirm-close ---
# NOT touched by any bulk action (not even -GoDark). Local-config stand-ins for
# what a hardened env does with WSUS / a local NTP server / network isolation.
[void]$c.Add( (New-RegControl -Name 'Windows Update Auto' -Category 'Critical' `
-Hive HKLM -Path 'SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' `
-ValueName 'NoAutoUpdate' -OnValue 0 -OffValue 1 -Default On -RemoveOnEnable -Critical `
-Note 'BREAKS SECURITY PATCHING. Off = no automatic update check/download. Use WSUS instead.') )
[void]$c.Add( (New-ServiceControl -Name 'Windows Time Sync (W32Time)' -Category 'Critical' `
-ServiceName 'W32Time' -DefaultStartupType Manual -Critical `
-Note 'BREAKS time sync (Kerberos/TLS drift). Off = no NTP to time.windows.com. Use a local time server.') )
# --- Services --- # --- Services ---
[void]$c.Add( (New-ServiceControl -Name 'Connected User Experiences' ` [void]$c.Add( (New-ServiceControl -Name 'Connected User Experiences' `
-ServiceName 'DiagTrack' -DefaultStartupType Automatic ` -ServiceName 'DiagTrack' -DefaultStartupType Automatic `
@@ -581,7 +641,7 @@ function Show-Status {
Write-Host ('{0,-4}{1,-36}{2,-18}{3}' -f '---', '-------', '--------', '-----') -ForegroundColor DarkGray Write-Host ('{0,-4}{1,-36}{2,-18}{3}' -f '---', '-------', '--------', '-----') -ForegroundColor DarkGray
$i = 0 $i = 0
$nEnabled = 0; $nDisabled = 0; $nAbsent = 0; $nSecOff = 0; $nSec = 0; $anyLock = $false $nEnabled = 0; $nDisabled = 0; $nAbsent = 0; $nSecOff = 0; $nSec = 0; $nNet = 0; $nCrit = 0; $anyLock = $false
foreach ($ctrl in $Controls) { foreach ($ctrl in $Controls) {
$i++ $i++
$state = Get-ControlState -Ctrl $ctrl $state = Get-ControlState -Ctrl $ctrl
@@ -592,6 +652,10 @@ function Show-Status {
if ($ctrl.Security) { if ($ctrl.Security) {
$sec = ' [SEC]'; $nSec++ $sec = ' [SEC]'; $nSec++
if ($state -like 'Disabled*') { $nSecOff++ } if ($state -like 'Disabled*') { $nSecOff++ }
} elseif ($ctrl.Critical) {
$sec = ' [CRIT]'; $nCrit++
} elseif ($ctrl.GoDark) {
$sec = ' [NET]'; $nNet++
} }
$lock = '' $lock = ''
if ($ctrl.AdminReq -and -not $Script:IsAdmin) { $lock = ' *'; $anyLock = $true } if ($ctrl.AdminReq -and -not $Script:IsAdmin) { $lock = ' *'; $anyLock = $true }
@@ -604,6 +668,12 @@ function Show-Status {
if ($nSec -gt 0) { if ($nSec -gt 0) {
Write-Host ' [SEC] = anti-malware reputation check. Disabling REDUCES protection' -ForegroundColor DarkYellow Write-Host ' [SEC] = anti-malware reputation check. Disabling REDUCES protection' -ForegroundColor DarkYellow
} }
if ($nNet -gt 0) {
Write-Host ' [NET] = ambient Microsoft callback. Disabling can break functionality' -ForegroundColor DarkYellow
}
if ($nCrit -gt 0) {
Write-Host ' [CRIT] = breaks patching/time/trust. Never bulk-disabled; needs per-item confirm (X)' -ForegroundColor DarkYellow
}
if ($nSecOff -gt 0) { if ($nSecOff -gt 0) {
Write-Host (' WARNING: {0} security SmartScreen feature(s) are currently OFF' -f $nSecOff) -ForegroundColor Red Write-Host (' WARNING: {0} security SmartScreen feature(s) are currently OFF' -f $nSecOff) -ForegroundColor Red
} }
@@ -653,26 +723,48 @@ function Invoke-ControlAction {
} }
} }
function Invoke-ConfirmClose {
param($Ctrl) # per-item "confirm close" for [CRIT] items
Write-Host ''
Write-Host ('CONFIRM CLOSE: {0}' -f $Ctrl.Name) -ForegroundColor Red
if ($Ctrl.Note) { Write-Host (' {0}' -f $Ctrl.Note) -ForegroundColor DarkYellow }
Write-Host ' Local-config only - a hardened env would use WSUS / local NTP / network isolation.' -ForegroundColor DarkYellow
if ((Read-Host ('Close this? type CLOSE')) -ceq 'CLOSE') {
Invoke-ControlAction -Ctrl $Ctrl -Action Disable
} else {
Write-Host ' Skipped.' -ForegroundColor DarkGray
}
}
function Invoke-AllAction { function Invoke-AllAction {
param( param(
$Controls, $Controls,
[ValidateSet('Enable','Disable')][string]$Action, [ValidateSet('Enable','Disable')][string]$Action,
[switch]$WithSecurity # when disabling, also include [SEC] SmartScreen features [switch]$WithSecurity, # when disabling, also include [SEC] SmartScreen/Defender features
[switch]$WithGoDark # when disabling, also include [NET] ambient callbacks
) )
$verb = if ($Action -eq 'Enable') { 'ENABLE (restore Windows default)' } else { 'DISABLE (harden)' } $verb = if ($Action -eq 'Enable') { 'ENABLE (restore Windows default)' } else { 'DISABLE (harden)' }
Write-Host '' Write-Host ''
Write-Host ("Applying {0} to ALL items..." -f $verb) -ForegroundColor Cyan Write-Host ("Applying {0} to ALL items..." -f $verb) -ForegroundColor Cyan
$skippedSec = 0 $skippedSec = 0; $skippedNet = 0; $skippedCrit = 0
foreach ($ctrl in $Controls) { foreach ($ctrl in $Controls) {
# Never auto-disable SmartScreen reputation checks in bulk unless explicitly requested. if ($Action -eq 'Disable') {
if ($Action -eq 'Disable' -and $ctrl.Security -and -not $WithSecurity) { # [CRIT] items are NEVER bulk-disabled (not even by go-dark) - confirm-close only.
$skippedSec++ if ($ctrl.Critical) { $skippedCrit++; continue }
continue # Never auto-disable security or ambient-callback items in bulk unless asked.
if ($ctrl.Security -and -not $WithSecurity) { $skippedSec++; continue }
if ($ctrl.GoDark -and -not $WithGoDark) { $skippedNet++; continue }
} }
Invoke-ControlAction -Ctrl $ctrl -Action $Action Invoke-ControlAction -Ctrl $ctrl -Action $Action
} }
if ($skippedSec -gt 0) { if ($skippedSec -gt 0) {
Write-Host (' Kept {0} [SEC] SmartScreen feature(s) ON (use -IncludeSecurity / menu "S" to disable).' -f $skippedSec) -ForegroundColor DarkYellow Write-Host (' Kept {0} [SEC] security feature(s) ON (use -IncludeSecurity / menu "S", or -GoDark).' -f $skippedSec) -ForegroundColor DarkYellow
}
if ($skippedNet -gt 0) {
Write-Host (' Kept {0} [NET] ambient callback(s) ON (use -GoDark / menu "G").' -f $skippedNet) -ForegroundColor DarkYellow
}
if ($skippedCrit -gt 0) {
Write-Host (' Kept {0} [CRIT] item(s) ON - close individually with confirm (menu "X").' -f $skippedCrit) -ForegroundColor DarkYellow
} }
Write-Host '' Write-Host ''
} }
@@ -692,6 +784,8 @@ function Start-Menu {
Write-Host ' D disable ALL shown (harden; keeps [SEC] SmartScreen ON)' Write-Host ' D disable ALL shown (harden; keeps [SEC] SmartScreen ON)'
Write-Host ' E enable ALL shown (restore Windows default)' Write-Host ' E enable ALL shown (restore Windows default)'
Write-Host ' S disable ALL [SEC] SmartScreen features (reduces security)' Write-Host ' S disable ALL [SEC] SmartScreen features (reduces security)'
Write-Host ' G GO DARK - disable EVERYTHING incl [SEC] + [NET] (max privacy)'
Write-Host ' X close [CRIT] items (Update/Time) one-by-one with confirm'
if ($Script:UserMode) { if ($Script:UserMode) {
Write-Host ' m switch to FULL mode (also show system-wide items)' Write-Host ' m switch to FULL mode (also show system-wide items)'
} else { } else {
@@ -737,6 +831,21 @@ function Start-Menu {
} }
Read-Host 'Press Enter'; continue Read-Host 'Press Enter'; continue
} }
'^G$' {
Write-Host ''
Write-Host '============================ GO DARK ============================' -ForegroundColor Red
Write-Host 'Disables EVERYTHING shown: telemetry + [SEC] anti-malware reputation' -ForegroundColor Red
Write-Host '(SmartScreen, Defender cloud) + [NET] ambient callbacks (connectivity' -ForegroundColor Red
Write-Host 'probe, root-cert auto-update, Store updates, font/DRM). This REDUCES' -ForegroundColor Red
Write-Host 'SECURITY and can BREAK functionality. Windows Update, activation,' -ForegroundColor Red
Write-Host 'cert revocation and time sync are intentionally left working.' -ForegroundColor Red
Write-Host 'Reverse anytime with E (enable all).' -ForegroundColor DarkYellow
Write-Host '==================================================================' -ForegroundColor Red
if ((Read-Host 'Proceed? type GO-DARK') -ceq 'GO-DARK') {
Invoke-AllAction -Controls $view -Action Disable -WithSecurity -WithGoDark
}
Read-Host 'Press Enter'; continue
}
'^[Cc]\s+(.+)$' { '^[Cc]\s+(.+)$' {
Export-StatusCsv -Controls $view -Path $Matches[1].Trim('"') Export-StatusCsv -Controls $view -Path $Matches[1].Trim('"')
Read-Host 'Press Enter'; continue Read-Host 'Press Enter'; continue
@@ -749,8 +858,22 @@ function Start-Menu {
} }
'^[Dd]\s+(\d+)$' { '^[Dd]\s+(\d+)$' {
$n = [int]$Matches[1] $n = [int]$Matches[1]
if ($n -ge 1 -and $n -le $view.Count) { Invoke-ControlAction -Ctrl $view[$n-1] -Action Disable } if ($n -ge 1 -and $n -le $view.Count) {
else { Write-Host 'Out of range' -ForegroundColor Red } $ctrl = $view[$n-1]
if ($ctrl.Critical) { Invoke-ConfirmClose -Ctrl $ctrl } else { Invoke-ControlAction -Ctrl $ctrl -Action Disable }
} else { Write-Host 'Out of range' -ForegroundColor Red }
Read-Host 'Press Enter'; continue
}
'^X$' {
$critList = @($view | Where-Object { $_.Critical })
if ($critList.Count -eq 0) {
Write-Host 'No [CRIT] items shown.' -ForegroundColor DarkYellow
Read-Host 'Press Enter'; continue
}
Write-Host ''
Write-Host ('Closing {0} [CRIT] connection(s), one at a time with confirmation.' -f $critList.Count) -ForegroundColor Red
Write-Host 'These break patching / time sync / trust. Reverse with E (enable all).' -ForegroundColor DarkYellow
foreach ($ctrl in $critList) { Invoke-ConfirmClose -Ctrl $ctrl }
Read-Host 'Press Enter'; continue Read-Host 'Press Enter'; continue
} }
'^\d+$' { '^\d+$' {
@@ -758,12 +881,13 @@ function Start-Menu {
if ($n -ge 1 -and $n -le $view.Count) { if ($n -ge 1 -and $n -le $view.Count) {
$ctrl = $view[$n-1] $ctrl = $view[$n-1]
$state = Get-ControlState -Ctrl $ctrl $state = Get-ControlState -Ctrl $ctrl
if ($state -like 'Enabled*') { Invoke-ControlAction -Ctrl $ctrl -Action Disable } if ($state -like 'Enabled*') {
else { Invoke-ControlAction -Ctrl $ctrl -Action Enable } if ($ctrl.Critical) { Invoke-ConfirmClose -Ctrl $ctrl } else { Invoke-ControlAction -Ctrl $ctrl -Action Disable }
} else { Invoke-ControlAction -Ctrl $ctrl -Action Enable }
} else { Write-Host 'Out of range' -ForegroundColor Red } } else { Write-Host 'Out of range' -ForegroundColor Red }
Read-Host 'Press Enter'; continue Read-Host 'Press Enter'; continue
} }
default { Write-Host 'Unknown command (d/e need an item number; D/E/S/m act on the view)' -ForegroundColor Red; Start-Sleep -Milliseconds 600 } default { Write-Host 'Unknown command (d/e need an item number; D/E/S/G/X/m act on the view)' -ForegroundColor Red; Start-Sleep -Milliseconds 600 }
} }
} }
} }
@@ -781,6 +905,15 @@ if ($UserMode) {
Write-Host ('USER MODE: showing {0} per-user item(s) only (no admin needed).' -f $active.Count) -ForegroundColor Magenta Write-Host ('USER MODE: showing {0} per-user item(s) only (no admin needed).' -f $active.Count) -ForegroundColor Magenta
} }
if ($GoDark) {
Write-Host ''
Write-Host 'GO DARK: disabling ALL telemetry + [SEC] security + [NET] ambient callbacks.' -ForegroundColor Red
Write-Host 'Reduces security and can break functionality. Reverse with -EnableAll.' -ForegroundColor DarkYellow
Invoke-AllAction -Controls $active -Action Disable -WithSecurity -WithGoDark
Show-Status -Controls $active
if ($Csv) { Export-StatusCsv -Controls $active -Path $Csv }
return
}
if ($DisableAll) { if ($DisableAll) {
Invoke-AllAction -Controls $active -Action Disable -WithSecurity:$IncludeSecurity Invoke-AllAction -Controls $active -Action Disable -WithSecurity:$IncludeSecurity
Show-Status -Controls $active Show-Status -Controls $active
+53 -1
View File
@@ -80,6 +80,9 @@ services and scheduled tasks** from one place.
# ... also disable the SmartScreen reputation checks (see [SEC] note below) # ... also disable the SmartScreen reputation checks (see [SEC] note below)
.\Manage-WindowsTelemetry.ps1 -DisableAll -IncludeSecurity .\Manage-WindowsTelemetry.ps1 -DisableAll -IncludeSecurity
# GO DARK - maximum privacy: everything incl [SEC] security + [NET] callbacks
.\Manage-WindowsTelemetry.ps1 -GoDark
# Restore Windows default behaviour: turn everything back ON # Restore Windows default behaviour: turn everything back ON
.\Manage-WindowsTelemetry.ps1 -EnableAll .\Manage-WindowsTelemetry.ps1 -EnableAll
@@ -154,8 +157,57 @@ The header shows `MODE: USER` or `MODE: FULL`. In the interactive menu press
| `<n>` | Toggle item *n* (Enabled <-> Disabled) | | `<n>` | Toggle item *n* (Enabled <-> Disabled) |
| `e <n>` / `d <n>` | Enable / disable item *n* | | `e <n>` / `d <n>` | Enable / disable item *n* |
| `E` / `D` (uppercase) | Enable / disable **ALL** shown items (asks for `YES` confirmation; `D` keeps `[SEC]` items ON) | | `E` / `D` (uppercase) | Enable / disable **ALL** shown items (asks for `YES` confirmation; `D` keeps `[SEC]` items ON) |
| `S` | Disable the `[SEC]` SmartScreen features (requires typing `DISABLE-SECURITY`) | | `S` | Disable the `[SEC]` SmartScreen/Defender-cloud features (requires typing `DISABLE-SECURITY`) |
| `G` | **GO DARK** - disable everything incl `[SEC]` + `[NET]` (requires typing `GO-DARK`) |
| `X` | Close `[CRIT]` items (Windows Update / Time) one-by-one, each with a `CLOSE` confirm |
| `m` | Toggle between USER mode (per-user only) and FULL mode (all items) | | `m` | Toggle between USER mode (per-user only) and FULL mode (all items) |
## Go dark (`-GoDark` / menu `G`) - maximum privacy
Ordinary `-DisableAll` deliberately leaves two classes of item **on**, because
turning them off has real costs:
- **`[SEC]`** - anti-malware reputation checks: Windows SmartScreen (apps/files,
Store, phishing) and Defender cloud (MAPS + sample submission). Off = no
malicious-URL/app warnings and weaker AV.
- **`[NET]`** - ambient Microsoft callbacks: NCSI connectivity probe, root-cert
auto-update, Store app auto-update, font streaming, Media DRM online. Off can
break captive-portal detection, new-CA trust, app updates and some fonts/DRM.
**Go dark disables all of it** - all telemetry **plus** every `[SEC]` and
`[NET]` item - for the smallest practical Microsoft footprint. It requires an
explicit switch/confirmation (`GO-DARK`) and is fully reversible with
`-EnableAll` (menu `E`).
## `[CRIT]` - the last-mile connections (confirm-close only)
Even go-dark won't touch these, because disabling them breaks core function.
They exist as **local-config stand-ins** for what a hardened environment does
properly (WSUS, a local NTP server, network isolation). Each is **never**
bulk-disabled - not by `-DisableAll` and not by `-GoDark` - and can only be
closed **individually, with a per-item `CLOSE` confirmation**:
| `[CRIT]` item | Key/service | Cost of closing | Proper alternative |
|---|---|---|---|
| **Windows Update Auto** | `...\WindowsUpdate\AU\NoAutoUpdate=1` | No automatic security patching | WSUS / managed updates |
| **Windows Time Sync** | `W32Time` service disabled | Clock drift breaks Kerberos/TLS | Internal NTP time server |
Close them from the menu with **`X`** (walks each with a confirm) or by
toggling the item's number (a `[CRIT]` disable always prompts `CLOSE`). Reverse
with `-EnableAll` / menu `E`, which restores auto-update and sets `W32Time`
back to its default (Manual) start.
**Activation** and **certificate revocation (OCSP/CRL)** are *not* included:
there is no clean single-key local toggle for them (disabling activation
de-activates Windows; blanket revocation-off is dangerous and messy). In a
locked-down build those are handled by **network isolation / firewall**, which
is the right layer for them - a point you already make with WSUS + isolation +
a time server.
Some `[SEC]` Defender items may be blocked by **Tamper Protection** (they'll
report a failure rather than silently not applying) - turn it off in Windows
Security first if you truly need them off. For the network callbacks that a
registry setting cannot stop, pair go-dark with host-firewall rules.
| `r` | Refresh the view | | `r` | Refresh the view |
| `c <path>` | Export status to CSV | | `c <path>` | Export status to CSV |
| `q` | Quit | | `q` | Quit |
+99 -19
View File
@@ -125,11 +125,56 @@ function Get-DsRegStatus {
# identifier baked into the TPM at manufacture. Needs admin. Uses the built-in # identifier baked into the TPM at manufacture. Needs admin. Uses the built-in
# Get-TpmEndorsementKeyInfo cmdlet; returns the EKpub hash and (if RSA) the # Get-TpmEndorsementKeyInfo cmdlet; returns the EKpub hash and (if RSA) the
# modulus so the actual public key can be recorded. # modulus so the actual public key can be recorded.
# Minimal ASN.1/DER walker: collects every INTEGER value in the blob, descending
# into SEQUENCEs and BIT STRINGs. Used to pull the RSA modulus (the longest
# INTEGER) out of the EKpub without relying on .NET Core-only import APIs.
function Invoke-Asn1Walk {
param([byte[]]$d, [int]$s, [int]$e, [int]$depth = 0)
if ($depth -gt 24) { return }
$i = $s
while ($i -lt $e) {
$tag = $d[$i]; $i++
if ($i -ge $e) { break }
$b = $d[$i]; $i++
if ($b -lt 0x80) { $len = $b } else {
$n = $b -band 0x7f
if ($n -eq 0 -or ($i + $n) -gt $e) { break }
$len = 0; for ($k = 0; $k -lt $n; $k++) { $len = ($len * 256) + $d[$i]; $i++ }
}
if (($i + $len) -gt $e) { break }
if ($tag -eq 0x02) { [void]$Script:Asn1Ints.Add([byte[]]($d[$i..($i + $len - 1)])) }
elseif ($tag -eq 0x03) { if ($len -gt 1) { Invoke-Asn1Walk $d ($i + 1) ($i + $len) ($depth + 1) } }
elseif (($tag -band 0x20) -ne 0) { Invoke-Asn1Walk $d $i ($i + $len) ($depth + 1) }
$i += $len
}
}
function Get-RsaModulus {
param([byte[]]$Der)
$Script:Asn1Ints = New-Object System.Collections.ArrayList
try { Invoke-Asn1Walk -d $Der -s 0 -e $Der.Length } catch { }
$best = $null
foreach ($b in $Script:Asn1Ints) { if ($null -eq $best -or $b.Length -gt $best.Length) { $best = $b } }
if ($null -eq $best -or $best.Length -lt 128) { return $null } # <1024-bit => not an RSA modulus (e.g. ECC EK)
if ($best.Length -gt 1 -and $best[0] -eq 0) { $best = $best[1..($best.Length - 1)] } # strip DER sign byte
return [byte[]]$best
}
function Get-Sha {
param([byte[]]$Bytes, [ValidateSet('SHA1','SHA256')][string]$Algo = 'SHA256')
$h = $null
try {
$h = [System.Security.Cryptography.HashAlgorithm]::Create($Algo)
$hb = $h.ComputeHash($Bytes)
return (-join ($hb | ForEach-Object { $_.ToString('x2') }))
} finally { if ($h) { $h.Dispose() } }
}
function Get-TpmEkPub { function Get-TpmEkPub {
# Windows exposes PublicKey as AsnEncodedData (DER SubjectPublicKeyInfo) and # Windows exposes PublicKey as AsnEncodedData (DER SubjectPublicKeyInfo) and
# often leaves PublicKeyHash empty - so we base64 the DER (the actual EKpub) # often leaves PublicKeyHash empty - so we base64 the DER (the actual EKpub)
# and compute the SHA-256 ourselves as a stable fingerprint. # and compute the SHA-256 ourselves as a stable fingerprint.
$info = [pscustomobject]@{ Available = $false; Present = $false; WinHash = $null; Sha256 = $null; DerB64 = $null } $info = [pscustomobject]@{ Available = $false; Present = $false; WinHash = $null; Sha256 = $null; Sha1 = $null; ModSha256 = $null; DerB64 = $null }
if (-not (Get-Command Get-TpmEndorsementKeyInfo -ErrorAction SilentlyContinue)) { return $info } if (-not (Get-Command Get-TpmEndorsementKeyInfo -ErrorAction SilentlyContinue)) { return $info }
$info.Available = $true $info.Available = $true
try { try {
@@ -142,12 +187,10 @@ function Get-TpmEkPub {
if ($raw -and $raw.Length -gt 0) { if ($raw -and $raw.Length -gt 0) {
$bytes = [byte[]]$raw $bytes = [byte[]]$raw
$info.DerB64 = [Convert]::ToBase64String($bytes) $info.DerB64 = [Convert]::ToBase64String($bytes)
$sha = $null $info.Sha256 = Get-Sha -Bytes $bytes -Algo SHA256 # hash of the DER SubjectPublicKeyInfo
try { $info.Sha1 = Get-Sha -Bytes $bytes -Algo SHA1
$sha = [System.Security.Cryptography.SHA256]::Create() $mod = Get-RsaModulus -Der $bytes # raw RSA modulus (null for ECC EKs)
$hb = $sha.ComputeHash($bytes) if ($mod) { $info.ModSha256 = Get-Sha -Bytes $mod -Algo SHA256 }
$info.Sha256 = -join ($hb | ForEach-Object { $_.ToString('x2') })
} finally { if ($sha) { $sha.Dispose() } }
} }
} catch { } } catch { }
return $info return $info
@@ -188,18 +231,28 @@ function Get-DeviceIdentityAsSystem {
$taskName = "WID_DeviceExtract_$PID" $taskName = "WID_DeviceExtract_$PID"
$payload = @" $payload = @"
`$ErrorActionPreference='SilentlyContinue' `$ErrorActionPreference='SilentlyContinue'
`$out=@() `$found=@{}
`$base='HKCU:\SOFTWARE\Microsoft\IdentityCRL\UserExtendedProperties' `$roots=@('HKCU:\SOFTWARE\Microsoft\IdentityCRL','HKLM:\SOFTWARE\Microsoft\IdentityStore\Cache\S-1-5-18')
if(Test-Path `$base){ foreach(`$root in `$roots){
foreach(`$k in (Get-ChildItem `$base)){ if(Test-Path `$root){
`$cid=(Get-ItemProperty -LiteralPath `$k.PSPath).cid `$keys=@(Get-Item `$root -ErrorAction SilentlyContinue) + @(Get-ChildItem `$root -Recurse -ErrorAction SilentlyContinue)
if(`$cid){ foreach(`$k in `$keys){
`$puid=`$null; try{`$puid=([Convert]::ToUInt64(`$cid,16)).ToString()}catch{} `$props=Get-ItemProperty -LiteralPath `$k.PSPath -ErrorAction SilentlyContinue
`$out+=[pscustomobject]@{Account=(Split-Path `$k.Name -Leaf);Cid=`$cid;Puid=`$puid} if(`$props){
foreach(`$vn in @('cid','CID')){
if(`$props.PSObject.Properties.Match(`$vn).Count -gt 0){
`$cid="`$(`$props.`$vn)"
if(`$cid -and -not `$found.ContainsKey(`$cid)){
`$puid=`$null; try{`$puid=([Convert]::ToUInt64(`$cid,16)).ToString()}catch{}
`$found[`$cid]=[pscustomobject]@{Account=(Split-Path `$k.Name -Leaf);Cid=`$cid;Puid=`$puid;KeyPath="`$(`$k.Name)"}
}
}
}
}
} }
} }
} }
`$out | ConvertTo-Json -Depth 3 | Out-File -FilePath '$tmp' -Encoding ASCII @(`$found.Values) | ConvertTo-Json -Depth 4 | Out-File -FilePath '$tmp' -Encoding ASCII
"@ "@
try { try {
Set-Content -LiteralPath $ps1 -Value $payload -Encoding ASCII Set-Content -LiteralPath $ps1 -Value $payload -Encoding ASCII
@@ -399,6 +452,23 @@ function Get-Identifiers {
[void]$ids.Add( (New-Id 'Activation' 'Installed product key (decoded)' $decoded -Sensitive -Source 'CurrentVersion\DigitalProductId' ` [void]$ids.Add( (New-Id 'Activation' 'Installed product key (decoded)' $decoded -Sensitive -Source 'CurrentVersion\DigitalProductId' `
-Explain 'Full key recovered from the DigitalProductId blob (classic base-24 decode). Retail/OEM installs decode to a real key; volume (MAK/KMS) installs do not store a recoverable key.') ) -Explain 'Full key recovered from the DigitalProductId blob (classic base-24 decode). Retail/OEM installs decode to a real key; volume (MAK/KMS) installs do not store a recoverable key.') )
# Consolidated "full product key": pick the best available source, or say why none exists.
$keyPattern = '^[A-Z0-9]{5}(-[A-Z0-9]{5}){4}$'
$fullKey = $null; $fullSrc = $null
if ($oemKey -and "$oemKey" -match $keyPattern) { $fullKey = "$oemKey"; $fullSrc = 'OEM firmware (OA3xOriginalProductKey)' }
elseif ($decoded -and "$decoded" -match $keyPattern) { $fullKey = "$decoded"; $fullSrc = 'DigitalProductId decode' }
if ($fullKey) {
[void]$ids.Add( (New-Id 'Activation' 'Full product key' $fullKey -Sensitive -Source $fullSrc `
-Explain ('The full 25-character product key for this install, recovered from ' + $fullSrc + '. Masked unless -Reveal.')) )
} else {
$reason =
if ("$chan" -match 'Volume|MAK|KMS') { '(not recoverable - Volume/MAK/KMS: only the last 5 chars are stored on the device)' }
elseif (-not $Script:IsAdmin) { '(not recoverable unelevated - run as admin with -Reveal; may exist in OEM firmware)' }
else { '(not recoverable on this install)' }
[void]$ids.Add( (New-Id 'Activation' 'Full product key' $reason -Source 'best-effort' `
-Explain 'The complete 25-character key. It is only recoverable for retail/OEM installs (via OEM firmware OA3 key or DigitalProductId decode). Volume MAK/KMS keys are NOT stored on the device by design - Windows keeps only the last 5 characters - so no tool can display them.') )
}
# ---- Advertising / user ---- # ---- Advertising / user ----
[void]$ids.Add( (New-Id 'User' 'User name' ('{0}\{1}' -f $env:USERDOMAIN, $env:USERNAME) -Source 'env' ` [void]$ids.Add( (New-Id 'User' 'User name' ('{0}\{1}' -f $env:USERDOMAIN, $env:USERNAME) -Source 'env' `
-Explain 'Domain (or machine) and username of the current account.') ) -Explain 'Domain (or machine) and username of the current account.') )
@@ -540,12 +610,22 @@ function Get-Identifiers {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub hash (Windows)' $ek.WinHash -Sensitive -Source 'Get-TpmEndorsementKeyInfo.PublicKeyHash' -Explain $ekExplain) ) [void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub hash (Windows)' $ek.WinHash -Sensitive -Source 'Get-TpmEndorsementKeyInfo.PublicKeyHash' -Explain $ekExplain) )
} }
if ($ek.Sha256) { if ($ek.Sha256) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub SHA-256' $ek.Sha256 -Sensitive -Source 'SHA-256 of PublicKey.RawData' ` [void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub SHA-256 (SPKI)' $ek.Sha256 -Sensitive -Source 'SHA-256 of PublicKey.RawData (DER SPKI)' `
-Explain ($ekExplain + ' (SHA-256 fingerprint of the EKpub, computed here since Windows left PublicKeyHash empty.)')) ) -Explain ($ekExplain + ' (SHA-256 over the DER SubjectPublicKeyInfo; computed here since Windows left PublicKeyHash empty.)')) )
}
if ($ek.Sha1) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub SHA-1 (SPKI)' $ek.Sha1 -Sensitive -Source 'SHA-1 of PublicKey.RawData (DER SPKI)' `
-Explain 'SHA-1 fingerprint of the EKpub DER SubjectPublicKeyInfo. Some tools/attestation flows key off SHA-1 rather than SHA-256.') )
}
if ($ek.ModSha256) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub modulus SHA-256' $ek.ModSha256 -Sensitive -Source 'SHA-256 of raw RSA modulus' `
-Explain 'SHA-256 over the RAW RSA modulus (public key value only, DER wrapper stripped) - the "raw-modulus" fingerprint some Microsoft/attestation formats use, as opposed to hashing the whole SubjectPublicKeyInfo.') )
} }
if ($ek.DerB64) { if ($ek.DerB64) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub (DER, b64)' $ek.DerB64 -Sensitive -Source 'Get-TpmEndorsementKeyInfo.PublicKey.RawData' ` [void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub (DER, b64)' $ek.DerB64 -Sensitive -Source 'Get-TpmEndorsementKeyInfo.PublicKey.RawData' `
-Explain 'The actual EKpub public key: the DER-encoded SubjectPublicKeyInfo, base64. This IS the endorsement public key (not just a hash). Use -Reveal to record it.') ) -Explain 'The actual EKpub public key: the DER-encoded SubjectPublicKeyInfo, base64. This IS the endorsement public key (not just a hash) - from it you can derive any required format. Use -Reveal to record it.') )
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub Name (TPM2B_NAME)' '(not derivable from this cmdlet)' -Source 'n/a' `
-Explain 'The TPM "Name" (TPM2B_NAME = alg id + hash of the TPMT_PUBLIC area) is NOT computable from the DER SubjectPublicKeyInfo this cmdlet returns - it needs the raw TPMT_PUBLIC blob (via tpm2-tools or the NCrypt/PCPKSP provider). Shown for completeness; the values above are what is available here.') )
} }
} elseif ($ek.Present) { } elseif ($ek.Present) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub' '(EK present but public key not readable)' -Source 'Get-TpmEndorsementKeyInfo' -Explain $ekExplain) ) [void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub' '(EK present but public key not readable)' -Source 'Get-TpmEndorsementKeyInfo' -Explain $ekExplain) )