> Windows Syscalls
ntoskrnl.exeT1106T1087

NtAccessCheck

Performs a security access check of a security descriptor against an impersonation token, returning the granted access mask.

Prototype

NTSTATUS NtAccessCheck(
  PSECURITY_DESCRIPTOR SecurityDescriptor,
  HANDLE               ClientToken,
  ACCESS_MASK          DesiredAccess,
  PGENERIC_MAPPING     GenericMapping,
  PPRIVILEGE_SET       PrivilegeSet,
  PULONG               PrivilegeSetLength,
  PACCESS_MASK         GrantedAccess,
  PNTSTATUS            AccessStatus
);

Arguments

NameTypeDirDescription
SecurityDescriptorPSECURITY_DESCRIPTORinSelf-relative security descriptor of the protected resource to evaluate.
ClientTokenHANDLEinHandle to an impersonation token (TOKEN_QUERY) — must be an impersonation token, not a primary token.
DesiredAccessACCESS_MASKinAccess rights the caller wants to check (may include generic rights — those are mapped via GenericMapping).
GenericMappingPGENERIC_MAPPINGinObject-type-specific mapping from GENERIC_READ/WRITE/EXECUTE/ALL to concrete access bits.
PrivilegeSetPPRIVILEGE_SEToutReceives the privileges the kernel actually used during the check (e.g. SeSecurityPrivilege when reading SACLs).
PrivilegeSetLengthPULONGin/outIn: size in bytes of the PrivilegeSet buffer. Out: size actually required.
GrantedAccessPACCESS_MASKoutReceives the access mask that would actually be granted to ClientToken.
AccessStatusPNTSTATUSoutReceives STATUS_SUCCESS if access would be granted, STATUS_ACCESS_DENIED otherwise. Distinct from the function's own NTSTATUS.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070x0win10-1507
Win10 16070x0win10-1607
Win10 17030x0win10-1703
Win10 17090x0win10-1709
Win10 18030x0win10-1803
Win10 18090x0win10-1809
Win10 19030x0win10-1903
Win10 19090x0win10-1909
Win10 20040x0win10-2004
Win10 20H20x0win10-20h2
Win10 21H10x0win10-21h1
Win10 21H20x0win10-21h2
Win10 22H20x0win10-22h2
Win11 21H20x0win11-21h2
Win11 22H20x0win11-22h2
Win11 23H20x0win11-23h2
Win11 24H20x0win11-24h2
Server 20160x0winserver-2016
Server 20190x0winserver-2019
Server 20220x0winserver-2022
Server 20250x0winserver-2025

Kernel module

ntoskrnl.exeNtAccessCheck

Related APIs

AccessCheckAccessCheckByTypeAccessCheckAndAuditAlarmWNtAccessCheckByTypeNtAccessCheckAndAuditAlarmNtPrivilegeCheck

Syscall stub

4C 8B D1            mov r10, rcx
B8 00 00 00 00      mov eax, 0x00
F6 04 25 08 03 FE 7F 01   test byte ptr [0x7FFE0308], 1
75 03               jne short +3
0F 05               syscall
C3                  ret
CD 2E               int 2Eh
C3                  ret

Undocumented notes

NtAccessCheck holds the literal first slot in the System Service Descriptor Table — SSN `0x0` on every Windows build since NT 4 — which makes it a delightful curiosity but useless as an offensive primitive. It is the user-mode entrypoint for *manual* access-check logic: a service that has just impersonated a client uses NtAccessCheck (typically via the Win32 AccessCheck wrapper) to decide whether the client *would* be allowed to perform an operation against a resource the service controls. It does not itself open anything — it is a pure evaluation function over a SECURITY_DESCRIPTOR + token + GENERIC_MAPPING.

Common malware usage

Almost never used for offense directly. The two patterns that do show up: (1) post-exploitation tools enumerating which DACL'd objects a stolen impersonation token can reach without actually opening them (silent reconnaissance — no audit fires unless SACLs are configured); (2) exploit code that probes whether a forged or relogged token grants the intended access. Honest assessment: NtAccessCheck is dominated by legitimate usage and is a weak malware signal.

Detection opportunities

Almost no useful telemetry exists. NtAccessCheck does not itself trigger object-access auditing (Event ID 4663) — only the subsequent open does. ETW Microsoft-Windows-Security-Auditing's privilege-use events can fire if the call references a SACL-bearing object during evaluation, but coverage is sparse. Useful as a *correlation* signal: an unusual process performing many NtAccessCheck calls back-to-back against varied security descriptors, followed by selective NtOpen* calls only on the ones that succeeded, is a classic silent-reconnaissance pattern.

Direct syscall examples

asmx64 direct stub (SSN 0x0 — first in the SSDT)

; Direct syscall stub for NtAccessCheck (SSN 0x00 on every Windows build)
NtAccessCheck PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 0            ; SSN
    syscall
    ret
NtAccessCheck ENDP

cService-style: impersonate client and probe access

// After ImpersonateNamedPipeClient / RpcImpersonateClient, the service evaluates
// whether the caller would be allowed FILE_WRITE_DATA on a private resource.
HANDLE hClientToken = NULL;
NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, FALSE, &hClientToken);

GENERIC_MAPPING fileMapping = {
    .GenericRead    = FILE_GENERIC_READ,
    .GenericWrite   = FILE_GENERIC_WRITE,
    .GenericExecute = FILE_GENERIC_EXECUTE,
    .GenericAll     = FILE_ALL_ACCESS,
};

BYTE privBuf[256];
ULONG privLen = sizeof(privBuf);
ACCESS_MASK granted = 0;
NTSTATUS accessStatus = 0;

NtAccessCheck(pResourceSD,
              hClientToken,
              FILE_WRITE_DATA,
              &fileMapping,
              (PPRIVILEGE_SET)privBuf,
              &privLen,
              &granted,
              &accessStatus);

if (NT_SUCCESS(accessStatus) && (granted & FILE_WRITE_DATA)) {
    // proceed under impersonation
}

rustwindows-sys + naked syscall stub

// Cargo: windows-sys = "0.59"  (Win32_Security)
use std::arch::asm;

#[unsafe(naked)]
unsafe extern "system" fn nt_access_check_stub() {
    asm!(
        "mov r10, rcx",
        "mov eax, 0x00",
        "syscall",
        "ret",
        options(noreturn),
    );
}

MITRE ATT&CK mappings

Last verified: 2026-05-20