Add files via upload

This commit is contained in:
Daniel Card
2026-07-07 05:32:16 +01:00
committed by GitHub
parent f1b7ead583
commit 2bf42bcb43
+325 -38
View File
@@ -7,12 +7,22 @@
the values that can be used to fingerprint or correlate the the values that can be used to fingerprint or correlate the
device. Nothing is changed; this only reports. device. Nothing is changed; this only reports.
Shows : Machine identifiers (MachineGuid, Product ID / "PUID", Shows : Machine identifiers (MachineGuid, Windows Product ID, build GUID,
install ID, SQM/telemetry client IDs), Windows activation / SMBIOS UUID, SQM/DiagTrack telemetry client IDs), Windows
licensing (edition, status, partial + full product key, activation / licensing (edition, status, partial + full product
OEM firmware key), advertising / user identifiers (Advertising key, OEM firmware key), user/account identifiers (user SID,
ID, user SID, MSA), and hardware identifiers (SMBIOS UUID, BIOS Advertising ID, and the Microsoft account PUID - the real
/ baseboard / disk serials, MAC addresses, TPM, CPU). "Passport Unique Identifier"), device registration (Entra/Azure
AD Device ID and the wlidsvc device-login concept), and hardware
identifiers (BIOS / baseboard / disk serials, MAC addresses,
CPU, TPM presence/vendor, and the TPM EKpub - the permanent
per-TPM Endorsement Key that uniquely fingerprints the device).
NOTE: the Windows Product ID is a licensing/install ID and is
NOT the PUID. The PUID is a Microsoft ACCOUNT identifier whose
native form is hexadecimal (per .NET PassportIdentity.HexPUID);
on modern Windows it is the signed-in account's CID, read here
in hex with a derived decimal form.
Privacy : Output contains SENSITIVE data. By default, sensitive values are Privacy : Output contains SENSITIVE data. By default, sensitive values are
MASKED so the report is safe to share/screenshot. Use -Reveal to MASKED so the report is safe to share/screenshot. Use -Reveal to
@@ -23,9 +33,20 @@
- Some values (OEM product key, certain WMI/TPM data) require - Some values (OEM product key, certain WMI/TPM data) require
Administrator; without it they show "(needs admin)". Administrator; without it they show "(needs admin)".
Explain : Each identifier is explained inline BY DEFAULT (what it is, how
unique/stable it is, why it matters). Use -Brief for a compact
list without the explanations.
Device : -ExtractDeviceId (needs admin) reads the DEVICE account PUID that
wlidsvc registered under the SYSTEM account (S-1-5-18). It runs a
one-shot scheduled task as SYSTEM to read SYSTEM's IdentityCRL,
then removes the task. Read-only w.r.t. the identity store.
Usage : Usage :
.\View-WindowsIdentifiers.ps1 # masked report .\View-WindowsIdentifiers.ps1 # masked report + explanations
.\View-WindowsIdentifiers.ps1 -Brief # compact, no explanations
.\View-WindowsIdentifiers.ps1 -Reveal # full values (sensitive!) .\View-WindowsIdentifiers.ps1 -Reveal # full values (sensitive!)
.\View-WindowsIdentifiers.ps1 -ExtractDeviceId -Reveal # incl. device PUID (admin)
.\View-WindowsIdentifiers.ps1 -Csv .\ids.csv # export (respects masking) .\View-WindowsIdentifiers.ps1 -Csv .\ids.csv # export (respects masking)
.\View-WindowsIdentifiers.ps1 -Reveal -Csv .\ids.csv .\View-WindowsIdentifiers.ps1 -Reveal -Csv .\ids.csv
.\View-WindowsIdentifiers.ps1 -Json .\ids.json .\View-WindowsIdentifiers.ps1 -Json .\ids.json
@@ -38,6 +59,8 @@
[CmdletBinding()] [CmdletBinding()]
param( param(
[switch]$Reveal, [switch]$Reveal,
[switch]$Brief, # suppress the per-identifier explanations (compact view)
[switch]$ExtractDeviceId, # admin: read the SYSTEM-hive device account PUID via a SYSTEM task
[string]$Csv, [string]$Csv,
[string]$Json [string]$Json
) )
@@ -75,6 +98,139 @@ function Get-Cim {
} catch { return $null } } catch { return $null }
} }
# StrictMode-safe property read.
function Get-PropSafe {
param($Obj, [string]$Name)
if ($null -ne $Obj -and $Obj.PSObject.Properties.Match($Name).Count -gt 0) { return $Obj.$Name }
return $null
}
# Device registration status via the built-in dsregcmd tool (read-only).
# Returns a hashtable of key -> value parsed from "dsregcmd /status".
function Get-DsRegStatus {
$result = @{}
try {
$out = & dsregcmd.exe /status 2>$null
foreach ($line in $out) {
if ("$line" -match '^\s*(\S[^:]*?)\s+:\s+(.+?)\s*$') {
$k = $Matches[1]; $v = $Matches[2]
if (-not $result.ContainsKey($k)) { $result[$k] = $v }
}
}
} catch { }
return $result
}
# TPM Endorsement Key public part (EKpub) - a permanent, globally-unique
# identifier baked into the TPM at manufacture. Needs admin. Uses the built-in
# Get-TpmEndorsementKeyInfo cmdlet; returns the EKpub hash and (if RSA) the
# modulus so the actual public key can be recorded.
function Get-TpmEkPub {
# Windows exposes PublicKey as AsnEncodedData (DER SubjectPublicKeyInfo) and
# often leaves PublicKeyHash empty - so we base64 the DER (the actual EKpub)
# and compute the SHA-256 ourselves as a stable fingerprint.
$info = [pscustomobject]@{ Available = $false; Present = $false; WinHash = $null; Sha256 = $null; DerB64 = $null }
if (-not (Get-Command Get-TpmEndorsementKeyInfo -ErrorAction SilentlyContinue)) { return $info }
$info.Available = $true
try {
$ek = Get-TpmEndorsementKeyInfo -ErrorAction Stop
$info.Present = [bool](Get-PropSafe $ek 'IsPresent')
$h = Get-PropSafe $ek 'PublicKeyHash'
if ($h -and "$h".Trim()) { $info.WinHash = "$h".Trim() }
$pk = Get-PropSafe $ek 'PublicKey' # System.Security.Cryptography.AsnEncodedData
$raw = if ($pk) { Get-PropSafe $pk 'RawData' } else { $null }
if ($raw -and $raw.Length -gt 0) {
$bytes = [byte[]]$raw
$info.DerB64 = [Convert]::ToBase64String($bytes)
$sha = $null
try {
$sha = [System.Security.Cryptography.SHA256]::Create()
$hb = $sha.ComputeHash($bytes)
$info.Sha256 = -join ($hb | ForEach-Object { $_.ToString('x2') })
} finally { if ($sha) { $sha.Dispose() } }
}
} catch { }
return $info
}
# Microsoft account identity: reads the signed-in account's CID (hex) and
# derives the PUID (decimal). Legacy "Microsoft Passport" backend identifier.
function Get-MsaAccounts {
$out = New-Object System.Collections.ArrayList
$base = 'HKCU:\SOFTWARE\Microsoft\IdentityCRL\UserExtendedProperties'
try {
if (Test-Path -LiteralPath $base) {
foreach ($sub in (Get-ChildItem -LiteralPath $base -ErrorAction SilentlyContinue)) {
$props = $null
try { $props = Get-ItemProperty -LiteralPath $sub.PSPath -ErrorAction Stop } catch { }
$cid = Get-PropSafe $props 'cid'
$email = Split-Path $sub.Name -Leaf
if ($cid) {
$puid = $null
try { $puid = ([Convert]::ToUInt64($cid, 16)).ToString() } catch { $puid = $null }
[void]$out.Add([pscustomobject]@{ Account = $email; Cid = $cid; Puid = $puid })
}
}
}
} catch { }
return $out
}
# Extract the DEVICE account PUID. Windows registers the device with Microsoft
# under the SYSTEM account (S-1-5-18), so its IdentityCRL entry is not visible
# from a normal admin's HKCU. With admin we run a one-shot scheduled task as
# SYSTEM that reads SYSTEM's IdentityCRL and returns the device CID/PUID via a
# temp file. Read-only w.r.t. the identity store; the temp task is removed.
function Get-DeviceIdentityAsSystem {
$results = New-Object System.Collections.ArrayList
$tmp = Join-Path $env:TEMP ('wid_dev_{0}.json' -f $PID)
$ps1 = Join-Path $env:TEMP ('wid_dev_{0}.ps1' -f $PID)
$taskName = "WID_DeviceExtract_$PID"
$payload = @"
`$ErrorActionPreference='SilentlyContinue'
`$out=@()
`$base='HKCU:\SOFTWARE\Microsoft\IdentityCRL\UserExtendedProperties'
if(Test-Path `$base){
foreach(`$k in (Get-ChildItem `$base)){
`$cid=(Get-ItemProperty -LiteralPath `$k.PSPath).cid
if(`$cid){
`$puid=`$null; try{`$puid=([Convert]::ToUInt64(`$cid,16)).ToString()}catch{}
`$out+=[pscustomobject]@{Account=(Split-Path `$k.Name -Leaf);Cid=`$cid;Puid=`$puid}
}
}
}
`$out | ConvertTo-Json -Depth 3 | Out-File -FilePath '$tmp' -Encoding ASCII
"@
try {
Set-Content -LiteralPath $ps1 -Value $payload -Encoding ASCII
if (Test-Path -LiteralPath $tmp) { Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue }
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument ('-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File "{0}"' -f $ps1)
$principal = New-ScheduledTaskPrincipal -UserId 'S-1-5-18' -RunLevel Highest
Register-ScheduledTask -TaskName $taskName -Action $action -Principal $principal -Force -ErrorAction Stop | Out-Null
Start-ScheduledTask -TaskName $taskName -ErrorAction Stop
$tries = 0
while ($tries -lt 100) {
Start-Sleep -Milliseconds 200
$st = (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue).State
if ((Test-Path -LiteralPath $tmp) -and $st -ne 'Running') { break }
$tries++
}
if (Test-Path -LiteralPath $tmp) {
$json = Get-Content -LiteralPath $tmp -Raw
if ($json -and $json.Trim()) {
foreach ($e in @($json | ConvertFrom-Json)) { [void]$results.Add($e) }
}
}
} catch {
# surfaced by the caller as "(extraction failed)"
} finally {
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $ps1 -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue
}
return $results
}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Product key decoder (classic DigitalProductId base-24 decode) # Product key decoder (classic DigitalProductId base-24 decode)
# Decodes the product key stored in the registry. Legitimate: reads YOUR # Decodes the product key stored in the registry. Legitimate: reads YOUR
@@ -136,13 +292,14 @@ function Format-Masked {
# Identifier collection # Identifier collection
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
function New-Id { function New-Id {
param([string]$Category, [string]$Name, $Value, [switch]$Sensitive, [string]$Source = '') param([string]$Category, [string]$Name, $Value, [switch]$Sensitive, [string]$Source = '', [string]$Explain = '')
[pscustomobject]@{ [pscustomobject]@{
Category = $Category Category = $Category
Name = $Name Name = $Name
Raw = $Value Raw = $Value
Sensitive = [bool]$Sensitive Sensitive = [bool]$Sensitive
Source = $Source Source = $Source
Explain = $Explain
} }
} }
@@ -161,26 +318,37 @@ function Get-Identifiers {
$cpu = Get-Cim 'Win32_Processor' $cpu = Get-Cim 'Win32_Processor'
# ---- Machine ---- # ---- Machine ----
[void]$ids.Add( (New-Id 'Machine' 'Computer name' $env:COMPUTERNAME -Source 'env') ) [void]$ids.Add( (New-Id 'Machine' 'Computer name' $env:COMPUTERNAME -Source 'env' `
[void]$ids.Add( (New-Id 'Machine' 'MachineGuid' (Get-RegVal $crypto 'MachineGuid') -Sensitive -Source 'Cryptography\MachineGuid') ) -Explain 'NetBIOS/host name of this PC; broadcast on local networks and written into many logs.') )
[void]$ids.Add( (New-Id 'Machine' 'Product ID (PUID)' (Get-RegVal $cvNt 'ProductId') -Sensitive -Source 'CurrentVersion\ProductId') ) [void]$ids.Add( (New-Id 'Machine' 'MachineGuid' (Get-RegVal $crypto 'MachineGuid') -Sensitive -Source 'Cryptography\MachineGuid' `
[void]$ids.Add( (New-Id 'Machine' 'Build GUID' (Get-RegVal $cvNt 'BuildGUID') -Source 'CurrentVersion\BuildGUID') ) -Explain 'Random GUID created at OS install. Stable for the life of the install and used by many Microsoft services as a device correlator - often a better cross-service fingerprint than the Product ID.') )
[void]$ids.Add( (New-Id 'Machine' 'Install date' $(if ($os) { $os.InstallDate } else { $null }) -Source 'Win32_OperatingSystem') ) [void]$ids.Add( (New-Id 'Machine' 'Windows Product ID' (Get-RegVal $cvNt 'ProductId') -Sensitive -Source 'CurrentVersion\ProductId' `
-Explain '20-digit Windows licensing/installation ID derived from your product key plus install-specific data at setup. Not the key and not reversible to it; changes on reinstall. NOTE: this is the Windows Product ID - it is NOT the PUID. The PUID is a Microsoft ACCOUNT identifier (see User section).') )
[void]$ids.Add( (New-Id 'Machine' 'Build GUID' (Get-RegVal $cvNt 'BuildGUID') -Source 'CurrentVersion\BuildGUID' `
-Explain 'Identifies the specific OS build image, not the user. Same across all PCs on the same build.') )
[void]$ids.Add( (New-Id 'Machine' 'Install date' $(if ($os) { $os.InstallDate } else { $null }) -Source 'Win32_OperatingSystem' `
-Explain 'When this Windows was installed; helps date/distinguish an install.') )
if ($csp) { if ($csp) {
[void]$ids.Add( (New-Id 'Machine' 'SMBIOS UUID' $csp.UUID -Sensitive -Source 'Win32_ComputerSystemProduct') ) [void]$ids.Add( (New-Id 'Machine' 'SMBIOS UUID' $csp.UUID -Sensitive -Source 'Win32_ComputerSystemProduct' `
[void]$ids.Add( (New-Id 'Machine' 'System SKU/IdentifyingNumber' $csp.IdentifyingNumber -Sensitive -Source 'Win32_ComputerSystemProduct') ) -Explain 'System UUID set in firmware. Hardware-bound: survives OS reinstalls, so a strong permanent device fingerprint.') )
[void]$ids.Add( (New-Id 'Machine' 'System SKU/IdentifyingNumber' $csp.IdentifyingNumber -Sensitive -Source 'Win32_ComputerSystemProduct' `
-Explain 'OEM system serial/SKU from firmware; identifies the physical unit/model.') )
} }
# ---- Telemetry client IDs ---- # ---- Telemetry client IDs ----
[void]$ids.Add( (New-Id 'Telemetry' 'SQM Machine ID' (Get-RegVal $sqm 'MachineId') -Sensitive -Source 'SQMClient\MachineId') ) [void]$ids.Add( (New-Id 'Telemetry' 'SQM Machine ID' (Get-RegVal $sqm 'MachineId') -Sensitive -Source 'SQMClient\MachineId' `
[void]$ids.Add( (New-Id 'Telemetry' 'SQM User ID' (Get-RegVal "$sqm\Windows" 'UserId') -Sensitive -Source 'SQMClient\Windows\UserId') ) -Explain 'Client ID tagging this device in the legacy CEIP/SQM ("Software Quality Metrics") telemetry pipeline.') )
[void]$ids.Add( (New-Id 'Telemetry' 'SQM User ID' (Get-RegVal "$sqm\Windows" 'UserId') -Sensitive -Source 'SQMClient\Windows\UserId' `
-Explain 'Per-user CEIP/SQM identifier.') )
# Diagnostics / Universal Telemetry Client ID (if present) # Diagnostics / Universal Telemetry Client ID (if present)
$utcClientId = Get-RegVal 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Diagnostics\DiagTrack\SettingsRequests' 'ClientId' $utcClientId = Get-RegVal 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Diagnostics\DiagTrack\SettingsRequests' 'ClientId'
[void]$ids.Add( (New-Id 'Telemetry' 'DiagTrack Client ID' $utcClientId -Sensitive -Source 'DiagTrack\SettingsRequests\ClientId') ) [void]$ids.Add( (New-Id 'Telemetry' 'DiagTrack Client ID' $utcClientId -Sensitive -Source 'DiagTrack\SettingsRequests\ClientId' `
-Explain 'Identifier used by the Connected User Experiences and Telemetry (DiagTrack) service that ships diagnostic data to Microsoft.') )
# ---- Activation / licensing ---- # ---- Activation / licensing ----
if ($os) { if ($os) {
[void]$ids.Add( (New-Id 'Activation' 'Windows edition' $os.Caption -Source 'Win32_OperatingSystem') ) [void]$ids.Add( (New-Id 'Activation' 'Windows edition' $os.Caption -Source 'Win32_OperatingSystem' `
-Explain 'The installed Windows edition/SKU.') )
} }
$partial = $null; $licStatus = $null; $licDesc = $null; $chan = $null $partial = $null; $licStatus = $null; $licDesc = $null; $chan = $null
$lic = Get-Cim 'SoftwareLicensingProduct' -Filter "ApplicationID='55c92734-d682-4d71-983e-d6ec3f16059f' AND PartialProductKey IS NOT NULL" $lic = Get-Cim 'SoftwareLicensingProduct' -Filter "ApplicationID='55c92734-d682-4d71-983e-d6ec3f16059f' AND PartialProductKey IS NOT NULL"
@@ -192,10 +360,14 @@ function Get-Identifiers {
$licStatus = if ($map.ContainsKey([int]$lic.LicenseStatus)) { $map[[int]$lic.LicenseStatus] } else { $lic.LicenseStatus } $licStatus = if ($map.ContainsKey([int]$lic.LicenseStatus)) { $map[[int]$lic.LicenseStatus] } else { $lic.LicenseStatus }
$licDesc = $lic.Description $licDesc = $lic.Description
} }
[void]$ids.Add( (New-Id 'Activation' 'License status' $licStatus -Source 'SoftwareLicensingProduct') ) [void]$ids.Add( (New-Id 'Activation' 'License status' $licStatus -Source 'SoftwareLicensingProduct' `
[void]$ids.Add( (New-Id 'Activation' 'License channel' $chan -Source 'SoftwareLicensingProduct') ) -Explain 'Current activation state (Licensed, grace period, notification, etc.).') )
[void]$ids.Add( (New-Id 'Activation' 'License description' $licDesc -Source 'SoftwareLicensingProduct') ) [void]$ids.Add( (New-Id 'Activation' 'License channel' $chan -Source 'SoftwareLicensingProduct' `
[void]$ids.Add( (New-Id 'Activation' 'Partial product key' $partial -Sensitive -Source 'SoftwareLicensingProduct') ) -Explain 'How Windows was licensed: Retail, OEM, or Volume (MAK/KMS).') )
[void]$ids.Add( (New-Id 'Activation' 'License description' $licDesc -Source 'SoftwareLicensingProduct' `
-Explain 'Human-readable licensing product description.') )
[void]$ids.Add( (New-Id 'Activation' 'Partial product key' $partial -Sensitive -Source 'SoftwareLicensingProduct' `
-Explain 'Last 5 characters of the active product key, exactly as Windows itself displays them.') )
# OEM firmware key (needs admin) # OEM firmware key (needs admin)
$oemKey = $null $oemKey = $null
@@ -206,7 +378,8 @@ function Get-Identifiers {
} else { } else {
$oemKey = '(needs admin)' $oemKey = '(needs admin)'
} }
[void]$ids.Add( (New-Id 'Activation' 'OEM firmware key (OA3)' $oemKey -Sensitive -Source 'SoftwareLicensingService.OA3xOriginalProductKey') ) [void]$ids.Add( (New-Id 'Activation' 'OEM firmware key (OA3)' $oemKey -Sensitive -Source 'SoftwareLicensingService.OA3xOriginalProductKey' `
-Explain 'Full product key the OEM embedded in firmware (BIOS/ACPI MSDM table). Reading it needs admin. Useful to record before a reinstall.') )
# Decoded installed product key from DigitalProductId # Decoded installed product key from DigitalProductId
$decoded = $null $decoded = $null
@@ -223,29 +396,104 @@ function Get-Identifiers {
$distinct = @($clean.ToCharArray() | Select-Object -Unique).Count $distinct = @($clean.ToCharArray() | Select-Object -Unique).Count
if ($distinct -le 3) { $decoded = '(unavailable - volume/MAK license)' } if ($distinct -le 3) { $decoded = '(unavailable - volume/MAK license)' }
} }
[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.') )
# ---- 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.') )
try { try {
$sid = ([Security.Principal.WindowsIdentity]::GetCurrent()).User.Value $sid = ([Security.Principal.WindowsIdentity]::GetCurrent()).User.Value
[void]$ids.Add( (New-Id 'User' 'User SID' $sid -Sensitive -Source 'WindowsIdentity') ) [void]$ids.Add( (New-Id 'User' 'User SID' $sid -Sensitive -Source 'WindowsIdentity' `
-Explain 'Security Identifier that uniquely identifies this account on the machine/domain. Embedded in ACLs and many logs.') )
} catch { } } catch { }
$advId = Get-RegVal $adv 'Id' $advId = Get-RegVal $adv 'Id'
[void]$ids.Add( (New-Id 'User' 'Advertising ID' $advId -Sensitive -Source 'AdvertisingInfo\Id') ) [void]$ids.Add( (New-Id 'User' 'Advertising ID' $advId -Sensitive -Source 'AdvertisingInfo\Id' `
-Explain 'Per-user identifier apps use to correlate you for ad targeting across apps.') )
$advOn = Get-RegVal $adv 'Enabled' $advOn = Get-RegVal $adv 'Enabled'
[void]$ids.Add( (New-Id 'User' 'Advertising ID enabled' $advOn -Source 'AdvertisingInfo\Enabled') ) [void]$ids.Add( (New-Id 'User' 'Advertising ID enabled' $advOn -Source 'AdvertisingInfo\Enabled' `
-Explain 'Whether the Advertising ID is active (1) or turned off (0).') )
# Microsoft account PUID (the "Passport Unique Identifier").
# The PUID's native representation is HEXADECIMAL - the .NET Passport API
# exposed it as PassportIdentity.HexPUID (a hex string). On modern Windows
# that hex value is the signed-in account's CID; decimal is derived from it.
$msa = @(Get-MsaAccounts)
if ($msa.Count -eq 0) {
[void]$ids.Add( (New-Id 'User' 'Microsoft account PUID' '(no Microsoft account signed in)' -Source 'IdentityCRL' `
-Explain 'PUID (Passport Unique Identifier): a permanent ID Microsoft assigns your ACCOUNT in its backend identity system (legacy "Microsoft Passport", later Windows Live ID, now Microsoft account). Natively hexadecimal (see .NET PassportIdentity.HexPUID). None found here - this appears to be a local account.') )
} else {
foreach ($a in $msa) {
[void]$ids.Add( (New-Id 'User' 'Microsoft account email' $a.Account -Sensitive -Source 'IdentityCRL\UserExtendedProperties' `
-Explain 'Email of a Microsoft account signed in on this machine.') )
[void]$ids.Add( (New-Id 'User' 'MS account PUID (hex / CID)' $a.Cid -Sensitive -Source 'IdentityCRL\...\cid' `
-Explain 'PUID in HEX - its native form (the value the Passport API returned as HexPUID). On modern Windows this is the account CID, also seen in OneDrive URLs and auth tokens.') )
[void]$ids.Add( (New-Id 'User' 'MS account PUID (decimal)' $a.Puid -Sensitive -Source 'derived: ToUInt64(HexPUID,16)' `
-Explain 'The same PUID converted to decimal (~18 digits). Passport Unique Identifier: a permanent Microsoft ACCOUNT ID from the legacy "Microsoft Passport" system (later Windows Live ID), identifying the account across Microsoft services regardless of device.') )
}
}
# ---- Device (registration with Microsoft, distinct from the user) ----
# On first internet connection wlidsvc (Windows Live ID) registers THIS
# DEVICE with Microsoft using hardware identity (disk, SMBIOS, TPM) and
# receives a device PUID/GDID + device token. The consumer device token is
# DPAPI-protected (not a plain value); the documented device ID that IS
# readable is the Entra/Azure AD Device ID via dsregcmd.
$ds = Get-DsRegStatus
$devId = if ($ds.ContainsKey('DeviceId')) { $ds['DeviceId'] } else { $null }
if ([string]::IsNullOrWhiteSpace($devId)) { $devId = '(device not registered with Entra/Azure AD)' }
[void]$ids.Add( (New-Id 'Device' 'Entra/Azure AD Device ID' $devId -Sensitive -Source 'dsregcmd /status' `
-Explain 'GUID identifying this device to Microsoft Entra ID (Azure AD) when the device is registered/joined. This is the readable device identity; the consumer MSA device token issued by wlidsvc is DPAPI-protected and not shown.') )
foreach ($jk in @('AzureAdJoined','EnterpriseJoined','DomainJoined','WorkplaceJoined')) {
if ($ds.ContainsKey($jk)) {
[void]$ids.Add( (New-Id 'Device' $jk $ds[$jk] -Source 'dsregcmd /status' `
-Explain 'Device join/registration state reported by dsregcmd.') )
}
}
if ($ds.ContainsKey('TenantId')) {
[void]$ids.Add( (New-Id 'Device' 'Entra Tenant ID' $ds['TenantId'] -Sensitive -Source 'dsregcmd /status' `
-Explain 'Identifier of the Entra ID (Azure AD) tenant this device is registered to, if any.') )
}
$wlid = if ($msa.Count -gt 0) { 'MSA present - device registered with Microsoft' } else { 'no Microsoft account signed in' }
[void]$ids.Add( (New-Id 'Device' 'MSA device registration' $wlid -Source 'wlidsvc (concept)' `
-Explain 'On first internet connection the Windows Live ID service (wlidsvc) logs this DEVICE in to Microsoft using hardware identifiers (disk serial, SMBIOS UUID, TPM) and receives a device PUID/GDID and a device token. Pass -ExtractDeviceId (admin) to read the device account PUID from the SYSTEM hive; the device token itself is DPAPI-protected and not surfaced.') )
# Device account PUID - registered under SYSTEM (S-1-5-18), read via -ExtractDeviceId
if ($ExtractDeviceId) {
if (-not $Script:IsAdmin) {
[void]$ids.Add( (New-Id 'Device' 'Device account PUID' '(needs admin)' -Source 'IdentityCRL as SYSTEM' `
-Explain 'The device MSA PUID lives in the SYSTEM account hive; extracting it needs Administrator (the tool runs a one-shot SYSTEM task).') )
} else {
$dev = @(Get-DeviceIdentityAsSystem)
if ($dev.Count -eq 0) {
[void]$ids.Add( (New-Id 'Device' 'Device account PUID' '(none found - no device registration)' -Source 'IdentityCRL as SYSTEM' `
-Explain 'No device account was found in the SYSTEM IdentityCRL store (device may never have registered with an MSA).') )
} else {
foreach ($d in $dev) {
[void]$ids.Add( (New-Id 'Device' 'Device account' (Get-PropSafe $d 'Account') -Sensitive -Source 'SYSTEM HKCU IdentityCRL\UserExtendedProperties' `
-Explain 'The hidden device MSA account name Windows created to log this device in to Microsoft.') )
[void]$ids.Add( (New-Id 'Device' 'Device PUID (hex / CID)' (Get-PropSafe $d 'Cid') -Sensitive -Source 'SYSTEM IdentityCRL\...\cid' `
-Explain 'The device account CID in hex - the device PUID/GDID Microsoft issued when this device registered using its hardware identity. Native (hex) form.') )
[void]$ids.Add( (New-Id 'Device' 'Device PUID (decimal)' (Get-PropSafe $d 'Puid') -Sensitive -Source 'derived: ToUInt64(CID,16)' `
-Explain 'The device PUID converted to decimal (~18 digits).') )
}
}
}
}
# ---- Hardware ---- # ---- Hardware ----
if ($bios) { if ($bios) {
[void]$ids.Add( (New-Id 'Hardware' 'BIOS serial number' $bios.SerialNumber -Sensitive -Source 'Win32_BIOS') ) [void]$ids.Add( (New-Id 'Hardware' 'BIOS serial number' $bios.SerialNumber -Sensitive -Source 'Win32_BIOS' `
-Explain 'System serial from firmware; hardware-bound and survives OS reinstall - a strong permanent fingerprint.') )
} }
if ($base) { if ($base) {
[void]$ids.Add( (New-Id 'Hardware' 'Baseboard serial' $base.SerialNumber -Sensitive -Source 'Win32_BaseBoard') ) [void]$ids.Add( (New-Id 'Hardware' 'Baseboard serial' $base.SerialNumber -Sensitive -Source 'Win32_BaseBoard' `
-Explain 'Motherboard serial number.') )
} }
if ($cpu) { if ($cpu) {
$cpu1 = @($cpu)[0] $cpu1 = @($cpu)[0]
[void]$ids.Add( (New-Id 'Hardware' 'CPU ProcessorId' $cpu1.ProcessorId -Sensitive -Source 'Win32_Processor') ) [void]$ids.Add( (New-Id 'Hardware' 'CPU ProcessorId' $cpu1.ProcessorId -Sensitive -Source 'Win32_Processor' `
-Explain 'Processor signature/feature ID. Not globally unique, but adds entropy to a hardware fingerprint.') )
} }
# Disk serials # Disk serials
$disks = Get-Cim 'Win32_DiskDrive' $disks = Get-Cim 'Win32_DiskDrive'
@@ -254,24 +502,56 @@ function Get-Identifiers {
foreach ($d in @($disks)) { foreach ($d in @($disks)) {
$n++ $n++
$ser = if ($d.SerialNumber) { ($d.SerialNumber).Trim() } else { '(none)' } $ser = if ($d.SerialNumber) { ($d.SerialNumber).Trim() } else { '(none)' }
[void]$ids.Add( (New-Id 'Hardware' ("Disk {0} serial" -f $n) $ser -Sensitive -Source 'Win32_DiskDrive') ) [void]$ids.Add( (New-Id 'Hardware' ("Disk {0} serial" -f $n) $ser -Sensitive -Source 'Win32_DiskDrive' `
-Explain 'Physical drive serial number; a strong, portable hardware fingerprint.') )
} }
} }
# MAC addresses (physical adapters with a MAC) # MAC addresses (physical adapters with a MAC)
$nics = Get-Cim 'Win32_NetworkAdapter' -Filter 'PhysicalAdapter=TRUE AND MACAddress IS NOT NULL' $nics = Get-Cim 'Win32_NetworkAdapter' -Filter 'PhysicalAdapter=TRUE AND MACAddress IS NOT NULL'
if ($nics) { if ($nics) {
foreach ($nic in @($nics)) { foreach ($nic in @($nics)) {
[void]$ids.Add( (New-Id 'Network' ("MAC - {0}" -f $nic.NetConnectionID) $nic.MACAddress -Sensitive -Source 'Win32_NetworkAdapter') ) [void]$ids.Add( (New-Id 'Network' ("MAC - {0}" -f $nic.NetConnectionID) $nic.MACAddress -Sensitive -Source 'Win32_NetworkAdapter' `
-Explain 'Hardware address of this network adapter; identifies the device on every network it joins (unless MAC randomization is on).') )
} }
} }
# TPM (needs admin; separate namespace) # TPM (needs admin; separate namespace)
$tpm = Get-Cim 'Win32_Tpm' -Namespace 'root\cimv2\Security\MicrosoftTpm' $tpm = Get-Cim 'Win32_Tpm' -Namespace 'root\cimv2\Security\MicrosoftTpm'
if ($tpm) { if ($tpm) {
$tpm1 = @($tpm)[0] $tpm1 = @($tpm)[0]
[void]$ids.Add( (New-Id 'Hardware' 'TPM present' $tpm1.IsEnabled_InitialValue -Source 'Win32_Tpm') ) [void]$ids.Add( (New-Id 'Hardware' 'TPM present' $tpm1.IsEnabled_InitialValue -Source 'Win32_Tpm' `
[void]$ids.Add( (New-Id 'Hardware' 'TPM manufacturer ID' $tpm1.ManufacturerId -Source 'Win32_Tpm') ) -Explain 'Whether a Trusted Platform Module is enabled; underpins device attestation and disk encryption keys.') )
[void]$ids.Add( (New-Id 'Hardware' 'TPM manufacturer ID' $tpm1.ManufacturerId -Source 'Win32_Tpm' `
-Explain 'Vendor ID of the TPM chip.') )
} elseif (-not $Script:IsAdmin) { } elseif (-not $Script:IsAdmin) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM info' '(needs admin)' -Source 'Win32_Tpm') ) [void]$ids.Add( (New-Id 'Hardware' 'TPM info' '(needs admin)' -Source 'Win32_Tpm' `
-Explain 'TPM chip details (presence, vendor); requires Administrator to read.') )
}
# TPM Endorsement Key public part (EKpub) - the strongest permanent ID
$ekExplain = 'TPM Endorsement Key (public part). A permanent, globally-unique key burned into the TPM at manufacture - it cannot be changed, reset or removed. Windows/Microsoft device attestation and registration use the EKpub (or its hash) to uniquely identify THIS exact TPM, and therefore this device, for its entire lifetime. The strongest hardware fingerprint on the machine.'
if (-not (Get-Command Get-TpmEndorsementKeyInfo -ErrorAction SilentlyContinue)) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub' '(TPM cmdlets unavailable)' -Source 'Get-TpmEndorsementKeyInfo' -Explain $ekExplain) )
} elseif (-not $Script:IsAdmin) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub' '(needs admin)' -Source 'Get-TpmEndorsementKeyInfo' -Explain $ekExplain) )
} else {
$ek = Get-TpmEkPub
if ($ek.DerB64 -or $ek.WinHash -or $ek.Sha256) {
if ($ek.WinHash) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub hash (Windows)' $ek.WinHash -Sensitive -Source 'Get-TpmEndorsementKeyInfo.PublicKeyHash' -Explain $ekExplain) )
}
if ($ek.Sha256) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub SHA-256' $ek.Sha256 -Sensitive -Source 'SHA-256 of PublicKey.RawData' `
-Explain ($ekExplain + ' (SHA-256 fingerprint of the EKpub, computed here since Windows left PublicKeyHash empty.)')) )
}
if ($ek.DerB64) {
[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.') )
}
} elseif ($ek.Present) {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub' '(EK present but public key not readable)' -Source 'Get-TpmEndorsementKeyInfo' -Explain $ekExplain) )
} else {
[void]$ids.Add( (New-Id 'Hardware' 'TPM EKpub' '(no EK present / not readable)' -Source 'Get-TpmEndorsementKeyInfo' -Explain $ekExplain) )
}
} }
return $ids return $ids
@@ -294,7 +574,7 @@ function Show-Report {
} }
Write-Host '==================================================================' -ForegroundColor Cyan Write-Host '==================================================================' -ForegroundColor Cyan
$categories = @('Machine','Activation','Telemetry','User','Hardware','Network') $categories = @('Machine','Activation','Telemetry','User','Device','Hardware','Network')
foreach ($cat in $categories) { foreach ($cat in $categories) {
$group = @($Ids | Where-Object { $_.Category -eq $cat }) $group = @($Ids | Where-Object { $_.Category -eq $cat })
if ($group.Count -eq 0) { continue } if ($group.Count -eq 0) { continue }
@@ -306,10 +586,16 @@ function Show-Report {
$tag = if ($id.Sensitive) { '!' } else { ' ' } $tag = if ($id.Sensitive) { '!' } else { ' ' }
Write-Host (' {0} {1,-32}' -f $tag, $id.Name) -NoNewline Write-Host (' {0} {1,-32}' -f $tag, $id.Name) -NoNewline
Write-Host $disp -ForegroundColor $color Write-Host $disp -ForegroundColor $color
if (-not $Brief -and $id.Explain) {
Write-Host (' {0}' -f $id.Explain) -ForegroundColor DarkGray
}
} }
} }
Write-Host '' Write-Host ''
Write-Host ' ! = sensitive identifier (masked unless -Reveal).' -ForegroundColor DarkYellow Write-Host ' ! = sensitive identifier (masked unless -Reveal).' -ForegroundColor DarkYellow
if ($Brief) {
Write-Host ' (explanations hidden by -Brief; omit it to show them.)' -ForegroundColor DarkGray
}
if (-not $Script:IsAdmin) { if (-not $Script:IsAdmin) {
Write-Host ' Some values need Administrator (OEM key, TPM) - shown as (needs admin).' -ForegroundColor DarkYellow Write-Host ' Some values need Administrator (OEM key, TPM) - shown as (needs admin).' -ForegroundColor DarkYellow
} }
@@ -325,6 +611,7 @@ function Export-Rows {
Value = (Format-Masked -Value $id.Raw -Sensitive:$id.Sensitive) Value = (Format-Masked -Value $id.Raw -Sensitive:$id.Sensitive)
Sensitive = $id.Sensitive Sensitive = $id.Sensitive
Source = $id.Source Source = $id.Source
Explanation = $id.Explain
} }
} }
} }