友链
非即插即用驱动
helloddk.h
// 保证头文件只被编译一次
// 因为在实际项目中,一个头文件可能会被另一个头文件包含
// 比如b.h中包含了a.h
// 然后在c.c中有如下代码:
/*
#include<a.h>
#include<b.h>
*/
// 这样一来,a.h就被包含了两次
// 而#pragma once可以保证a.h只被编译一次
// 从而提高编译效率
#pragma once// 下面的这段条件编译在C++项目中是非常常见的
// 它使得我们可以在C++项目中使用C中的头文件
#ifdef __cplusplus
extern "C"
{
#endif
#include <NTDDK.h>
#ifdef __cplusplus
}
#endif// 分页标记、非分页标记和初始化内存块
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
// INIT标志指明函数只是在加载的时候需要载入内存
// 在驱动程序成功加载之后,函数可以从内存中卸载掉
#define INITCODE code_seg("INIT")#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")#define arraysize(p) (sizeof(p)/sizeof((p)[0]))// 定义机构体 _DEVICE_EXTENSION ,并起别名
// 设备扩展结构体
// 该结构体广泛应用于驱动程序中
// 根据不同程序的需要,用于补充定义设备的相关信息
typedef struct _DEVICE_EXTENSION {PDEVICE_OBJECT pDevice;UNICODE_STRING ustrDeviceName; // 设备名称UNICODE_STRING ustrSymLinkName; // 符号链接名称
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;// 函数声明
// 这个IN关键字可能是用来说明参数是传入参数
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject);
VOID HelloDDKUnload(IN PDRIVER_OBJECT pDriverObject);
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp);
helloddk.cpp
#include "HelloDDK.h"/*
初始化驱动程序,定位和申请硬件资源,创建内核对象
参数列表pDriverObject:从IO管理器中传进来的驱动对象pRegistryPath:驱动程序在注册表中的路径
返回值:返回初始化驱动状态
*/
// 使用extern "C"对该函数进行修饰,这样在编译的时候会编译成_DriverEntry@8
// 如果没有这个修饰符的话,编译器会按照C++的符号名进行编译,链接的时候会报错
// 指明函数是加载到INIT内存区域中
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)
{NTSTATUS status;// 运行在内核中的程序是没有console的,所以只能使用KdPrint宏来输出调试信息// 这个宏只在调试版本中有作用(Free)// 在发行版本中不执行任何操作(Checked)KdPrint(("Enter DriverEntry\n"));// 注册其他驱动调用函数入口// 将我们自己定义的函数的地址传送给操作系统// 操作系统会在合适的时候调用这些函数// 通过下面的赋值操作// 在驱动卸载的时候,HelloDDKUnload函数会被调用// 在驱动程序创建、关闭和读写相关的IRP时,HelloDDKDispatchRoutine函数会被调用pDriverObject->DriverUnload = HelloDDKUnload;pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;// 创建驱动设备对象status = CreateDevice(pDriverObject);KdPrint(("DriverEntry end\n"));// 返回CreateDevice的结果return status;
}// 定义CreateDevice函数
#pragma INITCODE
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject)
{NTSTATUS status;PDEVICE_OBJECT pDevObj;PDEVICE_EXTENSION pDevExt; // 创建设备名称// 构造Unicode字符串用来存储此设备对象的名称UNICODE_STRING devName;RtlInitUnicodeString(&devName, L"\\Device\\MyDDKDevice");// 创建设备status = IoCreateDevice(pDriverObject,sizeof(DEVICE_EXTENSION),&(UNICODE_STRING)devName,FILE_DEVICE_UNKNOWN,0, TRUE,&pDevObj);if(!NT_SUCCESS(status))return status;// 表明该设备为BUFFERED_IO设备// 设备对内存的操作分为两种,一种是BUFFERED_IO,一种是DO_DIRECT_IO,后面解释pDevObj->Flags = pDevObj->Flags | DO_BUFFERED_IO;// 填写设备的扩展结构体pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;pDevExt->pDevice = pDevObj;pDevExt->ustrDeviceName = devName;// 创建符号链接// 设备名称只在内核态可见,用户程序是看不到的// 因此需要暴露出一个符号连接,该符号链接指向真正的设备名称UNICODE_STRING SymLinkName;RtlInitUnicodeString(&SymLinkName, L"\\??\\HelloDDK");pDevExt->ustrSymLinkName = SymLinkName;// 创建成功则返回,否则调用IoDeleteDevice删除设备status = IoCreateSymbolicLink(&SymLinkName, &devName);if(!NT_SUCCESS(status)){IoDeleteDevice(pDevObj);return status;}return STATUS_SUCCESS;
}// 定义驱动卸载函数
#pragma PAGEDCODE
VOID HelloDDKUnload(IN PDRIVER_OBJECT pDriverObject)
{PDEVICE_OBJECT pNextObj;KdPrint(("Enter DriverUnload\n"));// 从驱动对象中获得设备对象pNextObj = pDriverObject->DeviceObject;// 遍历设备对象while(pNextObj != NULL){PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;// 删除符号链接UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;// 删除设备对象的符号链接IoDeleteSymbolicLink(&pLinkName);pNextObj = pNextObj->NextDevice;IoDeleteDevice(pDevExt->pDevice);}
}// 定义默认派遣例程
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)
{KdPrint(("Enter HelloDDKDispatchRoutine\n"));NTSTATUS status = STATUS_SUCCESS;// 完成IRP// 关于IRP的介绍,在后面会介绍pIrp->IoStatus.Status = status;pIrp->IoStatus.Information = 0;IoCompleteRequest(pIrp, IO_NO_INCREMENT);KdPrint(("Leave HelloDDKDispatchRoutine\n"));return status;
}
source
TARGETNAME=HelloDDK
TARGETTYPE=DRIVER
TARGETPATH=OBJINCLUDES=$(BASEDIR)\inc;\$(BASEDIR)\inc\ddk;\SOURCES=helloddk.cpp\