0

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 :)

Marcel Mar
  • 43
  • 6

1 Answers1

1

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;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanks for the advanced answer. FindProcessName is actually returning a DWORD. Since I have been looking around, I think I have already tried your Version and BOSD'd, but I'll give it a try tomorrow. – Marcel Mar May 09 '17 at 21:14
  • Then you are likely not processing the IRP buffer correctly. Please [edit] your question to show your actual driver code that handles `SENDPID`. – Remy Lebeau May 10 '17 at 16:52
  • @MarcelMar: you altered your app code to define `pID` as a `DWORD` (good), but you are passing it **by value** to the `lpInBuffer` parameter of `DeviceIoControl()`, which will not compile as that parameter is expecting a `void*` pointer instead. You need to pass `&pID` instead. Also, your app code is using `SENDPID`, but your driver code is looking for `PUSHPID` instead, which is expecting a pointer to an `IDCONTAINER` struct instead of a `DWORD`. What is `IDCONTAINER` defined as? And why are you using `PUSHPID` instead of `SENDPID`? – Remy Lebeau May 10 '17 at 18:15
  • oops, sorry, I was trying stuff out and messed up copy & pasting, this is the code that is up to date right now. – Marcel Mar May 10 '17 at 18:33
  • @MarcelMar: Now the passing of `pID` looks OK. But your use of `SystemBuffer` is wrong. As I stated in my answer, your driver code needs to use `pID = *(DWORD*)(IRP->AssociatedIrp.SystemBuffer);` instead. – Remy Lebeau May 10 '17 at 19:17
  • For some reason, even with your edited code, I can't seem to send the pID correctly. I think that `*(DWORD*)` is not correct, but I may be wrong. – Marcel Mar May 11 '17 at 17:56
  • Then you need to actually **debug your code** and find out what is really happening. StackOverflow is not a debugging service. – Remy Lebeau May 11 '17 at 17:57
  • Yeah, you are right about that. I'll let everyone know once I managed to find the problem. – Marcel Mar May 11 '17 at 17:58
  • Wow.. I have been using DbgPrint and tried to print the value of pID. It didn't print anything, so I assumed that the value is just invalid. To my surprise it's actually valid. Thanks for helping me :) – Marcel Mar May 12 '17 at 13:40