> Windows Syscalls
ntoskrnl.exeT1106

NtCreateProfile

Creates a kernel-mode sampling profiler object that buckets the program counter into a histogram.

Prototype

NTSTATUS NtCreateProfile(
  PHANDLE        ProfileHandle,
  HANDLE         Process,
  PVOID          ProfileBase,
  SIZE_T         ProfileSize,
  ULONG          BucketSize,
  PULONG         Buffer,
  ULONG          BufferSize,
  KPROFILE_SOURCE ProfileSource,
  KAFFINITY      Affinity
);

Arguments

NameTypeDirDescription
ProfileHandlePHANDLEoutReceives a handle to the newly created profile object.
ProcessHANDLEinHandle of the process being profiled, or NULL to profile the entire system.
ProfileBasePVOIDinStart of the virtual address range whose RIP samples will be counted.
ProfileSizeSIZE_TinLength in bytes of the address range being profiled.
BucketSizeULONGinlog2 of the bucket size in bytes, e.g. 2 for 4-byte buckets.
BufferPULONGinCaller-supplied buffer that will receive per-bucket sample counts.
BufferSizeULONGinSize of Buffer in bytes; must hold ProfileSize >> BucketSize ULONG entries.
ProfileSourceKPROFILE_SOURCEinSampling source (ProfileTime, ProfileTotalIssues, ProfileBranchMispredictions, etc.).
AffinityKAFFINITYinBitmask of CPUs the profile applies to. 0 selects all available CPUs.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070xAEwin10-1507
Win10 16070xB0win10-1607
Win10 17030xB3win10-1703
Win10 17090xB4win10-1709
Win10 18030xB5win10-1803
Win10 18090xB5win10-1809
Win10 19030xB6win10-1903
Win10 19090xB6win10-1909
Win10 20040xBAwin10-2004
Win10 20H20xBAwin10-20h2
Win10 21H10xBAwin10-21h1
Win10 21H20xBBwin10-21h2
Win10 22H20xBBwin10-22h2
Win11 21H20xBFwin11-21h2
Win11 22H20xC0win11-22h2
Win11 23H20xC0win11-23h2
Win11 24H20xC2win11-24h2
Server 20160xB0winserver-2016
Server 20190xB5winserver-2019
Server 20220xBEwinserver-2022
Server 20250xC2winserver-2025

Kernel module

ntoskrnl.exeNtCreateProfile

Related APIs

NtStartProfileNtStopProfileNtSetIntervalProfileNtQueryIntervalProfileNtCreateProfileEx

Syscall stub

4C 8B D1                  mov r10, rcx
B8 C2 00 00 00            mov eax, 0xC2
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

NtCreateProfile, together with NtStartProfile / NtStopProfile / NtSetIntervalProfile / NtQueryIntervalProfile, drives the kernel's classic sampling profiler — the same backend that powered KernRate decades ago and that today is still used by some PerfView and WPR scenarios. The profiler attaches to the high-resolution timer (or a hardware PMU source on supported CPUs) and on each tick increments the histogram bucket corresponding to the interrupted RIP, provided RIP falls inside [ProfileBase, ProfileBase + ProfileSize). The Buffer pointer is supplied by user-mode but the kernel writes into it directly from ISR context, which is why the buffer must remain resident (in practice the caller should VirtualLock it). Requires SeSystemProfilePrivilege for system-wide profiling.

Common malware usage

Genuinely weak offensive signal. The historical abuses are narrow: (1) academic side-channel work that used sampling profilers to fingerprint kernel scheduler behaviour and infer secrets from victim threads on shared cores; (2) niche timing-attack PoCs that abused `ProfileTotalIssues` to count retired instructions in foreign code paths. Neither has translated into real-world commodity malware — the SeSystemProfilePrivilege requirement and the unusual syscall footprint make it more trouble than it's worth compared to QueryPerformanceCounter for timing or PMU-MSR access for fine-grained side channels. Mostly defensive tooling territory.

Detection opportunities

Very low background usage in user-mode malware. A non-system process calling NtCreateProfile, especially with `Process == NULL` (system-wide) and `ProfileSource != ProfileTime`, is essentially never benign. Audit `SeSystemProfilePrivilege` grants the same way you audit SeDebugPrivilege. The profile object itself is visible under `\KernelObjects` and named profile handles can be enumerated via NtQueryObject. EDR coverage is poor — this is one of the rare native syscalls that almost nothing hooks because almost nothing calls it.

Direct syscall examples

cProfile the current process's main module

// Minimal NtCreateProfile + NtStartProfile sequence — defensive perf use.
#include <windows.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI *PNTCREATEPROFILE)(PHANDLE, HANDLE, PVOID, SIZE_T, ULONG,
                                           PULONG, ULONG, ULONG, ULONG_PTR);
typedef NTSTATUS (NTAPI *PNTSTARTPROFILE)(HANDLE);

void profile_self(PVOID base, SIZE_T size) {
    ULONG bucketShift = 2;                 // 4-byte buckets
    ULONG entries     = (ULONG)(size >> bucketShift);
    ULONG *buf        = (ULONG*)VirtualAlloc(NULL, entries * sizeof(ULONG),
                                             MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    VirtualLock(buf, entries * sizeof(ULONG));

    HMODULE nt = GetModuleHandleA("ntdll.dll");
    PNTCREATEPROFILE pNtCreateProfile = (PNTCREATEPROFILE)GetProcAddress(nt, "NtCreateProfile");
    PNTSTARTPROFILE  pNtStartProfile  = (PNTSTARTPROFILE) GetProcAddress(nt, "NtStartProfile");

    HANDLE hProf = NULL;
    pNtCreateProfile(&hProf, GetCurrentProcess(), base, size, bucketShift,
                     buf, entries * sizeof(ULONG), 0 /* ProfileTime */, 0);
    pNtStartProfile(hProf);
}

asmx64 direct stub (Win11 24H2, SSN 0xC2)

NtCreateProfile PROC
    mov  r10, rcx
    mov  eax, 0C2h
    syscall
    ret
NtCreateProfile ENDP

MITRE ATT&CK mappings

Last verified: 2026-05-20