Log in

View Full Version : DeviceIoControl problem


Foreigner
March 19th, 2006, 16:50
Hi All.
I'm trying to talk with a driver but I have a problem with DeviceIoControl function.
I can send data to driver but I can't receive anything from the driver. The funny things are:
- no errors are generated
- the lpBytesReturned shows me the driver returns something but the lpOutBuffer is always empty
Since of I don't receive errors I don't know where to look for a solution...I'm not using some strange code, the procedure are all standard. Did you ever had the same problem? Do you know why!?!

f.

Opcode
March 19th, 2006, 18:27
Hi...
The problem may be the device driver...
What driver are you using? Do you have the source?
If not, try to reverse the DriverDispatch functions or
see what is happening with SoftICE or WinDBG.

Regards,
Opcode

LLXX
March 19th, 2006, 22:04
Quote:
[Originally Posted by Foreigner] the lpBytesReturned shows me the driver returns something but the lpOutBuffer is always empty
Maybe the driver is just returning nulls?

As suggested above, you are strongly encouraged to examine the driver itself.

Foreigner
March 20th, 2006, 09:55
The code inside the driver seems to work. I'm using METHOD_BUFFERED to exchange data and the driver's skeleton is standard. This is the dispatch routine:
Code:

NTSTATUS DriverIoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS ntStatus = STATUS_NOT_SUPPORTED;
PIO_STACK_LOCATION pIoStackIrp = NULL;
unsigned int dwDataWritten = 0;

pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);
if (pIoStackIrp)
{
switch(pIoStackIrp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_EXAMPLE:
dwDataWritten = sizeof("example";
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,"example", dwDataWritten);
ntStatus = STATUS_SUCCESS;
break;
}
}
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = dwDataWritten;

IoCompleteRequest(Irp, IO_NO_INCREMENT);

return ntStatus;
}

Tracing the driver with a debugger I see the message passed to the driver that it's overwritten by the new message (string "example" so it should work. Or, am I missing something obvious?

Kayaker
March 20th, 2006, 16:56
I hate to ask the obvious, but what does your DeviceIoControl call look like, i.e. how do you declare and access lpOutBuffer?

Also take note that there are some restrictions in declaring the IOCTL call, the Function code must NOT be less than 2048 (0x800)

Code:

// Macro definition for defining IOCTL and FSCTL function control codes.
// Function codes 0-2047 are reserved for Microsoft Corporation, and
// 2048-4095 are reserved for customers.

//////////////////////////////////////////////////////////////////////

#ifndef CTL_CODE
#define CTL_CODE( DeviceType, Function, Method, Access ) ( \
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
)

//
// Define the method codes for how buffers are passed for I/O and FS controls
//

#define METHOD_BUFFERED 0
#define METHOD_IN_DIRECT 1
#define METHOD_OUT_DIRECT 2
#define METHOD_NEITHER 3

#define FILE_ANY_ACCESS 0
#define FILE_DEVICE_UNKNOWN 0x22

#endif // CTL_CODE

//////////////////////////////////////////////////////////////////////

#define IOCTL_Call_1 \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_Call_2 \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)


Kayaker

Foreigner
March 21st, 2006, 06:55
I feel stupid but here is my error:
#define METHOD_BUFFERED 0
#define METHOD_IN_DIRECT 1
I passed 1 instead of 0 thinking 1 stands for method_buffered. Thanks for your help and sorry

Kayaker
March 21st, 2006, 18:48
No problem, sh*t happens

If you're interested in the details between the different METHOD_ codes, dig up these 2 OSR articles:

Sharing Memory Between Drivers and Applications
Defining Custom Device I/O Control Codes

For most uses METHOD_BUFFERED is all you need, either the input or output buffers can be declared as NULL in the DeviceIoControl call if you don't need them. METHOD_NEITHER can be used if you use, well, neither.

You should however *always* validate your buffer sizes first in your IOCTL call. A convenient way to make things portable is to declare shared I/O structures in a common header file for both the user app and driver.

For example:
Code:

// Define incoming and outgoing buffers for METHOD_BUFFERED use

// struct format of data passed to driver
typedef struct _IO_IN_BUFFER {
ULONG dummy;
UCHAR string[0x32];
} IO_IN_BUFFER, *PIO_IN_BUFFER;

// struct format of data returned from driver
typedef struct _IO_OUT_BUFFER {
ULONG someotherdummy;
UCHAR someotherstring[0x64];
} IO_OUT_BUFFER, *PIO_OUT_BUFFER;


These buffers become the relevant lpInBuffer and lpOutBuffer in the DeviceIoControl call. Then in the driver you validate their lengths. You can then access each buffer independantly.

Code:

// ULONG inputBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
// ULONG outputBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;

// IO_IN_BUFFER pInputBuffer;
// IO_OUT_BUFFER pOutputBuffer;


case IOCTL_Call_1:

//////////////////////////////////////////////////////////////////
// Validate buffer sizes
//////////////////////////////////////////////////////////////////
if ( inputBufferLength < sizeof(IO_IN_BUFFER) ||
outputBufferLength < sizeof(IO_OUT_BUFFER)
)
{
IoStatus->Status = STATUS_INVALID_BUFFER_SIZE;
IoStatus->Information = 0;
return FALSE;
}

// Cast Irp->AssociatedIrp.SystemBuffer to our input structure
pInputBuffer = *(PIO_IN_BUFFER) Irp->AssociatedIrp.SystemBuffer;

// Cast Irp->AssociatedIrp.SystemBuffer to our output structure
pOutputBuffer = *(PIO_OUT_BUFFER) Irp->AssociatedIrp.SystemBuffer;

...


Kayaker

OHPen
March 22nd, 2006, 04:27
I can recommend you to take a look at www.rootkit.com.
There are greate sources about coding drivers.
Maybe you are interested in hooking some apis to you will find there a lot of stuff too

Driver coding brings up lot of fun, and bsods, heeh _

Regards,

PAPiLLiON