学习了驱动对象与设备对象的关系以及创建对象和符号连接与应用层通信的方式。
在驱动层的代码设置:
#include <ntifs.h>
#define DEVICE_NAME L"\\Device\\MyFirstDevice" //设备名称
#define SYM_NAME L"\\??\\MyFirstDevice" //符号链接
//分发函数
NTSTATUS MyCreate(PDEVICE_OBJECT pdevice, PIRP pirp)
{
UNREFERENCED_PARAMETER(pdevice);
NTSTATUS status = STATUS_SUCCESS;
DbgPrint("My Device has be opened!");
pirp->IoStatus.Status = status;
pirp->IoStatus.Information = 0;
IoCompleteRequest(pirp, IO_NO_INCREMENT);
return status;
}
NTSTATUS MyClearUp(PDEVICE_OBJECT pdevice, PIRP pirp)
{
UNREFERENCED_PARAMETER(pdevice);
NTSTATUS status = STATUS_SUCCESS;
DbgPrint("My Device MyClearUp!");
pirp->IoStatus.Status = status;
pirp->IoStatus.Information = 0;
IoCompleteRequest(pirp, IO_NO_INCREMENT);
return status;
}
NTSTATUS MyClose(PDEVICE_OBJECT pdevice, PIRP pirp)
{
UNREFERENCED_PARAMETER(pdevice);
NTSTATUS status = STATUS_SUCCESS;
DbgPrint("My Device MyClose!");
pirp->IoStatus.Status = status;
pirp->IoStatus.Information = 0;
IoCompleteRequest(pirp, IO_NO_INCREMENT);
return status;
}
//卸载函数
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
DbgPrint("basedriver 卸载驱动\n");
if (DriverObject->DeviceObject)
{
IoDeleteDevice(DriverObject->DeviceObject);
}
UNICODE_STRING symLink = RTL_CONSTANT_STRING(SYM_NAME);
IoDeleteSymbolicLink(&symLink);
}
//入口函数
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
UNREFERENCED_PARAMETER(RegistryPath);
//注册卸载函数
DriverObject->DriverUnload = DriverUnload;
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT pdevice = NULL; // 用来接收创建的设备对象
UNICODE_STRING devicename = { 0 };
RtlInitUnicodeString(&devicename, DEVICE_NAME);
//创建设备对象
status = IoCreateDevice(DriverObject,
0,
&devicename,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&pdevice);
if (!NT_SUCCESS(status))
{
KdPrint(("IoCreateDevice 虚拟设备打开失败 状态码 (0x%08X)\n",status));
DbgPrint("IoCreateDevice 虚拟设备打开失败 状态码 (0x%08X)\n", status);
}
//
//创建成功,创建符号链接
//
UNICODE_STRING symname = { 0 };
RtlInitUnicodeString(&symname, SYM_NAME);
status = IoCreateSymbolicLink(&symname, &devicename); // 符号链接名 设备名
if (!NT_SUCCESS(status))
{
KdPrint(("IoCreateSymbolicLink 符号链接创建失败 状态码 (0x%08X)", status));
DbgPrint("IoCreateSymbolicLink 符号链接创建失败 状态码 (0x%08X)", status);
}
//设置分发例程
DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MyClearUp;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyClose;
return 0;
}
在应用层的操作
(设备对象可以在内核中暴露出来给应用层,应用层可以像操作文件一样操作它):
#include <stdio.h>
#include <windows.h>
#include <winioctl.h>
#include <stdlib.h>
int main()
{
HANDLE hdevice = NULL;
hdevice = CreateFile(L"\\\\.\\MyFirstDevice", GENERIC_WRITE | GENERIC_READ, 0
, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hdevice != INVALID_HANDLE_VALUE)
{
printf("打开设备SUCCESS!\n");
}
else {
printf("打开设备失败!\n");
system("pause");
}
system("pause");
if (hdevice != nullptr)
{
CloseHandle(hdevice);
printf("关闭句柄!\n");
}
system("pause");
return 0;
}
注意,判断是否成功打开驱动对象句柄要通过这样判断:
hdevice != INVALID_HANDLE_VALUE
执行结果:
通过DeviceTree可以看到驱动对象和它支持的IRP请求:
通过WinObj可以看到我们设置的符号链接名:
小结:
1、设备对象在应用层可以像文件那样被操作。
2、驱动工具的使用,可以更直观的获取信息。
3、有个疑问,为什么分发函数只对应用程序的打开关闭有响应?这个通信并不是双向的,而是应用层一直在访问。