在“进程内存管理器中”的一个Ring0,Ring3层通信问题,之前也见过这样的代码,这次拆分出来详细总结一下。

  先通过CreateFile函数得到设备句柄,CreateFile函数原型:

  

HANDLE CreateFile(
LPCTSTR lpFileName, // 文件名/设备路径 设备的名称
DWORD dwDesiredAccess, // 访问方式
DWORD dwShareMode, // 共享方式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全描述符指针
DWORD dwCreationDisposition, // 创建方式
DWORD dwFlagsAndAttributes, // 文件属性及标志
HANDLE hTemplateFile // 模板文件的句柄
);

打开:createFile

关闭:closehandle

与普通文件名有所不同,设备驱动的“文件名”(常称为“设备路径”)形式固定为“\\.\DeviceName”(注意写法为“\\\\.\\DeviceName”),DeviceName必须与设备驱动程序内定义的设备名称一致。

在“进程内存管理器”中:

Ring0层的kProcessMemory.h

#define DEVICE_NAME L"\\Device\\KProcessMemoryDeviceName"
#define LINK_NAME L"\\DosDevices\\KProcessMemoryLinkName"

Ring3层的ProcessMemoryManager.cpp

OpenDeviceObject(L"\\\\.\\KProcessMemoryLinkName");

BOOL OpenDeviceObject(LPCTSTR DeviceFullPathData)
{
m_DeviceHandle = CreateFile(DeviceFullPathData,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL); if (m_DeviceHandle == INVALID_HANDLE_VALUE)
{
return FALSE;
} return TRUE; }

可以看到在kProcessMemory.h中define了一个驱动设备名和一个符号链接名。

驱动设备名是调用IoCreateDevice时使用的,IoCreateDevice函数原型:

NTSTATUS IoCreateDevice(
_In_ PDRIVER_OBJECT DriverObject,
_In_ ULONG DeviceExtensionSize,
_In_opt_ PUNICODE_STRING DeviceName,
_In_ DEVICE_TYPE DeviceType,
_In_ ULONG DeviceCharacteristics,
_In_ BOOLEAN Exclusive,
_Out_ PDEVICE_OBJECT *DeviceObject
);

驱动程序中调用IoCreateDevice函数:

RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
Status = IoCreateDevice(DriverObject, , &DeviceName, FILE_DEVICE_UNKNOWN, , FALSE, &DeviceObject);

关于在Ring0层中要设置驱动设备名的同时还要设置符号链接名的原因,是因为只有符号链接名才可以被用户模式下的应用程序识别。

windows下的设备是以"\Device\[设备名]”形式命名的。例如磁盘分区的c盘,d盘的设备名称就是"\Device\HarddiskVolume1”,"\Device\HarddiskVolume2”, 当然也可以不指定设备名称。如果IoCreateDevice中没有指定设备名称,那么I/O管理器会自动分配一个数字作为设备的名称。例如"\Device\00000001"。\Device\[设备名],不容易记忆,通常符号链接可以理解为设备的别名,更重要的是设备名,只能被内核模式下的其他驱动所识别,而别名可以被用户模式下的应用程序识别,例如c盘,就是名为"c:"的符号链接,其真正的设备对象是"\Device\HarddiskVolume1”,所以在写驱动时候,一般我们创建符号链接,即使驱动中没有用到,这也算是一个好的习惯吧。

驱动中符号链接名是这样写的
L"\\??\\HelloDDK" --->\??\HelloDDK

或者
L"\\DosDevices\\HelloDDK"--->\DosDevices\HelloDDK
在应用程序中,符号链接名:
L"\\\\.\\HelloDDK"-->\\.\HelloDDK

DosDevices的符号链接名就是??, 所以"\\DosDevices\\XXXX"其实就是\\??\\XXXX

winobj和DeviceTree可以用来查看这些信息。

关于驱动设备名和符号链接名,可以参考这篇博客:

http://www.cnblogs.com/findumars/p/5636505.html

