DeviceIoControl方式 sys和exe通信

 
 
常识:
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\n");
36 
37     if (hDevice == INVALID_HANDLE_VALUE)
38     {
39         printf("获取驱动句柄失败: %s with Win32 error code: %d\n","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 \n",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\n"));
			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 \n"));
					//缓冲区方式IOCTL
					//获取缓冲区数据	a,b		
					int * InputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer;




					//KdPrint(("%d,%d\n",cbin,cbout));

					_asm
					{
						mov eax,InputBuffer
							mov ebx,[eax]
						mov a,ebx
							mov ebx,[eax+4]
						mov b,ebx
					}
					KdPrint(("a=%d,b=%d \n", 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 \n",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(("离开派遣函数\n"));//调试信息
	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\n"));
			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 \n"));
					//缓冲区方式IOCTL
					//获取缓冲区数据	a,b		
					int * InputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer;

					//KdPrint(("%d,%d\n",cbin,cbout));

					_asm
					{
						mov eax,InputBuffer
							mov ebx,[eax]
						mov a,ebx
							mov ebx,[eax+4]
						mov b,ebx
					}
					KdPrint(("a=%d,b=%d \n", 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 \n",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(("离开派遣函数\n"));//调试信息
	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~

 
posted @ 2013-09-20 14:32  JoyChou  阅读(2273)  评论(0编辑  收藏  举报