内核模式驱动框架
内核模式驱动程序KMD,文件后缀.sys。
KMD通过接收和处理I/O请求包(IRP)的数据块与I/O管理器进行交互。用户模式应用程序通过windows api的调用与硬件设备进行通讯。为了向驱动程序传递信息,I/O管理器把IRP的地址传递给KMD,IRP将在用户模式和内核模式之间传递命令和数据。
NT驱动框架
NT式驱动程序模型是一种比较老式的驱动程序模型,但适用于现有的Windows系统。而对于WDM式驱动来说,它支持即插即用功能要导入的头文件为wdm.h。这是Windows2000后加入的新的驱动模型,比NT式驱动更加复杂一些,完成一个设备操作,至少要两个驱动设备共同完成,分别是物理设备对象(PDO)和功能设备对象(FDO),FDO会附加在PDO上。这里只讲解NT框架。
C代码及注释:
#include <ntddk.h> //标准驱动头文件
//设备名称 比如C盘对应的设备名:\Device\HarddiskVolume3
#define DEVICE_NAME L"\\device\\ntmodeldrv"
//用户可见驱动名称 类似C盘
#define LINK_NAME L"\\dosdevices\\ntmodeldrv"
#define IOCTRL_BASE 0x800 // 0x000-0x7FF被微软占用
#define MYIOCTRL_CODE(i) \
CTL_CODE(FILE_DEVICE_UNKNOWN, IOCTRL_BASE+i, METHOD_BUFFERED,FILE_ANY_ACCESS)
#define CTL_HELLO MYIOCTRL_CODE(0)
#define CTL_PRINT MYIOCTRL_CODE(1)
#define CTL_BYE MYIOCTRL_CODE(2)
NTSTATUS DispatchCommon(PDEVICE_OBJECT pObject, PIRP pIrp) //释放IRP
{
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0; //返回R3,不再往下发,过滤驱动会继续下发
IoCompleteRequest(pIrp, IO_NO_INCREMENT); //终止IRP
return STATUS_SUCCESS; //返回IO管理器
}
NTSTATUS DispatchCreate(PDEVICE_OBJECT pObject, PIRP pIrp) //打开文件
{
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchRead(PDEVICE_OBJECT pObject, PIRP pIrp) //读
{
PVOID pReadBuffer = NULL; //要读的buffer地址
ULONG uReadLength = 0; //要读的buffer长度
PIO_STACK_LOCATION pStack = NULL;
ULONG uMin = 0;
ULONG uHelloStr = 0;
uHelloStr = (wcslen(L"hello world") + 1) * sizeof(WCHAR); //* sizeof(WCHAR)等价于x2
//第一步,拿到缓存的地址和长度(irp分头和栈)
//从头部拿缓存地址
pReadBuffer = pIrp->AssociatedIrp.SystemBuffer; //SystemBuffer(buffered io),MdlAddress(direct io),UserBuffer(neither io)
//从栈上拿缓存长度
pStack = IoGetCurrentIrpStackLocation(pIrp);
uReadLength = pStack->Parameters.Read.Length; //Length为应用层缓冲区长度
//第二步:读,写等操作
uMin = uReadLength>uHelloStr ? uHelloStr : uReadLength; //传最小的值(安全考虑)字符串的话字符串长度-1
RtlCopyMemory(pReadBuffer, L"hello world", uMin); //内核中拷贝内存函数:RtlCopyMemory
//第三步,完成IRP
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = uMin; //实际读的长度
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchWrite(PDEVICE_OBJECT pObject, PIRP pIrp)
{
PVOID pWriteBuff = NULL;
ULONG uWriteLength = 0;
PIO_STACK_LOCATION pStack = NULL;
PVOID pBuffer = NULL;
pWriteBuff = pIrp->AssociatedIrp.SystemBuffer;
pStack = IoGetCurrentIrpStackLocation(pIrp);
uWriteLength = pStack->Parameters.Write.Length;
//分配内存,需要指定分页内存还是非分页内存,非分页内存级别要求高,TSET给内存打标签(4字节),低位优先
pBuffer = ExAllocatePoolWithTag(PagedPool, uWriteLength, 'TSET');
if (pBuffer == NULL)
{
pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_INSUFFICIENT_RESOURCES;
}
memset(pBuffer, 0, uWriteLength);
RtlCopyMemory(pBuffer, pWriteBuff, uWriteLength);
ExFreePool(pBuffer); //释放
pBuffer = NULL;
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = uWriteLength;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchIoctrl(PDEVICE_OBJECT pObject, PIRP pIrp)
{
ULONG uIoctrlCode = 0;
PVOID pInputBuff = NULL;
PVOID pOutputBuff = NULL;
ULONG uInputLength = 0;
ULONG uOutputLength = 0;
PIO_STACK_LOCATION pStack = NULL;
pInputBuff = pOutputBuff = pIrp->AssociatedIrp.SystemBuffer;
pStack = IoGetCurrentIrpStackLocation(pIrp);
uInputLength = pStack->Parameters.DeviceIoControl.InputBufferLength;
uOutputLength = pStack->Parameters.DeviceIoControl.OutputBufferLength;
uIoctrlCode = pStack->Parameters.DeviceIoControl.IoControlCode;
switch (uIoctrlCode)
{
case CTL_HELLO:
DbgPrint("Hello iocontrol\n");
break;
case CTL_PRINT:
DbgPrint("%ws\n", pInputBuff);
//*(DWORD *)pOutputBuff =2;
break;
case CTL_BYE:
DbgPrint("Goodbye iocontrol\n");
break;
default:
DbgPrint("Unknown iocontrol\n");
}
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;//sizeof(DWORD);
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchClean(PDEVICE_OBJECT pObject, PIRP pIrp)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchClose(PDEVICE_OBJECT pObject, PIRP pIrp)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING uLinkName = { 0 };
RtlInitUnicodeString(&uLinkName, LINK_NAME);
IoDeleteSymbolicLink(&uLinkName);
IoDeleteDevice(pDriverObject->DeviceObject);
DbgPrint("Driver unloaded\n");
}
NTSTATUS DriverEntry //程序入口
(
PDRIVER_OBJECT pDriverObject,
PUNICODE_STRING pRegPath //pRegPath:注册表路径
)
{
UNICODE_STRING uDeviceName = { 0 }; //UNICODE_STRING内核中表示字符串的方法
UNICODE_STRING uLinkName = { 0 };
NTSTATUS ntStatus = 0; //驱动中返回值0代表成功
PDEVICE_OBJECT pDeviceObject = NULL;
ULONG i = 0;
DbgPrint("Driver load begin\n");
RtlInitUnicodeString(&uDeviceName, DEVICE_NAME); //将设备名的宏定义转化为UNICODE_STRING类型
RtlInitUnicodeString(&uLinkName, LINK_NAME);
//创建设备对象
ntStatus = IoCreateDevice //IoCreateDevice定义在wdm.h中
(
pDriverObject,
0, //DeviceExtensionSize设备扩展长度
&uDeviceName,
FILE_DEVICE_UNKNOWN, //DeviceType设备类型
0, //DeviceCharacteristics设备特征
FALSE, //Exclusive驱动对象是否独占,为了安全设为TRUE
&pDeviceObject //传指针(指针的指针)
); //返回&pDeviceObject
if (!NT_SUCCESS(ntStatus)) //宏定义:#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
{
DbgPrint("IoCreateDevice failed:%x", ntStatus);
return ntStatus;
}
//DO_BUFFERED_IO规定R3和R0之间read和write通信的方式:
//1,buffered io 缓存会拷贝一次,安全,但是效率低
//2,direct io 先映射到物理地址,然后R0和R3共用,效率高
//3,neither io R3直接传到R0
//DO_DEVICE_INITIALIZING 防止初始化之前发送IO请求
pDeviceObject->Flags |= DO_BUFFERED_IO;
//创建符号链接
ntStatus = IoCreateSymbolicLink(&uLinkName, &uDeviceName);
if (!NT_SUCCESS(ntStatus))
{
DbgPrint("IoCreateSymbolicLink failed:%x\n", ntStatus);
IoDeleteDevice(pDeviceObject); //如果创建失败,删除之前创建的设备对象
return ntStatus;
}
for (i = 0; i<IRP_MJ_MAXIMUM_FUNCTION + 1; i++) //初始化分发函数
{
pDriverObject->MajorFunction[i] = DispatchCommon;
}
//初始化重要的分发函数
pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; //打开文件
pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead; //读,数据从R0->R3
pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWrite; //写,数据从R3->R0
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctrl; //关键函数,任何功能,可以实现所有功能
pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = DispatchClean;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
pDriverObject->DriverUnload = DriverUnload; //卸载驱动
DbgPrint("Driver load ok!\n");
return STATUS_SUCCESS;
}
安装代码:
#include <windows.h>
#include <winsvc.h>
#include <conio.h>
#include <stdio.h>
#include <winioctl.h>
#pragma warning(disable:4996)
#define DRIVER_NAME "ntmodeldrv"
#define DRIVER_PATH ".\\ntmodeldrv.sys" //要加载的驱动路径
#define IOCTRL_BASE 0x800
#define MYIOCTRL_CODE(i) \
CTL_CODE(FILE_DEVICE_UNKNOWN, IOCTRL_BASE+i, METHOD_BUFFERED,FILE_ANY_ACCESS)
#define CTL_HELLO MYIOCTRL_CODE(0)
#define CTL_PRINT MYIOCTRL_CODE(1)
#define CTL_BYE MYIOCTRL_CODE(2)
//装载NT驱动程序
BOOL LoadDriver(char* lpszDriverName, char* lpszDriverPath)
{
//char szDriverImagePath[256] = "D:\\DriverTest\\ntmodelDrv.sys";
char szDriverImagePath[256] = { 0 };
//得到完整的驱动路径
GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL);
BOOL bRet = FALSE;
SC_HANDLE hServiceMgr = NULL;//SCM管理器的句柄
SC_HANDLE hServiceDDK = NULL;//NT驱动程序的服务句柄
//打开服务控制管理器
hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hServiceMgr == NULL)
{
//OpenSCManager失败
printf("OpenSCManager() Failed %d ! \n", GetLastError());
bRet = FALSE;
goto BeforeLeave;
}
else
{
////OpenSCManager成功
printf("OpenSCManager() ok ! \n");
}
//创建驱动所对应的服务
hServiceDDK = CreateService(hServiceMgr,
lpszDriverName, //驱动程序的在注册表中的名字
lpszDriverName, // 注册表驱动程序的 DisplayName 值
SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限
SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序
SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值(3) 决定启动顺序
SERVICE_ERROR_IGNORE, //注册表驱动程序的 ErrorControl 值
szDriverImagePath, // 注册表驱动程序的 ImagePath 值 errcode:2
NULL, //GroupOrder 在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GroupOrderList处决定启动顺序
NULL,
NULL,
NULL,
NULL);
DWORD dwRtn;
//判断服务是否失败
if (hServiceDDK == NULL)
{
dwRtn = GetLastError();
if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS)
{
//由于其他原因创建服务失败
printf("CrateService() Failed %d ! \n", dwRtn);
bRet = FALSE;
goto BeforeLeave;
}
else
{
//服务创建失败,是由于服务已经创立过
printf("CrateService() Failed Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n");
}
// 驱动程序已经加载,只需要打开
hServiceDDK = OpenService(hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS);
if (hServiceDDK == NULL)
{
//如果打开服务也失败,则意味错误
dwRtn = GetLastError();
printf("OpenService() Failed %d ! \n", dwRtn);
bRet = FALSE;
goto BeforeLeave;
}
else
{
printf("OpenService() ok ! \n");
}
}
else
{
printf("CrateService() ok ! \n");
}
//开启此项服务
bRet = StartService(hServiceDDK, NULL, NULL);
if (!bRet)
{
DWORD dwRtn = GetLastError();
if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING)
{
printf("StartService() Failed %d ! \n", dwRtn);
bRet = FALSE;
goto BeforeLeave;
}
else
{
if (dwRtn == ERROR_IO_PENDING)
{
//设备被挂住
printf("StartService() Failed ERROR_IO_PENDING ! \n");
bRet = FALSE;
goto BeforeLeave;
}
else
{
//服务已经开启
printf("StartService() Failed ERROR_SERVICE_ALREADY_RUNNING ! \n");
bRet = TRUE;
goto BeforeLeave;
}
}
}
bRet = TRUE;
//离开前关闭句柄
BeforeLeave:
if (hServiceDDK)
{
CloseServiceHandle(hServiceDDK);
}
if (hServiceMgr)
{
CloseServiceHandle(hServiceMgr);
}
return bRet;
}
//卸载驱动程序
BOOL UnloadDriver(char * szSvrName)
{
BOOL bRet = FALSE;
SC_HANDLE hServiceMgr = NULL;//SCM管理器的句柄
SC_HANDLE hServiceDDK = NULL;//NT驱动程序的服务句柄
SERVICE_STATUS SvrSta;
//打开SCM管理器
hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (hServiceMgr == NULL)
{
//打开SCM管理器失败
printf("OpenSCManager() Failed %d ! \n", GetLastError());
bRet = FALSE;
goto BeforeLeave;
}
else
{
//打开SCM管理器失败成功
printf("OpenSCManager() ok ! \n");
}
//打开驱动所对应的服务
hServiceDDK = OpenService(hServiceMgr, szSvrName, SERVICE_ALL_ACCESS);
if (hServiceDDK == NULL)
{
//打开驱动所对应的服务失败
printf("OpenService() Failed %d ! \n", GetLastError());
bRet = FALSE;
goto BeforeLeave;
}
else
{
printf("OpenService() ok ! \n");
}
//停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。
if (!ControlService(hServiceDDK, SERVICE_CONTROL_STOP, &SvrSta))
{
printf("ControlService() Failed %d !\n", GetLastError());
}
else
{
//打开驱动所对应的失败
printf("ControlService() ok !\n");
}
//动态卸载驱动程序。
if (!DeleteService(hServiceDDK))
{
//卸载失败
printf("DeleteSrevice() Failed %d !\n", GetLastError());
}
else
{
//卸载成功
printf("DelServer:deleteSrevice() ok !\n");
}
bRet = TRUE;
BeforeLeave:
//离开前关闭打开的句柄
if (hServiceDDK)
{
CloseServiceHandle(hServiceDDK);
}
if (hServiceMgr)
{
CloseServiceHandle(hServiceMgr);
}
return bRet;
}
void TestDriver()
{
//测试驱动程序
HANDLE hDevice = CreateFile("\\\\.\\ntmodeldrv", //打开驱动链接,对应R0的DispatchCreate
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hDevice != INVALID_HANDLE_VALUE)
{
printf("Create Device ok ! \n");
}
else
{
printf("Create Device Failed %d ! \n", GetLastError());
return;
}
CHAR bufRead[1024] = { 0 };
WCHAR bufWrite[1024] = L"Hello, world";
DWORD dwRead = 0;
DWORD dwWrite = 0;
ReadFile(hDevice, bufRead, 1024, &dwRead, NULL); //对应R0的DispatchRead
printf("Read done!:%ws\n", bufRead);
printf("Please press any key to write\n");
getch();
WriteFile(hDevice, bufWrite, (wcslen(bufWrite) + 1) * sizeof(WCHAR), &dwWrite, NULL); //对应R0的DispatchWrite
printf("Write done!\n");
printf("Please press any key to deviceiocontrol\n");
getch();
CHAR bufInput[1024] = "Hello, world";
CHAR bufOutput[1024] = { 0 };
DWORD dwRet = 0;
WCHAR bufFileInput[1024] = L"c:\\docs\\hi.txt";
printf("Please press any key to send PRINT\n");
getch();
DeviceIoControl(hDevice, //对应R0的DispatchIoctrl
CTL_PRINT,
bufFileInput,
sizeof(bufFileInput),
bufOutput,
sizeof(bufOutput),
&dwRet,
NULL);
printf("Please press any key to send HELLO\n");
getch();
DeviceIoControl(hDevice,
CTL_HELLO,
NULL,
0,
NULL,
0,
&dwRet,
NULL);
printf("Please press any key to send BYE\n");
getch();
DeviceIoControl(hDevice,
CTL_BYE,
NULL,
0,
NULL,
0,
&dwRet,
NULL);
printf("DeviceIoControl done!\n");
CloseHandle(hDevice);
}
int main(int argc, char* argv[])
{
//加载驱动
BOOL bRet = LoadDriver(DRIVER_NAME, DRIVER_PATH);
if (!bRet)
{
printf("LoadNTDriver error\n");
return 0;
}
//加载成功
printf("press any key to create device!\n");
getch();
TestDriver();
//这时候你可以通过注册表,或其他查看符号连接的软件验证。
printf("press any key to stop service!\n");
getch();
//卸载驱动
bRet = UnloadDriver(DRIVER_NAME);
if (!bRet)
{
printf("UnloadNTDriver error\n");
return 0;
}
return 0;
}