龙马谷

 找回密码
 立即注册

QQ登录

只需一步,快速开始

龙马谷VIP会员办理客服QQ:82926983(如果临时会话没有收到回复,请先加QQ好友再发。)
1 [已完结] GG修改器新手入门与实战教程 31课 2 [已完结] GG修改器美化修改教程 6课 3 [已完结] GG修改器Lua脚本新手入门教程 12课
4 [已完结] 触动精灵脚本新手入门必学教程 22课 5 [已完结] 手游自动化脚本入门实战教程 9课 6 [已完结] C++射击游戏方框骨骼透视与自瞄教程 27课
7 [已完结] C++零基础UE4逆向开发FPS透视自瞄教程 29课 8 [已完结] C++零基础大漠模拟器手游自动化辅助教程 22课 9 [已完结] C++零基础开发DXF内存脚本辅助教程 32课
以下是天马阁VIP教程,本站与天马阁合作,赞助VIP可以获得天马阁对应VIP会员,名额有限! 点击进入天马阁论坛
1 [已完结] x64CE与x64dbg入门基础教程 7课 2 [已完结] x64汇编语言基础教程 16课 3 [已完结] x64辅助入门基础教程 9课
4 [已完结] C++x64内存辅助实战技术教程 149课 5 [已完结] C++x64内存检测与过检测技术教程 10课 6 [已完结] C+x64二叉树分析遍历与LUA自动登陆教程 19课
7 [已完结] C++BT功能原理与x64实战教程 29课 8 [已完结] C+FPS框透视与自瞄x64实现原理及防护思路
查看: 5878|回复: 0

驱动和应用层的三种通信方式

[复制链接]

17

主题

8

回帖

30

积分

编程入门

Rank: 1

龙马币
52
百鬼夜行天 | 显示全部楼层 |阅读模式

驱动程序和客户应用程序经常需要进行数据交换,但我们知道驱动程序和客户应用程序可能不在同一个地址空间,因此操作系统必须解决两者之间的数据交换。
驱动层和应用层通信,主要是靠DeviceIoControl函数,下面是该函数的原型:

  1. BOOL DeviceIoControl (
  2. HANDLE hDevice, // 设备句柄
  3. DWORD dwIoControlCode, // IOCTL请求操作代码
  4. LPVOID lpInBuffer, // 输入缓冲区地址
  5. DWORD nInBufferSize, // 输入缓冲区大小
  6. LPVOID lpOutBuffer, // 输出缓冲区地址
  7. DWORD nOutBufferSize, // 输出缓冲区大小
  8. LPDWORD lpBytesReturned, // 存放返回字节数的指针
  9. LPOVERLAPPED lpOverlapped // 用于同步操作的Overlapped结构体指针
  10. );
复制代码


dwIoControlCode
要进行操作的控制码。驱动程序可以通过CTL_CODE宏来组合定义一个控制码,并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层,irpStack->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。

IOCTL请求有四种缓冲策略,下面一一介绍。
1、 输入输出缓冲I/O(METHOD_BUFFERED)
2、 直接输入缓冲输出I/O(METHOD_IN_DIRECT)
3、 缓冲输入直接输出I/O(METHOD_OUT_DIRECT)
4、 上面三种方法都不是(METHOD_NEITHER)

为了对这些类型更详细的描述,请看msdn上的解释,我抄录如下:

"缓冲"方法(METHOD_BUFFERED)
备注:在下面的讨论中,"输入"表示数据从用户模式的应用程序到驱动程序,"输出"表示数据从驱动程序到应用程序。

对于读取请求,I/O 管理器分配一个与用户模式的缓冲区大小相同的系统缓冲区。IRP 中的 SystemBuffer 字段包含系统地址。UserBuffer 字段包含初始的用户缓冲区地址。当完成请求时,I/O 管理器将驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区。对于写入请求,会分配一个系统缓冲区并将 SystemBuffer 设置为地址。用户缓冲区的内容会被复制到系统缓冲区,但是不设置 UserBuffer。对于 IOCTL 请求,会分配一个容量大小足以包含输入缓冲区或输出缓冲区的系统缓冲区,并将 SystemBuffer 设置为分配的缓冲区地址。输入缓冲区中的数据复制到系统缓冲区。UserBuffer 字段设置为用户模式输出缓冲区地址。内核模式驱动程序应当只使用系统缓冲区,且不应使用 UserBuffer 中存储的地址。

