KMDF驱动框架及案例分析


***【在线视频教程】***

好文章,来自【福优学苑@音视频+流媒体】

KMDF驱动框架及案例分析

参考资料:

官方文档:https://docs.microsoft.com/zh-cn/windows-hardware/drivers/wdf/



Windows设备驱动程序WDF开发

作者:武安河





一个小案例


https://blog.csdn.net/sadamoo/article/details/9273591?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.control




KMDF是WDF的内核级部分,为了理清KMDF的结构,又觉得内核编程很复杂,HelloWorld类型的程序实在说明不了什么,修改一下《windows设备驱动WDF开发》的CharSample,查了WDK帮助文档加上注释以帮助自己理解KMDF的大致运作过程。



CharSample原本是应用层输入数字字符,驱动读取输入缓冲区返还相应的中文,自己修改为返还英文(调试过程出现过数据类型的错误,Char CHAR   int   INT   size_t   注意ANSI C的函数)


另外,KMDF的IO处理例程中Create Close Cleanup要自己处理,Read Write DeviceControl可由IO队列管理,所以自行添加一个Create例程


PFN_WDF_IO_QUEUE_IO_DEFAULT                 EvtIoDefault;
    PFN_WDF_IO_QUEUE_IO_READ                    EvtIoRead;
    PFN_WDF_IO_QUEUE_IO_WRITE                   EvtIoWrite;
    PFN_WDF_IO_QUEUE_IO_DEVICE_CONTROL          EvtIoDeviceControl;
    PFN_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL EvtIoInternalDeviceControl;
    PFN_WDF_IO_QUEUE_IO_STOP                    EvtIoStop;
    PFN_WDF_IO_QUEUE_IO_RESUME                  EvtIoResume;
PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE       EvtIoCanceledOnQueue;


系列三wdf:DeviceIoControl例程

//DeviceIoControl例程
VOID
CharSample_EvtIoDeviceControl(
IN WDFQUEUE   Queue,
IN WDFREQUEST Request,/// IRP + IO_Stack_Location
IN size_t     OutputBufferLength,
IN size_t     InputBufferLength,
IN ULONG       IoControlCode
)
{
NTSTATUS   status;
PVOID   buffer;
CHAR   n;
INT len;
PAGED_CODE();
switch (IoControlCode) {
case CharSample_IOCTL_800:
if (InputBufferLength == 0 || OutputBufferLength < 2)
{ //检查输入、输出参数有效性
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
}
else
{
//输入缓冲区地址可通过调用WdfRequestRetrieveInputBuffer函数获得
//输出缓冲区地址可通过调用WdfRequestRetrieveOutputBuffer函数获得
//获取输入缓冲区地址buffer
//要求1字节空间
status = WdfRequestRetrieveInputBuffer(Request, 1, &buffer, NULL);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
break;
}
//这里buffer表示输入缓冲区地址
//输入n=应用程序传给驱动程序的数字ASCII码
n = *(CHAR *)buffer;
// #if DEBUGGING
// _asm int 3
// #endif
if ((n >= '0') && (n <= '9'))
{ //若为数字,则处理
n -= '0'; //n=数字(0-9)
len = strlen(szEngNum[n]) + 1;
//获取输出缓冲区地址buffer
status = WdfRequestRetrieveOutputBuffer(Request, (size_t)len, &buffer, NULL);
if (!NT_SUCCESS(status)) {
WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
break;
}
//这里buffer表示输出缓冲区地址
//输出:E文数组szEngNum[]中取出对应的数字的中文码,拷贝到输出缓冲区
strncpy((PCHAR)buffer, szEngNum[n], len);
//完成I/O请求,驱动程序传给应用程序的数据长度为len
WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, len);
}
else //否则返回无效参数
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
}
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
WdfRequestCompleteWithInformation(Request, status, 0);
break;
}
return;
}




系列一Wdm:DeviceIoControl例程:

