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

article/2025/10/10 18:45:35

一、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_ OUT BOUND_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开源项目下大功夫研究之后,取其精华部分从零开始重构得来。希望能为致力于此道者提供帮助。


http://chatgpt.dhexx.cn/article/wf7Qwu15.shtml

相关文章

从零开始学习Windows WDF驱动程序开发

从零开始学习Windows WDF驱动程序开发 ▼ 目录&#xff1a; 1. 第一部分 编译安装测试一个简单的WDF驱动程序 1.1 编译安装测试 1.2 Windows驱动开发就是要开发出INF文件和SYS文件 1.3 简单介绍下编译脚本 1.4 Windows应用程序如何访问一个设备 2. 第二部分 了解WDF框架并且开发…

亲手解剖WDF驱动

亲手解剖WDF驱动 创建一个WDF驱动(non-pnp) WDF驱动其实是微软公司提供的一套驱动开发的框架。 有了这个框架之后&#xff0c;开发驱动会简单一些。WDF本身是从WDM基础上封装而成的。WDF里面封装了很多对象&#xff0c;如WDFDRIVER等。如果要学习使用WDF来开发驱动&#xff0c;…

windows驱动开发4:WDM、WDF等驱动基本概念

一、WDM 1、WDM WDM是英文Windows Driver Model(WDM)的缩写&#xff0c;是Windows98和Windows2000使用的新的驱动程序设计规范。使用WDM使得硬件驱动程序更加稳定&#xff0c;让操作系统对硬件更加有效地控制硬件。 除了定义一个驱动程序与操作系统连接的标准接口以外&#…

WDF开发详解

原文地址&#xff1a;https://blog.csdn.net/lang_eva/article/details/109676808 WDF开发详解 添加设备&#xff1a;hdwwiz KMDF驱动程序框架 KMDF 驱动程序框架由对象和事件回调例程构成。KMDF 框架中所有的事物都由对象表示&#xff0c;各种事件处理都由事件回调例程来完…

windows驱动开发-WDF编程

文章目录 前言WDF编程前的准备工作WDF编程创建驱动对象创建设备对象设备对象的回调函数链表操作驱动的测试代码 其他 前言 注&#xff1a;本文的完整代码见仓库 18-WDF-reflect 代码参考自&#xff1a;junjiexing/libredirect 我不是从事驱动开发的专业人员&#xff0c;打酱…

javaWeb图书管理系统

javaWeb图书管理系统 1.项目简单介绍 a.项目用到的技术 IDE: Intellij IDEA语言&#xff1a;java&#xff0c;html ajax&#xff0c;js数据库&#xff1a;Mysql数据库可视化&#xff1a; navicatweb服务器&#xff1a;Tomcat框架&#xff1a;&#xff08;mybatis&#xff0…

基于Java基础的图书管理系统

文章目录 前言一、前期准备二、需求分析三、核心代码开发1、 model 层1.1、 Book 类1.2、 User 类1.3、 NormalUser 类1.4、 Admin 类 2、 service 层2.1、 IOOperation 接口2.1、 AddBook 类2.2、 DeleteBook 类2.3、 ReplaceBook 类2.4、 ListAllBook 类2.5、 BorrowBook 类2…

图书馆管理系统 Java

目录 要求&#xff1a; 代码 Operate接口 Book类 Reader类 BookList类 ReadList 类 Infor类 InforList类 main 功能实现 改进 错误 总结 要求&#xff1a; 为图书管理人员编写一个图书管理系统&#xff0c;图书管理系统的设计主要是实现对图书的管理和相关操作&a…

图书管理系统【Java实现】

目录 一、项目简介二、项目演示1.登录界面2.图书借阅信息管理界面3.新增图书借阅信息界面4.修改图书借阅信息界面5.删除图书借阅信息界面 三、项目流程1.准备开发环境&#xff08;1&#xff09;下载所需文件&#xff08;2&#xff09;在IDEA中配置Maven&#xff08;3&#xff0…

Java开发实现图书管理系统(超详细)

