作者:pker
/*++
Module Name:
FSHook.c
Abstract:
I wanted to build a filter driver when I first thought about doing such
kind of things. But then I found that it's not realistic 'cause it have to
hook every filesystem driver to prevent from missing any IRPs. But we don't
know when there's a new driver coming. When we install a virtual CD-ROM
program, for instance, it installs a driver to control the virtual CD-ROM.
There can be viruses in the image of the disk, so we have to hook that
driver as well.
Then a new idea, which then I found that it's being used by most of the AV
softs, comes out of my head. The driver's final goal is to inercept all the
I/O behavior to disk A~Z, so we can use ObReferenceObjectByXXX to get the
_FILE_OBJECT of /??/X:/, then we can get the device chain by
IoGetRelatedDeviceObject. We are interested in the MajorFunction field of
the _DEVICE_OBJECT. We can hook the IRP_MJ_XXX we interest by replacing the
entries of the dispatch routines with our own.
Environment:
Kernel mode.
Copyright:
Copyright (c) 2005, pker / CVC.GB
--*/
#include "fshook.h"
///
//
// Global Variables
//
///
// save hooked device objects and driver objects
PDEVICE_OBJECT g_pDevObjTab[MAX_DISK]={NULL};
PDRIVER_OBJECT g_pDrvObjTab[MAX_DISK]={NULL};
// entries of original dispatch routines of each hooked driver
PAV_DRIVER_DISPATCH_ROUTINE g_pOriginalDispatch[MAX_DISK];
// process ID of the AV scanner
ULONG g_ulPid;
// shared memory address (kernel mode)
PVOID g_pKrnlSBuffer=NULL;
///
//
// Functions
//
///
ULONG
PAV_GetObjectTabIndex (
IN ULONG *pObject,
IN ULONG **pObjTab
)
/*++
Routine Discription:
The routine searches the suitable object (device object and driver object)
and returns the index of the object in the global object table.
--*/
{
ULONG i;
for (i=0; i<MAX_DISK; i++)
{
if (pObjTab[i] == pObject)
{
return i;
}
}
// object not found
return (ULONG)-1;
}
NTSTATUS
PAV_DispatchRoutineHook (
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++
Routine Discription:
This is the hook of the IRPs. When in DEBUG mode. It DbgPrint the driver
name, which is dealing with the I/O request, and the target filename, with
which the I/O request is going to handle.
The main function of this procedure is to intercept all the I/O request,
and wakeup the Ring3 virus scanning routine.
--*/
{
ULONG i;
HANDLE pid;
PIO_STACK_LOCATION piosl;
#ifdef __PAV_MON_DEBUG__
ANSI_STRING as;
#endif
pid=PsGetCurrentProcessId ();
#ifdef __PAV_MON_DEBUG__
// debug print the pid
DbgPrint ("PavAP!PAV_DispatchRoutineHook: PsGetCurrentProcessId = %x/n",pid);
// get the driver name which is dealing with the IRP
RtlUnicodeStringToAnsiString (&as,&(pDeviceObject->DriverObject->DriverName),TRUE);
DbgPrint ("PavAP!PAV_DispatchRoutineHook: Intercepted a I/O Request from Driver: %s/n",as.Buffer);
RtlFreeAnsiString (&as);
#endif
// determine where the I/O request came from
i=PAV_GetObjectTabIndex ((ULONG *)pDeviceObject,(ULONG **)g_pDevObjTab);
if (i == (ULONG)-1)
{
i--;
}
#ifdef __PAV_MON_DEBUG__
DbgPrint ("Target Filename: %c:",(CHAR)i+'A');
#endif
// get the filename that the I/O request would effect
piosl=IoGetCurrentIrpStackLocation (pIrp);
#ifdef __PAV_MON_DEBUG__
if (piosl->FileObject != NULL)
{
RtlUnicodeStringToAnsiString (&as,&(piosl->FileObject->FileName),TRUE);
DbgPrint ("%s/n",as.Buffer);
RtlFreeAnsiString (&as);
}
#endif
// call original dispatch routine and returns
i=PAV_GetObjectTabIndex ((ULONG *)pDeviceObject->DriverObject,(ULONG **)g_pDrvObjTab);
if (i != (ULONG)-1)
{
switch (piosl->MajorFunction)
{
case IRP_MJ_CREATE:
#ifdef __PAV_MON_DEBUG__
DbgPrint ("MajorFunction Code: IRP_MJ_CREATE/n/n");
#endif
return g_pOriginalDispatch[i].pfnCreate (pDeviceObject,pIrp);
case IRP_MJ_WRITE:
#ifdef __PAV_MON_DEBUG__
DbgPrint ("MajorFunction Code: IRP_MJ_WRITE/n/n");
#endif
return g_pOriginalDispatch[i].pfnWrite (pDeviceObject,pIrp);
case IRP_MJ_CLOSE:
#ifdef __PAV_MON_DEBUG__
DbgPrint ("MajorFunction Code: IRP_MJ_CLOSE/n/n");
#endif
return g_pOriginalDispatch[i].pfnClose (pDeviceObject,pIrp);
case IRP_MJ_CLEANUP:
#ifdef __PAV_MON_DEBUG__
DbgPrint ("MajorFunction Code: IRP_MJ_CLEANUP/n/n");
#endif
return g_pOriginalDispatch[i].pfnCleanup (pDeviceObject,pIrp);
}
}
// no suitable driver object found, so complete this IRP
return PAV_CompleteRequest (pIrp,STATUS_SUCCESS,0);
}
PDEVICE_OBJECT
PAV_GetDeviceObject (
IN PUNICODE_STRING pusDeviceName
)
/*++
Routine Discription:
Get the related device object according to the device name.
--*/
{
NTSTATUS ns;
HANDLE hFile;
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK iosb;
PFILE_OBJECT pFileObject;
PDEVICE_OBJECT pDeviceObject;
// open the device
InitializeObjectAttributes (&oa,pusDeviceName,OBJ_CASE_INSENSITIVE,NULL,NULL);
ns=ZwCreateFile (&hFile,SYNCHRONIZE | FILE_ANY_ACCESS,&oa,&iosb,0,0,
FILE_SHARE_READ | FILE_SHARE_WRITE,FILE_OPEN,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,NULL,0);
if (!NT_SUCCESS(ns))
{
return NULL;
}
// reference the file object by the handle...
ns=ObReferenceObjectByHandle (hFile,FILE_READ_DATA,NULL,KernelMode,&pFileObject,NULL);
if (!NT_SUCCESS(ns))
{
ZwClose (hFile);
return NULL;
}
// get related device object chain
pDeviceObject=IoGetRelatedDeviceObject (pFileObject);
// dereference file object and close the file (device)
ObDereferenceObject (pFileObject);
ZwClose (hFile);
return pDeviceObject;
}
VOID
PAV_IrpHookInstall (
VOID
)
/*++
Routine Discription:
This function hooks IRPs of each disk device, from A~Z.
--*/
{
ULONG i;
NTSTATUS ns;
PDEVICE_OBJECT fdo;
UNICODE_STRING usDeviceName;
WCHAR DeviceName[]=L"//??//X://";
// hook disk A~Z
for (i=0; i<MAX_DISK; i++)
{
// get device object
DeviceName[4]='A'+(WCHAR)i;
RtlInitUnicodeString (&usDeviceName,DeviceName);
fdo=PAV_GetDeviceObject (&usDeviceName);
if (fdo == NULL)
{
continue;
}
// hook IRPs...
g_pDevObjTab[i]=fdo;
g_pDrvObjTab[i]=fdo->DriverObject;
if (PAV_DispatchRoutineHook != g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CREATE])
{
g_pOriginalDispatch[i].pfnCreate=g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CREATE];
g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CREATE]=PAV_DispatchRoutineHook;
}
if (PAV_DispatchRoutineHook != g_pDrvObjTab[i]->MajorFunction[IRP_MJ_WRITE])
{
g_pOriginalDispatch[i].pfnWrite=g_pDrvObjTab[i]->MajorFunction[IRP_MJ_WRITE];
g_pDrvObjTab[i]->MajorFunction[IRP_MJ_WRITE]=PAV_DispatchRoutineHook;
}
if (PAV_DispatchRoutineHook != g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CLOSE])
{
g_pOriginalDispatch[i].pfnClose=g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CLOSE];
g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CLOSE]=PAV_DispatchRoutineHook;
}
if (PAV_DispatchRoutineHook != g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CLEANUP])
{
g_pOriginalDispatch[i].pfnCleanup=g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CLEANUP];
g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CLEANUP]=PAV_DispatchRoutineHook;
}
}
}
VOID
PAV_IrpHookRemove (
VOID
)
/*++
Routine Discription:
Removes the IRP hooks and restore the original dispatch routines of each
hooked device.
--*/
{
ULONG i;
// restore every hooked driver's dispatch routine entries
for (i=0; i<MAX_DISK; i++)
{
if (g_pDrvObjTab[i] != NULL)
{
if (g_pOriginalDispatch[i].pfnCreate != NULL)
{
g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CREATE]=g_pOriginalDispatch[i].pfnCreate;
}
if (g_pOriginalDispatch[i].pfnWrite != NULL)
{
g_pDrvObjTab[i]->MajorFunction[IRP_MJ_WRITE]=g_pOriginalDispatch[i].pfnWrite;
}
if (g_pOriginalDispatch[i].pfnClose != NULL)
{
g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CLOSE]=g_pOriginalDispatch[i].pfnClose;
}
if (g_pOriginalDispatch[i].pfnCleanup != NULL)
{
g_pDrvObjTab[i]->MajorFunction[IRP_MJ_CLEANUP]=g_pOriginalDispatch[i].pfnCleanup;
}
}
}
}