The Big Picture: The Kitchen Window

In the restaurant picture from Section 1, the waiter is Win32. He speaks the customer's language and writes down orders neatly. Behind him is a kitchen window. The slips pass through it in a special format. Only the staff use it. The customers are not told how it works, and the restaurant may rebuild the kitchen without telling anyone.

That window is the Native API. It sits at the very bottom of the user-mode part of Windows. Every request, however it started, must pass through it on the way to the kernel.

3.1 Nt* and Zw* Functions in ntdll.dll

What it is: The Native API is the set of functions in ntdll.dll. Their names start with Nt (and often also Zw). The "NT" stands for New Technology. It is the name of the family of Windows that all modern versions (10, 11) belong to.

Win32 functionNative API function it ends up usingJob
CreateFileWNtCreateFile (or NtOpenFile)Open or create a file
ReadFileNtReadFileRead data
WriteFileNtWriteFileWrite data
CloseHandleNtCloseRelease a handle
VirtualAllocNtAllocateVirtualMemoryGet memory
CreateProcessWNtCreateUserProcessStart a program
RegOpenKeyExWNtOpenKeyOpen a registry key

Notice how the Native names are shorter and more "mechanical". They follow the pattern Nt + Verb + Object: NtReadFile, NtClose, NtOpenKey.

What an Nt function does in user mode: almost nothing. Each one is a tiny stub (a very short piece of code). It does three things:

  1. Puts a service number into a CPU register. The number says which kernel service is wanted.
  2. Runs the syscall instruction (Section 4).
  3. Returns the result when the kernel is done.
NtCreateFile:               (the idea, not exact machine code)
    put service number into a register
    execute "syscall"        <-- crosses into kernel mode
    return

The service number is different on different Windows versions. That fact is a big reason the Native API is not a safe place to build programs on (see 3.3).

What about Zw? Both NtCreateFile and ZwCreateFile exist. In user mode (your programs) they are essentially the same thing. The difference only matters inside the kernel, where the two names handle security checks differently. As a beginner you can remember:

NameIn user modeInside the kernel
Nt...Enters the kernel via syscallDirect call; may check that the caller is allowed
Zw...Same stub as Nt...Makes the call act as if it came from trusted kernel code

Keep it simple: in your programs, the two names do the same job. The deeper difference is for driver writers.

3.2 The Undocumented and Internal Nature

What it is: Microsoft publishes the Win32 API in full. The Native API is a different story:

Function groupDocumentation status
A few Native functionsDocumented, often with a warning that they may change
Most Native functionsLittle or no official documentation. People learn them from experience, debugging and unofficial references
The system call numbersNot documented at all, and they change between Windows versions

"Undocumented" does not mean "secret" or "illegal". It means Microsoft has not promised to keep these functions the same. You can still see their names in ntdll.dll with common tools.

A wise way to say it: Native API functions are an internal detail of Windows, like the wiring inside a wall. The light switch (Win32) is for you. The wiring is for the electrician.

What a Native function looks like:

Typical featureMeaning
Returns an NTSTATUSA 32-bit result code (see 3.4)
Uses handlesTickets for objects (Chapter 9)
Uses UNICODE_STRINGA text structure with its own length, not the usual end marker. It holds Unicode text
Uses OBJECT_ATTRIBUTESA structure that describes the object name and security
Takes many parametersOften 8 to 11 inputs for one call

Look at how much more the Native function wants:

Win32  :  CreateFileW( name, access, share, security, create, flags, template )
Native :  NtCreateFile( &handle, access, &object_attributes, &io_status,
                        &size, attributes, share, disposition, options,
                        ea_buffer, ea_length )

You do not need to memorise these. The point is that the Native layer is more detailed and fussier, and Win32 hides that for you.

3.3 Why Win32 Wraps the Native API Instead of Exposing It

What it is: Win32 does not replace the Native API. It wraps it, like putting a gift in a nice box. Win32 sits on top, tidies up the inputs, then calls Native.

There are five reasons Windows is built this way:

1. Stability. The Native API and system call numbers can change from one Windows version to the next. If every program used them directly, every Windows update could break programs. Win32 absorbs the changes. Programs call the same CreateFileW year after year while the Native layer below adapts.

2. Simplicity. CreateFileW has friendly parameters and sensible defaults. Win32 turns them into the longer Native request.