本文用Java代码实现图书代码管理系统&#xff0c;有些地方可能会有纰漏&#xff0c;希望各位大佬鉴赏&#xff01;&#xff01; 文章目录 文章目录 一、Java实现图书管理系统 1.1创建book包 二、创建图书管理系统的操作包 2.1创建Operation接口 三、创建User包 3.1创建User类 四…

Java实现图书管理系统(新手友好)

图书管理系统 一、初识工作二、具体实现三、代码book包Book类BookList类 operation包IOperation接口AddOperationBorrowOperationDelOperationDisplayOperationExitOperationFindOperationReturnOperation user包AdminUserNormalUserUser Main 一、初识工作 我们首先要清楚的是…

图书管理系统【java】

目录 &#x1f947;1.设计背景 &#x1f50e;2.设计思路 &#x1f511;3.book包 &#x1f4d7;3.1 Book类的实现 &#x1f4d5;3.2 BookList类的实现(书架) &#x1f511;4.user包 &#x1f4d9;4.1 User类的实现 &#x1f4d2;4.2 AdminUser&#xff08;管理员&#x…

图书管理系统(Java)

&#x1f4da; 一、前言1.1整体框架 二、book包2.1Book2.2BookList 三、user包3.1、User3.2、Adminuser3.3、Normaluser 四、Operation包4.1、IOperation接口4.2、AddOperattion4.3、ShowOperation4.4、FindOperation4.5、DelOperation4.6、BorrowOperation4.7、ReturnOperatio…

图书管理系统(Java简单版)(完整代码+详解)

目录 详解&#xff1a; BookList类&#xff1a; InOperation接口 User类&#xff08;父类&#xff09; 和 Main类&#xff08;这俩要一起看&#xff09; 完整代码 book包 Book类 BookList类 operation包 AddBook类 BorrowBook类 DeleteBook类 FindBook类 Pr…

用java实现图书管理系统。

在学完java基础篇的时候&#xff0c;安排做了一个综合版的java基础项目&#xff0c;可以把之前学到的知识综合运用。 图书管理系统。 一.项目设计到的知识1.MVC设计模式思想&#xff08;分包&#xff09;>项目分包>MVC简单介绍 2.GUI&#xff08;图形化界面&#xff09;3…

Java实现简易版的【图书管理系统】

目录 &#x1f30e;1.分析图书管理系统的功能 &#x1f30d; 2.在IDEA中进行功能类的创建 &#x1f984;2.1 创建一个名为book的包&#xff0c;里面存放书相关的 &#x1f984; 2.2 创建一个名为Operation的包&#xff0c;里面存放对书的所有操作 &#x1f30e; 3.进行用户…

Java图书管理系统(代码及详解)

图书管理系统在C、C、Java学习中我们都会进行练习来串联我们学习的一些知识以及使用场景&#xff0c;这里跟着我我带大家敲Java版本&#xff01; 结果展示&#xff1a; 这是我们最终实现的图书管理系统&#xff0c;下面我们慢慢来 思路&#xff1a; Java是面向对象的语言特点…

JAVA实现简易的图书管理系统(含过程)

目录 设计背景 成果展示 设计思路 详细代码 book包 Book类 BookList类 operation包 IOperation接口 AddOperation类 BorrowOperation类 DelOperation类 DisplayOperation类 ExitOperation类 ReturnOperation类 user包 User类 Admin类 NormalUser类 Test类…

图书管理系统(Java实现)[附完整代码]

作者&#xff1a;爱塔居的博客_CSDN博客-JavaSE领域博主 专栏&#xff1a;JavaSE 作者专栏&#xff1a;大三学生&#xff0c;希望跟大家一起进步&#xff01; 文章目录 目录 文章目录 一、图书管理系统菜单 二、实现基本框架 三、实现业务 3.1 打印所有图书 3.2 退出系统 3.3 查…

JAVA->实现图书管理系统

目录 一、图书管理系统展示 1.管理员 2.普通用户 ​编辑3.操作 二、图书管理系统基本实现思路 book --- 包 1.Book类 注意&#xff1a; 2.BookList类 注意&#xff1a; Opreration --- 包 Operration接口 1.AddOperation -- 增加图书 注意&#xff1a; 2. BorrowO…