Network Home  Network Map  Our Publications
Windows IT Library
  - Advertise
Windows & .NET Magazine Network Logo

  Home  |   Books  |   Chapters  |   Topics  |   Authors  |   Book Reviews  |   About Us  |   Contact Us

search for  on    power search   help
 






Hooking Windows NT System Services
View the book table of contents

Author: Prasad Dabak
Milind Borate
Sandeep Phadke
Published: October 1999
Copyright: 1999
Publisher: M&T Books
  Find related articles
Find related products

WINDOWS NT SYSTEM SERVICES

Windows NT has been designed with several design goals in mind. Support for multiple (popular) APIs, extensibility, isolation of various APIs from each other, and security are some of the most important ones. The present design incorporates several protected subsystems (for example, the Win32 subsystem, the POSIX subsystem, and others) that reside in the user space isolated from each other. The NT executive runs in the kernel mode and provides native support to all the subsystems. All subsystems use the NT system services provided by the NT executive to implement most of their core functionality.

Windows programmers, when they link with the KERNEL32, USER32, and GDI32 DLLs, are completely unaware of the existence of the NT system services supporting the various Win32 calls they make. Similarly, POSIX clients using the POSIX API end up using more or less the same set of NT system services to get what they want from the kernel. Thus, NT system services represent the fundamental interface for any user-mode application or subsystem to the kernel.

For example, when a Win32 application calls CreateProcess() or when a POSIX application calls the fork() call, both ultimately call the NtCreateProcess() system service from the NT executive.

NT system services represent routines, which run entirely in the kernel mode. For those familiar with the Unix world, NT system services can be considered the equivalent of system calls in Unix.

Figure 6-2 A caller program invoking an NT system service

Currently, Windows NT system services are not completely documented. The only place where you can find some documentation regarding the NT system services is on Windows NT DDK CD-ROMs from Microsoft. The DDK discusses about 25 different system services and covers the parameters passed to them in some detail. You’ll see from Appendix A that this is only the tip of the iceberg. In Windows NT 3.51, 0xC4 different system services exist, in Windows NT 4.0, 0xD3 different system services exist, and in Windows 2000 Beta-2, 0xF4 different system services exist.

We deciphered the parameters of 90% of the system services. Prototypes for all these system services can be found in UNDOCNT.H on the CD-ROM included with this book. We also provide detailed documentation of some of the system services in Appendix A.

In the following section, you will learn how to hook these system services.


HOOKING NT SYSTEM SERVICES

Let’s first look at how NT System Services are implemented in the Windows NT operating system. We also will discuss the exact mechanics of hooking an NT system service. In addition, we’ll explore the kernel data structures involved and provide sample code to aid hooking of system services.

On the CD: Check out hookdrv.c on the accompanying CD-ROM.

Implementation of a System Service in Windows NT
The user mode interface to the system services of NTOSKRNL is provided in the form of wrapper functions. These wrapper functions are present in a DLL called NTDLL.DLL. These wrappers use the INT 2E instruction to switch to the kernel mode and execute the requested system service. The Win32 API functions (mainly in KERNEL32.DLL and ADVAPI32.DLL) use these wrappers for calling a system service. The Win32 API functions performs validations on the parameters passed to the API functions, and translates everything to Unicode. After this, the Win32 API function calls an appropriate wrapper function in NTDLL corresponding to the required service. Each system service in NTOSKRNL is identified by the Service ID. The wrapper function in NTDLL fills in the service id of the requested system service in the EAX register, fills in the pointer to stack frame of the parameters in EDX register, and issues the INT 2E instruction. This instruction changes the processor to the kernel mode, and the processor starts executing the handler specified for the INT 2E in the Interrupt Descriptor Table (IDT). The Windows NT executive sets up this handler. The INT 2E handler copies the parameters from user-mode stack to kernel-mode stack. The base of the stack frame is identified by the contents of the EDX register. The INT 2E handler provided by NT Executive is internally called as KiSystemService().

During the initialization of NTOSKRNL, it creates a function table, hereafter referred to as the System Service Dispatch Table (SSDT), for different services provided by NTOSKRNL (see Figure 6-3). Each entry in the table contains the address of the function to be executed for a given service ID. The INT 2Eh handler looks up this table based on the service ID passed in EAX register and calls the corresponding system service. The code for each function resides in the kernel. Similarly, another table called the ParamTable (hereafter referred to as System Service Parameter Table [SSPT]) provides the handler with the number of parameter bytes to expect from a particular service.

Hooking NT System Services
The easiest way to put a hook into the system services is to locate the System Service Dispatch Table used by the operating system and change the function pointers to point to some other function inserted by the developer. You can do this only from a kernel-mode device driver because this table is protected by the operating system at the page table level. The page attribute for these pages is set so that only kernel-mode components can read from and write to this table. User-level applications cannot read or write these memory locations.

LOCATING THE SYSTEM SERVICE DISPATCH TABLE IN THE NTOSKRNL
There is one undocumented entry in the export list of NTOSKRNL called KeServiceDescriptorTable(). This entry is the key to accessing the System Service Dispatch Table. The structure of this entry looks like this:
typedef struct ServiceDescriptorTable {

	PVOID ServiceTableBase;

	PVOID ServiceCounterTable(0);

	unsigned int NumberOfServices;

	PVOID ParamTableBase;

}
where

