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 Software Interrupts
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

Abstract
This chapter covers how operating systems use software interrupts, why software interrupts need hooking, and how to hook software interrupts. An example hooks INT 2E (the system service interrupt) in Windows NT.

WHAT ARE INTERRUPTS?

An interrupt refers to a mechanism that breaks into the normal execution of an application program and transfers control to operating system code. There are three kinds of interrupts: hardware interrupts, software interrupts, and exceptions.

Hardware interrupts come from the physical devices in the machine. For example, whenever there is a character waiting on the COM port, a hardware interrupt will be triggered. When an I/O operation completes, a hardware interrupt also will be triggered.

Software interrupts occur as a result of an explicit INT nn request from the application. Applications typically use this mechanism to get different services from the operating system. Exceptions occur as a result of an application’s attempt to perform illegal operations, such as dividing by zero.

The next sections detail how processors handle software interrupts in real, protected, and V86 modes.

Interrupt Processing in Real Mode
In real mode, the lower 1K of memory holds a data structure known as the Interrupt Vector Table (IVT). There are nominally 256 entries in this table. (Since the 80286, the IVT is not required to have 256 entries or start at physical address 0. The base and address and length of the IVT are determined by looking at the Interrupt Descriptor Table Register.) Each entry contains a far pointer to an Interrupt Service Routine. Any type of interrupt routes to the appropriate Interrupt Service Routine through this table. The processor indexes the interrupt number in this table; pushes current CS, IP, and flags on the stack; and calls the far pointer specified in the IVT. The handler processes the interrupt and then executes an IRET instruction to return control to the place where the processor executed at the time of the interrupt.

Interrupt Processing in Protected Mode
In protected mode, interrupts are handled in a similar way as real mode. The Interrupt Descriptor Table (IDT) does what the IVT does in real mode. IDT consists of an array of 8-byte segment descriptors called gates. The Interrupt Descriptor Table Register (IDTR) holds the base address and the limit of IDT. The IDT must exist in physical memory and should never swap out to virtual memory. This is because if an interrupt were to occur while the IDT were swapped out, the processor would generate an exception, requiring the IDT to get the handler for handling this exception, and so on until the system crashed. The gates in the IDT can consist of three types: interrupt gates, trap gates, and task gates. We won’t dwell on the details of the trap and task gates. For further information, refer to Intel processor documentation.

Interrupt gates interest us. The important fields of interrupt gates include the code segment selector and the offset of the code for execution for this interrupt, as well as the privilege level of the interrupt descriptor. The interrupt processing closely resembles that in real mode. When the interrupt occurs, the processor indexes the interrupt number in IDT, pushes EFLAGS, CS, and EIP onto the stack, and calls the handler specified in the IDT. When the handler finishes executing, it should execute the IRET instruction to return control. Depending upon the type of interrupt, an error code may be pushed on the stack. The handler must clear this error code from the stack. The DPL field in the interrupt gate controls the software interrupts. The current privilege level must be at least as privileged as DPL to call these software interrupts. If not, then a General Protection Fault is triggered. This protection feature permits the operating system to reserve certain software interrupts for its own use. Hardware interrupts and exceptions process without regard to the current privilege level.

Interrupt Processing in V86 Mode
In V86 mode, any INT nn instruction causes a General Protection Fault. Windows NT uses this to map INT 21h calls made from an MS-DOS application to Win32 API calls. This mapping occurs as part of a GPF handler for Windows NT. Other types of interrupts are handled similarly to those in protected mode.


HOW OPERATING SYSTEMS USE SOFTWARE INTERRUPTS

MS-DOS uses INT 21 to provide core system services to the applications. Other software interrupts are also provided, such as multiplex interrupt 2F. Applications fill in the parameters in various registers and execute the INT nn instruction to access these services from the operating system. Various compiler libraries provide wrappers around these interrupt interfaces and provide useful C functions, such as _open, _read, _write, and others.

Not much changes in the way software interrupts are used in Windows 95/98 and Windows NT. Windows NT provides user-callable software interrupts. The following table lists the important software interrupts provided.

TABLE 9-1 WINDOWS SOFTWARE INTERRUPTS


WHY SOFTWARE INTERRUPTS NEED TO BE HOOKED

Software interrupts need to be hooked for several reasons. One reason is to change the behavior of the system services exported by the operating system. By hooking the software interrupts, you can write monitoring applications. Hooking can prove useful in studying operating system internals. This can also serve as a way to hook system services, although the mechanism discussed in Chapter 6 provides a better way of doing that.

