一、WDF驱动模型介绍:

WDF驱动模型是微软推出的驱动程序开发环境,是Vista及其以后OS的驱动模型。在visia之前win2000之后用的是WDM驱动模型。WDF是以WDM为基础进行了建模和封装,降低了开发难度。WDF将驱动程序与操作系统内核之间进行了分离,驱动程序与操作系统交互工作交给框架内封装的方法(函数)完成,这样驱动开发者只需专注处理硬件的行为即可。

二、WFP网络过滤平台介绍:

WFP网络过滤平台是一组 API 和系统服务,提供用于创建网络筛选应用程序的平台。 WFP API 允许开发人员编写与操作系统网络堆栈中多个层发生的数据包处理交互的代码。 可以在网络数据到达目标之前对其进行筛选和修改。WFP 旨在取代以前的数据包筛选技术,例如LSP、TDI(不能对包内容进行查看,只能对已知协议进行过滤)、NDIS Filter(需要与TDI配合才能查看进程信息)。

WFP有一个内置的过滤引擎(Filter Engine)提供支持。该框架包括用户层API和内核层API,应用层可以使用该框架完成简单过滤,涉及包内容修改等需要在内核完成。(例如开源项目windivert,本文代码主要借鉴该项目过滤思想)。

WFP在TCP/IP栈各个位置添加了垫片(Shim),使得整个网络数据从应用层生成到发送至网卡,或者从网卡接收数据到应用层收到数据,WFP都能够通过Shim拿到关于数据的信息以及相关数据。即在不同的分层(Layer)能够获取不同的数据那么,我们可以在感兴趣的位置,注册callout,进行过滤。

三、常见分层标识 

数据层:
FWPM_LAYER_STREAM_V4
FWPM_LAYER_DATAGRAM_DATA_V4
ALE层
FWPM_LAYER_ALE_AUTH_CONNECT_V4
FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4
传输层
FWPM_LAYER_INBOUND_TRANSPORT_V4
FWPM_LAYER_OUTBOUND_TRANSPORT_V4
网络层
FWPM_LAYER_INBOUND_IPPACKET_V4

FWPM_LAYER_OUTBOUND_IPPACKET_V4

四、示意图   

五、开发流程

主要针对网络层的子层FWPM_LAYER_INBOUND_IPPACKET_V4和FWPM_LAYER_OUTBOUND_IPPACKET_V4进行数据拦截、修改和注入。主要流程为:

  • WDF驱动模型初始化
  • 注册WFP子层
  • 打开过滤引擎
  • 注册呼出函数
  • 在呼出函数中对拦截到的数据进行解析,修改
  • 将修改后的数据重新注入到原始路径

下面是各模块主要代码:

1、WDF驱动模型初始化

