by EliCZ

When I read an old computer magazine, there was written: 8086 runs in real mode, 80286 runs in
protected mode and 386 runs in native mode. Perhaps they meant native == 32bit protected mode.
Native mode is also one of two modes of software FPU exception handling.

One possible subsystem in PE header is Native. It marks executables running in kernel mode or
executables - as Quick View says - which don't require a subsystem. So I thought Native
application uses Native API. No, there was a little misunderstanding. Native application -
kernel-mode driver (KD extentions have also Native mark, although they use user-mode API; I
will ignore them here) uses KERNEL-MODE API, not Native API. Native API is API which name
begins with Nt.

It would be good to have description of all KM APIs. MSDN partially describes only few KM APIs.
If there is some book with descriptions it must be a bestseller.  Native API is undocummented,
some NtAPIs are ZwAPIs so read docs about Zw routines. For more see Inside the Native API.

Native API is for both modes, but runs in KM in NTOSKRNL. In UM is Native API connected to KM
from NTDLL by following stub:
                              MOV   EAX, TableFunction
                              LEA   EDX, [ESP+4]
                              INT   2Eh     ;go to KM
                              RET   XX

This is mechanism of syscall - in NT NTcall. I want to write about it now.

All below is undocummented (my research):

In whole article: SERVICE = ROUTINE   ; not Services as mechanism

NTcalls are used by 
 A) NTDLL.dll for UM/KM transitions to NTOSKRNL.exe
 B) USER32.dll,GDI32.dll,WINSRV.dll for UM/KM transitions to WIN32K.sys
 C) NTOSKRNL.exe for KM/KM transitions to NTOSKRNL.exe

 Case C) is typically for transitions  ZwFunction -> NtFunction

    ZwClose     one in NTDLL  and  one in NTOSKRNL
    NtClose     one in NTDLL  and  one in NTOSKRNL  too!

    Address:  NTDLL.ZwClose == NTDLL.NtClose
    Stub:     NTDLL.ZwClose == NTDLL.NtClose == NTOSKRNL.ZwClose
    So these all call NTOSKRNL.NtClose, which is the functional part, all other ??Close are

 Note that NTcalls 0A0h, resp. 0C2h - NtSetHighWaitLowThread, resp. NtSetLowWaitHighThread -
 go not via INT 2Eh  but INT 2Bh, resp. INT 2Ch.

NTcalls which use pointers as parameters were security hole and often used for attacks in the
past (see GetAdmin exploit and NT Syscalls insecurity).

In the stub:

    MOV   EAX, TableFunction
               TableFunction is constant comprised from two numbers:
                TableFunction  =  TableNumber SHL 12  +  FunctionNumber
                where FunctionNumber can be    0..0FFFh
                      TableNumber    can be    0..3      (for NT 4.0)

                Table  0 is for NTOSKRNL.exe  ONLY    (base)
                Table  1 is for WIN32K.sys    ONLY    (graphics) 
                Table  2 and 3 are for the one who knows

                NT 4.0 1381 SP5: Table 0 has 0D3h entries,
                                 table 1 has 20Bh entries.

    LEA   EDX, [ESP+4]   is pointer to parameters
    INT   2Eh            is the transition
    RET   xx             is return from API + cleanup stack

Table has structure:
   ServiceTable           STRUCT
     PointerToEntries     DWORD ?
     MinimumService       DWORD ?
     MaximumService       DWORD ?
     PointerToParameters  DWORD ?
   ServiceTable           ENDS

     PointerToEntries     DWORD  Entry0, Entry1,...,EntryFFF
     MinimumService       is ignored (usually 0)
     MaximumService       is number of services; you can call service < MaximumService only    
     PointerToParameters  DWORD  ParamsSizeForEntry0,...,ParamsSizeForEntryFFF

So there are 4 service tables (NT 4.0) pointer to them is NTOSKRNL export:
   KeServiceDescriptorTable  ServiceTable  4 DUP ({})
There is moreover one nonexported (internal or "shadow") ServiceDescriptorTable in NTOSKRNL.

Of course Entries can be hooked and are hooked. For example in "NTdump/Preserve BPM" I'm
hooking NtContinue (NTcall 13h), in EDump I'm hooking NtSetContextThread (NTcall 99h). So the
problem is not HOW to hook, but to FIND and to KNOW what I'm hooking! Simply trace your
application and hook there where execution flows. Of course NTcalls can be added/changed/
removed among OS versions. See also my DriverSkeleton.

 ServiceTable can be added via KeAddSystemServiceTable:

  AL := KeAddSystemServiceTable(PointerToEntries, MinimumService, MaximumService,
                                PointerToParameters, TableNo)

  As I already mentioned TableNo can be 2 or 3.

  If AL = 1  then your table was successfully added.

 KeAdd.. checks if both "visible" and "shadow" table entries are free (=0), then fills "shadow"
 table with passed parameters; if TableNo != 1 it fills also "visible" table. Shortly: table 1
 is invisible = KeServiceDescriptorTable+10h is always empty (=0).

 Has it sense?
 Partially. It is FASTER interface than DeviceIoControl (which is btw NTcall (2Dh) too).

 INT 2Eh goes to INT 2Eh KM handler, from EAX it extracts TableNo and Function, selects this
 ServiceDescriptorTable ("visible"/"shadow") which address is in KTEB at offset 0DCh, compares
 if Function < MaximumService, copies parameters from user (=EDX) to kernel stack and calls
 Entry. If TableNo is 1 then before copying parameters and calling Entry can be called a routine
 which was passed as parameter to PsEstablishWin32Callouts (see WIN32K.sys for more). As you can
 see table 1 is something special - ALL FOR GRAPHICS! De facto KeAdd.. and PsEst.. are used by
 WIN32K.sys only, so they were created for WIN32K only?

I think in the future will be INT 2Eh replaced with Pentium II+ instructions SYSENTER/SYSEXIT.
These instructions are designed for NT: SYSENTER requires ring0_SS = ring0_CS + 08h, SYSEXIT
requires ring3_CS = ring0_CS + 10h, ring3_SS = ring0_CS + 18h.  In NT is: ring0_CS = 08h,
ring0_SS = 10h, ring3_CS = 1Bh and ring3_SS = 23h  ;-) -> Intel made what Microsoft wanted.
From Intel documentation:
The SYSENTER and SYSEXIT instructions do not constitute a call/return pair; therefore, the
system call "stub" routines executed by user code (typically in shared libraries or DLLs) must
perform the required register state save to create a system call/return pair.

I hope this example explains all.

EliCZ, Aug-15-1999