MS-DOS provides system services to hook software interrupts by means of INT 21h, and functions 25h and 35h. Compiler libraries provide wrapper functions such as _dos_getvect and _dos_setvect to hook software interrupts. Windows 95 provides a mechanism to hook software interrupts by means of Set_PM_Int_Vector and Hook_V86_Int_Chain VxD services. However, Windows NT does not officially support any way to hook software interrupts. The DDK does provide functions such as HalGetInterruptVector() and IoConnectInterrupt() to hook hardware interrupts. Once we understand Intel data structures such as IDT and interrupt gates, we can easily hook software interrupts in Windows NT. Hooking software interrupts basically amounts to changing the code selector and offset fields in the Interrupt Gate Descriptor. However, this certainly becomes a platform-dependent situation. It will work only on an Intel implementation of Windows NT.

You can apply the same technique for hooking software interrupts to hook hardware interrupts or exceptions although you should use the documented IoConnectInterrupt() function to hook hardware interrupts. You have to write an interrupt handler keeping in mind the type of interrupt it is hooking into because the stack frame might differ in various situations. The new interrupt handler must be written in Assembly language because of the restrictions imposed by 32-bit compilers.


HOW TO HOOK SOFTWARE INTERRUPTS

As we already discussed, the two Intel data structures–IDTR and Interrupt Gate Descriptor–play crucial roles in interrupt processing. You can discover the contents of IDTR with the sidt Assembly instruction. This instruction places the base and limit of IDT in a 6-byte location specified by the operand. Once you get the base address of IDT, you can index the interrupt number you want to hook in this table and change the code selector and offset specified. Before doing this, you must save the old code selector and offset. Also, your new handler should ensure that the interrupt is chained properly to the old handler, meaning the new handler should maintain the state of registers and stack in such a way that the old handler should be called as if it were directly called by the processor through the IDT.

The sample application that we write in this chapter hooks INT 2Eh (System Service Interrupt) and maintains the counters of how many times a particular system service was called. The sample maintains only the counter of system services provided by NTOSKRNL.EXE. The user-level application issues DeviceIoControl to this driver to obtain the statistics about the service usage. As we already saw in Chapter 7, there are a total of 0xC4 system services in NT 3.51, 0xD3 services in NT 4.0, and 0xF4 services in Windows 2000 provided by NTOSKRNL.EXE. This sample works on all versions of Windows NT to date.
HOOKINT.C

#include "ntddk.h"

#include "stdarg.h"

#include "stdio.h"

#include "Hookint.h"


