驱动开发:通过ReadFile与内核层通信( 二 )

代码运行效果如下:

驱动开发:通过ReadFile与内核层通信

文章插图
通用框架有了,接下来就是让该驱动支持使用ReadWrite的方式实现通信,首先我们需要在DriverEntry处增加两个派遣处理函数的初始化 。
// 入口函数// By: LySharkNTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegistryPath){ DbgPrint("hello lyshark \n"); // 调用创建设备 CreateDriverObject(pDriver); // 初始化其他派遣 for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {DbgPrint("初始化派遣: %d \n", i);pDriver->MajorFunction[i] = DriverDefaultHandle; } pDriver->DriverUnload = UnDriver;                          // 卸载函数 pDriver->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;    // 创建派遣函数 pDriver->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;      // 关闭派遣函数 // 增加派遣处理 pDriver->MajorFunction[IRP_MJ_READ] = DispatchRead;        // 读取派遣函数 pDriver->MajorFunction[IRP_MJ_WRITE] = DispatchWrite;      // 写入派遣函数 DbgPrint("驱动加载完成..."); return STATUS_SUCCESS;}接着,我们需要分别实现这两个派遣处理函数,如下DispatchRead负责读取时触发,与之对应DispatchWrite负责写入触发 。
  • 引言:
  • 对于读取请求I/O管理器分配一个与用户模式的缓冲区大小相同的系统缓冲区SystemBuffer , 当完成请求时I/O管理器将驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区 。
  • 对于写入请求,会分配一个系统缓冲区并将SystemBuffer设置为地址,用户缓冲区的内容会被复制到系统缓冲区 , 但是不设置UserBuffer缓冲 。
通过IoGetCurrentIrpStackLocation(pIrp)接收读写请求长度,偏移等基本参数,AssociatedIrp.SystemBuffer则是读写缓冲区,IoStatus.Information是输出缓冲字节数,Parameters.Read.Length是读取写入的字节数 。
// 读取回调函数NTSTATUS DispatchRead(PDEVICE_OBJECT pDevObj, PIRP pIrp){ NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(pIrp); ULONG ulReadLength = Stack->Parameters.Read.Length; char szBuf[128] = "hello lyshark"; pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = ulReadLength; DbgPrint("读取长度:%d \n", ulReadLength); // 取出字符串前5个字节返回给R3层 memcpy(pIrp->AssociatedIrp.SystemBuffer, szBuf, ulReadLength); IoCompleteRequest(pIrp, IO_NO_INCREMENT); return Status;}// 接收传入回调函数// By: LySharkNTSTATUS DispatchWrite(struct _DEVICE_OBJECT *DeviceObject, struct _IRP *Irp){ NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); ULONG ulWriteLength = Stack->Parameters.Write.Length; PVOID ulWriteData = Irp->AssociatedIrp.SystemBuffer; // 输出传入字符串 DbgPrint("传入长度: %d 传入数据: %s \n", ulWriteLength, ulWriteData); IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status;}如上部分都是在讲解驱动层面的读写派遣 , 应用层还没有介绍,在应用层我们只需要调用ReadFile函数当调用该函数时驱动中会使用DispatchRead派遣例程来处理这个请求,同理调用WriteFile函数则触发的是DispatchWrite派遣例程 。
我们首先从内核中读出前五个字节并放入缓冲区内,输出该缓冲区内的数据,然后在调用写入,将hello lyshark写回到内核里里面,这段代码可以这样来写 。
#include <iostream>#include <Windows.h>#include <winioctl.h>int main(int argc, char *argv[]){  HANDLE hDevice = CreateFileA("\\\\.\\LySharkDriver", GENERIC_READ | GENERIC_WRITE, 0,    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);  if (hDevice == INVALID_HANDLE_VALUE)  {    CloseHandle(hDevice);    return 0;  }  // 从内核读取数据到本地  char buffer[128] = { 0 };  ULONG length;  // 读入到buffer长度为5  // By:lyshark.com  ReadFile(hDevice, buffer, 5, &length, 0);  for (int i = 0; i < (int)length; i++)  {    printf("读取字节: %c", buffer[i]);  }  // 写入数据到内核  char write_buffer[128] = "hello lyshark";  ULONG write_length;  WriteFile(hDevice, write_buffer, strlen(write_buffer), &write_length, 0);  system("pause");  CloseHandle(hDevice);  return 0;}

推荐阅读