#pragma PAGEDCODE
NTSTATUS HelloDDKDeviceIOControl(IN PDEVICE_OBJECT pDevObj,
 IN PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
KdPrint(("Enter HelloDDKDeviceIOControl\n"));
//得到当前堆栈
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
//得到输入缓冲区大小
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
//得到IOCTL码
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
ULONG info = 0;
switch (code)
{// process request
case IOCTL_TEST1:
{
KdPrint(("IOCTL_TEST1\n"));
//缓冲区方式IOCTL
//显示输入缓冲区数据
 UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
for (ULONG i=0;i<cbin;i++)
{
KdPrint(("%X\n",InputBuffer[i]));
}
//操作输出缓冲区
UCHAR* OutputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
memset(OutputBuffer,0xAA,cbout);
//设置实际操作输出缓冲区长度
 info = cbout;
break;
}
case IOCTL_TEST2:
{
KdPrint(("IOCTL_TEST2\n"));
//缓冲区方式IOCTL
//显示输入缓冲区数据
//缓冲区方式IOCTL
//显示输入缓冲区数据
 UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
for (ULONG i=0;i<cbin;i++)
{
KdPrint(("%X\n",InputBuffer[i]));
}
//pIrp->MdlAddress为DeviceIoControl输出缓冲区地址相同
KdPrint(("User Address:0X%08X\n",MmGetMdlVirtualAddress(pIrp->MdlAddress)));
 UCHAR* OutputBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
 //InputBuffer被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间
 memset(OutputBuffer,0xAA,cbout);
//设置实际操作输出缓冲区长度
 info = cbout;
break;
}
case IOCTL_TEST3:
{
KdPrint(("IOCTL_TEST3\n"));
//缓冲区方式IOCTL
//缓冲区方式IOCTL
//显示输入缓冲区数据
 UCHAR* UserInputBuffer = (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
KdPrint(("UserInputBuffer:0X%0X\n",UserInputBuffer));
//得到用户模式地址
PVOID UserOutputBuffer = pIrp->UserBuffer;
KdPrint(("UserOutputBuffer:0X%0X\n",UserOutputBuffer));
__try
{
KdPrint(("Enter __try block\n"));
//判断指针是否可读
ProbeForRead(UserInputBuffer,cbin,4);
//显示输入缓冲区内容
for (ULONG i=0;i<cbin;i++)
{
KdPrint(("%X\n",UserInputBuffer[i]));
}
//判断指针是否可写
ProbeForWrite(UserOutputBuffer,cbout,4);
//操作输出缓冲区
memset(UserOutputBuffer,0xAA,cbout);
//由于在上面引发异常,所以以后语句不会被执行!
info = cbout;
KdPrint(("Leave __try block\n"));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("Catch the exception\n"));
KdPrint(("The program will keep going\n"));
status = STATUS_UNSUCCESSFUL;
}
 info = cbout;
break;
}
default:
status = STATUS_INVALID_VARIANT;
}
// 完成IRP
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = info;// bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave HelloDDKDeviceIOControl\n"));
return status;
}




WDM驱动程序框架分析

参考: Windows设备驱动程序WDF开发

作者:武安河




WDF驱动程序框架分析

参考:Windows设备驱动程序WDF开发

作者:武安河




typedef struct _WDF_FILEOBJECT_CONFIG {
    //
    // Size of this structure in bytes
    //
    ULONG Size;
    //
    // Event callback for create requests
    //
    PFN_WDF_DEVICE_FILE_CREATE  EvtDeviceFileCreate;
    //
    // Event callback for close requests
    //
    PFN_WDF_FILE_CLOSE   EvtFileClose;
    //
    // Event callback for cleanup requests
    //
    PFN_WDF_FILE_CLEANUP EvtFileCleanup;
    //
    // If WdfTrue, create/cleanup/close file object related requests will be
    //      sent down the stack.
    //
    // If WdfFalse, create/cleanup/close will be completed at this location in
    //      the device stack.
    //
    // If WdfDefault, behavior depends on device type
    //      FDO, PDO, Control:  use the WdfFalse behavior
    //      Filter:  use the WdfTrue behavior
    //
    WDF_TRI_STATE AutoForwardCleanupClose;
    //
    // Specify whether framework should create WDFFILEOBJECT and also
    // whether it can FsContexts fields in the WDM fileobject to store
    // WDFFILEOBJECT so that it can avoid table look up and improve perf.
    //
    WDF_FILEOBJECT_CLASS FileObjectClass;
} WDF_FILEOBJECT_CONFIG, *PWDF_FILEOBJECT_CONFIG;


WDF基本对象

参考:Windows设备驱动程序WDF开发

作者:武安河




WDFREQUEST

WdfRequestXXX(......)

WdfRequestRetrieveInputBuffer(.....): //retrieve

MDL: Memery Description List