3. Convenience and checking. Win32 converts plain file paths such as C:\notes.txt into the NT style (\??\C:\notes.txt) that the kernel understands. It also converts ANSI text to Unicode and turns NTSTATUS codes into Win32 errors.

4. Many "personalities". Windows was designed so it could run several kinds of programs on one kernel. The Native API is the neutral base. Win32 is one friendly face (the "subsystem") built on it. Others once existed.

5. Safety. Using only the official layer means a program stays within the rules Microsoft tests heavily.

        Win32 (stable, friendly, documented)
              │  wraps and translates
              ▼
        Native API (precise, fussy, may change)
              │
              ▼
        Kernel

Rule of thumb for programmers: use Win32 (or a language that does). Calling ntdll.dll yourself is only for special cases, and Microsoft warns that those functions may change. Security tools and some research tools do look at this layer, but it is not where normal programs belong.

Seeing a Native name in the wild. You can see these names on your own PC. In PowerShell:

(Get-Item C:\Windows\System32\ntdll.dll).VersionInfo.FileVersion

This prints the version of ntdll.dll. On Windows 10 and 11 it is tied to the Windows build number. Because the file changes with every update, programs must not rely on what is inside it.

3.4 Native Errors: NTSTATUS and How They Become Win32 Errors

What it is: The Native API reports results with an NTSTATUS. It is a 32-bit number. Its top bits say what kind of result it is.

Top bitsMeaningExample
0x0...SuccessSTATUS_SUCCESS = 0x00000000
0x4...Information(rare)
0x8...WarningSTATUS_BUFFER_OVERFLOW
0xC...ErrorSTATUS_ACCESS_DENIED = 0xC0000022

(0x simply means "the number after this is written in hexadecimal", base 16.)

When Win32 gets an NTSTATUS from ntdll.dll, it translates it into a Win32 error code (and stores it for GetLastError()):

NTSTATUS (Native)Win32 error code
STATUS_SUCCESS (0x00000000)0 (ERROR_SUCCESS)
STATUS_OBJECT_NAME_NOT_FOUND (0xC0000034)2 (ERROR_FILE_NOT_FOUND)
STATUS_ACCESS_DENIED (0xC0000022)5 (ERROR_ACCESS_DENIED)
STATUS_INVALID_PARAMETER (0xC000000D)87 (ERROR_INVALID_PARAMETER)

So the same failure looks different depending on the layer you watch:

Kernel says:      0xC0000022   (STATUS_ACCESS_DENIED)
Native API sees:  0xC0000022
Win32 shows:      error 5      (ERROR_ACCESS_DENIED)

Many tools, including Process Monitor (Section 6), show the NTSTATUS-style names, such as NAME NOT FOUND and ACCESS DENIED. Blue Screen codes and many crash messages are also NTSTATUS values. A few you may know already: 0xC0000005 is an access violation, which means a program touched memory it should not.

net helpmsg takes Win32 numbers (for example 5), not NTSTATUS numbers.

3.5 Seeing the Native Layer: Safe Looks

You can look at ntdll.dll without changing anything.

dir C:\Windows\System32\ntdll.dll

You will see a file of around 2 MB (the exact size depends on your Windows build). It is small. It is a thin layer mostly made of the tiny stubs described in 3.1, plus helper code such as the loader that loads DLLs for every new program (Chapter 22).

Other safe facts:

FactHow you can see it
ntdll.dll is in every user-mode processRun tasklist /m ntdll.dll in Command Prompt (Task Manager itself does not list modules)
It is the first DLL every program getsThe loader starts there, before kernel32.dll
It exists in both 32-bit and 64-bit forms on 64-bit WindowsSystem32 has the 64-bit one, SysWOW64 has the 32-bit one

Try tasklist /m ntdll.dll. The list will be very long. That is the point: everything uses it.

Summary

TermPlain meaning
Native APIThe Nt and Zw functions in ntdll.dll
StubA tiny function that loads a service number and runs syscall
Nt vs ZwThe same in user mode; different only inside the kernel
UndocumentedMicrosoft does not promise it will stay the same
Why wrap itWin32 gives stability, simplicity and translation
NTSTATUSA 32-bit result code from the Native layer; 0xC... means error
Win32 errorA small number from GetLastError, translated from NTSTATUS
ntdll.dllThe lowest user-mode DLL, present in every process