Public/Get-AutopilotHash.ps1
|
<#
.SYNOPSIS Extracts and structurally validates the genuine Windows Autopilot 4K-8K hardware hash. .DESCRIPTION Queries the official MDM WMI provider (root/cimv2/mdm/dmmap:MDM_DevDetail_Ext01) for the complete hardware hash. Supports structurally verified manual hash override (-ManualHash) for lab testing, automatic dmwappushservice recovery, and a 10-attempt backoff loop. #> function Get-AutopilotHash { [CmdletBinding()] param( [Parameter()] [string]$GroupTag = '', [Parameter()] [string]$AssignedUser = '', [Parameter()] [string]$ManualHash = '', [Parameter()] [ValidateSet('Object', 'Csv', 'Json')] [string]$Format = 'Object' ) $isAdmin = $false try { $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($identity) $isAdmin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } catch { } # Structural Validation of Hardware Hash (Base64 + Binary Length + ASN.1 DER Header Check) function Test-AutopilotHashStructure { param([string]$HashString) if ([string]::IsNullOrWhiteSpace($HashString)) { return $false } $clean = $HashString.Trim() if ($clean -notmatch '^[A-Za-z0-9+/=]+$') { throw "Hardware hash failed Base64 charset validation." } try { $bytes = [Convert]::FromBase64String($clean) $byteLen = $bytes.Length # Valid OA3 / MDM Autopilot hashes are binary blobs between 1KB and 16KB if ($byteLen -lt 1024 -or $byteLen -gt 16384) { throw "Hardware hash decoded length ($byteLen bytes) is outside valid Autopilot 4K/8K specification (1024 - 16384 bytes)." } # Verify standard OA3 / ASN.1 DER sequence header (0x30 or device hardware descriptor tag) $headerByte = $bytes[0] if ($headerByte -ne 0x30 -and $headerByte -ne 0x01 -and $headerByte -ne 0x02) { Write-Warning "Hardware hash header byte (0x$($headerByte.ToString('X2'))) does not match standard ASN.1 DER / OA3 descriptor structure." } return $true } catch { throw "Autopilot hardware hash structural verification failed: $($_.Exception.Message)" } } if ($ManualHash) { Test-AutopilotHashStructure -HashString $ManualHash | Out-Null $hardwareHash = $ManualHash.Trim() $statusMessage = 'ManualOverride (Structurally Verified)' } else { $hardwareHash = '' $statusMessage = 'Captured' } $serial = '' $uuid = '' $model = '' $manufacturer = '' $pkid = '' # 1. Ensure dmwappushservice is enabled and running try { $svc = Get-Service -Name 'dmwappushservice' -ErrorAction SilentlyContinue if ($svc) { if ($svc.StartType -eq 'Disabled') { Write-Host " [+] Configuring dmwappushservice startup to Automatic..." -ForegroundColor Cyan Set-Service -Name 'dmwappushservice' -StartupType Automatic -ErrorAction SilentlyContinue } if ($svc.Status -ne 'Running') { Write-Host " [+] Starting dmwappushservice for MDM WMI provider initialization..." -ForegroundColor Cyan Start-Service -Name 'dmwappushservice' -ErrorAction SilentlyContinue } } } catch { } # 2. Retrieve BIOS and System Product info via CIM try { $bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop $serial = $bios.SerialNumber } catch { $serial = (Get-WmiObject -Class Win32_BIOS -ErrorAction SilentlyContinue).SerialNumber } try { $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop $model = $cs.Model $manufacturer = $cs.Manufacturer } catch { } try { $csp = Get-CimInstance -ClassName Win32_ComputerSystemProduct -ErrorAction Stop $uuid = $csp.UUID } catch { } # 3. Retrieve Product ID from Registry try { $regKey = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $pkid = (Get-ItemProperty -Path $regKey -Name ProductId -ErrorAction SilentlyContinue).ProductId } catch { } # 4. Retrieve Hardware Hash with 10-Attempt Backoff Loop (if not manual) if (-not $hardwareHash) { $maxAttempts = 10 for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { try { $devDetail = Get-CimInstance -Namespace 'root/cimv2/mdm/dmmap' -ClassName 'MDM_DevDetail_Ext01' -Filter "InstanceID='Ext01' AND ParentID='./DevDetail'" -ErrorAction Stop $rawHash = $devDetail.DeviceHardwareData if ($rawHash) { Test-AutopilotHashStructure -HashString $rawHash | Out-Null $hardwareHash = $rawHash break } } catch { try { $devDetailWmi = Get-WmiObject -Namespace 'root/cimv2/mdm/dmmap' -Class 'MDM_DevDetail_Ext01' -Filter "InstanceID='Ext01' AND ParentID='./DevDetail'" -ErrorAction Stop $rawHash = $devDetailWmi.DeviceHardwareData if ($rawHash) { Test-AutopilotHashStructure -HashString $rawHash | Out-Null $hardwareHash = $rawHash break } } catch { if ($attempt -lt $maxAttempts) { Start-Sleep -Seconds 5 } else { $isVm = ($model -match 'Virtual|VMware|Hyper-V|KVM|QEMU' -or $manufacturer -match 'Microsoft Corporation|VMware|QEMU') if ($isVm) { $statusMessage = "VirtualMachine_NonOA3 (VM detected without OEM OA3 injection. Use Virtual TPM 2.0 or Autopilot v2 Device Preparation)" } elseif (-not $isAdmin) { $statusMessage = "AccessDenied (Administrator privileges required to query MDM WMI provider)" } else { $statusMessage = "MDM_Provider_Uninitialized (MDM stack not yet initialized after $maxAttempts attempts: $($_.Exception.Message))" } } } } } } # 5. Cache Captured Hash Locally (Capture Once, Never Lose) if ($hardwareHash) { try { $cacheDir = Join-Path $env:TEMP "AutopilotFast" if (-not (Test-Path $cacheDir)) { New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null } $cacheFile = Join-Path $cacheDir "last_hardware_hash.bin" [System.IO.File]::WriteAllText($cacheFile, $hardwareHash) } catch { } } $result = [PSCustomObject]@{ SerialNumber = $serial SmbiosUuid = $uuid Manufacturer = $manufacturer Model = $model WindowsProductID = $pkid GroupTag = $GroupTag AssignedUser = $AssignedUser HardwareHash = $hardwareHash HardwareHashStatus = $statusMessage HashLengthBytes = if ($hardwareHash) { $hardwareHash.Length } else { 0 } IsElevated = $isAdmin CaptureTimestamp = (Get-Date).ToString('o') } switch ($Format) { 'Csv' { return "$serial,$pkid,$hardwareHash,$GroupTag,$AssignedUser" } 'Json' { return ($result | ConvertTo-Json -Depth 5) } Default { return $result } } } |