常识:
IRP:I/O Request Package 即输入输出请求包
exe和sys通信时,exe会发出I/O请求。操作系统会将I/O请求转化为相应的IRP数据,
不同类型传递到不同的dispatch function
过程:
DeviceIoControl函数产生IRP_MJ_DEVICE_CONTROL 派遣例程
DeviceIoControl函数是用来向指定设备发送控制码,当指定的设备接收到DeviceIoControl函数发来的控制码后
会调用IRP_MJ_DEVICE_CONTROL对应的派遣例程,针对不同的控制码进行处理。
BOOL DeviceIoControl( HANDLE hDevice, // handle to device
DWORD dwIoControlCode, // operation control code
LPVOID lpInBuffer, // input data buffer
DWORD nInBufferSize, // size of input data buffer
LPVOID lpOutBuffer, // output data buffer
DWORD nOutBufferSize, // size of output data buffer
LPDWORD lpBytesReturned, // byte count
LPOVERLAPPED lpOverlapped // overlapped information);
直接内存模式IOCTL:
DDK中有CTL_CODE定义,应用层中没有CTL_CODE定义
即驱动程序中使用CTL_CODE需要只需要包含ntddk.h头文件,
而在应用程序中需要包含winioctl.h头文件
#define CTL_CODE(DeviceType, Function, Method, Access) 直接看MSDN
DeviceType应和IoCreateDevice的类型相匹配。形如:FILE_DEVICE_XX的宏
Function:这是驱动程序定义的IOCTL码,0x800到0xFFFF由程序猿自己定义
mothod参数:应该指定METHOD_IN_DIRECT
效果:同样可以避免驱动程序访问用户模式的内存地址。
例如:
#define add_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_IN_DIRECT,FILE_ANY_ACCESS)
Direct Mode:
exe应用层代码:
cpp
1 // test_exe.cpp : Defines the entry point for the console application. 2 // 3 4 #include "stdafx.h" 5 #include <windows.h> 6 #include "ctl_code.h" 7 8 int add(HANDLE hDevice, int a,int b) 9 { 10 11 int port[2]; 12 int bufret; 13 ULONG dwWrite; 14 port[0]=a; 15 port[1]=b; 16 17 DeviceIoControl(hDevice, add_code , &port, 8, &bufret, 4, &dwWrite, NULL); 18 return bufret; 19 20 } 21 22 int main(int argc, char* argv[]) 23 { 24 25 //CreateFile 打开设备 获取hDevice 26 HANDLE hDevice = 27 CreateFile("\\.\Templet", //\??\My_DriverLinkName 28 GENERIC_READ | GENERIC_WRITE, 29 0, // share mode none 30 NULL, // no security 31 OPEN_EXISTING, 32 FILE_ATTRIBUTE_NORMAL, 33 NULL ); // no template 34 35 printf("start "); 36 37 if (hDevice == INVALID_HANDLE_VALUE) 38 { 39 printf("获取驱动句柄失败: %s with Win32 error code: %d ","MyDriver", GetLastError() ); 40 getchar(); 41 return -1; 42 } 43 int a=11; 44 int b=33; 45 int r=add(hDevice,a,b); 46 printf("%d+%d=%d ",a,b,r); 47 getchar(); 48 CloseHandle(hDevice); 49 return 0; 50 }
.h
#define add_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_IN_DIRECT,FILE_ANY_ACCESS)
sys驱动层核心代码:
#pragma code_seg("PAGE")
NTSTATUS DispatchRoutine_DeviceIoControl(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp )
{ //
ULONG info;
//得到当前栈指针
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG mf=stack->MajorFunction;//区分IRP
switch (mf)
{
case IRP_MJ_DEVICE_CONTROL:
{
KdPrint(("Enter myDriver_DeviceIOControl
"));
NTSTATUS status = STATUS_SUCCESS;
//得到输入缓冲区大小
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
//得到IOCTL码
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
switch (code)
{
case add_code:
{
int a,b;
KdPrint(("add_code Direct Mode
"));
//缓冲区方式IOCTL
//获取缓冲区数据 a,b
int * InputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer;
//KdPrint(("%d,%d
",cbin,cbout));
_asm
{
mov eax,InputBuffer
mov ebx,[eax]
mov a,ebx
mov ebx,[eax+4]
mov b,ebx
}
KdPrint(("a=%d,b=%d
", a,b));
a=a+b;
//C、驱动层返回数据至用户层
//操作输出缓冲区
//int* OutputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer;
UCHAR* OutputBuffer =(UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
KdPrint(("MmGetSystemAddressForMdlSafe=%x",OutputBuffer));
_asm
{
mov eax,a
mov ebx,OutputBuffer
mov [ebx],eax //bufferet=a+b
}
KdPrint(("a+b=%d
",a));
//设置实际操作输出缓冲区长度
info = cbout;
break;
}
case sub_code:
{
break;
}
}//end code switch
break;
}
case IRP_MJ_CREATE:
{
break;
}
case IRP_MJ_CLOSE:
{
break;
}
case IRP_MJ_READ:
{
break;
}
}
//对相应的IPR进行处理
pIrp->IoStatus.Information = info;//设置操作的字节数
pIrp->IoStatus.Status=STATUS_SUCCESS;//返回成功
IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP
KdPrint(("离开派遣函数
"));//调试信息
return STATUS_SUCCESS; //返回成功
}
缓冲区内存模式IOCTL
强调下:在驱动中最好不要直接访问用户模式下的内存地址,缓冲区方式可以避免程序猿访问内存模式下的内存地址。
#pragma code_seg("PAGE")
NTSTATUS DispatchRoutine_DeviceIoControl(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp )
{ //
ULONG info;
//得到当前堆栈
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG mf=stack->MajorFunction;//区分IRP
switch (mf)
{
case IRP_MJ_DEVICE_CONTROL:
{
KdPrint(("Enter myDriver_DeviceIOControl
"));
NTSTATUS status = STATUS_SUCCESS;
//得到输入缓冲区大小
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
//得到IOCTL码
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
switch (code)
{
case add_code:
{
int a,b;
KdPrint(("add_code Buffered Mode
"));
//缓冲区方式IOCTL
//获取缓冲区数据 a,b
int * InputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer;
//KdPrint(("%d,%d
",cbin,cbout));
_asm
{
mov eax,InputBuffer
mov ebx,[eax]
mov a,ebx
mov ebx,[eax+4]
mov b,ebx
}
KdPrint(("a=%d,b=%d
", a,b));
a=a+b;
//C、驱动层返回数据至用户层
//Buffered Mode
UCHAR* OutputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
//Direct Mode
//UCHAR* OutputBuffer =(UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
KdPrint(("MmGetSystemAddressForMdlSafe=%x",OutputBuffer));
_asm
{
mov eax,a
mov ebx,OutputBuffer
mov [ebx],eax //bufferet=a+b
}
KdPrint(("a+b=%d
",a));
//设置实际操作输出缓冲区长度
info = cbout;
break;
}
case sub_code:
{
break;
}
default:
status = STATUS_INVALID_VARIANT; //其他的控制码
}//end code switch
}
case IRP_MJ_CREATE:
{
break;
}
case IRP_MJ_CLOSE:
{
break;
}
case IRP_MJ_READ:
{
break;
}
}
//对相应的IPR进行处理
pIrp->IoStatus.Information = info;//设置操作的字节数
pIrp->IoStatus.Status=STATUS_SUCCESS;//返回成功
IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP
KdPrint(("离开派遣函数
"));//调试信息
return STATUS_SUCCESS; //返回成功
}
两者不同的地方:
1) 定义CTL_CODE宏时的method参数,
direct mode(直接方式)是METHOD_IN_DIRECT,
buffered mode(缓冲区方式)是METHOD_BUFFERED.
当然,exe和sys都需要修改。
2)在操作输出缓冲区的时候,
//Buffered Mode UCHAR* OutputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer; //Direct Mode UCHAR* OutputBuffer =(UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
IDE:VS2010+WDK VC++6.0
测试环境:XP SP3
最后当然是保证能够不蓝屏,能够输出correct。
over~