• [Driver] v0.2 -Windows [7] - IOCTL

    Tutorial 1
    Tutorial 2 || Einführung in die Gerätetreiberprogrammierung unter Windows von Niklas Bartz

    Es geht um die Kommunikation eines Treibers mit einem User-Programm.

    Willkomen zum zweiten Treiber Tutorial. Diesmal werden wir die Kommunikation zwischen einem Treiber und einem Programm im User-Mode behandeln.

    Um mit einem anderen Programm zu kommunizieren muss ein Treiber I/O Request Packets (IRP) benutzen. Dies ist eine einfach Datenstruktur, welche Puffer mit Daten enthält. Das ganze funktioniert, wie sollte es auch anders sein unter Windows, mit Datei-Handles. Wenn also nun ein User-Programm ein String in das Datei-Handle des Treibers schreibt, dann wird vom Kernel ein IRP erzeugt, welches diesen String enthält. Um nun IRPs verarbeiten zu können muss unser Kerneltreiber eine Funktion zur verarbeitung bereitstellen. Dies funktioniert genau so wie mit der Endladeroutine. Es wird einfach der zugehörige Funktionszeiger gesetzt.

    Alle Hauptfunktionen sind in einem Array namens MajorFunction gespeichert. Es sind Werte definiert wodurch man die Funktionen setzten kann.
    Code c:
    1
    2
    3
    4
    5
    
    theDriverObject->MajorFunction[IRP_MJ_CREATE]  = CreateProcessing;
    theDriverObject->MajorFunction[IRP_MJ_CLOSE]    = CloseProcessing;
    theDriverObject->MajorFunction[IRP_MJ_READ]      = ReadProcessing;
    theDriverObject->MajorFunction[IRP_MJ_WRITE]    = WriteProcessing;
    theDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]   = IoControl;

    Die einzelnen Funktionen werden durch folgenden Methoden der Windows-API aufgerufen:
    Code c:
    1
    2
    3
    4
    5
    
    CreateFile()
    CloseHandle()
    ReadFile()
    WriteFile()
    DeviceIoControl()

    Diese Funktionen könnte man nun alle ausformulieren
    Code c:
    1
    2
    3
    4
    5
    6
    7
    8
    
    NTSTATUS CreateProcessing( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
    {
        // Mache irgendetwas
    //...
        return STATUS_SUCCESS;
    }
    // u.s.w.
    //...

    Abbildung 1: Ein Kerneltreiber kann für jede Hauptfunktion eine eigene Callbackfuntkion definieren

    Man sollte sich im klaren seien, dass man ohne ein Request von dem User-Mode-Programm nichts vom Treiber senden kann oder habt ihr schonmal eine Nachricht von einer Datei empfangen?
    Also jede Kommunikation erfolgt vom User-Programm aus. Wenn man nun dafür sorgen möchte, dass der Treiber einem immer etwas senden kann, dann muss man immer genügend Request bereitstellen, bevor diese verbraucht sind.
    Man sendet also ein Request an den Treiber welches dieses erst verwendet, wenn er Daten zu übermitteln hat. Dann reagiert er auf das Request und sendet etwas an das Programm zurück.


    Um nun von einem Programm im User-Mode aus einen Treiber zu verwenden, muss dieses Programm ein Handle zum Treiber öffen. Dies geht jedoch nur, wenn der Treiber zuvor ein Gerät registriert hat. Danach kann man mit dem User-Programm das Gerät öffnen und darauf zugreifen, als sei es eine Datei. So ähnlich ist es in der Regel auch bei UNIX-Systemen.
    So wollen wir mal mit unserem Kerneltreiber ein Gerät registrieren:
    Code c:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
    #define FILE_DEVICE_TESTDEVICE 0x00008001
     
    const WCHAR deviceNameBuffer[] = L"\\Device\\vicesys";
    const WCHAR symbolicLinkNameBuffer[] = L"\\DosDevices\\vicesys";
    PDEVICE_OBJECT g_testDevice; // global pointer on the driverdevice
     
    NTSTATUS DriverEntry (IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING theRegistryPath) {
        NTSTATUS        ntStatus;
        UNICODE_STRING  deviceNameUni;
        UNICODE_STRING  symbolicLinkNameUni;
        // name and symbolic link init.
        RtlInitUnicodeString( &deviceNameUni,
                               deviceNameBuffer );
        RtlInitUnicodeString( &symbolicLinkNameUni,
                               symbolicLinkNameBuffer );
     
        // device init
        ntStatus = IoCreateDevice ( DriverObject,
                                    0,                          // device extension
                                    &deviceNameUni,
                                    FILE_DEVICE_TESTDEVICE,
                                    0,
                                    TRUE,
                                    &g_TestDevice );
        if(NT_SUCCESS(ntStatus)) {
            DbgPrint("IoDevice built");
     
            ntStatus = IoCreateSymbolicLink( &symbolicLinkNameUni,
                                             &deviceNameUni );
            if (NT_SUCCESS(ntStatus)) {
                DbgPrint("Symbolic Link created");
            } else {
                DbgPrint("Symbolic link creation failed");
            }
        } else {
            DbgPrint("Cant't create a IoDevice");
        }
    /*
    ...
    */

    Wem es entfallen ist: Durch das Präfix L wird ein String als Unicode definiert.
    Durch IoCreateDevice() wird ein Gerät registriert und von da an kann man im User-Programm auf den Treiber zugreifen. Da wir jedoch ein Stück kompfortabler auf den Treiber zugreifen wollen erstellen wir noch einen Symbolische-Link.
    Da wir einen sybolischen Link erstellt haben, kann ein User-Programm ein Handle für die Datei über den String \\.\vicesys öffnen:

    Code c:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    hDevice = CreateFile("\\\\.\\vicesys",
                                    GENERIC_READ | GENERIC_WRITE,
                                    0,
                                    NULL,
                                    OPEN_EXISTING,
                                    FILE_ATTRIBUTE_NORMAL,
                                    NULL);
        if (hDevice == ((HANDLE)-1)) {
            cout<<"Device Handle konnte nicht erstellt werden"<<endl;
            return FALSE;
        }
        cout<<"Device Handle erstellt"<<endl;
        return true;

    Es ist absolut egal ob Sie einen symbolischen Link erstellen oder nicht. Ich finde es "schöner". Falls Sie ohne einen symbolischen Link erstellt zu haben auf einen Treiber zugreifen wollen, dann geht das wie folgt:

    Code c:
    1
    2
    3
    4
    5
    6
    7
    
    hDevice = CreateFile("\\\\Device\\vicesys",
                                    GENERIC_READ | GENERIC_WRITE,
                                    0,
                                    NULL,
                                    OPEN_EXISTING,
                                    FILE_ATTRIBUTE_NORMAL,
                                    NULL);

    Im folgenden habe ich ein Beispiel erstellt wodurch ein User-Mode-Programm mit einem Treiber über Events kommuniziert.
    Dazu folgendes: Wenn man eine Event im Kernel-Mode erstellt, dann kann ma dies im User-Mode nicht modifizieren, man kann nur den Status abfragen. Allerdings möchten wir, dass unsere GUI Anwendung den Status verändern kann.
    Da der Treiber mit höherer Priorität als eine GUI Anwendung läuft, können wir vom Treiber aus auf alles in einer GUI Anwendung zugreifen. Somit müssen wir das Event in unserem Programm erstellen und an den Treiber weiterleiten (referenzieren).

    Wenn man eine neue IOCTL schreibt, welcher es möglich seien soll mit User-Mode-Software zu kommunizieren, dann muss diese IRP_MJ_DEVICE_CONTROL Requests benutzen (MSDN(27.10.2011)). Von Windows wird das CTL_CODE Makro geliefert, welches in der Wdm.h und Ntddk.h definiert ist (Def. s.unten oder hier MSDN (27.10.2011)).

    An dieser Stelle Zeige ich einfach einmal den ganzen Code des Treibers und der GUI Testanwendung. Der Code sollte eigentlich selbst erklärend seien, für jeden der meint er ist weit genug um Treiber zu programmieren.

    Das komplette Beispiel ist auch im Anhang vorhanden.

    Treiber:

    IOCTL.h // wird von dem Treiber und der GUI App verwendet
    Code c:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    #ifndef __IO_CONTROL_H__
    #define __IO_CONTROL_H__
     
    const WCHAR deviceNameBuffer[] = L"\\Device\\vicesys";
    const WCHAR symbolicLinkNameBuffer[] = L"\\DosDevices\\vicesys";
     
    /* _____________________________________________________________________________ ______________________________________________________________________________________________
     .   IOCTL's are defined by the following bit layout.
     .   [ Common | Device Type | Required Access | Custom | Function Code | Transfer Type ]
     .      31     30         16 15             14    13    12           2  1            0
     .
     .   #define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
     . 
     .   Common          - 1 bit.  This is set for user-defined device types.
     .   Device Type     - This is the type of device the IOCTL belongs to.  This can be user defined (Common bit set).  This must match the device type of the device object.
     .                   - Values of less than 0x8000 are reserved for Microsoft.
     .   Required Access - FILE_READ_DATA, FILE_WRITE_DATA, etc.  This is the required access for the device.
     .   Custom          - 1 bit.  This is set for user-defined IOCTL's.  This is used in the same manner as "WM_USER".
     .   Function Code   - This is the function code that the system or the user defined (custom bit set)
     .                   - Values of less than 0x800 are reserved for Microsoft
     .   Transfer Type   - Indicates how the system will pass data between the caller of DeviceIoControl (or IoBuildDeviceIoControlRequest) and the driver that handles the IRP.
     .                   - METHOD_IN_DIRECT, METHOD_OUT_DIRECT, METHOD_NEITHER, METHOD_BUFFERED.
     . _____________________________________________________________________________ ______________________________________________________________________________________________*/
     
    #define FILE_DEVICE_TESTDEVICE 0x00008001
     
    #define     IO_REFERENCE_EVENT          (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define     IO_DEREFERENCE_EVENT        (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)
     
    #define     IO_SET_EVENT                (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x803, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define     IO_CLEAR_EVENT              (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x804, METHOD_NEITHER, FILE_ANY_ACCESS)
    #define     IO_QUERY_EVENT_STATE        (ULONG) CTL_CODE(FILE_DEVICE_TESTDEVICE, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS)
     
    #endif // #ifndef __IO_CONTROL_H__

    mydriver.h
    Code c:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    #ifndef __MY_DRIVER_H__
    #define __MY_DRIVER_H__
     
    /* __________________________________NOTE_______________________________________
     . Maintain device state information.
     .
     . Every driver that has an ISR must provide storage for a pointer to a set 
     . of kernel-defined interrupt objects, and most device drivers store this 
     . pointer in a device extension. Each driver determines the size of 
     . the device extension when it creates a device object, and each driver 
     . defines the contents and structure of its own device extensions.
     . _____________________________________________________________________________ */
    typedef struct deviceExtensions // extensions structure
    {
        HANDLE hDeviceEvent;
    } DEVICE_EXT;
     
     
    #endif // #ifndef __MY_DRIVER_H__


    mydriver.c
    Code c:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    
    // our second devi cedriver
     
    #include "ntddk.h"
    #include "mydriver.h"
    #include "..\inc\IOCTL.h"
     
    PDEVICE_OBJECT  g_testDevice;           // global pointer on the driver device
    PVOID           g_pEventObject = NULL;  // global pointer on the event object
     
    DRIVER_INITIALIZE   DriverEntry;
    DRIVER_UNLOAD       OnUnload;
    DRIVER_DISPATCH     CreateProcessing;
    DRIVER_DISPATCH     CloseProcessing;
    DRIVER_DISPATCH     IoControl;
     
    /* _____________________________________________________________________________
     . OnDispatchRoutine is called, if we need to complete a IRP.
     .
     . This minimal DispatchCreateClose routine completes the create IRP without 
     . boosting the priority of the originator of the IRP (IO_NO_INCREMENT), 
     . because the originator is assumed to wait for an indeterminate but very 
     . small interval for the request to complete.
     . _____________________________________________________________________________ */
    void OnDispatchRoutine(IN PIRP irp) {
        irp->IoStatus.Status = STATUS_SUCCESS; 
        irp->IoStatus.Information = 0; 
        IoCompleteRequest(irp, IO_NO_INCREMENT); 
    }
     
    /* _____________________________________________________________________________
     . CreateProcessing is called, if the user-mode programm calls create file.
     .
     . HANDLE WINAPI CreateFile(
     .  __in      LPCTSTR lpFileName,
     .  __in      DWORD dwDesiredAccess,
     .  __in      DWORD dwShareMode,
     .  __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
     .  __in      DWORD dwCreationDisposition,
     .  __in      DWORD dwFlagsAndAttributes,
     .  __in_opt  HANDLE hTemplateFile
     .  );
     . _____________________________________________________________________________ */
    NTSTATUS CreateProcessing (IN PDEVICE_OBJECT deviceObject, IN PIRP irp) {
        OnDispatchRoutine(irp);
        DbgPrint("CreateProcessing called\n");
        return STATUS_SUCCESS;
    }
     
    /* _____________________________________________________________________________
     . CloseProcessing is called, if the user-mode programm calls close handle.
     .
     . BOOL WINAPI CloseHandle(
     .  __in  HANDLE hObject
     .  );
     . _____________________________________________________________________________ */
    NTSTATUS CloseProcessing (IN PDEVICE_OBJECT deviceObject, IN PIRP irp) {
        OnDispatchRoutine(irp); 
        DbgPrint("CloseProcessing called\n");
        return STATUS_SUCCESS;
    }
     
    /* _____________________________________________________________________________
     . IoControl is called, if the user-mode programm calls device io control.
     .
     . BOOL WINAPI DeviceIoControl(
     .  __in         HANDLE hDevice,
     .  __in         DWORD dwIoControlCode,
     .  __in_opt     LPVOID lpInBuffer,
     .  __in         DWORD nInBufferSize,
     .  __out_opt    LPVOID lpOutBuffer,
     .  __in         DWORD nOutBufferSize,
     .  __out_opt    LPDWORD lpBytesReturned,
     .  __inout_opt  LPOVERLAPPED lpOverlapped
     .  );
     . _____________________________________________________________________________ */
    NTSTATUS IoControl (IN PDEVICE_OBJECT deviceObject, IN PIRP irp) {
     
        PIO_STACK_LOCATION          irpSp;
        ULONG                       functionCode;
        HANDLE                      hEvent;
        OBJECT_HANDLE_INFORMATION   objHandleInfo;
        LONG*                       outBuffer;
        NTSTATUS                    status = STATUS_SUCCESS;
     
        irpSp = IoGetCurrentIrpStackLocation(irp);
        functionCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
     
        switch (functionCode) {
            case IO_REFERENCE_EVENT:
            hEvent = (HANDLE) irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
            status = ObReferenceObjectByHandle(
                    hEvent,
                    GENERIC_ALL,
                    NULL,
                    KernelMode,
                    &g_pEventObject,
                    &objHandleInfo);
            if(status != STATUS_SUCCESS)
            {
                DbgPrint("ObReferenceObjectByHandle failed! ERROR: %x\n", status);
                break;
            }
            DbgPrint("ReferenceObject complete\n");
            break;
            
            case IO_DEREFERENCE_EVENT:
                if(g_pEventObject) {
                    ObDereferenceObject(g_pEventObject);
                }
                DbgPrint("DereferenceObject complete\n");
                break;
     
            case IO_SET_EVENT:
                KeSetEvent(g_pEventObject, 0, FALSE);
                DbgPrint("KeSetEvent complete\n");
                break;
                
            case IO_CLEAR_EVENT:
                KeClearEvent(g_pEventObject);
                DbgPrint("KeClearEvent complete\n");
                break;
            
            case IO_QUERY_EVENT_STATE:
                outBuffer = (LONG*) irp->UserBuffer;
                *outBuffer = KeReadStateEvent(g_pEventObject);
                DbgPrint("KeReadStateEvent complete\n");
                
                irp->IoStatus.Status = STATUS_SUCCESS;
                irp->IoStatus.Information = sizeof(LONG);
                IoCompleteRequest(irp, IO_NO_INCREMENT);
                return status;
     
            default:
                break;
        }
     
        OnDispatchRoutine(irp); 
        DbgPrint("IoControl called\n");
        return STATUS_SUCCESS;
    }
     
     
    /* _____________________________________________________________________________
     . OnUnload is called, if the driver is unloading
     . free all used memorie
     . _____________________________________________________________________________ */
    VOID OnUnload (IN PDRIVER_OBJECT driverObject) {
        UNICODE_STRING  symbolicLinkName;
        RtlInitUnicodeString( &symbolicLinkName,
                               symbolicLinkNameBuffer );
     
        IoDeleteSymbolicLink(&symbolicLinkName);
        IoDeleteDevice(g_testDevice);
     
        DbgPrint("Driver unloaded\n");
    }
     
    NTSTATUS DriverEntry (IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING theRegistryPath) {
        NTSTATUS        ntStatus;
        UNICODE_STRING  deviceNameUni;
        UNICODE_STRING  symbolicLinkNameUni;
        // name and symbolic link init.
        RtlInitUnicodeString( &deviceNameUni,
                               deviceNameBuffer );
        RtlInitUnicodeString( &symbolicLinkNameUni,
                               symbolicLinkNameBuffer );
     
        // device init
        ntStatus = IoCreateDevice ( driverObject,
                                    sizeof(DEVICE_EXT),                         // device extension
                                    &deviceNameUni,
                                    FILE_DEVICE_TESTDEVICE,
                                    0,
                                    TRUE,
                                    &g_testDevice );
        if(NT_SUCCESS(ntStatus)) {
            DbgPrint("IoDevice built");
     
            ntStatus = IoCreateSymbolicLink( &symbolicLinkNameUni,
                                             &deviceNameUni );
            if (NT_SUCCESS(ntStatus)) {
                DbgPrint("Symbolic Link created");
            } else {
                DbgPrint("Symbolic link creation failed");
            }
        } else {
            DbgPrint("Cant't create a IoDevice");
        }
     
        
     
        // set the pointer for the unload function 
        // in our driver object
        driverObject->DriverUnload  = OnUnload;
        // IOCTL init.
        driverObject->MajorFunction[IRP_MJ_CREATE]          = CreateProcessing;
        driverObject->MajorFunction[IRP_MJ_CLOSE]           = CloseProcessing;
        driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = IoControl;
     
        DbgPrint("Driver loaded!\n");
        return STATUS_SUCCESS;
    }

    Testanwendung

    User_Programm.cpp
    Code cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    
    #include <stdio.h>
    #include <iostream>
    #include "IOAccess.h"
     
    using namespace std;
     
    int main(int argc, char* argv[])
    {
        IOAccess *pIO = NULL;
        int eingabe = 99;
        
        cout<<"________    Menu    ________\n"<<endl;
        cout<<"- press 0 for exit"<<endl;
        cout<<"- press 1 for OPEN  DEVICE"<<endl;
        cout<<"- press 2 for CLOSE DEVICE"<<endl;
        cout<<"- press 3 for QUERY EVENT"<<endl;
        cout<<"- press 4 for SET   EVENT"<<endl;
        cout<<"- press 5 for CLEAR EVENT"<<endl;
        cout<<"____________________________\n"<<endl;
        
        while(eingabe != 0) {
            cin >> eingabe; 
            switch(eingabe)
            {
                case 1: cout<<"OPEN  DEVICE"<<endl;
                    if(!pIO) {
                        pIO = new IOAccess();
                    }
                    break;
                case 2: cout<<"CLOSE DEVICE"<<endl;
                    if(pIO) {
                        delete pIO;
                        pIO = NULL;
                    }
                    break;
                case 3: cout<<"QUERY EVENT"<<endl;
                    if(pIO) {
                        pIO->queryEvent();
                    }
                    break;
                case 4: cout<<"SET   EVENT"<<endl;
                    if(pIO) {
                    pIO->setEvent();
                    }
                    break;
                case 5: cout<<"CLEAR EVENT"<<endl;
                    if(pIO) {
                        pIO->clearEvent();
                    }
                    break;
                default:
                    break;
     
            }
        
        }
        return 0;
    }

    IoAccess.h
    Code cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    #ifndef __IO_ACCESS_H__
    #define __IO_ACCESS_H__
     
    #include <windows.h>
     
    class IOAccess {
    private:
        HANDLE  hDevice;
        HANDLE  hEvent;
        DWORD   dwBytesReturned;
    public:
        IOAccess();
        ~IOAccess();
     
        bool openDriver();  
        void closeDriver(); 
     
        void queryEvent();
        void clearEvent();
        void setEvent();
     
    };
     
    #endif // #ifndef __IO_ACCESS_H__

    IoAccess.cpp
    Code cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    
    #include "IOAccess.h"
    #include <stdio.h>
    #include <winioctl.h>
    #include "..\inc\IOCTL.h"
     
    IOAccess::IOAccess()
    {
        hDevice =   NULL;
        hEvent  =   NULL;
        openDriver();
    }
     
    IOAccess::~IOAccess()
    {
        closeDriver();
    }
     
    bool IOAccess::openDriver()
    {
        hDevice = CreateFile("\\\\.\\vicesys",
                                    GENERIC_READ | GENERIC_WRITE,
                                    FILE_SHARE_READ,
                                    NULL,
                                    OPEN_EXISTING,
                                    FILE_ATTRIBUTE_NORMAL,
                                    NULL);
     
        if (INVALID_HANDLE_VALUE == hDevice) {
            printf("Device Handle konnte nicht erstellt werden\n");
            return FALSE;
        }
        printf("Device Handle erstellt\n");
        
        hEvent = CreateEvent(NULL, false, false, NULL);
        
        //download event object to device driver
        DeviceIoControl(hDevice, 
                        IO_REFERENCE_EVENT, 
                        (LPVOID) hEvent,
                        0, 
                        NULL, 
                        0, 
                        &dwBytesReturned, 
                        NULL);
        return TRUE;
     
    }
     
    void IOAccess::closeDriver()
    {
        if(hDevice) {
            if(hEvent) {
                DeviceIoControl(hDevice, 
                                IO_DEREFERENCE_EVENT, 
                                NULL,0, 
                                NULL,0, 
                                &dwBytesReturned, 
                                NULL);
                CloseHandle(hEvent);
            }
     
            CloseHandle(hDevice);
        }
        printf("Device Handle geschlossen\n");
    }
     
    void IOAccess::queryEvent()
    {
        ULONG dwState;
     
        if(hEvent)
        {
            DeviceIoControl(hDevice, 
                    IO_QUERY_EVENT_STATE, 
                    NULL,
                    0, 
                    (LPVOID) &dwState, 
                    sizeof(dwState), 
                    &dwBytesReturned, 
                    NULL);
            if(dwState) {
                printf("Event signaled state\n");
            }
            else {
                printf("Event not signaled state\n");
            }
        }   
    }
     
    void IOAccess::clearEvent()
    {
        if(hEvent)
        {
            DeviceIoControl(hDevice, 
                    IO_CLEAR_EVENT, 
                    NULL,
                    0, 
                    NULL, 
                    0, 
                    &dwBytesReturned, 
                    NULL);
        }
    }
     
    void IOAccess::setEvent()
    {
        if(hEvent)
        {
            DeviceIoControl(hDevice, 
                    IO_SET_EVENT, 
                    NULL,
                    0, 
                    NULL, 
                    0, 
                    &dwBytesReturned, 
                    NULL);
        }
    }

    Ok was ist noch zu erwähnen. Vielleicht folgendes:

    g_pEventObject ist ein PRKEVENT Objekt, damit kann man KeEvent... undKeWaitFor... benutzen.

    Code c:
    1
    2
    3
    
    //User-Mode
    WaitForSingleObject(hEvent, INFINITE);
    // Nach dieser Funktion ist das Event auf "not signaled" gesetzt.

    Den folgenden Code benutzen, wenn das Objekt Event eingetreten ist:
    Code c:
    1
    2
    
    // Dirver
    KeSetEvent(g_pEventObject, 0, FALSE);

    So das wars auch schon wieder ich hoffe es hat gefallen und bis zum nächsten mal. Es wird schon bald weiter gehen und dort werde ich zeigen wie man mit einem User-Programm seinen eigenen Treiber läd und wieder entläd.

    MFG Cyb3r
     
    Miniaturansichten angehängter Grafiken Miniaturansichten angehängter Grafiken content/attachments/58610-kerneltreiber-callback-funktionen.jpg.html  
    Angehängte Dateien Angehängte Dateien