IRP + IO_STACK_LOCATION



pIrp->status = XXX;

pIrp->Information = INtxxx;

IoCompleteRequest(pIrp);//完成IO请求



IoSetCancelRoutine(..., True/False);


创建IRP:

同步:IoBuildSynchronousFsdRequest(....)

异步:IoBuildAsynchronousFsdRequest(....)

手工:IoAllocateIrp(...)



////// 伪代码

IoCallDriver(pIrp, device_target);


完成例程: 回卷




ForwardAndWait(......)

/// KEvent,

/// IoSetCompletionRoutine(...)

/// IoCallDriver(...)

/// function.callback..(...setEvent)







WDFQUEUE


WDF_IO_QUEUE_CONFIG

WdfIoQueueXXX(...)





WDFTIMER




WDFDPC




WDFWORKITEM



WDFMEMORY



QueueSample









KMDF编程入门


RegSample


//打开驱动对象的注册表键

    WdfDriverOpenParametersRegistryKey

//关闭注册表键

WdfRegistryClose(...)


WdfRegistryQueryXXX(...)

WdfRequestRetrieveOutputBuffer


pReqContext = GetRequestContext(Request);

outBuf = WdfMemoryGetBuffer(pReqContext->OutputMemoryBuffer, &outBufLength);

WdfRequestRetrieveUnsafeUserOutputBuffer

WdfRequestProbeAndLockUserBufferForWrite


系列一nt驱动:设备读写方式: buffered, direct_io, neither


//

    // In order to support METHOD_NEITHER Device controls, or

    // NEITHER device I/O type, we need to register for the

    // EvtDeviceIoInProcessContext callback so that we can handle the request

    // in the calling threads context.

//

Pre/post: 

WdfDeviceInitSetIoInCallerContextCallback(DeviceInit,

                                    RegSample_EvtDeviceIoInCallerContext);


RegSample_EvtDeviceIoInCallerContext




Vs2015 : 解决“错误 D8016 “/ZI”和“/Gy-”命令行选项不兼容 ”问题



https://blog.csdn.net/lyj_viviani/article/details/51487877











KMDF驱动程序和应用程序之间的通信



IOSample

IdleSample

EventSample






官方文档解读

KMDF

官方文档:https://docs.microsoft.com/zh-cn/windows-hardware/drivers/wdf/



WDF_DRIVER_CONFIG

WDFDRIVER

WDF_DRIVER_CONFIG_INIT

WdfDriverCreate



UMDF

官方文档的参考:

https://docs.microsoft.com/zh-cn/windows-hardware/drivers/wdf/getting-started-with-umdf-version-2




User-Mode Driver Framework(用户模式驱动程序架构,简称UMDF),是美国微软公司所提出视窗驱动程序基础(Windows Driver Foundation)的一部分,运行于用户模式(user mode),仅能访问用户地址空间,是核心模式驱动程序框架(Kernel-Mode Driver Framework,KMDF)的子集合(subset),因此UMDF所提供的函数支持少于KMDF,两者使用相同的状态机器、使用相同的 I/O 模型。


UMDF提供即插即用(PNP)、电源管理(Power Manager)、异步输出输入等功能,可设置 I/O 队列,但他的限制在于不处理中断(Interrupt)、不运行 DMA(直接存储器访问,Direct Memory Access),且不能使用核心模式资源如:未标签页集区(NonPaged Pool),也不完全支持同步化领域(Synchronization scope),在技术上UMDF使用COM(Component Object Model)的动态链接程序库(DLL)为基底技术


COM架构

UMDF驱动程序是一个基于COM架构的动态链接档(DLL),但UMDF并不使用COM的动态时期运行函数(runtimelibrary),单仅是借用了COM的样式。

UMDF 调用 DllGetClassObject API获取一个指针(pointer),这个指针指向一个IClassFactory的接口,并且激活 CreateInstance 这个属于IClassFactory 接口的函数来产生一个驱动程序的存储器实体(instance)。DLL 照例提供一些函数可以让COM 使用IWDFDriver-based的对象:

DllCanUnloadNow

DllGetClassObject

DllRegisterServer

DllUnregisterServer


DllMain

UMDF 驱动程序是一个动态链接库(Dynamic Link Library),运行的时候如同一个进程内(in-process)的 COM server,其代码中包括了DllMain,这是著名的DLL档的进入点(entry point)。 