ServiceTableBase Base address of the System Service Dispatch Table.
NumberOfServices Number of services described by ServiceTableBase.
ServiceCounterTable This field is used only in checked builds of the operating system and contains the counter of how many times each service in SSDT is called. This counter is updated by INT 2Eh handler (KiSystemService).
ParamTableBase Base address of the table containing the number of parameter bytes for each of the system services.

ServiceTableBase and ParamTableBase contain NumberOfServices entries. Each entry represents a pointer to a function implementing the corresponding system service.

The following program provides an example of hooking system services, under Windows NT. The system service NtCreateFile() hooks and the name of the file created prints when the hook gets invoked. We encourage you to insert code for hooking any other system service of choice. Note the proper places for inserting new hooks in the following code.

Here are the steps to try out the sample (assuming that the sample binaries are copied in C:\SAMPLES directory):
  1. Run “instdrv hooksys c:\samples\hooksys.sys.” This will install the hooksys.sys driver. The driver will hook the NtCreateFile system service.
  2. Try to access the files on your hard disk. For each accessed file, the hooksys.sys will trap the call and display the name of the file accessed in the debugger window. These messages can be seen in SoftICE or using the debug message-capturing tool.
#include "ntddk.h"

#include "stdarg.h"

#include "stdio.h"

#include "hooksys.h"

#define DRIVER_SOURCE

#include "..\..\include\wintype.h"

#include "..\..\include\undocnt.h"


typedef NTSTATUS (*NTCREATEFILE)( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize OPTIONAL, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer OPTIONAL, ULONG EaLength );
#define SYSTEMSERVICE(_function) KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)] NTCREATEFILE OldNtCreateFile;
NTSTATUS NewNtCreateFile( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize OPTIONAL, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer OPTIONAL, ULONG EaLength) { int rc; char ParentDirectory[1024]; PUNICODE_STRING Parent=NULL;
ParentDirectory[0]='\0'; if (ObjectAttributes->RootDirectory!=0) { PVOID Object; Parent=(PUNICODE_STRING)ParentDirectory;
rc=ObReferenceObjectByHandle(ObjectAttributes->RootDirectory, 0, 0, KernelMode, &Object, NULL); if (rc==STATUS_SUCCESS) { extern NTSTATUS ObQueryNameString(void *, void *, int size, int *); int BytesReturned;
rc=ObQueryNameString(Object, ParentDirectory,
sizeof(ParentDirectory), &BytesReturned); ObDereferenceObject(Object);
if (rc!=STATUS_SUCCESS) RtlInitUnicodeString(Parent, L"Unknown\\"); } else { RtlInitUnicodeString(Parent, L"Unknown\\"); } }
DbgPrint("NtCreateFile : Filename = %S%S%S\n", Parent?Parent->Buffer:L"", Parent?L"\\":L"", ObjectAttributes- >ObjectName->Buffer); rc=((NTCREATEFILE)(OldNtCreateFile)) ( FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength); DbgPrint("NtCreateFile : rc = %x\n", rc); return rc; }
NTSTATUS HookServices() {
OldNtCreateFile=(NTCREATEFILE)(SYSTEMSERVICE(ZwCreateFile)); _asm cli
(NTCREATEFILE)(SYSTEMSERVICE(ZwCreateFile))=NewNtCreateFile; _asm sti return STATUS_SUCCESS; } void UnHookServices() { _asm cli
(NTCREATEFILE)(SYSTEMSERVICE(ZwCreateFile))=OldNtCreateFile; _asm sti return; }

  NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { MYDRIVERENTRY(DRIVER_DEVICE_NAME, FILE_DEVICE_HOOKSYS, HookServices()); return ntStatus; }
NTSTATUS DriverDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest (Irp, IO_NO_INCREMENT ); return Irp->IoStatus.Status; }

VOID DriverUnload( IN PDRIVER_OBJECT DriverObject ) { WCHAR deviceLinkBuffer[] = L"\\DosDevices\\"DRIVER_DEVICE_NAME; UNICODE_STRING deviceLinkUnicodeString;

UnHookServices();
RtlInitUnicodeString (&deviceLinkUnicodeString, deviceLinkBuffer ); IoDeleteSymbolicLink (&deviceLinkUnicodeString); IoDeleteDevice (DriverObject->DeviceObject); }

SUMMARY

In this chapter, we explored system services under DOS, Windows 3.x, Windows 95/98, and Windows NT. We discussed the need for hooking these system services. We discussed kernel- and user-lever hooks. We discussed the data structures used during the system call and the mechanism used for hooking Windows NT system services. The chapter concluded with an example that hooked the NtCreateFile() system service.



Page: 1, 2



Post comments on this Chapter

Find related articles
Find related products



Sponsored Links
 • Recover Active Directory in minutes. Get a Free Aelita t-shirt!
 • FREE performance boost. Find out what you’re MISSING
 • Stop Fax Machine Madness! Network Fax Trial & Whitepaper!
 • Manage enterprise desktop challenges with Microsoft.
 • Be prepared for disaster. Try Winternals ERD Commander 2002 today
 • Boost server performance with Intel® Gigabit Network Connections
Featured Links
 • Get NAS how-tos at our storage Road Show -- FREE!
 • Windows & .NET Magazine - get a free sample issue!
 • Save time and money - get our certification eBook!




Network Home | Network Map | Our Pubs | Our Events | Contact Us | About Us | Advertising | Affiliates/Licensing
Copyright © 2003 Penton Media, Inc., All rights reserved. Legal | Privacy