• Windows驱动开发(中间层)


    Windows驱动开发

    一、前言

    依据《Windows内核安全与驱动开发》及MSDN等网络质料进行学习开发。

     

    二、初步环境

    1、下载安装WDK7.1.0(WinDDK7600.16385.1)

    地址:https://msdn.microsoft.com/en-us/windows/hardware/hh852365

     

    2、下载InstDrv软件(用于安装、启动、停止、卸载驱动)

    界面如下:

     

     

    注:srvinstw.exe 也可以安装、卸载sys文件,但需要手动开启、关闭,即在cmd命令窗口下执行net start 驱动名、net stop 驱动名来启动、停止服务。

     

    3、下载DbgWiew.exe 软件(查看内核模块的输出信息)

    地址:https://technet.microsoft.com/en-us/sysinternals/bb896647.aspx

     

    注:详见DebugView的使用文档。

     

    4、下载64Signer-V1.2(Win7 64位 私有测试数字签名软件)

    地址:http://www.yiiyee.cn/Blog/64signer-download/

     

    5、其他软件:虚拟光驱DAEMON Tools Lite、EasyBCD(系统引导文件修复工具)、PartitionManager(将C盘设置为活动分区)等

     

    三、初步示例

    1、编写一个简单的驱动样例(加载、卸载及信息输出等)

     

    注:详见TestDriver.c

     

    内核模块的入口是DriverEnter函数,在加载时被系统进程System调用。若想能卸载驱动,必须设置驱动对象的DriverUnload函数指针。

     

    Windows内核编程不是应用程序编程,它有自己的一套东西,所有的Win32函数都不能使用,部分C Runtime函数也不能使用,需要调用内核API函数,因此在使用函数前,现在帮助里查询该函数。

    帮助文档在“开始”-->“所有程序”-->“Windows Driver Kits”下。如图:

     

     

    2、使用WDK的build工具,编译时需要makefile.def和SOURCES文件。

    1)makfile.def文件固定,拷贝一份即可,不需要任何改动。

    2)SOURCES文件内容关联编译那些文件及编译出的.sys文件的名字等。

     

    WDK的build工具在“开始”-->“所有程序”-->“Windows Driver Kits”-->“WDK 7600.16385.1”下,选择要根据本地主机的版本,如:本机是64位的Win7旗舰版,则build工具要选x64 Checked build Environment(对应debug)或x64 Free build Environment(对应Release)。

     

    3、InstDrv软件直接安装生成的.sys文件,安装、卸载成功,启动失败,并提示“Windows需要已数字签名的驱动程序”,DebugView软件中无自定义信息输出。图如下:

     

     

     

    解决方法:

    1、从http://www.yiiyee.cn/Blog/64signer-download/下载Win7 64位私有测试数字签名软件(64Signer-V1.2),以管理员身份运行,浏览驱动文件,进行测试数字签名。图如下:

     

    2、重新安装签名后的.sys驱动文件,并启动,依然报并提示“Windows需要已数字签名的驱动程序”。 图如下:

     

    3、以管理员身份运行cmd.exe,,并执行bcdedit /set testsigning true 命令,若提示“操作成功完成”,则表示命令执行成功;否则,若失败,请将bcdedit命令所在boot文件夹的所在盘(一般C盘)设置为活动状态;若依然失败,请修复boot文件夹的所在盘下的Boot(系统引导文件所在),或干脆恢复|重装系统。

     

    4、将Windows 7 设置为测试版本成功后,须重启计算机,重启后,以管理员身份运行InstDrv.exe软件,对已数字签名的.sys驱动文件进行安装、启动、关闭、卸载均成功。图如下:

     

     

    注:若驱动中包含中断或错误,则计算机蓝屏,须重启恢复。恢复后的计算机系统内新安装的软件会遭到卸载或损坏,须重新安装。

     

     

     

     

     

    以上是一个简单的驱动程序开发步骤。若要进行较复杂的开发,一般需要配置VS开发环境、安装虚拟机、WinDgb.exe等软件进行调试等。

     

     

     

     

     

    四、完善环境

     

    1、下载并安装VS2010(64位)

    地址:https://www.visualstudio.com/downloads/download-visual-studio-vs

     

    2、下载WinDbg.exe软件

    地址:http://www.microsoft.com/whdc/devtools/debugging/default.mspx

     

    3、虚拟机(WMware10.0供双机调试)

    地址:http://www.microsoft.com/zh-CN/download/confirmation.aspx?id=8002

     

     

    五、配置VS2010

     

    1、依据本机版本配置管理器。win7旗舰64位系统配置如下:

    点击VS内工具栏中的解决方案配置的下按钮(即debug所处的下拉框按钮),点击配置管理器,点击活动方案配置,点击新建,输入DriverDebug64、默认空,在项目上下文中的平台项新建X64并在活动解决方案平台:选X64。

     

    注:32位系统选Win32

     

    2、在VS中VisualC++下新建“空项目”,将TestDrive.c文件添加到工程中(或直接新建.c文件),.c文件添加后,在属性中就显示出C/C++配置项。

     

    3、点击VS中“视图”菜单->“其他窗口”->“属性管理器”,右击属性管理器中的DriverDebug64,选择属性,在弹出的窗体中进行必要的设置。(或直接点击项目的属性)

     

    注: 1、WDK7.1.0默认安装在C:WinDDK7600.16385.1文件夹下

    2、此处的设置都是必须的, 对复杂些的驱动开发也许还要额外另加设置

    具体如下:

    1)常规

    目标文件扩展名:.sys

     

    2)VC++目录

    可执行文件目录(编译器路径):

    C:WinDDK7600.16385.1inamd64

    包含目录:

    C:WinDDK7600.16385.1incapi

    C:WinDDK7600.16385.1inccrt

    C:WinDDK7600.16385.1incddk

    库目录:

    C:WinDDK7600.16385.1libwin7amd64

    3)C/C++

    预处理器->预处理器定义:

    _AMD64_=1,AMD64=1,STD_CALL=1,WINVER=0x0501,_DEBUG =1

    高级->调用约定:

    __stdcall (/Gz)

    代码生成-->运行库:

    选择多线程调试/MTd 或者 多线程调试DLL/MDd

    (主要解决_DEBUG未预定义的问题)

     

    常规-->调试信息格式:用于“编辑并继续”的程序数据库 (/ZI)

    优化:已禁用 (/Od)

     

    4)连接器

    输入->附加依赖项:                                                                                        ntoskrnl.lib;Hal.lib;wdm.lib;wdmsec.lib;wmilib.lib;ndis.lib;MSVCRT.LIB;LIBCMT.LIB;%(AdditionalDependencies)

    输入->忽略所有默认库:是 (/NODEFAULTLIB)

    清单文件->启用用户账户控制(UAC):否 (/MANIFESTUAC:NO)

    系统->子系统:控制台 (/SUBSYSTEM:CONSOLE)

    系统->驱动程序:驱动程序 (/Driver)

    系统->堆栈保留大小:4194304(可修改)

    系统->堆栈提交大小:4096  (可修改)

    高级->入口点:DriverEntry

    高级->基址:0x10000

    高级->随机基址:(清空)

    高级->数据执行保护(dep):(清空)

            调试->生成调试信息:是 (/DEBUG)

    注:参考C:WinDDK7600.16385.1文件夹的ia64、X86等路径,进行修改可以配置为ia64、32位系统。编译若通过,则配置成功,并生产.sys等文件。

     

     

     

    六、配置WinDbg

     

    1、设置系统字符表路径:WinDbg->File->Symbol File Path界面中输入

    SRV*C:WINDOWSSymbols*http://msdl.microsoft.com/download/symbols; 并选择Reload,WinDbg会自动下载字符表,关键是勾选reload。

     

    2)设置自己生成的.sys对应的字符(Symbol)路径:

    C:UsersshencDesktopTestDriverobjchk_win7_amd64amd64

    3)设置自己生成.sys的原代码路径:

    C:UsersshencDesktopTestDriver

     

    注:

    1)在调试源码时,若发生窜码(调试驱动与对应的源码不一致),须将WinDbg软件File菜单下存储的旧源码路径删除。

    2)调试时WinDbg默认不输出Dbgprintf信息,须执行ednt!Kd_DEFAULT_Mask 0x8 命令。

    3)驱动运行时,按下Ctrol+Break组合键,进入中断状态,点击工具按钮(快捷键:F8、F10等)可进行详细调试。

    4)中断调试时,可查看局部变量等信息

     

    七、虚拟机

     

    1、安装虚拟机

     

    若主板默认没有开启虚拟化技术,则一般方法是开机或重启时按F12键进入BIOS菜单,将虚拟化(Virtualization)设置为Enable。

     

    2、配置虚拟机

    1)开始-->WMware Work Stations-->双击“我的电脑”下的一个虚拟机(Windows 7 x64)-->编辑虚拟机设置-->移除打印机-->添加窜行端口

    2)选中刚添加的窜行端口,在右侧的对话框中,设置如下:

    勾选 启动时连接

    选择 使用命名的管道(N)

    其下的两个下拉框分别选择:该端是服务器、另一端是应用程序

     

    八、安装并配置虚拟机上的Win7 64位系统

     

    1、将Win7设置为可调试状态

    1)以管理员身份打开cmd命令窗口:Win + R 打开运行输入框,输入cmd;或鼠标 点击系统开始图标,在输入框中输入cmd,右击上方搜索显示出的cmd.exe,以管理员身份运 行。

    2)依次输入:

    bcdedit /?

    Bcdedit /enum OSLOADER

    Bcdedit /copy {current} /d “Windows 7 Copy”

    Bcdedit /debug on

    Bcdedit /bootdebug on

    Bcdedit /dbgsettings

    Bcdedit /timeout 7

    2、将Win7设置为测试版

    管理员身份运行cmd命令,bcdedit /set testsigning true

    3、测试证书数字签名

    以管理员身份运行64Signer V1.2.exe,点击浏览找到并双机.sys文件,点击签名。

     4、安装.sys文件

    以管理员身份运行运行InstDrv.exe,选择.sys文件,进行安装、启动等操作

    5、查看内核输出信息

    以管理员身份运行Dbgview.exe,点击Capture菜单,勾选上Captrue kernel项。

    注:测试含有断点的内核,否则卡机,无法进行任何操作,因此不在本地主机测试,而采用双机调试(即新建个虚拟机,本地机与虚拟机通过管道进行通信)。

    九、双机调试

     

    1、源代码中添加中断语句

    #if _DEBUG   //DBG

                     __debugbreak(); //64位

    #endif

     

    注: 1)汇编_asm int 3 中断,在64位Win7上报错。

    2)如果未能进入断点进行调试,请检查sys文件是否在客户机(虚拟Win7系统)安装成功,系统字符集(Symbols)、本人字符Symbols是否下载或设置正确,管道端口是否正确等。

     

    2、虚拟机共享主机文件夹

    方法:

    1)启动VMware,选择虚拟机,点击编辑虚拟机设置;

    2) 点击选项,点击共享文件夹,右侧点击总是启用,然后点击添加按钮;

    3) 输入win7主机下需要共享的文件夹路径,制定共享名,点击下一步;

    4) 点击确定,点击虚拟机内的计算机,在网络下就可以访问共享的文件夹喽。

     

    /*

    3、Windows 7系统中的驱动签名强制要求,关闭强制驱动签名的命令为:

    bcdedit /set loadoptions DISABLE_INTEGRITY_CHECKS

    使用管理员的身份打开CMD命令行,然后输入上面的命令,完成之后重新启动计算机, 就可以在64位win7系统上使用未有数字签名的驱动程序。(测试时不签名启动失败!)

     */

     

     

    十、NDIS中间层驱动开发

     

    1、前言

    TDI(传输层驱动接口)是传输层的过滤技术,在Windows Vista之前,常用来开发网络数据过滤,Windows Vista之后的操作系统中不再支持,取而代之的是WFP(Widows过滤平台)技术,一套系统API和服务,其简单稳定,但微软没有介绍XP等Windows Vista之前的系统如何支持WFP。NDIS Filter(网络驱动接口规范)过滤框架,即支持XP等Windows Vista之前的系统,又支持Win7等Windows Vista之前的系统;并且WDK7.1.0安装环境下的passthru工程,就是NDIS Filter开发的示例,在其基础上可以方便开发。passthru工程路径如下图:

     

     

    2、Windows 网络架构

    1)最上层是网络应用层。Socket、WinInet编写的程序。

    2)第二层是网络API层,也是系统最顶层,为应用程序提供编程接口,且接口协议无关性。接口的定义必须在用户层,内部逻辑实现常在内核层。如:Windows套接字、WinInet库 API等

    3)第三层是网络API的内核实现(即第二层的内核层)。内核实现层总是以内核模式设备驱动程序的形式体现,并且他有一个统一的责任:将上层网络请求格式化为TDI格式,并将这个格式化后的IRP发送到下层NDIS协议驱动。

    4)再下一层是NDIS协议驱动,又叫TDI传输器。TCP/IP等都是NDIS协议驱动。

    5)最下层是NDIS小端口驱动程序,直接驱动物理网卡。

    图如下:

     

    网络驱动模型的每一层都定义了对外的公开的公共接口,与其相连的上下层驱动模块,不需要关心其内部实现就可以很好的支持扩展性、完成工作。

     

    3、NDIS中间层

    为了方便对网络操作进行过滤,依据网络驱动模型内NDIS协议驱动、NDIS小端口驱动的公共接口进行扩展出来的一层。对上面的NDIS协议驱动,扮演NDIS小端口特征的角色;对下面的NDIS小端口驱动,扮演NDIS协议特征的角色。

    图如下:

     

     

    注:NDIS中间层的数量理论上不限数量

     

    协议驱动绑定了所有小端口驱动,于是能截获所有接受到的包;而中间层驱动不仅绑定了所有小端口驱动,而且还被“所有驱动协议”绑定,因此理论上能截获所有发送和接受到的包。

     

    4、NDIS中间层驱动开发示例Passthru工程

    1)NDIS驱动的入口函数DriverEntry:做了初始化包装句柄、注册NDIS小端口特征集、注册NDIS协议特征集、关联NDIS两个接口等必做工作,也可以在其中创建设备对象、初始化分发函数表。

    初始化包装句柄:NdisMInitializeWrapper函数,获得NDIS包装句柄;

    注册小端口特征:先填写小端口特征,再使用NdisIMRegisterLayeredMiniport函数进行注册,输入参数是小端口特征、NDIS包装句柄等,获得关联小端口的NDIS_HANDLE类型句柄(DriverHandle)。

    注册小端口卸载关联程序:NdisMRegisterUnloadHandler函数,参数是NDIS包装句柄、卸载出来程序函数句柄。

    注销小端口:NdisIMDeregisterLayeredMiniport函数,参数是注册小端口特征时获得的DriverHandle句柄。

    注册协议特征:NdisRegisterProtocol函数,输入参数是协议特征、NDIS包装句柄等,获得关联协议的NDIS_HANDLE类型句柄(ProtHandle)。

    关联两个接口:NdisIMAssociateMiniport函数,输入参数是DriverHandle、ProtHandle两句柄,这样小端口层和协议层,在一个不透明体中进行了关联。

     

    注:中间层驱动本身就是集小端口驱动、协议驱动于一体的一个混合体驱动。

     

    2)动态绑定NIC设备

    绑定过程是由PNP管理器发起,当PNP管理器发现系统中有可用的NIC设备时,它最终会找到所有注册过的中间层驱动。依次调用它们的AddDevice函数。

     

    注:驱动的AddDevice函数都是被NDIS库中函数托管的。

     

    绑定的过程中会调用PtBindAdapter函数,在其函数内实现了协议驱动对小端口的绑定。PtBindAdapter函数调用NdisAllocatepacketPoolEx函数分配用于发送和接受数据包的缓冲池;调用NDISOpenAdapter函数绑定下层的NIC,本质是在NDIS的内核对象中,建立起中间层驱动和下层被绑定驱动之间的注册函数的调用关系;调用NdisIMInitializeDeviceInstanceEx函数,在这个函数内部,调用中间层驱动程序的MpInitialize函数来初始化驱动的虚拟NIC。

     

    3)小端口的初始化(MpInitialize)

     

    通过传入的DriverHandle句柄参数,驱动可以很方便地找到两个特征结构中的函数接口。

    调用NdisMSetAttributesEx函数设置适配器上下文,其第三个参数必须设置属性值:

    NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT :不对未决包进行超时处理。

                NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT:不对驱动程序维持的队         列中的查询和设置命令进行超时处理

    NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER:告诉NDIS这是一个中间层驱动

    调用PtRegisterDevice函数,生成一个控制设备对象,并设置其派遣函数。

     

    4)中间层发送数据包

    中间层驱动要发送网络数据包,最终都必须调用NDISSend/NDISSendPacket/NDISCoSendPackets这个系列的函数。以NDISSend为例,NdisSend在内部通过协议驱动的绑定句柄,找到所绑定的中间层驱动,并找到中间层驱动的MpSend/MpSendPackets函数调用。

     

    因此可以在MpSend/MpSendPackets中对发送的数据包直接进行处理。

     

    (包描述符进行重利用或重申请)

    MpSend返回值是NDIS_STATUS_PENDING,则表示发送数据包的异步完成。那么久不能再对包描述符做任何操作了,因为已经对它失去了控制权,响应的操作应该保留到完成函数PtSendComplete中进行。

     

    5)中间层驱动接受数据包

    底层面向无连接的小端口驱动可通过下面两种方式指示数据包接收。

    方式一:小端口驱动调用过滤无关的NdisMindicateReceivePacket函数,向上层驱动传递数据包描述符指针。当上层驱动处理完毕后,将向NIC驱动程序返回那些包描述符及其所指向的资源。此方式下,如果中间层驱动提供了PtReceivePacket处理函数,则PtReceivePacket函数被调用;否则PtReceive函数被调用。

    方式二:小端口驱动调用过滤相关的NdisMXxxindicateReceivePacket函数,传递包头及数据缓冲区指针和缓冲区大小。此方式下,PtReceive函数被调用。

     

    因此可以在PtReceive/PtReceivePacket中对接受的数据包直接进行处理。

     

    上层驱动收到网络包接收通知后,会在合适的时候调用NdisTransferData函数来要求底层驱动将完整的包数据发送给它。我们会在MpTransferData函数中得到上层的这个请求;但因为我们没有完整的报数据,所以应该在这个函数中继续把请求往底层传递。底层驱动如果立刻返回包数据。那么我们在MpTransferData中即能立刻截获到;否则在MpTransferData的异步完成函数PtTransferDataComplete中才能截获到完整的包内容。

     

    因此可以在MpTransferData、PtTransferDataComplete中对数据包进行处理。

     

    6)中间层驱动程序查询和设置

    查询和设置,是小端口特征回调中两个重要的接口:一个用来处理OID查询请求,一个用来处理OID设置请求。Passthru中分别对应的是MPQueryInformation和MPSetInformation.

     

     

    综上,可以在MpSend/MpSendPackets,PtReceive/PtReceivePacket,MpTransferData、PtTransferDataComplete中对数据包进行处理。

     

     

    5、NDIS包描述符

    调用NdisQueryPacket函数,可以找到第一个Ndis_Buffer,然后通过NdisGetNextBuffer函数,来获得后续的NDIS_BUFFER。

    调用NdisQueryBufferSafe函数,可以取得Ndis_Buffer中存储缓冲区的虚拟地址。

    调用NdisMoveMemory函数,可以将各NDIS_BUFFER内容拷贝到指定的存储区。

     

     

    十一、安装驱动

     

    1、将netsf.inf、netsf_m.inf、Passthru.sys放在同一目录下。

    2、安装

    (1) 打开“网络和共享中心”。

    (2) 右击“本地连接”或“无线网络”,选择“属性”。

    (3) 在弹出的“本地连接 属性”对话框中选中“常规”属性页,点击“安装”按钮。

    (4) 在弹出的“选择网络组件类型”对话框中选中“服务”,然后点击“添加”按钮。

    (5) 在弹出的“选择网络服务”对话框中点击“从磁盘安装”按钮。

    (6) 在弹出的“从磁盘安装”对话框中点击“浏览...”按钮。“netsf.inf”文件,点击“打开”按钮,确定。

    (7) 在弹出的“选择网络服务”对话框中选中“Passthru”,点击“确定”按钮。

    (8) 在安装过程中对弹出的数字签名对话框都要点击“确认”按钮。

    (9) 安装完成后,“Passthru”就出现在了组件列表中。

     

    十二、 其他

     first.c

    ///
    /// @file first.c
    /// @author crazy_chu
    /// @date2008-11-1
    /// 
    
    #include <ntddk.h>
        
    
    // 提供一个Unload函数只是为了
    VOID DriverUnload(PDRIVER_OBJECT driver)
    {
        // 但是实际上我们什么都不做,只打印一句话:
        DbgPrint("first: Our driver is unloading…
    ");
    }
    
    // DriverEntry,入口函数。相当于main。
    NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
    {
    #if DBG
          // _asm int 3
    #endif
        // 这是我们的内核模块的入口,可以在这里写入我们想写的东西。
        // 我在这里打印一句话。因为”Hello,world” 常常被高手耻笑,所以
        // 我们打印一点别的。
        DbgPrint("first: Hello, my salary!");
    
        // 设置一个卸载函数便于这个函数能退出。
        driver->DriverUnload = DriverUnload;
        return STATUS_SUCCESS;
    }
    View Code

    makefile

    !IF 0
    
    Copyright (C) Microsoft Corporation, 1999 - 2002
    
    Module Name:
    
        makefile.
    
    Notes:
    
        DO NOT EDIT THIS FILE!!!  Edit .sources. if you want to add a new source
        file to this component.  This file merely indirects to the real make file
        that is shared by all the components of Windows NT (DDK)
    
    !ENDIF
    
    !INCLUDE $(NTMAKEENV)makefile.def
    View Code

    sources

    TARGETNAME=first
    TARGETTYPE=DRIVER
    SOURCES=first.c
    TARGETPATH=obj
    View Code

     

  • 相关阅读:
    UESTC_秋实大哥搞算数 2015 UESTC Training for Data Structures<Problem N>
    UESTC_秋实大哥与线段树 2015 UESTC Training for Data Structures<Problem M>
    UESTC_Rain in ACStar 2015 UESTC Training for Data Structures<Problem L>
    UESTC_Sliding Window 2015 UESTC Training for Data Structures<Problem K>
    UESTC_Islands 2015 UESTC Training for Data Structures<Problem J>
    UESTC_秋实大哥下棋 2015 UESTC Training for Data Structures<Problem I>
    UESTC_秋实大哥打游戏 2015 UESTC Training for Data Structures<Problem H>
    ActiveX控件
    MFC ListControl用法
    c++串口上位机调试助手的总结和部分代码
  • 原文地址:https://www.cnblogs.com/shenchao/p/4947976.html
Copyright © 2020-2023  润新知