BOOL WINAPI DllMain(

       HINSTANCE ModuleHandle,

       DWORD Reason,

       PVOID /* Reserved */)

{  if (DLL_PROCESS_ATTACH == Reason)

    {    WPP_INIT_TRACING(MYDRIVER_TRACING_ID);

        g_ModuleHandle = ModuleHandle; 

    }  

else if (DLL_PROCESS_DETACH == Reason) {    

    WPP_CLEANUP();

  }  

    return TRUE;

};

UMDF接口

IWDFObject: 定义基本的 WDF 对象类型(WDF object)

IWDFDriver: 代表驱动程序对象(driver object)

IWDFDevice: 代表设备对象(device object)

IWDFIoQueue: 代表 I/O 要求的队列(IO Request Queue)

IWDFIoRequest: I/O 要求描述(IO Request Description)

IWDFIoTarget: 代表 I/O 要求的目标驱动程序(IO Target)

IWDFMemory: 提供访问存储器区域(Memory) [3]




UMDF 入门

 

本部分介绍用户模式驱动程序框架 (UMDF) ,并详细介绍了 UMDF 版本1和2之间的差异。 它还提供有关 UMDF 的高级体系结构信息。 使用此部分来确定 UMDF 驱动程序是否适合你的需求,并决定要使用哪个 UMDF 版本。

Windows 驱动程序框架 (WDF) 包含 UMDF,这是一个用于创建用户模式驱动程序的框架。 与内核模式驱动程序框架一样 (KMDF) ,UMDF 提供了一个来自 WDM 的抽象层,处理大部分即插即用 (PnP) 和电源管理功能,并允许该驱动程序选择启用特定功能和事件处理。

在 Windows 8.1 的版本中,有两个主要版本的 UMDF:版本1和2。 UMDF 版本 1.11 (一个点十一) 是最新版本的 UMDF 版本1,是 UMDF 2 出现之前的最终版本。 有关显示完整版本信息和操作系统相关性的表,请参阅 UMDF 版本历史记录。

使用 UMDF 版本1编写驱动程序需要使用 COM 编程模型来编写 c + + 代码。 虽然 UMDF 版本1基于与 KMDF 相同的概念驱动程序编程模型,但 UMDF 1 采用不同组件实现了模型、设备驱动程序接口 (DDIs) 和数据结构。

与此相反,从 UMDF 版本2开始,你可以使用 C 编程语言编写一个 UMDF 驱动程序,该驱动程序调用了可用于 KMDF 驱动程序的多种方法。 在 UMDF 版本2和 KMDF 之间共享的所有接口都具有相同的名称、参数和结构定义。 如果驱动程序只使用共享的功能,或在仅在一个框架中支持的调用周围使用条件宏,则可以编写一个可使用 UMDF 或 KMDF 编译的驱动程序。 有关详细信息,请参阅 如何从 KMDF 驱动程序生成 UMDF 驱动程序。

虽然 UMDF 2 与 KMDF 之间存在重大的通用性,但仍有一小部分功能只能在一个框架中使用。 有关详细信息,请参阅 将 UMDF 2 功能与 KMDF 进行比较。 有关所有 UMDF 2 和 KMDF 回调和方法的列表以及它们适用于) 哪些框架 (,请参阅 WDF 回调和方法摘要。 在少数情况下,方法的结构成员或参数仅适用于一个框架或另一个框架。 文档介绍了相应参考页上的这些差异。

您必须选择其中一个;不能编写从 UMDF 版本1和2调用方法的 UMDF 驱动程序。

UMDF 版本2驱动程序仅在 Windows 8.1 或更高版本上运行。 如果需要编写在早于 Windows 8.1 的操作系统上运行的 UMDF 驱动程序,则需要写入 UMDF 1.x 驱动程序。 你可以使用版本1.11 来构建在 Windows Vista 和更高版本上运行的驱动程序。 有关版本1的详细信息,请参阅 UMDF 1.X 设计指南。 本部分介绍了 UMDF 版本2。




驱动程序的安装及INF文件解析


参考资料:

竹林蹊径:深入浅出Windows驱动开发::

第12章:驱动程序安装入门

第13章:深入解析INF文件



设备标识: 设备实例ID

驱动的加载和安装

System\currentcontrolset\services\360AntiHijack\imagePath

-->设备栈


总线驱动-->PDO

pnp管理器,

注册表






好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***