如何通过C# Windows服务检测标准用户以Run as Administrator启动应用
检测标准用户以管理员身份启动的进程
要实现需求,你需要完成两个核心检测:判断进程是否处于提权(管理员)状态,以及确认启动该进程的用户是标准用户(不属于本地管理员组)。以下是具体实现步骤和代码:
1. 检测进程是否提权运行
通过Windows API读取进程令牌的TokenElevation信息,判断进程是否以管理员权限启动:
首先添加必要的P/Invoke声明:
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.DirectoryServices.AccountManagement; public static class ProcessSecurityHelper { [DllImport("advapi32.dll", SetLastError = true)] private static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle); [DllImport("advapi32.dll", SetLastError = true)] private static extern bool GetTokenInformation(IntPtr tokenHandle, TOKEN_INFORMATION_CLASS tokenInformationClass, IntPtr tokenInformation, uint tokenInformationLength, out uint returnLength); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); private enum TOKEN_INFORMATION_CLASS { TokenElevation = 20, TokenUser = 1 } private struct TOKEN_ELEVATION { public TokenElevationType TokenIsElevated; } private enum TokenElevationType { TokenElevationTypeDefault = 1, TokenElevationTypeFull = 2, TokenElevationTypeLimited = 3 }
然后实现检测提权的方法:
public static bool IsProcessElevated(int processId) { try { using (var process = Process.GetProcessById(processId)) { // 打开进程令牌,请求查询权限 if (!OpenProcessToken(process.Handle, 0x0008 /* TOKEN_QUERY */, out IntPtr tokenHandle)) return false; try { var elevationSize = (uint)Marshal.SizeOf(typeof(TOKEN_ELEVATION)); IntPtr elevationPtr = Marshal.AllocHGlobal((int)elevationSize); try { if (GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenElevation, elevationPtr, elevationSize, out _)) { var elevation = Marshal.PtrToStructure<TOKEN_ELEVATION>(elevationPtr); // TokenElevationTypeFull表示进程已提权 return elevation.TokenIsElevated == TokenElevationType.TokenElevationTypeFull; } } finally { Marshal.FreeHGlobal(elevationPtr); } } finally { CloseHandle(tokenHandle); } } } catch (Exception) { // 处理进程已退出、权限不足等异常 return false; } }
2. 检测启动进程的用户是否为标准用户
先获取进程对应的用户SID,再判断该用户是否不属于本地管理员组:
补充P/Invoke和相关方法:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool ConvertSidToStringSid(IntPtr sid, out string sidString); private struct TOKEN_USER { public SID_AND_ATTRIBUTES User; } private struct SID_AND_ATTRIBUTES { public IntPtr Sid; public uint Attributes; } public static string GetProcessUserSid(int processId) { try { using (var process = Process.GetProcessById(processId)) { if (!OpenProcessToken(process.Handle, 0x0008 /* TOKEN_QUERY */, out IntPtr tokenHandle)) return null; try { // 先获取所需缓冲区长度 GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, 0, out uint returnLength); IntPtr userPtr = Marshal.AllocHGlobal((int)returnLength); try { if (GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS.TokenUser, userPtr, returnLength, out _)) { var tokenUser = Marshal.PtrToStructure<TOKEN_USER>(userPtr); if (ConvertSidToStringSid(tokenUser.User.Sid, out string sidString)) return sidString; } } finally { Marshal.FreeHGlobal(userPtr); } } finally { CloseHandle(tokenHandle); } } } catch (Exception) { return null; } } public static bool IsStandardUser(string userSid) { try { using (var machineContext = new PrincipalContext(ContextType.Machine)) { using (var user = UserPrincipal.FindByIdentity(machineContext, IdentityType.Sid, userSid)) { if (user == null) return false; var adminGroup = GroupPrincipal.FindByIdentity(machineContext, IdentityType.Name, "Administrators"); // 用户不属于管理员组则为标准用户 return adminGroup != null && !user.IsMemberOf(adminGroup); } } } catch (Exception) { return false; } } }
3. 在WMI监控逻辑中整合检测
在你现有的ManagementEventWatcher回调中,获取进程ID后执行检测:
// 假设你从WMI事件中获取到进程对象 var processId = Convert.ToInt32(e.NewEvent["ProcessId"]); bool isElevated = ProcessSecurityHelper.IsProcessElevated(processId); string userSid = ProcessSecurityHelper.GetProcessUserSid(processId); bool isStandardUser = !string.IsNullOrEmpty(userSid) && ProcessSecurityHelper.IsStandardUser(userSid); if (isElevated && isStandardUser) { // 符合条件:标准用户以管理员身份启动的进程 // 执行你的黑白名单管控逻辑(比如终止进程、记录日志等) }
注意事项
- 服务运行身份:建议将你的Windows服务设置为
Local System身份运行,以获取足够权限访问其他进程的令牌;若使用其他身份,可能需要额外配置SeDebugPrivilege权限。 - 进程生命周期:进程可能在你检测前已退出,需做好异常捕获和处理。
- 内置管理员账户:内置管理员本身属于管理员组,即使通过UAC提权,
IsStandardUser也会返回false,符合你的需求(只检测标准用户的提权行为)。
内容的提问来源于stack exchange,提问作者Achal Parikh
相关产品推荐
相关产品推荐

