1. ホーム
  2. c++

[解決済み] DeviceIoControlで送信したデータをDriver.cのcontrolFunctionで受信するには?IOCTL,Driver

2022-02-15 14:49:38

質問

例えば、以下のようなコードがあるとします。ローディング、アンローディング、ドライバ入力などは動作します。

ドライバー.c

#define IO_INCREMENT_VALUE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0001, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
#define IO_RECEIVE_RANDOM_BUFFER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0002, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)


NTSTATUS IoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS Status = STATUS_INVALID_PARAMETER;
    ULONG BytesIO = 0;

    const IO_STACK_LOCATION stack = *IoGetCurrentIrpStackLocation(Irp);
    const ULONG ControlCode = stack.Parameters.DeviceIoControl.IoControlCode;

    if (ControlCode == IO_INCREMENT_VALUE)
    {

        //How to receive    LPVOID       lpInBuffer,
        //                  DWORD        nInBufferSize,

        //                  LPVOID       lpOutBuffer,
        //                  DWORD        nOutBufferSize,
                //send from DeviceIoControl

    }
    else if (ControlCode == IO_RECEIVE_RANDOM_BUFFER)
    {

        //How to receive    LPVOID       lpInBuffer,
//                  DWORD        nInBufferSize,

//                  LPVOID       lpOutBuffer,
//                  DWORD        nOutBufferSize,
//             /send from DeviceIoControl
        /*
        DWORD nOutBufferSize = ;
        for(DWORD i = 0; i< nOutBufferSize; ++i)
        {

        }
        */
    }

    // Complete the request
    Irp->IoStatus.Status = Status;
    Irp->IoStatus.Information = BytesIO;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return Status;
}

と、以下のUserMode.cpp

constexpr auto IO_INCREMENT_VALUE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0001, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);
constexpr auto IO_RECEIVE_RANDOM_BUFFER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0002, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);


int main()
{
    //Find our Driver we want to Communicate with
    //https://docs.microsoft.com/de-de/windows/win32/api/fileapi/nf-fileapi-createfilea

    const LPCSTR                lpFileName = R"(\\.\test)"; //Equals the Name we specified at DriverEntry
    const DWORD                 dwDesiredAccess = GENERIC_ALL;
    const DWORD                 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    const LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr;
    const DWORD                 dwCreationDisposition = OPEN_EXISTING;
    const DWORD                 dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
    const HANDLE                hTemplateFile = nullptr;


    const HANDLE driver = CreateFile(
        lpFileName,
        dwDesiredAccess,
        dwShareMode,
        lpSecurityAttributes,
        dwCreationDisposition,
        dwFlagsAndAttributes,
        hTemplateFile
    );

    if (driver == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    //Example 1: Send an uint64_t and receive the value + 1.
    uint64_t in_example1 = 1;
    uint64_t out_example1 = 0;

    LPDWORD  lpBytesReturned = nullptr;

    DeviceIoControl(driver, IO_INCREMENT_VALUE, &in_example1,
        sizeof(in_example1), &out_example1, sizeof(out_example1), lpBytesReturned, nullptr);

    std::cout << out_example1 << "\n"; //should return 2


    //Example 2: Get a buffer with random values. Should be later the readMemory()
    const UINT_PTR bytes_to_be_read = 357096;

    //Any Buffer should be possible
    char* data = new char[bytes_to_be_read];
    uint64_t* data2 = new uint64_t[bytes_to_be_read];

    DeviceIoControl(driver, IO_RECEIVE_RANDOM_BUFFER, nullptr,
        0, data, bytes_to_be_read, lpBytesReturned, nullptr);

    //should return data or data2 with some random values

}

DeviceIoControl関数は、アプリケーションがデバイスドライバと直接通信できるデバイス入出力制御(IOCTL)インターフェイスを提供します。

しかし、どのように受信するのですか?

LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize。

Driver.cのI/O関数内でDeviceIoControlから送信する?

完全を期すため。

使用したリンク

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_irp

https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol

https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_io_stack_location

IO_STACK_LOCATION は、ただ単にアクセス権を提供するだけです。

パラメータ.DeviceIoControl

パラメータ.DeviceIoControl.OutputBufferLength

パラメータ.DeviceIoControl.InputBufferLength

パラメータ.DeviceIoControl.IoControlCode

パラメータ.DeviceIoControl.Type3InputBuffer

解決方法は?

Buffer I/O方式では、I/Oマネージャが入力バッファを非ページドプールに割り当て、そのメモリへのポインタを Irp->AssociatedIrp.SystemBuffer このときだけ IoContorl が開始されます。

その後、リクエストが完了すると、I/O Managerはその SystemBuffer に従って)バイト量をコピーします。 Irp->IoStatus.Information ) を出力バッファに格納する。

ということで、解決策を紹介します。

#define IO_INCREMENT_VALUE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0001, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
#define IO_RECEIVE_RANDOM_BUFFER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0002, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)


NTSTATUS IoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS Status = STATUS_INVALID_PARAMETER;
    ULONG BytesIO = 0;

    const IO_STACK_LOCATION stack = *IoGetCurrentIrpStackLocation(Irp);
    const ULONG ControlCode = stack.Parameters.DeviceIoControl.IoControlCode;

    if (ControlCode == IO_INCREMENT_VALUE)
    {
        // Check input buffer size
        ULONG bytes = stack.Parameters.DeviceIoControl.InputBufferLength;
        if (bytes < sizeof(long long)) {
            // Error - should complete the request
            Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
            Irp->IoStatus.Information = 0;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);

            // Must return the same value as Irp.IoStatus.Status
            return STATUS_INVALID_BUFFER_SIZE;
        }

        long long* input = (long long*)Irp->AssociatedIrp.SystemBuffer;
        InterlockedAdd64(input, 1);

        // Same SystemBuffer is used for input and output so we just need
        // to complete the request with the appropriate bytes written.
        Status = STATUS_SUCCESS;
        BytesIO = sizeof(*input);
    }
    else if (ControlCode == IO_RECEIVE_RANDOM_BUFFER)
    {
        // Check input buffer size
        ULONG bytes = stack.Parameters.DeviceIoControl.InputBufferLength;
        if (bytes == 0) {
            // Error - should complete the request
            Irp->IoStatus.Status = STATUS_INVALID_BUFFER_SIZE;
            Irp->IoStatus.Information = 0;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);

            // Must return the same value as Irp.IoStatus.Status
            return STATUS_INVALID_BUFFER_SIZE;
        }

        PVOID buffer = Irp->AssociatedIrp.SystemBuffer;
        memset(buffer, 0, bytes);

        Status = STATUS_SUCCESS;
        BytesIO = bytes;
    }

    // Complete the request
    Irp->IoStatus.Status = Status;
    Irp->IoStatus.Information = BytesIO;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return Status;
}