はじめに
Windowsのコアな機能を強引に無効化したいときに、直接レジストリを書き換えるしか手がないという、よくある(?)ケース。
それを自動化するときは、バッチファイルでREG
コマンドを使ってやれば概ね実現できる。
ただ、稀に対象のレジストリキーのアクセス権が変更されていて、Administrators
グループにフルコントロールが割り当てられていない時がある。
困ったことに、REG
コマンドではアクセス権を編集できなくて、バッチファイルからどうにかするのは難しそう・・・(regini
コマンドで出来そうな雰囲気はあったけど、外部ファイルを用意する必要があったり微妙すぎた)
さらに調べてみると、できれば触りたくないPowerShell
のSet-Acl
を使えば出来るらしい。
(普段は、いちいちPowerShell使うくらいならC#でサクッと書いちゃう派)
よく見るPowerShellでレジストリキーのACLの変え方
Get-Acl
で取得したレジストリキーのACLを編集して、Set-Acl
で反映するやつ。
$acl = Get-Acl "HKLM:\SYSTEM\TEST\AAA"
$ruleFullControl = New-Object System.Security.AccessControl.RegistryAccessRule ("BUILTIN\Administrators", [System.Security.AccessControl.RegistryRights]::FullControl, [System.Security.AccessControl.InheritanceFlags]::ContainerInherit, [System.Security.AccessControl.PropagationFlags]::None, [System.Security.AccessControl.AccessControlType]::Allow)
$acl.SetAccessRule($ruleFullControl)
$acl | Set-Acl "HKLM:\SYSTEM\TEST\AAA"
これを読み取り専用になったレジストリキーで実行すると・・・
Set-Acl : 要求されたレジストリ アクセスは許可されていません。
発生場所 T:\TEST.ps1:103 文字:36
+ $acl | Set-Acl -Path Registry::$key
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (HKEY_LOCAL_MACHINE\SYSTEM\TEST\AAA:String) [Set-Acl], SecurityException
+ FullyQualifiedErrorId : System.Security.SecurityException,Microsoft.PowerShell.Commands.SetAclCommand
(´・ω・`)
レジストリエディターでは普通に編集できるのだから、なにか手はあるはず・・・
Microsoft.Win32.Registry クラスを使う
.NETのクラスを使って何とかする技を見つけた。
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("SYSTEM\TEST\AAA", [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree, [System.Security.AccessControl.RegistryRights]::ChangePermissions)
$acl = $key.GetAccessControl()
$ruleFullControl = New-Object System.Security.AccessControl.RegistryAccessRule ("BUILTIN\Administrators", [System.Security.AccessControl.RegistryRights]::FullControl, [System.Security.AccessControl.InheritanceFlags]::ContainerInherit, [System.Security.AccessControl.PropagationFlags]::None, [System.Security.AccessControl.AccessControlType]::Allow)
$acl.SetAccessRule($ruleFullControl)
$key.SetAccessControl($acl)
$key.Close()
これならAdministrators
グループのアクセス権限をフルコントロールに変更できた。
ちなみに同時に値も読み取りたいときは、OpenSubKey
の第3引数を
[System.Security.AccessControl.RegistryRights]::ChangePermissions -bor [System.Security.AccessControl.RegistryRights]::ReadKey
にする。
値を書き込むときは、
[System.Security.AccessControl.RegistryRights]::ChangePermissions -bor [System.Security.AccessControl.RegistryRights]::WriteKey
にする。
(FullControl
を設定してからじゃないと、OpenSubKey
メソッドで失敗する)
バッチファイルからps1を実行する
既にバッチファイルで色々と組んでいて、丸ごとPowerShellに移植するのは面倒くさい・・・
ってことで、バッチファイルからps1ファイルを実行することに。
powershell -ExecutionPolicy Bypass -File %~dp0\TEST.ps1
既定の設定だとPowerShellのスクリプトが実行できないので、-ExecutionPolicy Bypass
で回避。
所有者がTrustedInstallerのキーでも変更する(2023/9/26追記)
以前書いた方法だけでは、TrustedInstaller
が所有者になっているキーのアクセス権限を変更できないことが分かったので、さらに強引な手段を取ることに・・・
さらに値と書き換えたあとに、所有者やアクセス権の設定を元に戻せるように工夫もしてみた。
まず、別記事に書いた特権を有効にする方法を使って、2つ特権を有効にする。
Set-Privilege SeTakeOwnershipPrivilege $true
Set-Privilege SeRestorePrivilege $true
それから所有者をAdministrators
グループに変更。
一連の処理が終わったあとに、所有者を元に戻せるように保持しておく。
$path = "SYSTEM\CurrentControlSet\Control\Diagnostics\Performance"
# 所有者変更の権限を有効にして開く
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($path, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree, [System.Security.AccessControl.RegistryRights]::TakeOwnership)
# 現在の所有者を記憶
$acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::Owner)
$oldOwner = $acl.GetOwner([System.Security.Principal.NTAccount])
# 所有者を[Administrators]に変更
$adminAccount = [System.Security.Principal.NTAccount]("BUILTIN\Administrators")
$acl.SetOwner($adminAccount)
$key.SetAccessControl($acl)
# 1度閉じる
$key.Close()
次に、Administrators
グループのアクセス権をを変更。
既にフルコントロールの権限がある場合は何もしなかったり、ここでも元に戻せるよう少し小細工。
# アクセス権限の操作を有効にして開く
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($path, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree, [System.Security.AccessControl.RegistryRights]::ChangePermissions)
# 現在のアクセス権限を取得
$acl = $key.GetAccessControl()
foreach ($rule in $acl.GetAccessRules($true, $true, [System.Security.Principal.NTAccount])) {
# [Administrators]グループのルールが対象
if ($rule.IdentityReference.Value -eq "BUILTIN\Administrators") {
# サブキーのみが対象のものは除外
if ($rule.PropagationFlags -ne [System.Security.AccessControl.PropagationFlags]::InheritOnly) {
# フルコントロールの権限がない
if ($rule.RegistryRights -ne [System.Security.AccessControl.RegistryRights]::FullControl) {
$ruleFullControl = New-Object System.Security.AccessControl.RegistryAccessRule ("BUILTIN\Administrators", [System.Security.AccessControl.RegistryRights]::FullControl, [System.Security.AccessControl.InheritanceFlags]::ContainerInherit, [System.Security.AccessControl.PropagationFlags]::None, [System.Security.AccessControl.AccessControlType]::Allow)
if ($rule.IsInherited) {
# 一時的なルールを記録
$tmpRule = $ruleFullControl
# フルコントロールを追加
$acl.AddAccessRule($ruleFullControl)
} else {
# 元のルールを記録
$oldRule = $rule
# フルコントロールで上書き
$acl.SetAccessRule($ruleFullControl)
}
$key.SetAccessControl($acl)
break;
}
}
}
}
# 1度閉じる
$key.Close()
ようやく値の編集。
# 書き込み権限+αの操作を有効にして開く
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($path, [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree, [System.Security.AccessControl.RegistryRights]::TakeOwnership -bor [System.Security.AccessControl.RegistryRights]::ChangePermissions -bor [System.Security.AccessControl.RegistryRights]::WriteKey)
# 値を編集
$key.SetValue("DisableDiagnosticTracing", 1, [Microsoft.Win32.RegistryValueKind]::DWord)
最後に所有者とアクセス権を元に戻して閉じる。
# ACLを元に戻す
$acl = $key.GetAccessControl()
if ($tmpRule -ne $null) {
# 一時的なルールを削除
$acl.RemoveAccessRule($tmpRule)
$tmpRule = $null
} elseif ($oldRule -ne $null) {
# アクセス権限を編集した場合は元に戻す
$acl.SetAccessRule($oldRule)
$oldRule = $null
}
# 所有者を元に戻す
$acl.SetOwner($oldOwner)
$key.SetAccessControl($acl)
$key.Close()
これで、TrustedInstaller
が所有しているキーの値でも、強引に書き換えて、何事もなかったかのようにACLが元に戻るようにできた。
ハマったポイント
所有者を「TrustedInstaller
からAdministrators
に書き換える」というのは直ぐに出来たけど、「TrustedInstaller
に戻す」というのが上手くいかなくて手こずった・・・
結果的には、有効にする特権が SeTakeOwnershipPrivilege
だけではなく SeRestorePrivilege
も必要だった。
調べてる最中に、TrustedInstaller
が所有しているフォルダやファイルで、同じように一時的に所有者を変えて元に戻そうとすると上手くいかないって話をちらほら見かけたけど、それも同様の方法で元に戻せるはず。
おわりに
なにかと面倒で、どうにも好きになれないPowerShellくん・・・