WDF_DRIVER_CONFIG_INIT(&config,WDF_NO_EVENT_CALLBACK);config.DriverInitFlags |= WdfDriverInitNonPnpDriver;config.EvtDriverUnload = DrEnc_unload;// 创建驱动对象status = WdfDriverCreate(DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES,&config,&driver);if (!NT_SUCCESS(status)) {goto driver_entry_exit;}// 分配一个WDFDEVICE_INIT结构,驱动程序在创建一个新的控制设备对象时使用这个结构。device_init = WdfControlDeviceInitAllocate(driver,&SDDL_DEVOBJ_SYS_ALL_ADM_ALL);if (device_init == NULL){status = STATUS_INSUFFICIENT_RESOURCES;KdPrint(("[DrEnc] failed to allocate WDF control device init structure\n"));goto driver_entry_exit;}// 为指定的设备设置设备类型。WdfDeviceInitSetDeviceType(device_init, FILE_DEVICE_NETWORK);// 设置驱动程序如何访问包含在指定设备的读写请求中的数据缓冲区的方法或首选项。WdfDeviceIoDirect:直接I/O将用于访问数据缓冲区。WdfDeviceInitSetIoType(device_init, WdfDeviceIoDirect);// 将设备名称分配给设备的设备对象。status = WdfDeviceInitAssignName(device_init, &device_name);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create WDF device name\n"));WdfDeviceInitFree(device_init);goto driver_entry_exit;}WDF_OBJECT_ATTRIBUTES_INIT(&obj_attrs);// 创建一个框架设备对象。status = WdfDeviceCreate(&device_init, &obj_attrs, &device);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create WDF control device\n"));WdfDeviceInitFree(device_init);goto driver_entry_exit;}WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queue_config,WdfIoQueueDispatchParallel);queue_config.EvtIoRead = NULL;queue_config.EvtIoWrite = NULL;queue_config.EvtIoDeviceControl = DrEnc_ioctl;WDF_OBJECT_ATTRIBUTES_INIT(&obj_attrs);obj_attrs.ExecutionLevel = WdfExecutionLevelPassive;obj_attrs.SynchronizationScope = WdfSynchronizationScopeNone;// 为指定设备创建和配置I/O队列status = WdfIoQueueCreate(device, &queue_config, &obj_attrs, &queue);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create default WDF queue\n"));goto driver_entry_exit;}// 创建到指定设备的符号链接status = WdfDeviceCreateSymbolicLink(device, &dos_device_name);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create device symbolic link\n"));goto driver_entry_exit;}// 通知框架驱动程序已经完成了指定控制设备对象的初始化WdfControlFinishInitializing(device);

2、注册WFP子层

// 创建一个句柄,该句柄可用于报文注入函数将报文或流数据注入到TCP/IP网络栈中,// 也可用于FwpsQueryPacketInjectionState函数查询报文注入状态。status = FwpsInjectionHandleCreate(AF_INET,FWPS_INJECTION_TYPE_NETWORK | FWPS_INJECTION_TYPE_FORWARD,&inject_handle_forward);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create WFP forward packet injection handle, %ld\n", status));goto driver_entry_exit;}status = FwpsInjectionHandleCreate(AF_INET,FWPS_INJECTION_TYPE_NETWORK | FWPS_INJECTION_TYPE_FORWARD,&inject_handle_in);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create WFP inbound packet injection handle, %ld\n", status));goto driver_entry_exit;}status = FwpsInjectionHandleCreate(AF_INET,FWPS_INJECTION_TYPE_NETWORK | FWPS_INJECTION_TYPE_FORWARD,&inject_handle_out);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create WFP outbound packet injection handle, %ld\n", status));goto driver_entry_exit;}// Create a NET_BUFFER_LIST pool handle.RtlZeroMemory(&nbl_pool_params, sizeof(nbl_pool_params));nbl_pool_params.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;nbl_pool_params.Header.Revision =NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;nbl_pool_params.Header.Size = sizeof(nbl_pool_params);nbl_pool_params.fAllocateNetBuffer = TRUE;nbl_pool_params.PoolTag = WINDIVERT_TAG;nbl_pool_params.DataSize = 0;nbl_pool_handle = NdisAllocateNetBufferListPool(NULL, &nbl_pool_params);if (nbl_pool_handle == NULL){status = STATUS_INSUFFICIENT_RESOURCES;KdPrint(("[DrEnc] failed to allocate net buffer list pool, %ld\n", status));goto driver_entry_exit;}// Create a NET_BUFFER pool handle.RtlZeroMemory(&nb_pool_params, sizeof(nb_pool_params));nb_pool_params.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;nb_pool_params.Header.Revision = NET_BUFFER_POOL_PARAMETERS_REVISION_1;nb_pool_params.Header.Size =NDIS_SIZEOF_NET_BUFFER_POOL_PARAMETERS_REVISION_1;nb_pool_params.PoolTag = WINDIVERT_TAG;nb_pool_params.DataSize = 0;nb_pool_handle = NdisAllocateNetBufferPool(NULL, &nb_pool_params);if (nb_pool_handle == NULL){status = STATUS_INSUFFICIENT_RESOURCES;KdPrint(("[DrEnc] failed to allocate net buffer pool, %ld\n", status));goto driver_entry_exit;}// Open a handle to the filter engine:status = FwpmEngineOpen(NULL, RPC_C_AUTHN_DEFAULT, NULL, NULL,&engine_handle);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create WFP engine handle, %ld\n", status));goto driver_entry_exit;}// Register WFP sub-layers:status = FwpmTransactionBegin(engine_handle, 0);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to begin WFP transaction, %ld\n", status));FwpmTransactionAbort(engine_handle);goto driver_entry_exit;}status = DrEnc_install_provider();if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to install provider, %ld\n", status));FwpmTransactionAbort(engine_handle);goto driver_entry_exit;}status = DrEnc_install_sublayer(WINDIVERT_LAYER_INBOUND_NETWORK_IPV4);if (!NT_SUCCESS(status)){driver_entry_sublayer_error:KdPrint(("[DrEnc] failed to install WFP sub-layer, %ld\n", status));FwpmTransactionAbort(engine_handle);goto driver_entry_exit;}status = DrEnc_install_sublayer(WINDIVERT_LAYER_OUTBOUND_NETWORK_IPV4);if (!NT_SUCCESS(status)){goto driver_entry_sublayer_error;}// 提交当前会话中的当前事务status = FwpmTransactionCommit(engine_handle);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to commit WFP transaction, %ld\n", status));FwpmTransactionAbort(engine_handle);goto driver_entry_exit;}

