C#为LOCALSYSTEM应用添加SE_BACKUP_NAME等权限的实现方法
C# 为LOCALSYSTEM运行的进程启用SE_BACKUP_NAME与SE_RESTORE_NAME特权
以LOCALSYSTEM账户运行的进程,默认已经在访问令牌中持有SeBackupPrivilege(对应常量SE_BACKUP_NAME)和SeRestorePrivilege(对应常量SE_RESTORE_NAME),仅默认处于禁用状态,不需要额外向系统申请添加特权,通过P/Invoke调用Windows原生API启用对应特权即可。
实现步骤
- 引入advapi32.dll中令牌操作相关的原生函数,定义所需的结构体、访问常量
- 打开当前进程的访问令牌,获取对应特权的LUID(本地唯一标识符)
- 调用令牌调整接口启用目标特权,补充必要的错误校验逻辑
完整可运行代码
using System; using System.Runtime.InteropServices; using System.ComponentModel; public static class PrivilegeManager { // 目标特权常量定义 public const string SE_BACKUP_NAME = "SeBackupPrivilege"; public const string SE_RESTORE_NAME = "SeRestorePrivilege"; private const int SE_PRIVILEGE_ENABLED = 0x00000002; private const int TOKEN_ADJUST_PRIVILEGES = 0x0020; private const int TOKEN_QUERY = 0x0008; [StructLayout(LayoutKind.Sequential)] private struct LUID { public uint LowPart; public int HighPart; } [StructLayout(LayoutKind.Sequential)] private struct LUID_AND_ATTRIBUTES { public LUID Luid; public uint Attributes; } [StructLayout(LayoutKind.Sequential)] private struct TOKEN_PRIVILEGES { public uint PrivilegeCount; public LUID_AND_ATTRIBUTES Privileges; } [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid); [DllImport("advapi32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, [MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, uint BufferLength, IntPtr PreviousState, IntPtr ReturnLength); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CloseHandle(IntPtr hObject); /// <summary> /// 为当前进程启用指定特权 /// </summary> /// <param name="privilegeName">特权名称,传入SE_BACKUP_NAME/SE_RESTORE_NAME即可</param> /// <exception cref="Win32Exception">API调用失败时抛出对应系统错误信息</exception> public static void EnablePrivilege(string privilegeName) { IntPtr processToken = IntPtr.Zero; try { // 打开当前进程的访问令牌 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out processToken)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } // 查询特权对应的本地唯一标识LUID if (!LookupPrivilegeValue(null, privilegeName, out LUID luid)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } // 构造特权启用参数 TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES { PrivilegeCount = 1, Privileges = new LUID_AND_ATTRIBUTES { Luid = luid, Attributes = SE_PRIVILEGE_ENABLED } }; // 执行令牌特权调整 if (!AdjustTokenPrivileges(processToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } } finally { // 释放令牌句柄 if (processToken != IntPtr.Zero) { CloseHandle(processToken); } } } // 调用示例 public static void Main() { // 程序入口处优先启用两个特权,再执行业务逻辑 EnablePrivilege(SE_BACKUP_NAME); EnablePrivilege(SE_RESTORE_NAME); // 后续业务代码 Console.WriteLine("备份、还原特权已成功启用"); } }
注意事项
- 该逻辑仅对当前进程生效,特权在进程运行期间持续有效,进程退出后自动回收
- 这两项特权会绕过文件系统、注册表的ACL权限检查,安全风险较高,不需要使用时可以通过相同逻辑传入
0作为Attributes值禁用特权,缩小攻击面 - 由于程序以LOCALSYSTEM身份运行,不存在令牌中缺少对应特权的问题,不需要额外修改系统本地安全策略配置;如果是普通管理员账户运行的程序,需要提前在本地安全策略中将对应特权分配给运行账户,否则调用会抛出"特权不存在"的错误
内容的提问来源于stack exchange,提问作者Jeff
相关产品推荐
相关产品推荐