接着回到CreateFile函数上来,它的第二个参数,dwDesireAceess访问方式,一般设置为0或GENERIC_READ|GENERIC_WRITE,共享方式参数设置为FILE_SHARE_READ|FILE_SHARE_WRITE,创建方式参数设置为OPEN_EXISTING,其它参数一般设置为0或NULL。

Ring3层的CreateFile函数获取了设备句柄后,将使用DeviceIoControl函数向指定的设备驱动发送一个IO控制码,驱动程序通过这个控制码来完成特定的工作。该函数原型如下:

BOOL WINAPI DeviceIoControl(
_In_ HANDLE hDevice, //CreateFile函数打开的设备句柄
_In_ DWORD dwIoControlCode,//自定义的控制码
_In_opt_ LPVOID lpInBuffer, //输入缓冲区
_In_ DWORD nInBufferSize, //输入缓冲区的大小
_Out_opt_ LPVOID lpOutBuffer, //输出缓冲区
_In_ DWORD nOutBufferSize, //输出缓冲区的大小
_Out_opt_ LPDWORD lpBytesReturned, //实际返回的字节数,对应驱动程序中pIrp->IoStatus.Information。
_Inout_opt_ LPOVERLAPPED lpOverlapped //重叠操作结构指针。同步设为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作设计
);

  先介绍IO控制码,驱动程序可以通过CTL_CODE宏来组合定义一个控制码,并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层,IoStackLocation->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。发送不同的控制码,可以调用设备驱动程序的不同类型的功能。在头文件winioctl.h中,预定义的标准设备控制码,都以IOCTL或FSCTL开头。例如,IOCTL_DISK_GET_DRIVE_GEOMETRY是对物理驱动器取结构参数(介质类型、柱面数、每柱面磁道数、每磁道扇区数等)的控制码,FSCTL_LOCK_VOLUME是对逻辑驱动器的卷加锁的控制码。

lpInBuffer

由用户层发送的缓冲区数据。在“进程内存管理器“程序中,我们是通过进程ID来查询进程内存,故传入的是进程ID.在驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表。

传输类型 位置
METHOD_IN_DIRECT irp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECT irp->AssociatedIrp.SystemBuffer
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER irpStack->Parameters.DeviceIoControl.Type3InputBuffer

nInBufferSize

由用户层发送的缓冲区大小。在驱动层,这个值是IoStackLocation->Parameters.DeviceIoControl.InputBufferLength。

lpOutBuffer

由用户层指定,用于接收驱动层返回数据的缓冲区。在驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表。

传输类型 位置
METHOD_IN_DIRECT irp->MdlAddress
METHOD_OUT_DIRECT irp->MdlAddress
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER irp->UserBuffer

nOutBufferSize

由用户层指定,用于接收驱动层返回数据的缓冲区大小。在驱动层,这个值是IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength。

lpBytesReturned

由用户层指定,用于接收驱动层实际返回数据大小。在驱动层,这个值是irp->IoStatus->Information。

lpOverlapped

用于异步操作。

程序中的派遣函数:


#define CTL_QUERY_PROCESS_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_NEITHER,FILE_ANY_ACCESS)


#define CTL_READ_PROCESS_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x831,METHOD_NEITHER,FILE_ANY_ACCESS)


#define CTL_WRITE_PROCESS_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x832,METHOD_NEITHER,FILE_ANY_ACCESS)

......

RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject);

......

RtlInitUnicodeString(&LinkName, LINK_NAME);
Status = IoCreateSymbolicLink(&LinkName, &DeviceName);

......

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControlDispatch;