#define TEST_PAGING #define DRIVER_SOURCE
#include "..\..\include\intel.h" #include "..\..\include\wintype.h" #include "..\..\include\undocnt.h"
/* Interrupt to be hooked */ #define HOOKINT 0x2E
int OldHandler; ULONG *ServiceCounterTable; ULONG ServiceCounterTableSize; int NumberOfServices;
#ifdef TEST_PAGING void *PagedData; #endif
extern void _cdecl NewHandler();
/* Buffer to store result of sidt instruction */ char buffer[6];
/* Pointer to structure to identify the limit and * base of IDTR */ PIdtr_t Idtr=(PIdtr_t)buffer;
#pragma pack()
void NewHandlerCFunc(int ServiceId) { if (ServiceId>NumberOfServices) return; #ifdef TEST_PAGING memset(PagedData, 0, 100000); #endif ServiceCounterTable[ServiceId+1]++; return; }
NTSTATUS DriverSpecificInitialization() { PIdtEntry_t IdtEntry; extern PServiceDescriptorTableEntry_t KeServiceDescriptorTable;
NumberOfServices = KeServiceDescriptorTable->NumberOfServices; ServiceCounterTableSize = (NumberOfServices+1)*sizeof(int); ServiceCounterTable = ExAllocatePool(PagedPool, ServiceCounterTableSize);
if (!ServiceCounterTable) { return STATUS_INSUFFICIENT_RESOURCES; }
#ifdef TEST_PAGING PagedData=ExAllocatePool(PagedPool, 100000);
if (!PagedData) { ExFreePool(ServiceCounterTable); return STATUS_INSUFFICIENT_RESOURCES; } #endif
memset(ServiceCounterTable,0, ServiceCounterTableSize); *ServiceCounterTable=NumberOfServices;
trace(("NumberOfServices=%x, " "ServiceCounterTableSize=%x, @%x\n", NumberOfServices, ServiceCounterTableSize, ServiceCounterTable));
/* Get the Base and Limit of IDTR Register */ _asm sidt buffer IdtEntry=(PIdtEntry_t)Idtr->Base;
/* Index the interrupt number to be hooked specified by "HOOKINT define" in * appropriate IDT entry, extract and save away the Old * handler’s address */ OldHandler = ((unsigned int)IdtEntry[HOOKINT].OffsetHigh<<16U)| (IdtEntry[HOOKINT].OffsetLow);
/* Plug into the interrupt by changing the offset * field to point to NewHandler function */ _asm cli IdtEntry[HOOKINT].OffsetLow = (unsigned short)NewHandler; IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)NewHandler>16); _asm sti
return STATUS_SUCCESS; }
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { MYDRIVERENTRY(L"hookint", FILE_DEVICE_HOOKINT, DriverSpecificInitialization()); return ntStatus; }
NTSTATUS DriverDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PIO_STACK_LOCATION irpStack; PVOID ioBuffer; ULONG inputBufferLength; ULONG outputBufferLength; ULONG ioControlCode; NTSTATUS ntStatus;
Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0;
irpStack = IoGetCurrentIrpStackLocation (Irp);
ioBuffer = Irp->AssociatedIrp.SystemBuffer; inputBufferLength = irpStack->Parameters. DeviceIoControl.InputBufferLength; outputBufferLength = irpStack->Parameters. DeviceIoControl.OutputBufferLength;
switch (irpStack->MajorFunction) { case IRP_MJ_DEVICE_CONTROL: trace(("HOOKINT.SYS: IRP_MJ_DEVICE_CONTROL\n"));
ioControlCode = irpStack->Parameters. DeviceIoControl.IoControlCode;
switch (ioControlCode) {
case IOCTL_HOOKINT_SYSTEM_SERVICE_USAGE: { int i;
/* Check if sufficient sized buffer is * provided to hold the counters for system * service usage */ if (outputBufferLength >= ServiceCounterTableSize) { /* Output the counters describing the * system service usage */ trace((for (i=1; i<=NumberOfServices; i++) DbgPrint("%x ", ServiceCounterTable[i]))); trace((DbgPrint("\n")));
/* Copy the counter information in the user * supplied buffer */ memcpy(ioBuffer, ServiceCounterTable, ServiceCounterTableSize);
/* Fill in the number of bytes to be * returned to the caller */ Irp->IoStatus.Information = ServiceCounterTableSize; } else { Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; } break; }
default:
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
trace(("HOOKINT.SYS: unknown " "IRP_MJ_DEVICE_CONTROL\n"));
break;
}
break; }
ntStatus = Irp->IoStatus.Status;
IoCompleteRequest (Irp,IO_NO_INCREMENT);
return ntStatus; }
VOID DriverUnload( IN PDRIVER_OBJECT DriverObject ) { WCHAR deviceLinkBuffer[]=L"\\DosDevices\\hookint"; UNICODE_STRING deviceLinkUnicodeString; PIdtEntry_t IdtEntry;
ExFreePool(ServiceCounterTable); #ifdef TEST_PAGING ExFreePool(PagedData); #endif
/* Reach to IDT */ IdtEntry=(PIdtEntry_t)Idtr->Base;
/* Unplug the interrupt by replacing the offset * field in the Interrupt Gate Descriptor by the * old handler address. */ _asm cli IdtEntry[HOOKINT].OffsetLow = (unsigned short)OldHandler; IdtEntry[HOOKINT].OffsetHigh = (unsigned short)((unsigned int)OldHandler>16); _asm sti
RtlInitUnicodeString (&deviceLinkUnicodeString, deviceLinkBuffer );
IoDeleteSymbolicLink (&deviceLinkUnicodeString); IoDeleteDevice (DriverObject->DeviceObject);
trace(("HOOKINT.SYS: unloading\n")); } HANDLER.ASM .386 .model small
.code
include ..\..\include\undocnt.inc public _NewHandler extrn _OldHandler:near extrn _NewHandlerCFunc@4:near
_NewHandler proc near Ring0Prolog STI push eax call _NewHandlerCFunc@4 CLI Ring0Epilog jmp dword ptr cs:[_OldHandler] _NewHandler endp END

SUMMARY

In this chapter, we discussed interrupt processing in various modes of Intel processors. Then, we saw how the operating system makes use of interrupts. Next, we discussed the need for hooking software interrupts. We also explored a mechanism for hooking software interrupts. We concluded the chapter with an example that hooks Int 2E (the system service interrupt) in Windows N.



Page: 1



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