# gocmm-debug.ps1 - diagnose goCMM "Requested registry access is not allowed" post-lockdown. # # RUN AS THE OPERATOR (the locked-down shop-floor user), NOT elevated / not "Run as administrator". # Elevation would falsely succeed (admins have write) and hide the bug. # # Root cause being tested: goCMM's GEA_OFI_Common.RegistrySettings.GetRegistryString opens # HKLM\Software\General Electric\goCMM with writable:TRUE even to READ a value. goCMM is # 32-bit, so that redirects to HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM. If the # operator lacks WRITE on that key (lockdown stripped the BUILTIN\Users grant), the open throws. # # Output: C:\Logs\CMM\gocmm-debug--.txt (pull this back for review) $ErrorActionPreference = 'Continue' $ts = Get-Date -Format 'yyyyMMdd-HHmmss' $dir = 'C:\Logs\CMM' New-Item -ItemType Directory -Path $dir -Force -ErrorAction SilentlyContinue | Out-Null $log = Join-Path $dir "gocmm-debug-$env:COMPUTERNAME-$ts.txt" function W($m){ $m | Tee-Object -FilePath $log -Append } $key32native = 'SOFTWARE\General Electric\goCMM' # path under the 32-bit (Registry32) base $keyWow = 'HKLM:\SOFTWARE\WOW6432Node\General Electric\goCMM' W "================ goCMM debug ================" W "When : $(Get-Date)" W "PC : $env:COMPUTERNAME" W "User : $env:USERDOMAIN\$env:USERNAME" W "PS : $($PSVersionTable.PSVersion) (process is $([IntPtr]::Size*8)-bit)" W "Elevated: $((New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))" W "" W "================ whoami /all (identity + groups + privileges) ================" (whoami /all 2>&1 | Out-String) | W W "" W "================ goCMM key values (32-bit view, read-only open) ================" try { $base32 = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine','Registry32') $kr = $base32.OpenSubKey($key32native, $false) if ($kr) { foreach ($n in $kr.GetValueNames()) { W (" {0} = {1}" -f $n, $kr.GetValue($n)) } $kr.Close() } else { W " (key NOT found in 32-bit view)" } } catch { W " ERROR reading values: $($_.Exception.Message)" } W "" W "================ PROBE 1: mimic goCMM GetRegistryString -> OpenSubKey(writable:TRUE), 32-bit view ================" W "(this is the exact call that throws in the app)" try { $base32 = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine','Registry32') $kw = $base32.OpenSubKey($key32native, $true) if ($kw) { W " RESULT: SUCCESS - opened goCMM key WRITABLE. (operator HAS write; settings should work)"; $kw.Close() } else { W " RESULT: NULL - key missing; app would attempt CreateSubKey (also needs write)" } } catch [System.Security.SecurityException] { W " RESULT: *** REPRODUCED *** SecurityException: $($_.Exception.Message)" W " -> operator lacks WRITE on the goCMM key. Lockdown stripped the BUILTIN\Users grant, or a Deny applies." } catch { W " RESULT: OTHER $($_.Exception.GetType().FullName): $($_.Exception.Message)" } W "" W "================ PROBE 2: read-only open (writable:FALSE) - does the operator at least READ? ================" try { $base32 = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine','Registry32') $kr2 = $base32.OpenSubKey($key32native, $false) if ($kr2) { W " read-only open OK (read works; only WRITE-open is denied -> confirms the writable:true bug)"; $kr2.Close() } else { W " read-only open returned NULL (key missing)" } } catch { W " read-only open FAILED: $($_.Exception.Message)" } W "" W "================ ACL on goCMM key (the smoking gun) ================" try { $acl = Get-Acl -Path $keyWow W (" Owner : " + $acl.Owner) W (" SDDL : " + $acl.Sddl) W " Access rules:" $acl.Access | ForEach-Object { W (" {0,-30} {1,-22} {2,-6} Inherited={3}" -f $_.IdentityReference, $_.RegistryRights, $_.AccessControlType, $_.IsInherited) } $usersWrite = $acl.Access | Where-Object { $_.AccessControlType -eq 'Allow' -and "$($_.IdentityReference)" -match 'Users' -and ("$($_.RegistryRights)" -match 'WriteKey|SetValue|FullControl') } if ($usersWrite) { W " >> BUILTIN\Users WRITE ACE present (grant survived). Failure is elsewhere - check for a Deny ACE or wrong view." } else { W " >> NO BUILTIN\Users WRITE ACE. Confirms lockdown removed the grant. <<" } } catch { W " Get-Acl failed: $($_.Exception.Message)" } W "" W "================ raw reg export of the key (for record) ================" $regOut = Join-Path $dir "gocmm-key-$env:COMPUTERNAME-$ts.reg" reg export "HKLM\SOFTWARE\WOW6432Node\General Electric\goCMM" "$regOut" /y 2>&1 | Out-String | W W " exported -> $regOut" W "" W "================ goCMM version + install ================" foreach ($p in @( 'C:\Program Files (x86)\General Electric\goCMM\GEAOperatorFriendlyInterface.exe', 'C:\Program Files (x86)\General Electric\goCMM\GEA_OFI_Common.dll')) { if (Test-Path $p) { $vi = (Get-Item $p).VersionInfo; W (" {0} FileVer={1} ProductVer={2}" -f (Split-Path $p -Leaf), $vi.FileVersion, $vi.ProductVersion) } else { W " MISSING: $p" } } W "" W "================ UAC / registry virtualization ================" (reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableLUA 2>&1 | Out-String) | W (reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableVirtualization 2>&1 | Out-String) | W W "" W "================ DONE ================" W "Log : $log" W "Reg : $regOut" Write-Host "" Write-Host "Done. Collected:" -ForegroundColor Green Write-Host " $log" Write-Host " $regOut"