NTSTATUS DeviceControlDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{ NTSTATUS Status = STATUS_SUCCESS;
ULONG IoControlCode = ;
ULONG InputLength = ;
SIZE_T OutputLength = ;
PVOID InputData = NULL;
PVOID OutputData = NULL; PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
//BufferIO
//InputData = OutputData = Irp->AssociatedIrp.SystemBuffer;
/*
直接方式DO_DIRECT_IO / 非直接方式(缓冲方式)DO_BUFFERD_IO
1) 在buffered(AssociatedIrp.SystemBuffer)方式中,I/O管理器先创建一个与用户模式数据缓冲区大小相等的系统缓冲区。而你的驱动程序将使用这个系统缓冲区工作。
I/O管理器负责在系统缓冲区和用户模式缓冲区之间复制数据。
2) 在direct(MdlAddress)方式中,I/O管理器锁定了包含用户模式缓冲区的物理内存页,并创建一个称为MDL(内存描述符表)的辅助数据结构来描述锁定页。
因此你的驱动程序将使用MDL工作。
3) 在neither(UserBuffer)方式中,I/O管理器仅简单地把用户模式的虚拟地址传递给你。
而使用用户模式地址的驱动程序应十分小心。
*/
//Neither方式提高了通信效率,但是不够安全,在读写之前应使用ProbeForRead和ProbeForWrite函数探测地址是可读和可写
//详见eDiary笔记中“Driver——DeviceIoControl函数与IoControlCode”
InputData = IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer;//得到Ring3的输入缓冲区地址
OutputData = Irp->UserBuffer; //得到Ring3的输出缓冲区地址
InputLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
OutputLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength; switch (IoControlCode) //IO控制码
{
case CTL_QUERY_PROCESS_MEMORY:
{
if (!MmIsAddressValid(OutputData))
{
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = ;
break;
}
if (InputLength != sizeof(ULONG) || InputData == NULL)
{
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = ;
break;
}
__try
{ ProbeForWrite(OutputData, OutputLength, ); //检测内存是否可写,这个函数需要在用户模式下使用,详见MSDN:
//If Irp->RequestorMode = KernelMode, the Irp->AssociatedIrp.SystemBuffer and Irp->UserBuffer fields do not contain user-mode addresses,
//and a call to ProbeForWrite to probe a buffer pointed to by either field will raise an exception. Status = RtlQueryVirtualMemory(*(PULONG)InputData, OutputData, OutputLength);
Irp->IoStatus.Information = ;
Irp->IoStatus.Status = Status; }
__except (EXCEPTION_EXECUTE_HANDLER)
{
Irp->IoStatus.Information = ;
Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
}
break;
}
case CTL_READ_PROCESS_MEMORY:
{ if (!MmIsAddressValid(OutputData) || OutputLength>MAX_LENGTH)
{
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = ;
break;
} if (InputLength != sizeof(READ_OPERATION) || InputData == NULL)
{
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = ; break;
}
__try
{ ProbeForWrite(OutputData, OutputLength, );
Status = RtlReadVirtualMemory(InputData, OutputData, OutputLength);
Irp->IoStatus.Information = ;
Irp->IoStatus.Status = Status; }
__except (EXCEPTION_EXECUTE_HANDLER)
{
Irp->IoStatus.Information = ;
Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
} break;
}
case CTL_WRITE_PROCESS_MEMORY:
{ if (InputLength < sizeof(WRITE_OPERATION) || InputData == NULL)
{
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = ; break;
}
__try
{
Status = RtlWriteVirtualMemory(InputData);
Irp->IoStatus.Information = ;
Irp->IoStatus.Status = Status; }
__except (EXCEPTION_EXECUTE_HANDLER)
{
Irp->IoStatus.Information = ;
Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
}
break;
}
default:
{ Irp->IoStatus.Information = ;
Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
break;
}
} IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给IO管理器
return Status;
}

