간단한 IRP Hook 코드입니다.
해당 코드에 일부를 수정하여 다른 IRP를 손쉽게 훅킹할 수 있습니다.
보통 저에 경우에는 다른 드라이버에 대한 값을 모니터링 할 때 많이 사용합니다.
예제코드는 ATAPI.sys에 IRP_MJ_SCSI를 훅킹하고 있고 Cdb를 출력합니다.
Cdb와 SCSI에 대한 설명은 IRP Hook 범위를 넘어가므로 이에 대한 설명을 하지 않겠습니다.
개인적으로 만든 코드라서 개인적인 루틴도 추가되었습니다.
예를 들어 IRP 핸들러 시작부분과 끝부분에 g_IRPEnterCount변수 사용 및
IRPUnhook 시 이 변수를 체크합니다.
이는 드라이버 동적 언로드 시 극악의 확률로 발생가능한 동기화문제를 막기 위해서입니다.
#include <Wdm.h>
#include <Scsi.h>
PDEVICE_OBJECT g_HookDeviceObject = NULL;
PDRIVER_DISPATCH g_OrgHandler = NULL;
UCHAR g_HookMajorFunctionIndex = 0xFF;
LONG g_IRPEnterCount = 0;
PDEVICE_OBJECT MyIoGetDeviceObjectPointer(__in WCHAR *DeviceObjectName, __in BOOLEAN bRelated);
BOOLEAN
IrpHook(__in PDEVICE_OBJECT DeviceObject, __in UCHAR
MajorFunctionIndex, __out PDRIVER_DISPATCH *pOrgHandler,
PDRIVER_DISPATCH pNewHandler);
BOOLEAN IrpHookFormName(__in WCHAR
*DeviceObjectName, __in UCHAR MajorFunctionIndex, __out PDRIVER_DISPATCH
*pOrgHandler, PDRIVER_DISPATCH pNewHandler);
VOID IrpUnhook();
NTSTATUS NewIrpHandler(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp);
PDEVICE_OBJECT MyIoGetDeviceObjectPointer(__in WCHAR *DeviceObjectName, __in BOOLEAN bRelated)
{
NTSTATUS ntStatus;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
UNICODE_STRING Name;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE FileHandle;
IO_STATUS_BLOCK IoStatus;
DeviceObject = NULL;
RtlInitUnicodeString(&Name, DeviceObjectName);
if (bRelated == TRUE)
{
// IoGetDeviceObjectPointer는 내부적으로 IoGetRelatedDeviceObject 함수를 호출합니다.
// 따라서 DeviceObjectName 에 해당하는 디바이스 오브젝트 포인터를 얻어오는게 아니라
// 해당 디바이스 오브젝트에 대한 최상위 디바이스 오브젝트를 얻어옵니다.
ntStatus = IoGetDeviceObjectPointer(&Name, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
if (NT_SUCCESS(ntStatus) == TRUE)
{
return DeviceObject;
}
else
{
return NULL;
}
}
else
{
// 실제 해당 이름을 가진 디바이스 오브젝트를 얻옵니다.
InitializeObjectAttributes(&ObjectAttributes,
&Name,
0,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR)NULL);
ntStatus = ZwOpenFile(&FileHandle,
FILE_READ_ATTRIBUTES,
&ObjectAttributes,
&IoStatus,
0,
FILE_NON_DIRECTORY_FILE);
if (NT_SUCCESS(ntStatus) == FALSE)
{
// 해당 디바이스 오브젝트를 열 수 없습니다.
return NULL;
}
ntStatus = ObReferenceObjectByHandle(FileHandle,
0,
*IoFileObjectType,
KernelMode,
(PVOID *)&FileObject,
NULL);
if (NT_SUCCESS(ntStatus) == TRUE)
{
// Windows 2000 소스코드 참고
// 실제 내부 구현도 이와 비슷하게 되어 있습니다.
//
// If the file object was taken out against the mounted file system, it
// will have a Vpb. Traverse it to get to the DeviceObject. Note that in
// this case we should never follow FileObject->DeviceObject, as that
// mapping may be invalid after a forced dismount.
//
if (FileObject->Vpb != NULL && FileObject->Vpb->DeviceObject != NULL)
{
DeviceObject = FileObject->Vpb->DeviceObject;
//
// If a driver opened a disk device using direct device open and
// later on it uses IoGetRelatedTargetDeviceObject to find the
// device object it wants to send an IRP then it should not get the
// filesystem device object. This is so that if the device object is in the
// process of being mounted then vpb is not stable.
//
}
else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
FileObject->DeviceObject->Vpb != NULL &&
FileObject->DeviceObject->Vpb->DeviceObject != NULL)
{
DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
//
// This is a direct open against the device stack (and there is no mounted
// file system to strain the IRPs through).
//
}
else
{
DeviceObject = FileObject->DeviceObject;
}
ZwClose(FileHandle);
return DeviceObject;
}
else
{
ZwClose(FileHandle);
return NULL;
}
}
}
VOID DriverUnload(__in PDRIVER_OBJECT DriverObject)
{
KdPrint(("Driver unload.\n"));
// 드라이버 언로드 루틴에서 호출됩니다.
IrpUnhook(TRUE);
}
BOOLEAN
IrpHook(__in PDEVICE_OBJECT DeviceObject, __in UCHAR
MajorFunctionIndex, __out PDRIVER_DISPATCH *pOrgHandler,
PDRIVER_DISPATCH pNewHandler)
{
g_IRPEnterCount = 0;
*pOrgHandler = DeviceObject->DriverObject->MajorFunction[MajorFunctionIndex];
if (*pOrgHandler == NULL)
{
return FALSE;
}
g_HookDeviceObject = DeviceObject;
g_HookMajorFunctionIndex = MajorFunctionIndex;
InterlockedExchangePointer(&(DeviceObject->DriverObject->MajorFunction[MajorFunctionIndex]), pNewHandler);
return TRUE;
}
BOOLEAN
IrpHookFormName(__in WCHAR *DeviceObjectName, __in UCHAR
MajorFunctionIndex, __out PDRIVER_DISPATCH *pOrgHandler,
PDRIVER_DISPATCH pNewHandler)
{
PDEVICE_OBJECT DeviceObject;
DeviceObject = MyIoGetDeviceObjectPointer(DeviceObjectName, FALSE);
if (DeviceObject != NULL)
{
return (IrpHook(DeviceObject, MajorFunctionIndex, pOrgHandler, pNewHandler));
}
else
{
return FALSE;
}
}
VOID IrpUnhook(BOOLEAN DriverUnloadRoutine)
{
if ((g_HookDeviceObject == NULL)
|| (g_OrgHandler == NULL))
{
KdPrint(("Not hooked.\n"));
}
else
{
// IRP 핸들러를 원래의 핸들러로 되돌려줍니다.
InterlockedExchangePointer(&(g_HookDeviceObject->DriverObject->MajorFunction[g_HookMajorFunctionIndex]),
g_OrgHandler);
if (DriverUnloadRoutine == TRUE)
{
LARGE_INTEGER WaitTime;
WaitTime.QuadPart = -1 * 10 * 1000 * 1000;
//
// 드라이버 언로드 루틴에서 이 함수가 호출되었다면
// InterlockedExchangePointer 후에 바로 드라이버가 언로드 되므로
// 이 때 InterlockedExchangePointer를 하기전에 훅킹 핸들러가 실행되었고
// 그 후에 InterlockedExchangePointer함수가 리턴하게 되면
// 훅킹 핸들러를 원래대로 되돌려주었지만 현재 루틴이 실행중일 수 있으므로 이에 대한 동기화 처리를
// 진행합니다.
//
while (g_IRPEnterCount > 0)
{
// 현재 훅킹루틴이 실행중에 있으므로 잠시 대기합니다.
KeDelayExecutionThread(KernelMode ,FALSE, &WaitTime);
}
// 마지막으로 한 번 더 대기합니다.
KeDelayExecutionThread(KernelMode ,FALSE, &WaitTime);
}
}
}
NTSTATUS NewIrpHandler(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp)
{
NTSTATUS ntStatus;
// 동기화를 위한 전역 카운트를 증가시킵니다.
InterlockedIncrement(&g_IRPEnterCount);
// 디바이스 오브젝트에 대한 필터링을 하였지만 상황에 따라 하지 않아야 할 수도 있습니다.
if (DeviceObject == g_HookDeviceObject)
{
// 우리가 원하는 DeviceObject에서 발생한 Irp입니다.
PIO_STACK_LOCATION pIoStackLocation;
pIoStackLocation = IoGetCurrentIrpStackLocation(Irp);
if (pIoStackLocation->MajorFunction == g_HookMajorFunctionIndex)
{
//
// 이곳에 원하는 코드를 작성합니다.
// 예제코드에서는 간단히 CDB에 대해서 출력하겠습니다.
//
KdPrint(("DeviceObject : %p, Cdb : %#x\n", DeviceObject, pIoStackLocation->Parameters.Scsi.Srb->Cdb[0]));
}
}
ntStatus = g_OrgHandler(DeviceObject, Irp);
// 동기화를 위한 전역 카운트를 감소합니다.
InterlockedDecrement(&g_IRPEnterCount);
return (ntStatus);
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
NTSTATUS ntStatus;
PDEVICE_OBJECT DeviceObject;
// 드라이버가 언로드 될 수 있도록 합니다.
DriverObject->DriverUnload = DriverUnload;
// 예제로 훅킹하는 디바이스는 하드 디스크입니다.
// ( 실제 하드 디스크에 섹터를 읽어주는 디바이스입니다. )
//
// 해당 디바이스 이름은 시스템에 따라서 다릅니다.
// IRP_MJ_SCSI 를 훅킹합니다.
if (IrpHookFormName(L"\\Device\\Ide\\IdeDeviceP1T0L0-17", IRP_MJ_SCSI, &g_OrgHandler, NewIrpHandler) == TRUE)
{
KdPrint(("IrpHookFormName successed.\n"));
}
else
{
KdPrint(("IrpHookFormName failed.\n"));
}
return STATUS_SUCCESS;
}