驱动编写的全过程

    技术2022-05-11  39

    驱动编写的全过程(上) 2008-04-05 12:04

    目录:

        ☆ 概述    ☆ 编写hello.c文件    ☆ 编写dirs文件    ☆ 编写sources文件    ☆ 编写makefile文件    ☆ 编译产生hello.sys文件    ☆ 编写hello.inf文件    ☆ 安装hello.inf文件    ☆ 卸载hello.sys及相关设置    ☆ 察看KdPrint输出    ☆ 使用DriverMonitor    ☆ 参考资源

    --------------------------------------------------------------------------

    ☆ 概述

    在<<MSDN系列(1)--学习使用"/Device/PhysicalMemory">>中,我首次接触了Windows内核,并演习了通过调用门从Ring 3进入Ring 0,进而获取一些内核空间的信息。这种方式利弊岂有之,总的来说弊大于利。

    听说现在Windows驱动和Unix下LKM、KLD一样,允许动态加载/卸载。过去我们在Unix下常利用类似技术进行Kernel Hacking,今天我来学习如何在Windows下做同样的事。

    拣空看了三本书([1]、[2]、[3])的个别章节,感觉举步维艰,想说"Hello World"不容易。

    我安装了EWindows XP SP1、VC 7、Windows DDK 2600.1106、ActivePerl 5.6.1、Compuware SoftICE Release 2.7、VMware Workstation 3.2,以方便此次学习过程。如果你碰巧也有类似环境,并且也是此道初学者的话,后面的许多步骤将相当容易重现。

    下面是学习笔记。为了简捷起见,并没有时刻指明某项操作是在开发机还是在测试机进行,但你务必清楚区分开发机、测试机上应该进行的操作。

    ☆ 编写hello.c文件

    --------------------------------------------------------------------------/** For x86/EWindows XP SP1 & VC 7 & Windows DDK 2600.1106* build -cZ -x86*/

    /*************************************************************************                                                                      **                               Head File                              **                                                                      *************************************************************************/

    #include <wdm.h>

    /*************************************************************************                                                                      **                               Macro                                  **                                                                      *************************************************************************/

    #define PRIVATEDRIVERNAME "PRIVATE_HELLO_WORLD"

    /*************************************************************************                                                                      **                            Function Prototype                        **                                                                      *************************************************************************/

    /*************************************************************************                                                                      **                            Static Global Var                         **                                                                      *************************************************************************/

    /************************************************************************/

    /** DriverEntry is the first routine called after a driver is loaded, and* is responsible for initializing the driver.** you'll find a list of NTSTATUS status codes in the DDK header * ntstatus.h (/WINDDK/2600.1106/inc/ddk/wxp/)*/NTSTATUS DriverEntry ( IN PDRIVER_OBJECT DriverObject,                       IN PUNICODE_STRING RegistryPath                     ){    CHAR privatedrivername[] = PRIVATEDRIVERNAME;    /*     * 这是无效的用户空间地址     */    PVOID p                   = ( PVOID )1;

        /*     * kernel-mode functions and the functions in your driver use the      * __stdcall calling convention when compiled for an x86 computer.      * This shouldn't affect any of your programming, but it's something     * to bear in mind when you're debugging     */

        /*     * This routine is defined in ntddk.h, wdm.h, and ndis.h.     * A call to this macro requires double parentheses.     */    KdPrint(( "%s - Entering DriverEntry()/n", privatedrivername ));    __try    {        KdPrint(( "%s - You should see this message[1]/n", privatedrivername ));        /*         * 由于1未对齐在4字节边界上,引发SYSTEM_DATATYPE_MISALIGNMENT异常。         * Do not use this routine on kernel-mode addresses.         */        ProbeForWrite( p, 4, 4 );        KdPrint(( "%s - You shouldn't see this message[1]/n", privatedrivername ));    }    __except ( EXCEPTION_EXECUTE_HANDLER )    {        KdPrint(( "%s - __except{}/n", privatedrivername ));    }    KdPrint(( "%s - Kept control after exception/n", privatedrivername ));    __try    {        KdPrint(( "%s - You should see this message[2]/n", privatedrivername ));        __leave;        KdPrint(( "%s - You shouldn't see this message[2]/n", privatedrivername ));    }    __finally    {        KdPrint(( "%s - __finally{}/n", privatedrivername ));    }    KdPrint(( "%s - Exiting DriverEntry()/n", privatedrivername ));    /*     * 故意失败返回     */    return( STATUS_UNSUCCESSFUL );} /* end of DriverEntry */

    /************************************************************************/

    --------------------------------------------------------------------------

    用户空间编程,main()、WinMain()是总入口点。与之类似,DriverEntry()是驱动程序总入口点,它的两个形参对于目前阶段的我来说并不重要。其最后故意失败返回,使我可以不考虑绝大多数复杂情况,而专注于如何编译驱动程序、加载驱动程序,观察驱动程序调试输出信息。同样的技巧在Unix系统使用过。

    与用户空间的结构化异常处理(SEH)相比,内核空间的SEH主要致力于捕捉内核代码访问无效用户空间地址产生的异常,它无法捕捉除以零、内核代码访问无效内核空间地址产生的异常。

    hello.c中由于1未对齐在4字节边界上,ProbeForWrite( p, 4, 4 )引发相应异常。SEH机制使程序继续保持控制权。对于__try{}/__finally{},若因return、continue、break、goto等语句提前离开__try{},将导致一次开销可观的"局部展开"。__leave语句用于避免不必要的"局部展开"。

    KdPrint(())的用法与printf()类似,注意调用该宏时必须指定两层圆括号。

    ☆ 编写dirs文件

    与用户空间编程不同,只有hello.c不足以产生hello.sys,至少还需要三个文件:

    dirs、sources、makefile

    DDK文档"Running the Build Utility"小节对此有详细解释。build根据dirs文件遍历目录树,在子目录中发现dirs时继续遍历,发现sources时build开始为调用nmake做准备。nmake使用makefile,最终调用cl进行真正的编译。

    假设将来的目录/文件布局是这样的:

    hello/ --+-- dirs         |         +-- code/ --+-- hello.c                     |                     +-- sources                     |                     +-- makefile

    这里只列举了手工创建的目录/文件,不包括编译过程产生的目录/文件。

    此时dirs文件内容很简单,就一行:

    --------------------------------------------------------------------------DIRS=code--------------------------------------------------------------------------

    ☆ 编写sources文件

    --------------------------------------------------------------------------## Use the TARGETNAME macro to specify the name of the library to be built.# Do not include the file name extension#TARGETNAME=hello

    ## All build products (such as .exe, .dll, and .lib files) will be placed # in this directory#TARGETPATH=obj

    ## Use the TARGETTYPE macro to specify the type of product being built. # TARGETTYPE gives the Build utility clues about some of the input files# that it should expect. You must include this macro in your sources file.#TARGETTYPE=DRIVER

    ## Use the USE_PDB macro if your debug symbolic files will use a VC4 PDB. # This is the default in the Windows XP build environment.#USE_PDB=1

    # # Use the INCLUDES macro to indicate the location of the headers to be # included in your build#INCLUDES=

    ## Use the MSC_WARNING_LEVEL macro to set the warning level to use on the # compiler. The default is /W3.## After your code builds without errors, you might want to change # MSC_WARNING_LEVEL to /W3 /WX. Setting this value causes warnings to show# as errors.#MSC_WARNING_LEVEL=-W3 -WX

    ## The SOURCES macro specifies the files to be compiled. The SOURCES macro# is required by the Build utility. This macro must be placed in your # sources file. All files specified by this macro must reside in the # directory containing the sources file.#SOURCES=hello.c--------------------------------------------------------------------------

    必要的解释我都放在sources文件里了。单独多解释一下TARGETPATH,BUILD_ALT_DIR的值会被追加在TARGETPATH之后。假设进入了"Win XP Checked Build Environment":

    > set BUILD_ALT_DIRBUILD_ALT_DIR=chk_wxp_x86

    此时在hello/下执行build -cZ -x86,产生的目录/文件布局如下:

    hello/ --+-- buildchk_wxp_x86.log         |         +-- dirs         |         +-- code/ --+-- hello.c                     |                     +-- hello.inf                     |                     +-- sources                     |                     +-- makefile                     |                     +-- objchk_wxp_x86/ --+-- _objects.mac                                           |                                           +-- i386/ --+-- hello.sys                                                       |                                                       +-- hello.obj                                                       |                                                       +-- hello.pdb

    如果你嫌BUILD_ALT_DIR对于目前阶段太碍眼,可以删除该环境变量:

    > set BUILD_ALT_DIR=

    此时在hello/下执行build -cZ -x86,产生的目录/文件布局如下:

    hello/ --+-- build.log         |         +-- dirs         |         +-- code/ --+-- hello.c                     |                     +-- hello.inf                     |                     +-- sources                     |                     +-- makefile                     |                     +-- obj/ --+-- _objects.mac                                |                                +-- i386/ --+-- hello.sys                                            |                                            +-- hello.obj                                            |                                            +-- hello.pdb

    ☆ 编写makefile文件

    --------------------------------------------------------------------------!INCLUDE $(NTMAKEENV)/makefile.def--------------------------------------------------------------------------

    J:/source/driver/hello> set NTMAKEENVNTMAKEENV=J:/WINDDK/2600~1.110/bin

    ☆ 编译产生hello.sys文件

    在dirs文件所在目录里执行"build -cZ -x86":

    J:/source/driver/hello> build -cZ -x86BUILD: Adding /Y to COPYCMD so xcopy ops won't hang.BUILD: Compile and Link for i386BUILD: Examining j:/source/driver/hello directory tree for files to compile.BUILD: Compiling j:/source/driver/hello/code directoryCompiling - code/hello.c for i386BUILD: Linking j:/source/driver/hello/code directoryLinking Executable - code/obj/i386/hello.sys for i386BUILD: Done

        2 files compiled    1 executable built

    J:/source/driver/hello>

     

     

     

     

    驱动编写的全过程(下) 2008-04-05 12:24

    ☆ 编写hello.inf文件

    ; ------------------------------------------------------------------------

    ;; Copyright to satisfy the CHKINF utility;

    [Version]Signature           = "$Windows NT$"Class               = %ClassName%;; For a new device setup class, the INF must specify a newly generated; ClassGuid value;; 用/WINDDK/2600.1106/tools/other/i386/guidgen.exe生成;ClassGuid           = {EABDB9DF-F09D-44ba-81E5-551C2ABC1FA9}Provider            = %INFCreator%DriverVer           = 06/10/2003,1.00.1993.9

    ; ------------------------------------------------------------------------

    [ClassInstall32.ntx86] AddReg              = ClassInstall32Reg[ClassInstall32Reg]HKR,,,,%ClassName%

    ; ------------------------------------------------------------------------

    [Manufacturer]%INFCreator%        = HelloWorldSection

    ; ------------------------------------------------------------------------

    [HelloWorldSection]

    转载请注明原文地址: https://ibbs.8miu.com/read-500022.html

    最新回复(0)