CreateFile DeviceIoControl dwIoControlCode——应用程序与驱动程序通信的更多相关文章

  1. 应用程序与驱动程序通信 DeviceIoControl

    之前写过一篇关于通过DeviceIoControl函数来使应用程序与驱动程序通信的博客,这次再通过这个完整的代码来简要疏通总结一下. 这种通信方式,就是驱动程序和应用程序自定义一种IO控制码,然后调用 ...

  2. DeviceIoControl 应用层如何和驱动层通信?

    调用的方法之一的DeviceIoControl 驱动层提供设备名 例如filedisk 在驱动层 首先先是注册列表 用winObj查看 filedisk的驱动对象 但是 这八个对象时怎么生成的呢? 我 ...

  3. 客户端程序通过TCP通信传送"小文件"到服务器

    客户端程序通过TCP通信传送"小文件"到服务器 [c#源码分享]客户端程序通过TCP通信传送"小文件"到服务器 源码  (不包含通信框架源码,通信框架源码请另行 ...

  4. 计算机网络|C语言Socket编程,实现两个程序间的通信

    C语言Socket编程,实现两个程序间的通信 server和client通信流程图 在mooc上找到的,使用Socket客户端client和服务端server通信的流程图

  5. 小程序接入云通信IM

    小程序接入云通信IM--插件 小程序微信后台搜索AI情报官组件即可获得小程序云通信IM的即时通信能力

  6. Siverlight网页应用程序中WCF通信注意事项

    最近刚刚接触WCF通信,功能就是客户端点击按钮后,服务器端返回一个随机数字.在VS2010中调试的时候,通信都正常,但发布到IIS7中就没反应了,经过几天的摸索,发现WCF的配置要注意以下两点: 1. ...

  7. [转] 微信小程序页面间通信的5种方式

    微信小程序页面间通的5种方式 PageModel(页面模型)对小程序而言是很重要的一个概念,从app.json中也可以看到,小程序就是由一个个页面组成的. 如上图,这是一个常见结构的小程序:首页是一个 ...

  8. electron调用C#应用程序实现串口通信

    最近转入零售行业开发了一系列产品,包含便利店收银软件.会员系统.供应链系统.为了追赶潮流,收银软件使用了electron平台开发,界面效果.开发效率确实不错:但是涉及到串口通讯时遇到了麻烦,elect ...

  9. 将hello程序作为驱动程序编译进系统内核

    0x00开始 恩,可能是我比较愚钝,一个内核编译搞了一天,各种问题,各种bug,几度无奈,也是因为我突发奇想,并没有按照原来的那种操作,我直接把helloworld程序放到内核模块中编译成了一个驱动程 ...

随机推荐

  1. 第八章 对称加密算法--AES

    注意:本节内容主要参考自<Java加密与解密的艺术(第2版)>第7章“初等加密算法--对称加密算法” 8.1.AES 特点: 密钥建立时间短.灵敏性好.内存需求低(不管怎样,反正就是好) ...

  2. python 获取字符串中所有数字

    s = "dede323frf54de23" l = len(s) numbers = [] i = 0 while i < l: num = '' symbol = s[i ...

  3. Spring boot 遇到了连接mysql的错误

    问题1: Establishing SSL connection without server's identity verification is not recommended. Accordin ...

  4. 【转】总结C++中取成员函数地址的几种方法

    转自:“http://www.cnblogs.com/nbsofer/p/get_member_function_address_cpp.html” 这里, 我整理了4种C++中取成员函数地址的方法, ...

  5. C语言专题-基本数据类和占位符

    C语言中常用的几种基本数据类型有 基本数据类型的长度 unsigned unsigned unsigned unsigned float没有unsigned double没有unsigned 占位符的 ...

  6. Codeforces 916C - Jamie and Interesting Graph

    916C - Jamie and Interesting Graph 思路:构造. 对于1到n最短路且素数,那么1到n之间连2 对于最小生成树,找一个稍微大点的素数(比1e5大)构造一个和为这个素数的 ...

  7. Codeforces C - Om Nom and Candies

    C - Om Nom and Candies 思路:贪心+思维(或者叫数学).假设最大值max(wr,wb)为wr,当c/wr小于√c时,可以枚举r糖的数量(从0到c/wr),更新答案,复杂度√c:否 ...

  8. 各个安卓版本 使用的 Linux Kernel Version

    Android Version |API Level |Linux Kernel in AOSP --------------------------------------------------- ...

  9. jquery ajax中 php前台后台文件中编辑都是uft-8,返回数据还是乱码

    jquery ajax中 前台后台文件中编辑都是uft-8,返回数据还是乱码 解决如下: 在后台处理文件里面需要再加编辑 header("Content-Type:text/html;cha ...

  10. 使用Python Django在Ubuntu下搭建数据库型网站

    最近想做一个数据库网站,我对Python很熟悉,也了解到Django很好用,于是说搞就搞. 首先,在快云上买了一个vps,一元试用一个月,Ubuntu系统. 1.安装Django apt-get up ...