3、打开过滤引擎

    RtlZeroMemory(&session, sizeof(session));session.flags |= FWPM_SESSION_FLAG_DYNAMIC;status = FwpmEngineOpen(NULL, RPC_C_AUTHN_DEFAULT, NULL, &session,&g_engine_handle);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create WFP engine handle, %ld\n", status));goto windivert_create_exit;}

4、注册呼出函数

    HANDLE engine;FWPS_CALLOUT scallout;FWPM_CALLOUT mcallout;UINT32 callout_id;GUID callout_guid, filter_guid;FWPM_FILTER filter;UINT32 priority;UINT64 weight;NTSTATUS status = STATUS_SUCCESS;engine = g_engine_handle;priority = 1;weight = (UINT64)priority;callout_guid = g_callout_guid[idx];filter_guid = g_filter_guid[idx];RtlZeroMemory(&scallout, sizeof(scallout));scallout.calloutKey = callout_guid;scallout.classifyFn = layer->classify;scallout.notifyFn = windivert_notify;scallout.flowDeleteFn = layer->flow_delete;RtlZeroMemory(&mcallout, sizeof(mcallout));mcallout.calloutKey = callout_guid;mcallout.displayData.name = layer->callout_name;mcallout.displayData.description = layer->callout_desc;mcallout.applicableLayer = *(layer->layer_guid);RtlZeroMemory(&filter, sizeof(filter));filter.filterKey = filter_guid;filter.layerKey = *(layer->layer_guid);filter.displayData.name = layer->filter_name;filter.displayData.description = layer->filter_desc;filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN;filter.action.calloutKey = callout_guid;filter.subLayerKey = *(layer->sublayer_guid);filter.weight.type = FWP_UINT64;filter.weight.uint64 = &weight;//filter.rawContext = (UINT64)context;status = FwpsCalloutRegister(WdfDeviceWdmGetDeviceObject(g_device),&scallout, &callout_id);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to install WFP callout, %ld\n", status));return status;}status = FwpmTransactionBegin(engine, 0);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to begin WFP transaction, %ld\n", status));goto windivert_install_callout_error;}status = FwpmCalloutAdd(engine, &mcallout, NULL, NULL);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to add WFP callout, %ld\n", status));goto windivert_install_callout_error;}status = FwpmFilterAdd(engine, &filter, NULL, NULL);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to add WFP filter, %ld\n", status));goto windivert_install_callout_error;}status = FwpmTransactionCommit(engine);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to commit WFP transaction, %ld\n", status));goto windivert_install_callout_error;}g_nCalloutNum++;return STATUS_SUCCESS;windivert_install_callout_error:if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to DrEnc_install_callouts, %ld\n", status));}FwpmTransactionAbort(engine);FwpsCalloutUnregisterByKey(&callout_guid);return status;

