WDM 실습 - Kernel 에서의 Dispatch Routine

2007/12/01 15:21

1. CreateFile(), WriteFile(), ReadFile(), CloseHandle() 를 Dispatch
[ more.. | less.. ]
#include <ntddk.h>

#define DEVICE_NAME    L"\\Device\\WDM3"
#define DOS_NAME    L"\\DosDevices\\SimpleWDM3"

// non-paged pool
typedef struct _DEVICE_EXTENSION
{
    PDEVICE_OBJECT pDeviceObject;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

/**********************************************************
* Dispatch Routine
**********************************************************/

NTSTATUS DispatchCreate( IN PDEVICE_OBJECT    pDevObj,
                                        IN PIRP            pIrp )    // 사용자가 전달한 인자가 있다...
{
    DbgPrint("[WDM3] CreateFile() is Called");
   
    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);
   
    return STATUS_SUCCESS;
}

NTSTATUS DispatchClose( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    DbgPrint("[WDM3] CloseHandle() is Called");

    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

NTSTATUS DispatchRead( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    DbgPrint("[WDM3] ReadFile() is Called");

    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

NTSTATUS DispatchWrite( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    DbgPrint("[WDM3] WriteFile() is Called");   
   
    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

VOID DriverUnload( IN PDRIVER_OBJECT pDriverObject)
{
    UNICODE_STRING         ustrDosName;
    PDEVICE_OBJECT         pDeviceObject;
   
    pDeviceObject      = pDriverObject->DeviceObject;
   
    DbgPrint("[WDM3] DriverUnload");
   
    RtlInitUnicodeString( &ustrDosName, DOS_NAME);
   
    IoDeleteSymbolicLink( &ustrDosName );
    IoDeleteDevice( pDeviceObject );
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
    NTSTATUS status = STATUS_SUCCESS;
   
    UNICODE_STRING    ustrName;
    UNICODE_STRING     ustrDosName;
   
    PDEVICE_OBJECT         pDeviceObject;
    PDEVICE_EXTENSION     pDeviceExtension;
   
    DbgPrint("[WDM3] DriverEntry");
       
    RtlInitUnicodeString( &ustrName, DEVICE_NAME );
    RtlInitUnicodeString( &ustrDosName, DOS_NAME );

    // Unload는 반드시 디바이스 만들기 전에 지정 - 만들다 실패 했을 때 unload가 안되는 상황을 피한다.
    pDriverObject->DriverUnload = DriverUnload;

    status = IoCreateDevice( pDriverObject,
                             sizeof( DEVICE_EXTENSION),
                             &ustrName,
                             FILE_DEVICE_UNKNOWN,
                             0,
                             TRUE,
                             &pDeviceObject);
                            
    if ( ! NT_SUCCESS( status ) )
    {
        DbgPrint("[WDM3] Error IoCreateDevice");
        return status;
    }
   
    status = IoCreateSymbolicLink( &ustrDosName, &ustrName);
   
    if ( ! NT_SUCCESS( status ) )
    {
        DbgPrint("[WDM3] Error IoCreateSymbolicLink");
        IoDeleteDevice( pDeviceObject );
    }
   
    pDeviceObject->Flags |= DO_BUFFERED_IO;
   

   
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
    pDriverObject->MajorFunction[IRP_MJ_CLOSE]  = DispatchClose;
    pDriverObject->MajorFunction[IRP_MJ_WRITE]  = DispatchWrite;
    pDriverObject->MajorFunction[IRP_MJ_READ]   = DispatchRead;
       
    return status;
}

2. Bufferd I/O 를 사용한 Dispatch
[ more.. | less.. ]
#include <ntddk.h>

#define DEVICE_NAME    L"\\Device\\WDM3_1"
#define DOS_NAME    L"\\DosDevices\\SimpleWDM3_1"

#define MAX_BUF    256

typedef struct _DEVICE_EXTENSION
{
    ULONG    ulSize;
    char    strBuffer[MAX_BUF];
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

/**********************************************************
* Dispatch Routine
**********************************************************/

NTSTATUS DispatchCreate( IN PDEVICE_OBJECT    pDevObj,
                         IN PIRP            pIrp )
{
    DbgPrint("[WDM3_1] CreateFile() is Called");
   
    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);
   
    return STATUS_SUCCESS;
}

NTSTATUS DispatchClose( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    DbgPrint("[WDM3_1] CloseHandle() is Called");

    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

NTSTATUS DispatchRead( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    PDEVICE_EXTENSION pDevExt = pDevObj->DeviceExtension;
    PIO_STACK_LOCATION pIo = IoGetCurrentIrpStackLocation(pIrp);
   
    PVOID pBuffer = pIrp->AssociatedIrp.SystemBuffer;
    ULONG ulSize  = pIo->Parameters.Read.Length;

    DbgPrint("[WDM3_1] ReadFile() is Called");
   
    // 작은 버퍼를 보낸경우.
    if ( ulSize < pDevExt->ulSize )
    {
        pIrp->IoStatus.Status        = STATUS_BUFFER_TOO_SMALL;
        pIrp->IoStatus.Information    = ulSize;
        IoCompleteRequest( pIrp, IO_NO_INCREMENT);

        return 0;
    }

    RtlCopyMemory( pBuffer, pDevExt->strBuffer, pDevExt->ulSize );

    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = pDevExt->ulSize;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

NTSTATUS DispatchWrite( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    PDEVICE_EXTENSION pDevExt = pDevObj->DeviceExtension;

    // IRP 구조체안에 있는 IO_STACK_LOCATIONI 의 주소를 구한다.
    PIO_STACK_LOCATION pIo = IoGetCurrentIrpStackLocation(pIrp);
   
    // 사용자가 전달한 버퍼의 복사본의 주소.
    PVOID pBuffer = pIrp->AssociatedIrp.SystemBuffer;

    // 버퍼의 크기.
    ULONG ulSize  = pIo->Parameters.Write.Length;


    DbgPrint("[WDM3_1] WriteFile : %s", pBuffer);

    if ( ulSize > MAX_BUF ) ulSize = MAX_BUF;

    RtlZeroMemory( pDevExt->strBuffer, 0);
    RtlCopyMemory( pDevExt->strBuffer, pBuffer, ulSize );
   
    pDevExt->ulSize = ulSize;

    pIrp->IoStatus.Status        = STATUS_SUCCESS; // 성공.. WriteFile(,&len)이 TRUE 리턴.
    pIrp->IoStatus.Information    = ulSize;         // 실제 성공한 크기.
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}




VOID DriverUnload( IN PDRIVER_OBJECT pDriverObject)
{
    UNICODE_STRING         ustrDosName;
    PDEVICE_OBJECT         pDeviceObject;
   
    pDeviceObject      = pDriverObject->DeviceObject;
   
    DbgPrint("[WDM3_1] DriverUnload");
   
    RtlInitUnicodeString( &ustrDosName, DOS_NAME);
   
    IoDeleteSymbolicLink( &ustrDosName );
    IoDeleteDevice( pDeviceObject );
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
    NTSTATUS status = STATUS_SUCCESS;
   
    UNICODE_STRING    ustrName;
    UNICODE_STRING     ustrDosName;
   
    PDEVICE_OBJECT         pDeviceObject;
   
    DbgPrint("[WDM3_1] DriverEntry");
       
    RtlInitUnicodeString( &ustrName, DEVICE_NAME );
    RtlInitUnicodeString( &ustrDosName, DOS_NAME );
   
    // Dispatch Routine   
    pDriverObject->DriverUnload = DriverUnload;


    status = IoCreateDevice( pDriverObject,
                             sizeof( DEVICE_EXTENSION),
                             &ustrName,
                             FILE_DEVICE_UNKNOWN,
                             0,
                             TRUE,
                             &pDeviceObject);
                            
    if ( ! NT_SUCCESS( status ) )
    {
        DbgPrint("[WDM3_1] Error IoCreateDevice");
        return status;
    }

    status = IoCreateSymbolicLink( &ustrDosName, &ustrName);
   
    if ( ! NT_SUCCESS( status ) )
    {
        DbgPrint("[WDM3_1] Error IoCreateSymbolicLink");
        IoDeleteDevice( pDeviceObject );
    }
   
    // DeviceObject 안에 버퍼 전달 방식을 지정한다.
    pDeviceObject->Flags |= DO_BUFFERED_IO;     
   
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
    pDriverObject->MajorFunction[IRP_MJ_CLOSE]  = DispatchClose;
    pDriverObject->MajorFunction[IRP_MJ_WRITE]  = DispatchWrite;
    pDriverObject->MajorFunction[IRP_MJ_READ]   = DispatchRead;
       
    return status;
}

3. DeviceIoControl 을 사용한 Dispatch
[ more.. | less.. ]
#include <ntddk.h>

#define DEVICE_NAME    L"\\Device\\WDM4"
#define DOS_NAME    L"\\DosDevices\\SimpleWDM4"

#define MAX_BUF    256

typedef struct _DEVICE_EXTENSION
{
    ULONG    ulSize;
    char    strBuffer[MAX_BUF];
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

// IOCTL Code - DeviceIoControl..
#define IOCTL_TEST_CODE    CTL_CODE( FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)


/**********************************************************
* Dispatch Routine
**********************************************************/

NTSTATUS DispatchCreate( IN PDEVICE_OBJECT    pDevObj,
                         IN PIRP            pIrp )
{
    DbgPrint("[WDM4] CreateFile() is Called");
   
    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);
   
    return STATUS_SUCCESS;
}

NTSTATUS DispatchClose( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    DbgPrint("[WDM4] CloseHandle() is Called");

    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}


//++++++++++++++++++++++++++++++++++++++++++++++++++++++
NTSTATUS DispatchDeviceIoControl( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    PIO_STACK_LOCATION pIoStk = IoGetCurrentIrpStackLocation( pIrp );
    ULONG ctlCode = pIoStk->Parameters.DeviceIoControl.IoControlCode;
    ULONG inSize  = pIoStk->Parameters.DeviceIoControl.InputBufferLength;
    ULONG outSize = pIoStk->Parameters.DeviceIoControl.OutputBufferLength;

    // 복사된 버퍼는 한개 이다. 처음에 input buffer를 복사해 놓고
    // 읽어서 사용한 후에
    // User에게 보내고 싶은 data역시 이 버퍼에 담아 주면 된다.
    PVOID pBuffer = pIrp->AssociatedIrp.SystemBuffer;

    NTSTATUS status = STATUS_SUCCESS;
    ULONG    dwSize = 0;

    switch( ctlCode )
    {
    case IOCTL_TEST_CODE:
        {
            DbgPrint("IOCTL_TEST_CODE : %s", pBuffer);

            if ( inSize > outSize )
            {
                status = STATUS_BUFFER_TOO_SMALL;
                dwSize = 0;
            }
            else
            {
                dwSize = inSize;
            }
        }
    }
   
    DbgPrint("[WDM4] DeviceIoControl() is Called");

    pIrp->IoStatus.Status        = status;
    pIrp->IoStatus.Information    = dwSize;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}


//===========================================================
VOID DriverUnload( IN PDRIVER_OBJECT pDriverObject)
{
    UNICODE_STRING         ustrDosName;
    PDEVICE_OBJECT         pDeviceObject;
   
    pDeviceObject      = pDriverObject->DeviceObject;
   
    DbgPrint("[WDM4] DriverUnload");
   
    RtlInitUnicodeString( &ustrDosName, DOS_NAME);
   
    IoDeleteSymbolicLink( &ustrDosName );
    IoDeleteDevice( pDeviceObject );
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
    NTSTATUS status = STATUS_SUCCESS;
   
    UNICODE_STRING    ustrName;
    UNICODE_STRING     ustrDosName;
   
    PDEVICE_OBJECT         pDeviceObject;
   
    DbgPrint("[WDM4] DriverEntry");
       
    RtlInitUnicodeString( &ustrName, DEVICE_NAME );
    RtlInitUnicodeString( &ustrDosName, DOS_NAME );
   
    status = IoCreateDevice( pDriverObject,
                             sizeof( DEVICE_EXTENSION),
                             &ustrName,
                             FILE_DEVICE_UNKNOWN,
                             0,
                             TRUE,
                             &pDeviceObject);
                            
    if ( ! NT_SUCCESS( status ) )
    {
        DbgPrint("[WDM4] Error IoCreateDevice");
        return status;
    }

    status = IoCreateSymbolicLink( &ustrDosName, &ustrName);
   
    if ( ! NT_SUCCESS( status ) )
    {
        DbgPrint("[WDM4] Error IoCreateSymbolicLink");
        IoDeleteDevice( pDeviceObject );
    }
   
    pDeviceObject->Flags |= DO_BUFFERED_IO;
   
    // Dispatch Routine   
    pDriverObject->DriverUnload = DriverUnload;
   
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
    pDriverObject->MajorFunction[IRP_MJ_CLOSE]  = DispatchClose;
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = DispatchDeviceIoControl;
       
    return status;
}


4. IRP_MJ_DEVICE_CONTROL( DispatchDeviceIoControl ) 사용한 Dispatch ( Event )
[ more.. | less.. ]
#include <ntddk.h>

#define DEVICE_NAME    L"\\Device\\WDM4_1"
#define DOS_NAME    L"\\DosDevices\\SimpleWDM4_1"

#define MAX_BUF    256

typedef struct _DEVICE_EXTENSION
{
    ULONG    ulSize;
    char    strBuffer[MAX_BUF];
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

// IOCTL Code
#define IOCTL_START    CTL_CODE( FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)

PVOID    g_pEventObject = 0;

/**********************************************************
* Dispatch Routine
**********************************************************/

NTSTATUS DispatchCreate( IN PDEVICE_OBJECT    pDevObj,
                         IN PIRP            pIrp )
{
    DbgPrint("[WDM4] CreateFile() is Called");
   
    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);
   
    return STATUS_SUCCESS;
}

NTSTATUS DispatchClose( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    DbgPrint("[WDM4] CloseHandle() is Called");

    pIrp->IoStatus.Status        = STATUS_SUCCESS;
    pIrp->IoStatus.Information    = 0;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

VOID foo( HANDLE ParentID, HANDLE ProcessID, BOOLEAN b)
{
    DbgPrint("foo");
    KeSetEvent( g_pEventObject, 0, FALSE );    // 커널에서 Event를 signal 하는 함수..
}


//++++++++++++++++++++++++++++++++++++++++++++++++++++++
NTSTATUS DispatchDeviceIoControl( IN PDEVICE_OBJECT pDevObj,
                        IN PIRP              pIrp )
{
    PIO_STACK_LOCATION pIoStk = IoGetCurrentIrpStackLocation( pIrp );
    ULONG ctlCode = pIoStk->Parameters.DeviceIoControl.IoControlCode;
    ULONG inSize  = pIoStk->Parameters.DeviceIoControl.InputBufferLength;
    ULONG outSize = pIoStk->Parameters.DeviceIoControl.OutputBufferLength;

    PVOID pBuffer = pIrp->AssociatedIrp.SystemBuffer;

    NTSTATUS status = STATUS_SUCCESS;
    ULONG    dwSize = 0;

    OBJECT_HANDLE_INFORMATION objHandleInfo;
    HANDLE                      hEvent;


    switch( ctlCode )
    {
    case IOCTL_START:
        {
            // User에서 Event를 보낸다고 생각한다.
            hEvent = *((HANDLE*)pBuffer);

            // Event 핸들을 가지고 KEVENT 구조체의 주소로 변경한다.
            status = ObReferenceObjectByHandle( hEvent, GENERIC_ALL,
                                    NULL, KernelMode,
                                    &g_pEventObject, &objHandleInfo);

            if ( status != STATUS_SUCCESS)
            {
                DbgPrint("Error ObReferenceObjectByHandle");
                break;
            }
            // 프로세스가 생성되거나 종료될떄 호출되는 함수..
            PsSetCreateProcessNotifyRoutine( foo, FALSE);
        }
        break;
    }
   
    DbgPrint("[WDM4] DeviceIoControl() is Called");

    pIrp->IoStatus.Status        = status;
    pIrp->IoStatus.Information    = dwSize;
    IoCompleteRequest( pIrp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

//===========================================================
VOID DriverUnload( IN PDRIVER_OBJECT pDriverObject)
{
    UNICODE_STRING         ustrDosName;
    PDEVICE_OBJECT         pDeviceObject;
   
    pDeviceObject      = pDriverObject->DeviceObject;
   

    PsSetCreateProcessNotifyRoutine( foo, TRUE );

    if ( g_pEventObject )
        ObDereferenceObject( g_pEventObject );

    DbgPrint("[WDM4_1] DriverUnload");
   
    RtlInitUnicodeString( &ustrDosName, DOS_NAME);
   
    IoDeleteSymbolicLink( &ustrDosName );
    IoDeleteDevice( pDeviceObject );
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
    NTSTATUS status = STATUS_SUCCESS;
   
    UNICODE_STRING    ustrName;
    UNICODE_STRING     ustrDosName;
   
    PDEVICE_OBJECT         pDeviceObject;
   
    DbgPrint("[WDM4_1] DriverEntry");
       
    RtlInitUnicodeString( &ustrName, DEVICE_NAME );
    RtlInitUnicodeString( &ustrDosName, DOS_NAME );
   
    status = IoCreateDevice( pDriverObject,
                             sizeof( DEVICE_EXTENSION),
                             &ustrName,
                             FILE_DEVICE_UNKNOWN,
                             0,
                             TRUE,
                             &pDeviceObject);
                            
    if ( ! NT_SUCCESS( status ) )
    {
        DbgPrint("[WDM4_1] Error IoCreateDevice");
        return status;
    }

    status = IoCreateSymbolicLink( &ustrDosName, &ustrName);
   
    if ( ! NT_SUCCESS( status ) )
    {
        DbgPrint("[WDM4_1] Error IoCreateSymbolicLink");
        IoDeleteDevice( pDeviceObject );
    }
   
    pDeviceObject->Flags |= DO_BUFFERED_IO;
   
    // Dispatch Routine   
    pDriverObject->DriverUnload = DriverUnload;
   
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
    pDriverObject->MajorFunction[IRP_MJ_CLOSE]  = DispatchClose;
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = DispatchDeviceIoControl;
       
    return status;
}


Tags

WDM