原创 驱动编写的全过程(上)

2009-7-16 11:02 2946 6 6 分类: 通信
驱动编写的全过程(上)

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_DIR
BUILD_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 NTMAKEENV
NTMAKEENV=J:\WINDDK\2600~1.110\bin


☆ 编译产生hello.sys文件


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


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


    2 files compiled
    1 executable built


J:\source\driver\hello>

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
6
关闭 站长推荐上一条 /3 下一条