> Windows Syscalls
ntoskrnl.exeT1106

NtMakePermanentObject

Sets the OBJ_PERMANENT attribute on a named kernel object so it survives after the last handle closes.

Prototype

NTSTATUS NtMakePermanentObject(
  HANDLE Handle
);

Arguments

NameTypeDirDescription
HandleHANDLEinHandle to a named kernel object whose underlying object header will be marked OBJ_PERMANENT.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070xFDwin10-1507
Win10 16070x102win10-1607
Win10 17030x106win10-1703
Win10 17090x107win10-1709
Win10 18030x108win10-1803
Win10 18090x108win10-1809
Win10 19030x109win10-1903
Win10 19090x109win10-1909
Win10 20040x10Ewin10-2004
Win10 20H20x10Ewin10-20h2
Win10 21H10x10Ewin10-21h1
Win10 21H20x10Fwin10-21h2
Win10 22H20x10Fwin10-22h2
Win11 21H20x115win11-21h2
Win11 22H20x116win11-22h2
Win11 23H20x116win11-23h2
Win11 24H20x118win11-24h2
Server 20160x102winserver-2016
Server 20190x108winserver-2019
Server 20220x114winserver-2022
Server 20250x118winserver-2025

Kernel module

ntoskrnl.exeNtMakePermanentObject

Related APIs

NtMakeTemporaryObjectNtCreateSectionNtCreateEventAdjustTokenPrivileges (SeCreatePermanentPrivilege)

Syscall stub

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

Sets the `OBJ_PERMANENT` flag on the underlying `_OBJECT_HEADER` so the object is *not* freed when its last user handle closes. Kernel side: `ObSetPermanentObject(object, TRUE)` followed by an extra reference being kept by Ob. Permanent objects are how Win32 keeps named sections, events and semaphores alive after their creator exits — `\BaseNamedObjects\Global\Foo` survives because the SCM, csrss or the desktop window manager called `NtMakePermanentObject` (or created the object already with `OBJ_PERMANENT` in `OBJECT_ATTRIBUTES::Attributes`) on it. Requires `SeCreatePermanentPrivilege`, held by `LocalSystem` and a handful of service identities only — so use from a normal user-mode token usually returns `STATUS_PRIVILEGE_NOT_HELD`. The call has been on Microsoft's deprecation list since Vista (replaced in idiom by creating with `OBJ_PERMANENT` up-front in the call to `NtCreate*`), but is still exported and still functional. Counterpart: `NtMakeTemporaryObject`.

Common malware usage

Low malware signal. The narrow legitimate use case — making `\BaseNamedObjects` survivors — overlaps with a small set of persistence tricks. Named-section persistence (Skywiper / Flame era, and modern PoCs) creates a permanent named section, writes a payload into it, then exits — a later privileged-process map of the same section name brings the payload back into a privileged address space. Setting `OBJ_PERMANENT` at create time is the more common way to achieve this; calling `NtMakePermanentObject` after the fact is a less stealthy pattern. Some droppers and rootkit installers use it to keep an IPC `Event` or `Mutex` named object alive across process restarts as a presence beacon. The privilege requirement caps real-world abuse to already-elevated payloads.

Detection opportunities

No dedicated ETW event. The privileged nature of `SeCreatePermanentPrivilege` itself is the high-fidelity signal — any non-system process that successfully enables and uses it warrants investigation regardless of which syscall it calls. From a hunt angle, snapshot `\BaseNamedObjects` and `\BaseNamedObjects\Global` (`WinObj`, or `NtQueryDirectoryObject` walks) at boot and on a cadence; objects with the `Permanent` attribute that did not exist at boot and were created by a non-system process are the actual IOC. `Microsoft-Windows-Kernel-Object` ETW with `Handle Create` events lets you correlate the `OBJ_PERMANENT` attribute in `ObjectAttributes` at create time.

Direct syscall examples

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtMakePermanentObject (SSN 0x118 on Win11 24H2 / Server 2025)
NtMakePermanentObject PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 118h         ; SSN — drifts; resolve dynamically for portability
    syscall
    ret
NtMakePermanentObject ENDP

cPromote a named section so it outlives the creator

// Promote an existing named section to OBJ_PERMANENT so closing the last
// handle does not free it — payload-staging persistence pattern.
// Requires SeCreatePermanentPrivilege enabled in the calling token.
UNICODE_STRING name; RtlInitUnicodeString(&name, L"\\BaseNamedObjects\\Global\\__stage__");
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE hSec;
LARGE_INTEGER size = { .QuadPart = 0x10000 };
if (NT_SUCCESS(NtCreateSection(&hSec, SECTION_ALL_ACCESS, &oa,
                               &size, PAGE_EXECUTE_READWRITE,
                               SEC_COMMIT, NULL))) {
    NtMakePermanentObject(hSec);  // section now survives our process exit
    NtClose(hSec);                 // a later process can NtOpenSection() it
}

MITRE ATT&CK mappings

Last verified: 2026-05-20