5、在呼出函数中对拦截到的数据进行解析,修改

    PNET_BUFFER_LIST buffers;PNET_BUFFER buffer, buffer_itr;ULONG packet_len;ULONG packet_size;packet_t work;UINT8* work_data;WINDIVERT_PACKET info;NTSTATUS status;FWPS_PACKET_INJECTION_STATE packet_state;HANDLE packet_context;LONGLONG timestamp;BOOL match;INT32 packet_priority;BOOL impostor;UINT64 flags;NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO checksums;BOOL sniffed, ip_checksum, tcp_checksum, udp_checksum;result->actionType = FWP_ACTION_CONTINUE;flags = 0;buffers = (PNET_BUFFER_LIST)data;buffer = NET_BUFFER_LIST_FIRST_NB(buffers);if (NET_BUFFER_LIST_NEXT_NBL(buffers) != NULL){// This is a fragment group.  This can be ignored since each fragment// should have already been indicated.return;}if (outbound){packet_state = FwpsQueryPacketInjectionState(inject_handle_out,buffers, &packet_context);}else{packet_state = FwpsQueryPacketInjectionState(inject_handle_in,buffers, &packet_context);}impostor = FALSE;if (packet_state == FWPS_PACKET_INJECTED_BY_SELF ||packet_state == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF){packet_priority = (UINT32)(ULONG_PTR)packet_context;if (packet_priority <= priority){//WdfObjectDereference(object);return;}}else if (packet_state == FWPS_PACKET_INJECTED_BY_OTHER){// This is a packet injected by another driver, possibly an older// version of WinDivert.  To prevent block-clone-reinject infinite// loops, we mark this packet as an "impostor".impostor = TRUE;}// Get the timestamp.timestamp = KeQueryPerformanceCounter(NULL).QuadPart;// Retreat the NET_BUFFER to the IP header, if necessary.// If (advance != 0) then this must be in the inbound path, and the// NET_BUFFER_LIST must contain exactly one NET_BUFFER.if (advance != 0){status = NdisRetreatNetBufferDataStart(buffer, advance, 0, NULL);if (!NT_SUCCESS(status)){//WdfObjectDereference(object);return;}}sniffed = ((flags & WINDIVERT_FLAG_SNIFF) != 0);buffer_itr = buffer;while (buffer_itr != NULL){match = FALSE;packet_len = NET_BUFFER_DATA_LENGTH(buffer_itr);if (packet_len > WINDIVERT_MTU_MAX){// Cannot handle oversized packetbreak;}packet_size = WINDIVERT_PACKET_SIZE(WINDIVERT_DATA_NETWORK,packet_len);work = (packet_t)windivert_malloc(packet_size, FALSE);if (work == NULL){goto next_buffer;}work->packet_len = (UINT32)packet_len;work_data = WINDIVERT_LAYER_DATA_PTR(work);RtlCopyMemory(work_data, network_data, sizeof(WINDIVERT_DATA_NETWORK));work_data = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_DATA_NETWORK, work);if (!windivert_copy_data(buffer_itr, work_data, packet_len)){windivert_free(work);goto next_buffer;}if (WinDivertHelperParsePacketEx(work_data, packet_len, &info) == FALSE){KdPrint(("[DrEnc] WinDivertHelperParsePacketEx failed\n"));windivert_free(work);goto next_buffer;}if (info.PayloadLength <= 0){//KdPrint(("[DrEnc] WinDivertHelperParsePacketEx failed\n"));windivert_free(work);goto next_buffer;}if (info.Protocol == IPPROTO_TCP || info.Protocol == IPPROTO_UDP){if (outbound && MatchPolicy(&info)){//KdPrint(("[DrEnc] outbound\n"));match = ChangeMatchData(&info);}else if (!outbound && MatchPolicy(&info)){//KdPrint(("[DrEnc] inbound\n"));match = ChangeMatchData(&info);}}if (match){checksums.Value = NET_BUFFER_LIST_INFO(buffers,TcpIpChecksumNetBufferListInfo);if (outbound){ip_checksum = (checksums.Transmit.IpHeaderChecksum == 0);tcp_checksum = (checksums.Transmit.TcpChecksum == 0);udp_checksum = (checksums.Transmit.UdpChecksum == 0);}else{ip_checksum = (checksums.Receive.IpChecksumSucceeded == 0);tcp_checksum = (checksums.Receive.TcpChecksumSucceeded == 0);udp_checksum = (checksums.Receive.UdpChecksumSucceeded == 0);}work->sniffed = (sniffed ? 1 : 0);work->outbound = (outbound ? 1 : 0);work->loopback = (loopback ? 1 : 0);work->impostor = (impostor ? 1 : 0);work->ipv6 = 0;work->ip_checksum = (ip_checksum ? 1 : 0);work->tcp_checksum = (tcp_checksum ? 1 : 0);work->udp_checksum = (udp_checksum ? 1 : 0);work->icmp_checksum = 1;work->match = match;work->packet_size = packet_size;work->priority = priority;work->timestamp = timestamp;WinDivertHelperCalcChecksums(work_data, packet_len, NULL, 0);windivert_inject_packet(work);}else{//WdfWorkItemEnqueue(work);windivert_free(work);}next_buffer:buffer_itr = NET_BUFFER_NEXT_NB(buffer_itr);}if (advance != 0){// Advance the NET_BUFFER to its original position.  Note that we can// do this here, since if (advance != 0) then there is only one// NET_BUFFER in the NET_BUFFER_LIST, meaning that STEPS (1) and (3)// will be empty.NdisAdvanceNetBufferDataStart(buffer, advance, FALSE, NULL);}if (match){result->actionType = FWP_ACTION_BLOCK;result->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;result->rights &= ~FWPS_RIGHT_ACTION_WRITE;}

6、将修改后的数据重新注入到原始路径

    NTSTATUS status = 0;UINT8* packet_data;UINT32 packet_len;UINT64 checksums;PWINDIVERT_DATA_NETWORK network_data;PMDL mdl;PNET_BUFFER_LIST buffers;HANDLE handle;UINT32 priority;//if (packet->layer != WINDIVERT_LAYER_NETWORK &&//    packet->layer != WINDIVERT_LAYER_NETWORK_FORWARD)//{//    KdPrint(("[DrEnc] packet->layer != WINDIVERT_LAYER_NETWORK\n"));//    windivert_free_packet(packet);//    return STATUS_INVALID_PARAMETER;//}network_data = (PWINDIVERT_DATA_NETWORK)WINDIVERT_LAYER_DATA_PTR(packet);packet_data = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_DATA_NETWORK, packet);packet_len = packet->packet_len;// Fix checksums:checksums =(packet->ip_checksum == 0 ? 0 : WINDIVERT_HELPER_NO_IP_CHECKSUM) |(packet->tcp_checksum == 0 ? 0 : WINDIVERT_HELPER_NO_TCP_CHECKSUM) |(packet->udp_checksum == 0 ? 0 : WINDIVERT_HELPER_NO_UDP_CHECKSUM) |(packet->icmp_checksum == 0 ? 0 : WINDIVERT_HELPER_NO_ICMP_CHECKSUM |WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM);//WinDivertHelperCalcChecksums(packet_data, packet_len, NULL, checksums);if (WinDivertHelperCalcChecksums(packet_data, packet_len, NULL, checksums) == FALSE){KdPrint(("[DrEnc] CalcChecksums failed\n"));return STATUS_INVALID_PARAMETER;}// Decrement TTL for impostor packets:if (packet->impostor != 0 &&!WinDivertHelperDecrementTTL(packet_data, packet_len)){status = STATUS_HOPLIMIT_EXCEEDED;KdPrint(("[DrEnc] failed to inject ttl-exceeded impostor packet , %ld\n", status));windivert_free_packet(packet);return status;}// Inject packet:mdl = IoAllocateMdl(packet_data, packet_len, FALSE, FALSE, NULL);if (mdl == NULL){status = STATUS_INSUFFICIENT_RESOURCES;KdPrint(("[DrEnc] failed to allocate MDL for injected packet , %ld\n", status));windivert_free_packet(packet);return status;}MmBuildMdlForNonPagedPool(mdl);status = FwpsAllocateNetBufferAndNetBufferList(nbl_pool_handle, 0, 0,mdl, 0, packet_len, &buffers);if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to create NET_BUFFER_LIST for injected packet , %ld\n", status));IoFreeMdl(mdl);windivert_free_packet(packet);return status;}priority = packet->priority;if (packet->layer == WINDIVERT_LAYER_NETWORK_FORWARD){handle = inject_handle_forward;status = FwpsInjectForwardAsync(handle, (HANDLE)priority, 0,(packet->ipv6 ? AF_INET6 : AF_INET), UNSPECIFIED_COMPARTMENT_ID,network_data->IfIdx, buffers, windivert_inject_complete,(HANDLE)packet);}else if (packet->outbound){handle = inject_handle_out;status = FwpsInjectNetworkSendAsync(handle, (HANDLE)priority, 0,UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_inject_complete,(HANDLE)packet);}else{handle = inject_handle_in;status = FwpsInjectNetworkReceiveAsync(handle, (HANDLE)priority, 0,UNSPECIFIED_COMPARTMENT_ID, network_data->IfIdx,network_data->SubIfIdx, buffers, windivert_inject_complete,(HANDLE)packet);}if (!NT_SUCCESS(status)){KdPrint(("[DrEnc] failed to inject (packet=%p) , %ld\n", packet, status));FwpsFreeNetBufferList(buffers);IoFreeMdl(mdl);windivert_free_packet(packet);}//KdPrint(("[DrEnc] windivert_inject_packet success\n"));return status;

以上代码仅为作此开发的同仁提供思路及方向,作者开发的实现也是将windivert开源项目下大功夫研究之后,取其精华部分从零开始重构得来。希望能为致力于此道者提供帮助。

基于WDF驱动模型使用WFP框架进行网络层数据修改相关推荐

  1. 【嵌入式环境下linux内核及驱动学习笔记-(16)linux总线、设备、驱动模型之input框架】

    目录 1.Linux内核输入子系统概念导入 1.1 输入设备工作机制 1.2 运行框架 1.3 分层思想 2.驱动开发步骤 2.1 在init()或probe()函数中 2.2 在exit()或rem ...

  2. 【项目实战】Python实现基于LDA主题模型进行电商产品评论数据情感分析

    说明:这是一个机器学习.数据挖掘实战项目(附带数据+代码+文档+视频讲解),如需数据+代码+文档+视频讲解可以直接到文章最后获取. 视频: Python实现基于LDA模型进行电商产品评论数据情感分析 ...

  3. 基于领域驱动设计思想Abp框架的考试系统

    后端使用企业技术,但有部分阉割. 使用技术: 数据库:Sql Server 后端:.Net5.ABP vNext.AutoMapper.AutoFac.Dapper.EPlus 前端:Vue.js + ...

  4. win7构建成功helloworld驱动、WDF驱动中KMDF与UMDF区别

    做一个myhello.c, #include <ntddk.h>VOID DriverUnload(PDRIVER_OBJECT driver) {DbgPrint("goodb ...

  5. NT、WDM、WDF驱动概念

    一.概念介绍   NT是驱动模型:NT式驱动程序模型是一种比较老式的驱动程序模型,但适用于现有的Windows系统.NT式驱动模型没有固定的形式,最简单的NT式驱动程序模型这一特点,程序开发者可以编写 ...

  6. WDF驱动中KMDF与UMDF区别

    众所周知, 早期的Windows 95/98的设备驱动是VxD(Virtual Device Driver),其中x表示某一类设备.从Windows 2000开始,开发驱动程序必以WDM(Window ...

  7. linux驱动开发 - 12_platform 平台驱动模型

    文章目录 platform 平台驱动模型 1 platform 总线 platform匹配过程 2 platform 驱动 platform 驱动框架如下所示: 3 platform 设备 platf ...

  8. 领域驱动模型VO,BO,PO,DO,DTO概念及其区别

    本文来说下领域驱动模型VO,BO,PO,DO,DTO 概念及其区别 文章目录 概述 概念以及区别 本文小结 概述 随着编程工业化水平的不断加深,各种编程模型层出不穷(比如MVC,MVP等等),伴随着这 ...

  9. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型

    文章目录 前言 1.驱动编写的三种方法 1.1.传统写法 1.2.总线驱动模型 1.3.设备树驱动模型 2.Linux实现分离:Bus/Dev/Drv模型 2.1.Bus/Dev/Drv模型 2.2. ...

  10. 基于消费者驱动的契约测试

    JB Rainsberger 曾说过,"集成测试是一个陷阱,它像一个自我扩散的病毒,无情地威胁着代码库.项目和团队." 随着微服务系统复杂度的增加,集成测试所带来的弊端愈发明显. ...

最新文章

  1. 精通python-轻松打造11周精通python计划(完结) | 软件库
  2. Google Guice范例解说之使用入门
  3. svn提交错误file is scheduled for addition, but is missing
  4. 笔记本本地连接显示电缆拔出_没有安全电缆槽的笔记本电脑如何固定?
  5. 秘密潜入2小辣椒_短暂潜入2次,我显然不知道自己作为开发人员正在做什么
  6. mybatis连接oracle12乱码,使用mybatis链接oracle数据库出现账号密码错误解决
  7. php 字符组成数组,php分割字符串并返回由字符串组成的数组的函数explode()
  8. java推断字符串是否为乱码
  9. java在dos命令_JAVA中如何执行DOS命令
  10. 数据科学包9-pandas高级内容之数据IO
  11. Andorid Binder进程间通信---Binder本地对象,实体对象,引用对象,代理对象的引用计数...
  12. paip.转账功能设计流程
  13. 【Shiro第八篇】SpringBoot + Shiro使用Shiro标签
  14. 佳顺通用进销存系统去广告_怎样选择免费进销存软件?
  15. 我自己在学arm7——ourdev
  16. 【更改google chrome浏览器路径的方法】
  17. centos8安装和启动中文智能拼音
  18. HTML5期末作业:明星网站设计与实现——明星薛之谦介绍网页设计7个页面HTML+CSS+JavaScript
  19. mysql 增删修模型_48.Python中ORM模型实现mysql数据库基本的增删改查操作
  20. 自嗨模式计算机,关于自嗨模式的说说

热门文章

  1. linux新硬盘格式化,linux添加新硬盘并格式化
  2. 数据运营是什么?该如何做?
  3. 计算机c盘删除的文件怎么找回,两分钟恢复电脑误删除的文件数据
  4. 谈canvas转图片的方法(base64编码)
  5. oracle 计算标准差函数,Oracle数据库之使用oracle来计算方差及标准差
  6. BZOJ2339[HNOI2011]卡农——递推+组合数
  7. 3D检测入门知识梳理
  8. 互联网创新创业大赛优秀范例_创新创业大赛优秀作品-互联网+大赛作品
  9. java实现通讯录(手机版)
  10. 速记JVM内存模型和垃圾回收策略