Where did System Services 0 and 1 go?

System calls on Windows go through NTDLL.dll, where each system call is invoked by a syscall (x64) or sysenter (x86) CPU instruction, as can be seen from the following output of NtCreateFile from NTDLL:

0:000> u
ntdll!NtCreateFile:
00007ffc`c07fcb50 4c8bd1          mov     r10,rcx
00007ffc`c07fcb53 b855000000      mov     eax,55h
00007ffc`c07fcb58 f604250803fe7f01 test    byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
00007ffc`c07fcb60 7503            jne     ntdll!NtCreateFile+0x15 (00007ffc`c07fcb65)
00007ffc`c07fcb62 0f05            syscall
00007ffc`c07fcb64 c3              ret
00007ffc`c07fcb65 cd2e            int     2Eh
00007ffc`c07fcb67 c3              ret

The important instructions are marked in bold. The value set to EAX is the system service number (0x55 in this case). The syscall instruction follows (the condition tested does not normally cause a branch). syscall causes transition to the kernel into the System Service Dispatcher routine, which is responsible for dispatching to the real system call implementation within the Executive. I will not go to the exact details here, but eventually, the EAX register must be used as a lookup index into the System Service Dispatch Table (SSDT), where each system service number (index) should point to the actual routine.

On x64 versions of Windows, the SSDT is available in the kernel debugger in the nt!KiServiceTable symbol:

lkd> dd nt!KiServiceTable
fffff804`13c3ec20  fced7204 fcf77b00 02b94a02 04747400
fffff804`13c3ec30  01cef300 fda01f00 01c06005 01c3b506
fffff804`13c3ec40  02218b05 0289df01 028bd600 01a98d00
fffff804`13c3ec50  01e31b00 01c2a200 028b7200 01cca500
fffff804`13c3ec60  02229b01 01bf9901 0296d100 01fea002

You might expect the values in the SSDT to be 64-bit pointers, pointing directly to the system services (this is the scheme used on x86 systems). On x64 the values are 32 bit, and are used as offsets from the start of the SSDT itself. However, the offset does not include the last hex digit (4 bits): this last value is the number of arguments to the system call.

Let’s see if this holds with NtCreateFile. Its service number is 0x55 as we’ve seen from user mode, so to get to the actual offset, we need to perform a simple calculation:

kd> dd nt!KiServiceTable+55*4 L1
fffff804`13c3ed74  020b9207

Now we need to take this offset (without the last hex digit), add it to the SSDT and this should point at NtCreateFile:

lkd> u nt!KiServiceTable+020b920
nt!NtCreateFile:
fffff804`13e4a540 4881ec88000000  sub     rsp,88h
fffff804`13e4a547 33c0            xor     eax,eax
fffff804`13e4a549 4889442478      mov     qword ptr [rsp+78h],rax
fffff804`13e4a54e c744247020000000 mov     dword ptr [rsp+70h],20h

Indeed – this is NtCreateFile. What about the argument count? The value stored is 7. Here is the prototype of NtCreateFile (documented in the WDK as ZwCreateFile):

NTSTATUS NtCreateFile(
  PHANDLE            FileHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes,
  PIO_STATUS_BLOCK   IoStatusBlock,
  PLARGE_INTEGER     AllocationSize,
  ULONG              FileAttributes,
  ULONG              ShareAccess,
  ULONG              CreateDisposition,
  ULONG              CreateOptions,
  PVOID              EaBuffer,
  ULONG              EaLength);

Clearly, there are 11 parameters, not just 7. Why the discrepency? The stored value is the number of parameters that are passed using the stack. In x64 calling convention, the first 4 arguments are passed using registers: RCX, RDX, R8, R9 (in this order).

Now back to the title of this post. Here are the first few entries in the SSDT again:

lkd> dd nt!KiServiceTable
fffff804`13c3ec20  fced7204 fcf77b00 02b94a02 04747400
fffff804`13c3ec30  01cef300 fda01f00 01c06005 01c3b506

The first two entries look different, with much larger numbers. Let’s try to apply the same logic for the first value (index 0):

kd> u nt!KiServiceTable+fced720
fffff804`2392c340 ??              ???
                    ^ Memory access error in 'u nt!KiServiceTable+fced720'

Clearly a bust. The value is in fact a negative value (in two’s complement), so we need to sign-extend it to 64 bit, and then perform the addition (leaving out the last hex digit as before):

kd> u nt!KiServiceTable+ffffffff`ffced720
nt!NtAccessCheck:
fffff804`1392c340 4c8bdc          mov     r11,rsp
fffff804`1392c343 4883ec68        sub     rsp,68h
fffff804`1392c347 488b8424a8000000 mov     rax,qword ptr [rsp+0A8h]

This is NtAccessCheck. The function’s implementation is in lower addresses than the SSDT itself. Let’s try the same exercise with index 1:

kd> u nt!KiServiceTable+ffffffff`ffcf77b0
nt!NtWorkerFactoryWorkerReady:
fffff804`139363d0 4c8bdc          mov     r11,rsp
fffff804`139363d3 49895b08        mov     qword ptr [r11+8],rbx

And we get system call number 1: NtWorkerFactoryWorkerReady.

For those fond of WinDbg scripting – write a script to display nicely all system call functions and their indices.

 

Public Windows Kernel Programming Class

After a short twitter questionaire, I’m excited to announce a Remote Windows Kernel Programming class to be scheduled for the end of January 2019 (28 to 31).

If you want to learn how to write software drivers for Windows (not hardware, plug & play drivers), including file system mini filters – this is the class for you! You should be comfortable with programming on Windows in user mode (although we’ll discuss some of the finer points of working with the Windows API) and have a basic understanding of Windows OS concepts such as processes, threads and virtual memory.

If you’re interested, send an email to zodiacon@live.com with the title “Windows Kernel Programming Training” with your name, company name (if any), and time zone. I will reply with further details.

Here is the syllabus (not final, but should be close enough):

Windows Kernel Programming

Duration: 4 Days (January 28th to 31st, 2019)
Target Audience: Experienced windows developers, interested in developing kernel mode drivers
Objectives: · Understand the Windows kernel driver programming model

· Write drivers for monitoring processes, threads, registry and some types of objects

· Use documented kernel hooking mechanisms

· Write basic file system mini-filter drivers

Pre Requisites: · At least 1 year of experience working with the Windows API

· Basic understanding of Windows OS concepts such as processes, threads, virtual memory and DLLs

Software requirements: · Windows 10 Pro 64 bit (latest official release)

· Virtual machine (preferable Windows 10 64 bit) using any virtualization technology (for testing and debugging)

· Visual Studio 2017 (any SKU) + latest update

· Windows 10 SDK (latest)

· Windows 10 WDK (latest)

Cost: $1950

Syllabus

  • Module 1: Windows Internals quick overview
    • Processes and threads
    • System architecture
    • User / kernel transitions
    • Virtual memory
    • APIs
    • Objects and handles
    • Summary

 

  • Module 2: The I/O System and Device Drivers
    • I/O System overview
    • Device Drivers
    • The Windows Driver Model (WDM)
    • The Kernel Mode Driver Framework (KMDF)
    • Other device driver models
    • Driver types
    • Software drivers
    • Driver and device objects
    • I/O Processing and Data Flow
    • Accessing files and devices
    • Asynchronous I/O
    • Summary

 

  • Module 3: Kernel programming basics
    • Installing the tools: Visual Studio, SDK, WDK
    • C++ in a kernel driver
    • Creating a driver project
    • Building and deploying
    • The kernel API
    • Strings
    • Linked Lists
    • Kernel Memory Pools
    • The DriverEntry function
    • The Unload routine
    • Installation
    • Summary
    • Lab: create a simple driver; deploy a driver

 

  • Module 4: Building a simple driver
    • Creating a device object
    • Exporting a device name
    • Building a driver client
    • Driver dispatch routines
    • Introduction to I/O Request Packets (IRPs)
    • Completing IRPs
    • Dealing with user space buffers
    • Handling DeviceIoControl calls
    • Testing the driver
    • Debugging the driver
    • Using WinDbg with a virtual machine
    • Summary
    • Lab: open a process for any access; zero driver; debug a driver

 

  • Module 5: Kernel mechanisms
    • Interrupt Request Levels (IRQLs)
    • Interrupts
    • Deferred Procedure Calls (DPCs)
    • Dispatcher objects
    • Thread Synchronization
    • Spin locks
    • Work items
    • Summary

 

  • Module 6: Process and thread monitoring
    • Process creation/destruction callback
    • Specifying process creation status
    • Thread creation/destruction callback
    • Notifying user mode
    • Writing a user mode client
    • User/kernel communication
    • Summary
    • Labs: monitoring process/thread activity; prevent specific processes from running; protecting processes

 

  • Module 7: Object and registry notifications
    • Process/thread object notifications
    • Pre and post callbacks
    • Registry notifications
    • Performance considerations
    • Reporting results to user mode
    • Summary
    • Lab: protect specific process from termination; hiding registry keys; simple registry monitor

 

  • Module 8: File system mini filters
    • File system model
    • Filters vs. mini filters
    • The Filter Manager
    • Filter registration
    • Pre and Post callbacks
    • File name information
    • Contexts
    • File system operations
    • Driver to user mode communication
    • Debugging mini-filters
    • Summary
    • Labs: protect a directory from write; hide a file/directory; prevent file/directory deletion; log file operations

 

Silent Process Exit – Is It Really?

While working on my GflagsX tool, there was yet another feature the tool was missing compared to the classic GFlags tool – Silent Process Exit support. But what is Silent Process Exit?

According to the documentation there are two scenarios that trigger Silent Process Exit:

  • Self exiting – one of the threads in the process calls ExitProcess.
  • A TerminateProcess call is issued from another (or the same process).

The documentation states that if a process exits because all threads terminate normally, then Silent Process Exit is not in effect. (also if kernel code kills a process, Silent Process Exit is not invoked).

The documentation may lead us to belive that if a process exits normally (no abnormal termination or exception) then Silent Process Exit will not be invoked. Let’s test that theory.

First, let’s configure Silent Process Exit with GFlags. (GFlagsX support is on its way). Run GFlags and select the Silent Process Exit tab:

SilentProcessExit1

Let’s test it with notepad. Type notepad.exe in the Image text box and press Tab. Some of the options light up. Let’s try something simple – generating a dump file when notepad terminates. Check Enable Silent Process Exit Monitoring and then set a dump folder location and dump type, like so:

SilentProcessExit2

Click Apply to apply the settings. Now launch Notepad. If you terminate it using (say) Task Manager, you’ll find a subfolder under the configured Dump Folder Location named Notepad.exe-(PID xxxx)-yyyyyyyy where xxxx is the terminating process ID and yyyyyy is the value returned from GetTickCount at the time of the exit (the number of milliseconds elapsed since Windows booted). Inside the folder you’ll find the dump file itself.

However, if you launch notepad again and just close its main window, you’ll find, perhaps surprisingly, that yet another folder was created with a new dump file. But why? Isn’t this a normal process termination?

Since we can be pretty sure no process (including notepad) called TerminateProcess, this means notepad called ExitProcess. Is this “normal”? Are there processes that terminate by just ending all their threads?

Let’s launch another notepad instance and attach WinDbg to it. Break into the debugger and add a breakpoint for ExitProcess:

0:000> x kernel32!ExitProcess*
00007ffe`1509b190 KERNEL32!ExitProcessImplementation (<no parameter info>)
0:000> bp KERNEL32!ExitProcessImplementation

Now let the process go and close notepad’s window. The breakpoint should hit:

Breakpoint 0 hit
KERNEL32!ExitProcessImplementation:
00007ffe`1509b190 4883ec28 sub rsp,28h

Let’s look at the call stack:

0:000> k
# Child-SP RetAddr Call Site
00 000000a1`4294f718 00007ffe`17119ce5 KERNEL32!ExitProcessImplementation
01 000000a1`4294f720 00007ffe`1711a345 msvcrt!_crtExitProcess+0x15
02 000000a1`4294f750 00007ff7`ffef934a msvcrt!doexit+0x171
03 000000a1`4294f7c0 00007ffe`15093034 notepad!__mainCRTStartup+0x1b6
04 000000a1`4294f880 00007ffe`17281461 KERNEL32!BaseThreadInitThunk+0x14
05 000000a1`4294f8b0 00000000`00000000 ntdll!RtlUserThreadStart+0x21

Now it seems clear: when the first (“main”) thread of notepad returns from its main function, the C-runtime library calls ExitProcess explicitly. And in fact this is what you’ll find with most executables. This is why when the main thread exits in a C/C++ application, the process ends wven if other threads still exist and executing. From the Windows kernel’s perspective, there is no “main” thread – all threads are equal.

Silent Exit Process support is part of NTDLL and the Windows Error Reporting Service. This is in contrast to tools such as ProcDump from Sysinternals that attaches a debugger to the monitored process and creates a dump file when it exits. To set it up, the global flag with the value 0x200 (512) must be set in the “Image File Execution Options” (IFEO) subkey (just like all other global flags). However, once the bit is set, the actual details need to be written into the key HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe. This is done on an image name basis just as with the IFEO key. Here is the example for notepad just shown:

SilentProcessExit3

Stay tuned for more info on Silent Process Exit support in GFlagsX!