驱动是RING0的程序代码,我们Ring3代的程序如果想控制ring0级的功能,那么就需要学会ring0与ring3之间的通信。今天就开始学习驱动与应用程序之间的通信。我们需要在驱动产现一个加法运算,然后我们在应用程序将两个运算数传入到驱动层,由驱动层计算了结果返回给应用程。
我们向驱动传输命令需要用到:DeviceIoControl这个api,它是应用程的api,关于参数我不想过多解释,因为MSDN上很清楚。这儿给出一个实例代码:
int add(HANDLE hDevice, int a,int b) { int port[2]; int bufret; ULONG dwWrite; port[0]=a; port[1]=b; DeviceIoControl(hDevice, add_code , &port, 8, &bufret, 4, &dwWrite, NULL); return bufret; }
hDevice是我们找开驱动的符号联接的句柄,见代码:
HANDLE hDevice = CreateFile(L"\\\\.\\firstSysDevice", //\\??\\firstSysDevice GENERIC_READ | GENERIC_WRITE, 0, // share mode none NULL, // no security OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); // no template
一但应用层用了DeviceIoControl这个api,那么驱动就会收到IRP_MJ_DEVICE_CONTROL这个类型的IRP请求。 我们只需要在这个类型的请求的派遣函数中实现我们的加法运算就完成我们的功能了。关于它的更多说明请参看:http://blog.youkuaiyun.com/caperingrabbit/article/details/5134051
代码如下:
//派遣函数 #pragma PAGECODE NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp ) { ULONG info; NTSTATUS status = STATUS_SUCCESS; //得到当前栈指针 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp); ULONG mf=stack->MajorFunction;//区分IRP switch (mf) { case IRP_MJ_DEVICE_CONTROL: { KdPrint(("Enter myDriver_DeviceIOControl\n")); //得到输入缓冲区大小 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 1111111111111111111\n")); //缓冲区方式IOCTL //获取缓冲区数据 a,b int * InputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer; __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; __asm { mov eax,a mov ebx,OutputBuffer mov [ebx],eax //bufferet=a+b } KdPrint(("a+b=%d \n",a)); //设置实际操作输出缓冲区长度 info = 4; 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;//设置操作的字节数为4,返回字节数 pIrp->IoStatus.Status=STATUS_SUCCESS;//返回成功 IoCompleteRequest(pIrp,IO_NO_INCREMENT);//指示完成此IRP KdPrint(("离开派遣函数\n"));//调试信息 return STATUS_SUCCESS; //返回成功 }
本节是用缓冲区的方式通信的,下一节我们将介绍直接操作方式通信。
好了,介绍得差不多了,下载完整源码运行看效果。