Search code examples
c++deviceiocontrol

DeviceIoControl with DWORD


My OS is Win7 64bit. I'm trying to pass the value of a variable, not the address of it (process id, DWORD) via DeviceIoControl to my driver. I have tried it several times, but only got bluescreens or the error code of 998.

On usermode side, I make sure that hDevice is valid, the CTL_CODE looks like this:

#define SENDPID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x808, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)

Then I'm doing this:

DWORD pID = FindProcessName("someprocess.exe");
if (!pID) return false;
if (!DeviceIoControl(hDevice, SENDPID, &pID, sizeof(pID), NULL, 0, &BytesIO, 0))
{
    cout << GetLastError() << endl;
    return false;
}

and on Kernelmode (DriverEntry):

NTSTATUS DriverEntry(PDRIVER_OBJECT Object, PUNICODE_STRING RegistryPath)
{
UNICODE_STRING dNUS = { 0 };
RtlInitUnicodeString(&dNUS, L"\\Device\\testdriver");

UNICODE_STRING dSLU = { 0 };
RtlInitUnicodeString(&dSLU, L"\\DosDevices\\testdriver");

IoCreateDevice(Object, 0, &dNUS, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObj);
IoCreateSymbolicLink(&dSLU, &dNUS);

Object->MajorFunction[IRP_MJ_CREATE] = CCreate;
Object->MajorFunction[IRP_MJ_CLOSE] = CClose;
Object->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IOCTL;
Object->DriverUnload = Unload;

return(STATUS_SUCCESS);
}

and my IOCTL function:

NTSTATUS IOCTL(PDEVICE_OBJECT Object, PIRP IRP)
{
PIO_STACK_LOCATION StackLocation = IoGetCurrentIrpStackLocation(IRP);
size_t size = 0;
DWORD pID = 0;

if (StackLocation->Parameters.DeviceIoControl.IoControlCode == SENDPID)
{
    pID = IRP->AssociatedIrp.SystemBuffer;
    size = sizeof(pID);
}

IRP->IoStatus.Status = STATUS_SUCCESS;
IRP->IoStatus.Information = size;
IofCompleteRequest(IRP, IO_NO_INCREMENT);
return(STATUS_SUCCESS);
}

If anyone could tell me what to change, please let me know :)


Solution

  • On the app side, why is FindProcessName() returning a DWORD* pointer instead of a DWORD value? Or, does it really return a DWORD value and you are just erroneously type-casting it to a DWORD* pointer?

    By defining pID as a DWORD* pointer, sizeof(pID) is the wrong value to pass to DeviceIoControl(). It is passing the size of the pointer itself (4 on 32bit, 8 on 64bit), not the size of the DWORD value being pointed at (sizeof(DWORD) is always 4).

    If pID is a real pointer to a DWORD in memory, you would need to use sizeof(*pID) instead of sizeof(pID). But what if FindProcessName() returns 0? Then you would end up passing a NULL pointer to DeviceIoControl(), even though you are telling DeviceIoControl() that you are passing it 4 bytes.

    You should change FindProcessName() to return a DWORD value (if it not already) instead of a DWORD* pointer, and then use the & operator to pass the address of that DWORD to DeviceIoControl(), eg:

    DWORD pID = FindProcessName("DayZ.exe");
    if (!DeviceIoControl(hDevice, SENDPID, &pID, sizeof(pID), NULL, 0, &BytesIO, 0))
    

    Then on the kernel side, just type-cast the SystemBuffer to access the value (after validating that the SystemBuffer contains at least 4 bytes, of course):

    NTSTATUS IOCTL(PDEVICE_OBJECT Object, PIRP IRP)
    {
        PIO_STACK_LOCATION StackLocation = IoGetCurrentIrpStackLocation(IRP);
        size_t size = 0;
        DWORD pID = 0;
    
        if ((StackLocation->Parameters.DeviceIoControl.IoControlCode == SENDPID) &&
            (StackLocation->Parameters.DeviceIoControl.InputBufferLength >= sizeof(pID)))
        {
            pID = *(DWORD*)(IRP->AssociatedIrp.SystemBuffer);
            size = sizeof(pID);
        }
    
        IRP->IoStatus.Status = STATUS_SUCCESS;
        IRP->IoStatus.Information = size;
        IofCompleteRequest(IRP, IO_NO_INCREMENT);
    
        return STATUS_SUCCESS;
    }