You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

PowerShell权限检测异常:如何精准识别是否通过「以管理员身份运行」选项启动?

准确检测PowerShell脚本是否通过「以管理员身份运行」启动

你的问题核心在于:原来的检测逻辑混淆了账号是否属于管理员组进程是否已获得权限提升这两个不同的概念。

域管理员账号本身默认属于管理员组,所以IsInRole(Administrator)会返回True,哪怕你只是双击脚本、没有手动选择「以管理员身份运行」——这就是为什么在目标服务器上没有触发非管理员提示的原因。

要解决这个问题,你需要检测的是当前脚本进程是否处于权限提升状态,也就是是否通过右键菜单的「以管理员身份运行」启动的。Windows中,权限提升的进程会运行在高完整性级别(High Integrity Level),而普通启动的进程(哪怕账号是管理员)则处于中完整性级别。

下面是实现这个检测的PowerShell代码,它通过调用Windows原生API来获取进程的完整性级别,能准确判断进程是否被提升:

Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;

public class IntegrityLevelChecker {
    public enum TokenInformationClass {
        TokenIntegrityLevel = 25
    }

    public struct TokenMandatoryLabel {
        public IntPtr Label;
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool GetTokenInformation(
        IntPtr TokenHandle,
        TokenInformationClass TokenInformationClass,
        IntPtr TokenInformation,
        int TokenInformationLength,
        out int ReturnLength);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool CloseHandle(IntPtr hObject);

    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern bool OpenProcessToken(
        IntPtr ProcessHandle,
        uint DesiredAccess,
        out IntPtr TokenHandle);

    public static bool IsProcessElevated() {
        IntPtr hProcess = System.Diagnostics.Process.GetCurrentProcess().Handle;
        IntPtr hToken = IntPtr.Zero;
        try {
            if (!OpenProcessToken(hProcess, 0x0008 /* TOKEN_QUERY */, out hToken)) {
                return false;
            }

            int returnLength = 0;
            GetTokenInformation(hToken, TokenInformationClass.TokenIntegrityLevel, IntPtr.Zero, 0, out returnLength);
            IntPtr tokenInfo = Marshal.AllocHGlobal(returnLength);
            try {
                if (!GetTokenInformation(hToken, TokenInformationClass.TokenIntegrityLevel, tokenInfo, returnLength, out returnLength)) {
                    return false;
                }

                TokenMandatoryLabel label = (TokenMandatoryLabel)Marshal.PtrToStructure(tokenInfo, typeof(TokenMandatoryLabel));
                int integrityLevel = Marshal.ReadInt32(label.Label, 4);
                // 高完整性级别对应的数值是0x1000及以上
                return integrityLevel >= 0x1000;
            } finally {
                Marshal.FreeHGlobal(tokenInfo);
            }
        } finally {
            if (hToken != IntPtr.Zero) {
                CloseHandle(hToken);
            }
        }
    }
}
"@

# 检测进程是否处于权限提升状态(即通过「以管理员身份运行」启动)
$IsElevated = [IntegrityLevelChecker]::IsProcessElevated()

if ($IsElevated) {
    # 这里放入你的管理员任务逻辑:检测事件日志、创建日志源、写入日志等
    Write-Host "脚本已通过管理员权限启动,开始执行任务..."
    # 示例:检查并创建事件日志源
    $logName = "YourCustomLog"
    $sourceName = "YourCustomSource"
    if (-not [System.Diagnostics.EventLog]::Exists($logName)) {
        [System.Diagnostics.EventLog]::CreateEventSource($sourceName, $logName)
        Write-EventLog -LogName $logName -Source $sourceName -EventId 1000 -EntryType Information -Message "自定义事件日志及源已创建"
    } else {
        Write-EventLog -LogName $logName -Source $sourceName -EventId 1001 -EntryType Information -Message "脚本已通过管理员权限启动"
    }
} else {
    # 提示用户需要以管理员身份运行
    Write-Host "权限不足:请右键点击脚本,选择「以管理员身份运行」!"
    # 弹出可视化提示对话框
    Add-Type -AssemblyName PresentationFramework
    [System.Windows.MessageBox]::Show("请右键点击脚本,选择「以管理员身份运行」!", "权限不足", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning)
}

为什么这个方法有效?

  • 原来的IsInRole方法只是验证当前用户身份是否属于管理员组,但无法区分该身份是过滤后的普通权限令牌还是完整的管理员权限令牌
  • 而通过检测进程的完整性级别,我们直接判断的是进程的实际运行权限状态:
    • 双击启动脚本(哪怕是域管理员账号):进程处于中完整性级别IsProcessElevated返回False
    • 右键「以管理员身份运行」启动:进程处于高完整性级别IsProcessElevated返回True

这样无论登录账号是不是管理员,都能准确检测脚本是否是通过「以管理员身份运行」启动的。

内容的提问来源于stack exchange,提问作者Keeran

火山引擎 最新活动