{"record":{"id":"e25ee058bd41b357","repo":"BluePointLilac/ContextMenuManager","slug":"serestoreprivilege","errorCode":null,"errorMessage":"SeRestorePrivilege","messagePattern":"SeRestorePrivilege","errorType":"exception","errorClass":"PrivilegeNotHeldException","httpStatus":null,"severity":"error","filePath":"ContextMenuManager/BluePointLilac.Methods/RegTrustedInstaller.cs","lineNumber":180,"sourceCode":"            if(regPath.IsNullOrWhiteSpace()) return;\n            RegistryKey key = null;\n            WindowsIdentity id = null;\n            //利用试错判断是否有写入权限\n            try { key = RegistryEx.GetRegistryKey(regPath, true); }\n            catch\n            {\n                try\n                {\n                    //获取当前用户的ID\n                    id = WindowsIdentity.GetCurrent();\n\n                    //添加TakeOwnership特权\n                    bool flag = NativeMethod.TrySetPrivilege(NativeMethod.TakeOwnership, true);\n                    if(!flag) throw new PrivilegeNotHeldException(NativeMethod.TakeOwnership);\n\n                    //添加恢复特权(必须这样做才能更改所有者)\n                    flag = NativeMethod.TrySetPrivilege(NativeMethod.Restore, true);\n                    if(!flag) throw new PrivilegeNotHeldException(NativeMethod.Restore);\n\n                    //打开没有权限的注册表路径\n                    key = RegistryEx.GetRegistryKey(regPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);\n\n                    RegistrySecurity security = key.GetAccessControl(AccessControlSections.All);\n\n                    //得到真正所有者\n                    //IdentityReference oldId = security.GetOwner(typeof(SecurityIdentifier));\n                    //SecurityIdentifier siTrustedInstaller = new SecurityIdentifier(oldId.ToString());\n\n                    //使进程用户成为所有者\n                    security.SetOwner(id.User);\n                    key.SetAccessControl(security);\n\n                    //添加完全控制\n                    RegistryAccessRule fullAccess = new RegistryAccessRule(id.User, RegistryRights.FullControl,\n                        InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);\n                    security.AddAccessRule(fullAccess);","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/BluePointLilac/ContextMenuManager/blob/55507155dd8e49c7ab4606da97f2af192d590dfe/ContextMenuManager/BluePointLilac.Methods/RegTrustedInstaller.cs#L162-L198","documentation":"After SeTakeOwnershipPrivilege is enabled, the code enables SeRestorePrivilege (the Restore privilege) via NativeMethod.TrySetPrivilege(NativeMethod.Restore, true). This privilege is required to actually change the owner of a securable object — taking ownership alone does not let you write a new owner SID into the security descriptor. If TrySetPrivilege returns false, it throws PrivilegeNotHeldException with 'SeRestorePrivilege'.","triggerScenarios":"The process successfully enabled SeTakeOwnershipPrivilege but cannot enable SeRestorePrivilege, because the account/token lacks the 'Restore files and directories' user right. This can happen with an elevated token where the Restore right was removed by policy, or with a partially-restricted admin token.","commonSituations":"Admin elevation succeeded (so error [2] passes) but group policy removed the 'Restore files and directories' right from the Administrators group. Running under a custom service account that was granted TakeOwnership but not Restore. UAC-filted token that has Restore present-but-disabled and the enable call fails due to token restrictions.","solutions":["Run as Administrator to obtain a full, unfiltered token with SeRestorePrivilege enabled","Verify the privilege via Local Security Policy > User Rights Assignment > 'Restore files and directories'","Run 'whoami /priv' in an elevated prompt to confirm SeRestorePrivilege shows as Enabled","If the Administrators group was stripped of the right via GPO, restore it or run under an account that retains both privileges"],"exampleFix":"// before (passes TakeOwnership, fails on Restore)\nNativeMethod.TrySetPrivilege(NativeMethod.TakeOwnership, true);\nNativeMethod.TrySetPrivilege(NativeMethod.Restore, true);\n\n// after (check both before attempting ownership change)\nusing(var identity = WindowsIdentity.GetCurrent())\n{\n    var privs = identity.Token.GetPrivileges();\n    if(!privs.Contains(\"SeTakeOwnershipPrivilege\") ||\n       !privs.Contains(\"SeRestorePrivilege\"))\n        throw new InvalidOperationException(\n            \"Both SeTakeOwnershipPrivilege and SeRestorePrivilege are required.\");\n}","handlingStrategy":"validation","validationCode":"static bool HasAllRequiredPrivileges()\n{\n    // TrySetPrivilege with enable=false just checks availability without changing state\n    return NativeMethod.TrySetPrivilege(NativeMethod.TakeOwnership, false)\n        && NativeMethod.TrySetPrivilege(NativeMethod.Restore, false);\n}\n\nif(!HasAllRequiredPrivileges())\n    throw new InvalidOperationException(\n        \"Both SeTakeOwnershipPrivilege and SeRestorePrivilege are required.\");","typeGuard":null,"tryCatchPattern":"catch(PrivilegeNotHeldException ex) when(ex.Privilege == \"SeRestorePrivilege\")\n{\n    throw new InvalidOperationException(\n        \"SeRestorePrivilege is required to change registry key owner. \" +\n        \"Run elevated and verify 'Restore files and directories' right.\", ex);\n}","preventionTips":["Both SeTakeOwnershipPrivilege AND SeRestorePrivilege are needed atomically — verify both before attempting ownership changes","Use an elevated manifest (requestedExecutionLevel level='requireAdministrator') to guarantee a full token at startup","Verify privileges with 'whoami /priv' — both must show as Enabled, not just Present","If the Administrators group was stripped of the Restore right via GPO, coordinate with IT policy before proceeding","Cache the result of the double-privilege check and surface a clear UI message rather than letting the exception propagate mid-operation"],"tags":["registry","windows","privileges","trustedinstaller","security","elevation","uac"],"backgroundTag":null,"analyzedSha":"55507155dd8e49c7ab4606da97f2af192d590dfe","analyzedAt":"2026-08-13T13:32:34.501Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}