对于 IOCTL,驱动程序应当从系统缓冲区获取输入并将输出写入到系统缓冲区。当完成请求时,I/O 系统将输出数据从系统缓冲区复制到用户缓冲区。

"直接"方法(METHOD_IN/OUT_DIRECT)
对于读取和写入请求,用户模式缓冲区会被锁定,并且会创建一个内存描述符列表 (MDL)。MDL 地址会存储在 IRP 的 MdlAddress 字段中。SystemBuffer 和 UserBuffer 均没有任何含义。但是,驱动程序不应当更改这些字段的值。

对于 IOCTL 请求,如果在 METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT 中同时有一个输出缓冲区,则分配一个系统缓冲区(SystemBuffer 又有了地址)并将输入数据复制到其中。如果有一个输出缓冲区,且它被锁定,则会创建 MDL 并设置 MdlAddress。UserBuffer 字段没有任何含义。

"两者都不"方法(METHOD_NEITHER)
对于读取和写入请求,UserBuffer 字段被设置为指向初始的用户缓冲区。不执行任何其他操作。SystemAddress 和 MdlAddress 没有任何含义。对于 IOCTL 请求,I/O 管理器将 UserBuffer 设置为初始的用户输出缓冲区,而且,它将当前 I/O 栈位置的 Parameters.DeviceIoControl.Type3InputBuffer 设置为用户输入缓冲区。利用该 I/O 方法,由驱动程序来确定如何处理缓冲区:分配系统缓冲区或创建 MDL。

通常,驱动程序在访问用户数据时不应当将 UserBuffer 字段用作地址,即使当用户缓冲区被锁定时也是如此。这是由于在调用驱动程序时,在系统中可能看不到调用用户的地址空间。(对于该规则的一个例外是,在最高层驱动程序将 IRP 向下传递到较低层的驱动程序之前,它可能需要使用 UserBuffer 来复制数据。)如果使用"直接"或"两者都不"方法,在创建 MDL 之后,驱动程序可以使用 MmGetSystemAddressForMdl 函数来获取有效的系统地址以访问用户缓冲区。

在驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表。
传输类型                               位置
METHOD_IN_DIRECT                irp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECT             irp->AssociatedIrp.SystemBuffer
METHOD_BUFFERED                 irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                   irpStack->Parameters.DeviceIoControl.Type3InputBuffer

在驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表。
传输类型                              位置
METHOD_IN_DIRECT                irp->MdlAddress
METHOD_OUT_DIRECT             irp->MdlAddress
METHOD_BUFFERED                 irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                    irp->UserBuffer

所以只要确定了传输方式后,就可以根据各自的位置来读取和写入数据,从而实现应用层和驱动的通信。
下面看驱动层对ioctl控制码的处理代码:

代码:
  1. //METHOD_OUT_DIREC方式
  2. NTSTATUS COMM_DirectOutIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
  3. {
  4.     NTSTATUS status = STATUS_UNSUCCESSFUL;
  5.     PVOID pInputBuffer, pOutputBuffer;
  6.   ULONG  outputLength, inputLength;

  7.     DbgPrint("COMM_DirectOutIo\r\n");

  8.     outputLength = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;
  9.     inputLength  = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;
  10.     pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
  11.     pOutputBuffer = NULL;

  12.     if(Irp->MdlAddress)
  13.         pOutputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);

  14.     if(pInputBuffer && pOutputBuffer)
  15.     {                                                         
  16.         DbgPrint("COMM_DirectOutIo UserModeMessage = '%s'", pInputBuffer);
  17.         RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);
  18.     *sizeofWrite = outputLength;
  19.         status = STATUS_SUCCESS;
  20.     }
  21.     return status;
  22. }

  23. // METHOD_IN_DIRECT
  24. NTSTATUS COMM_DirectInIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
  25. {
  26.     NTSTATUS status = STATUS_UNSUCCESSFUL;
  27.     PVOID pInputBuffer, pOutputBuffer;
  28.   ULONG  outputLength, inputLength;

  29.     DbgPrint("COMM_DirectInIo\r\n");

  30.     outputLength = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;
  31.     inputLength  = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;
  32.     pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
  33.     pOutputBuffer = NULL;

  34.     if(Irp->MdlAddress)
  35.         pOutputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);

  36.     if(pInputBuffer && pOutputBuffer)
  37.     {                                                         
  38.         DbgPrint("COMM_DirectInIo UserModeMessage = '%s'", pInputBuffer);
  39.         RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);
  40.         *sizeofWrite = outputLength;
  41.         status = STATUS_SUCCESS;
  42.     }
  43.     return status;
  44. }

  45. // METHOD_BUFFERED
  46. NTSTATUS COMM_BufferedIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
  47. {
  48.     NTSTATUS status = STATUS_UNSUCCESSFUL;
  49.     PVOID pInputBuffer, pOutputBuffer;
  50.   ULONG  outputLength, inputLength;

  51.     DbgPrint("COMM_BufferedIo\r\n");

  52.   outputLength = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;
  53.     inputLength  = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;
  54.     pInputBuffer = Irp->AssociatedIrp.SystemBuffer;
  55.     pOutputBuffer = Irp->AssociatedIrp.SystemBuffer;

  56.     if(pInputBuffer && pOutputBuffer)
  57.     {              
  58.     DbgPrint("COMM_BufferedIo UserModeMessage = '%s'", pInputBuffer);
  59.         RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);
  60.     *sizeofWrite = outputLength;
  61.         status = STATUS_SUCCESS;
  62.     }
  63.     return status;
  64. }

  65. // METHOD_NEITHER
  66. NTSTATUS COMM_NeitherIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
  67. {
  68.     NTSTATUS status = STATUS_UNSUCCESSFUL;
  69.     PVOID pInputBuffer, pOutputBuffer;
  70.   ULONG  outputLength, inputLength;

  71.     DbgPrint("COMM_NeitherIo\r\n");

  72.   outputLength  = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;
  73.     inputLength   = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;
  74.     pInputBuffer  = pIoStackIrp->Parameters.DeviceIoControl.Type3InputBuffer;
  75.     pOutputBuffer = Irp->UserBuffer;

  76.     if(pInputBuffer && pOutputBuffer)
  77.     {              
  78.     DbgPrint("COMM_NeitherIo UserModeMessage = '%s'", pInputBuffer);
  79.         RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);
  80.     *sizeofWrite = outputLength;
  81.         status = STATUS_SUCCESS;
  82.     }
  83.     return status;
  84. }
复制代码


代码比较简单,都是取得输入的数据,然后把数据直接拷贝到输出,传输给应用层。

应用层的代码:
  1. procedure TfrmMain.Send_Recv_Data(AInData: String; var AOutData:String;
  2.   IoctlCode: DWORD);
  3. var
  4.   dwReturn: DWORD;
  5.   inData:array[0..1023] of char;
  6.   outData:array[0..1023] of char;
  7. begin
  8.   StrPCopy(inData, AInData);
  9.   if m_hCommDevice <> 0 then
  10.   begin
  11.     DeviceIoControl(m_hCommDevice, IoctlCode, @inData,  Length(inData), @outData, Length(outData), dwReturn, nil);
  12.     AOutData := StrPas(@outData);
  13.   end;
  14. end;
复制代码


上面是进行发送和接受的过程。

需要通信,只要如下做:
代码:
  1. procedure TfrmMain. btnDirect_IN_IOClick (Sender: TObject);
  2. var
  3.   outData:String;
  4. begin
  5.   Send_Recv_Data(Trim(edtDirect_in_in.Text), outData, IOCTL_COMM_DIRECT_IN_IO);
  6.   edtDirect_in_out.Text := outData;
  7. end;
复制代码


这是 direct_in方式通信,其他通信方式类似,大家可以参考代码了,这里就不列举了


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

龙马谷| C/C++辅助教程| 安卓逆向安全| 论坛导航| 免责申明|Archiver|
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表龙马谷立场!
任何人不得以任何方式翻录、盗版或出售本站视频,一经发现我们将追究其相关责任!
我们一直在努力成为最好的编程论坛!
Copyright© 2018-2021 All Right Reserved.
在线客服
快速回复 返回顶部 返回列表