You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用SetTokenInformation修改令牌完整性级别遇0x5拒绝访问问题

调整复制主令牌完整性级别时ERROR_ACCESS_DENIED(0x5)的解决方法

问题背景

尝试对当前进程的复制主令牌调用SetTokenInformation调整强制完整性级别时,始终返回ERROR_ACCESS_DENIED(0x5)。已执行以下操作仍未解决:

  • 启用所有权限排除权限问题
  • 以PROCESS_ALL_ACCESS打开当前进程,TOKEN_ALL_ACCESS打开进程令牌
  • 获取当前进程的复制主令牌
  • 验证函数调用及参数有效性、更换各类进程/令牌访问标识

怀疑SID构造存在问题,但未找到明确规则。

错误原因分析

  1. 令牌访问权限不足:DuplicateTokenEx调用时使用MAXIMUM_ALLOWED无法确保获得修改令牌完整性级别的必要权限,需要明确指定TOKEN_ADJUST_DEFAULT权限。
  2. 完整性标签属性缺失:TOKEN_MANDATORY_LABEL的Label.Attributes仅设置SE_GROUP_INTEGRITY,缺少SE_GROUP_MANDATORY属性,导致系统拒绝修改。
  3. 冗余权限干扰:启用大量不必要的权限(如SeTcbPrivilege)不仅无效,还可能引入额外的权限检查问题。

修复方案

  1. 明确复制令牌的权限:在DuplicateTokenEx中指定TOKEN_ADJUST_DEFAULT | TOKEN_QUERY权限,确保拥有修改令牌信息的权限。
  2. 修正完整性标签属性:将Label.Attributes设置为SE_GROUP_INTEGRITY | SE_GROUP_MANDATORY。
  3. 正确构造目标完整性SID:根据需要的完整性级别选择对应的RID(如低完整性用SECURITY_MANDATORY_LOW_RID)。
  4. 清理冗余权限:仅启用必要的权限(如SeDebugPrivilege用于打开进程令牌)。

修正后的代码

MinimalMain.cpp

// TokenMinimalExample.cpp : This file contains the 'main' function. Program execution begins and ends there.

#include <windows.h>
#include <stdio.h>
#include "token_example.h"

int main()
{
    // 仅启用必要权限
    if (!SetThreadProcessPrivilege(L"SeDebugPrivilege", true))
    {
        printf("Failed to enable SeDebugPrivilege: %d\n", GetLastError());
        return 1;
    }
    
    // 获取当前进程的复制主令牌
    HANDLE hToken = GetCurrentProcessPrimaryToken();
    if (hToken == INVALID_HANDLE_VALUE)
    {
        printf("Failed to get a duplicate token for the current process: %d\n", GetLastError());
        return 1;
    }
    
    // 创建低完整性SID
    SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_MANDATORY_LABEL_AUTHORITY;
    PSID pSid = NULL;
        
    if(!AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_MANDATORY_LOW_RID, 0, 0, 0, 0, 0, 0, 0, &pSid))
    {
        printf("Failed to create a mandatory low integrity SID: %d\n", GetLastError());
        CloseHandle(hToken);
        system("PAUSE");
        return 1;
    }
    
    // 设置令牌完整性级别
    TOKEN_MANDATORY_LABEL tml;
    tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_MANDATORY; // 修正属性
    tml.Label.Sid = pSid;

    if (!SetTokenInformation(hToken, TokenIntegrityLevel, &tml, sizeof(tml)))
    {
        printf("Failed to set token information: %d\n", GetLastError());
        FreeSid(pSid);
        CloseHandle(hToken);
        system("PAUSE");
        return 1;
    }

    printf("Token integrity level set successfully!\n");
    FreeSid(pSid);
    CloseHandle(hToken);
    system("PAUSE");
    return 0;
}

token_example.h

#pragma once
#include <Windows.h>
#include <stdio.h>

// 调整进程/线程权限的辅助函数
bool SetThreadProcessPrivilege(LPCWSTR PrivilegeName, bool Enable)
{
    HANDLE Token = NULL;
    TOKEN_PRIVILEGES TokenPrivs = {0};
    LUID TempLuid;

    if (!LookupPrivilegeValueW(NULL, PrivilegeName, &TempLuid))
    {
        printf("LookupPrivilegeValueW failed: %d\n", GetLastError());
        return false;
    }

    // 优先打开线程令牌,失败则打开进程令牌
    if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &Token))
    {
        if (GetLastError() != ERROR_NO_TOKEN)
        {
            printf("OpenThreadToken failed: %d\n", GetLastError());
            return false;
        }
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token))
        {
            printf("OpenProcessToken failed: %d\n", GetLastError());
            return false;
        }
    }

    TokenPrivs.PrivilegeCount = 1;
    TokenPrivs.Privileges[0].Luid = TempLuid;
    TokenPrivs.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;

    BOOL result = AdjustTokenPrivileges(Token, FALSE, &TokenPrivs, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
    DWORD lastErr = GetLastError();
    if (!result || lastErr != ERROR_SUCCESS)
    {
        printf("AdjustTokenPrivileges failed: %d\n", lastErr);
        CloseHandle(Token);
        return false;
    }

    CloseHandle(Token);
    return true;
}

// 获取当前进程的复制主令牌
HANDLE GetCurrentProcessPrimaryToken()
{
    HANDLE hProcess = NULL;
    HANDLE hOriginalToken = NULL;
    HANDLE hDuplicateToken = NULL;

    // 打开当前进程
    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
    if (!hProcess)
    {
        printf("OpenProcess failed: %d\n", GetLastError());
        goto Cleanup;
    }

    // 打开进程令牌(需要TOKEN_DUPLICATE权限)
    if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hOriginalToken))
    {
        printf("OpenProcessToken failed: %d\n", GetLastError());
        goto Cleanup;
    }

    // 复制主令牌,明确指定修改所需权限
    SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, FALSE };
    if (!DuplicateTokenEx(
        hOriginalToken,
        TOKEN_ADJUST_DEFAULT | TOKEN_QUERY, // 明确必要权限
        &sa,
        SecurityImpersonation,
        TokenPrimary,
        &hDuplicateToken))
    {
        printf("DuplicateTokenEx failed: %d\n", GetLastError());
        goto Cleanup;
    }

Cleanup:
    if (hProcess) CloseHandle(hProcess);
    if (hOriginalToken) CloseHandle(hOriginalToken);
    return hDuplicateToken ? hDuplicateToken : INVALID_HANDLE_VALUE;
}

额外说明

  • 完整性级别RID对应关系:
    • 低完整性:SECURITY_MANDATORY_LOW_RID (0x00001000)
    • 中完整性:SECURITY_MANDATORY_MEDIUM_RID (0x00002000)
    • 高完整性:SECURITY_MANDATORY_HIGH_RID (0x00003000)
    • 系统完整性:SECURITY_MANDATORY_SYSTEM_RID (0x00004000)
  • 修改令牌完整性级别后,该令牌仅可用于创建新进程,无法修改当前进程的完整性级别(当前进程的完整性级别不可动态调整)。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 15:11:13