ImageVerifierCode 换一换
格式:DOCX , 页数:77 ,大小:62.15KB ,
资源ID:24674560      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/24674560.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Windows驱动编程基础教程.docx)为本站会员(b****3)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Windows驱动编程基础教程.docx

1、Windows驱动编程基础教程版权声明 本书是免费电子书。作者保留一切权利。但在保证本书完整性(包括版权声明、前言、正文内容、后记、以及作者的信息),并不增删、改变其中任何文字内容的前提下,欢迎任何读者以任何形式(包括各种格式的文档)复制和转载本书。同时不限制利用此书赢利的行为(如收费注册下载,或者出售光盘或打印版本)。不满足此前提的任何转载、复制、赢利行为则是侵犯版权的行为。 发现本书的错漏之处,请联系作者。请不要修改本文中任何内容,不经过作者的同意发布修改后的版本。作者信息 作者网名楚狂人。真名谭文。在上海从事Windows驱动开发相关的工作。对本书任何内容有任何疑问的读者,可以用下列方式

2、和作者取得联系: QQ:16191935 MSN:walled_river Email:mfc_tan_wen,walled_river 前言 本书非常适合熟悉Windows应用编程的读者转向驱动开发。所有的内容都从最基础的编程方法入手。介绍相关的内核API,然后举出示范的例子。这本书只有不到70页,是一本非常精简的小册子。所以它并不直接指导读者开发某种特定类型的驱动程序。而是起到一个入门指导的作用。 即使都是使用C/C+语言的代码,在不同的应用环境中,常常看起来还是大相径庭。比如用TurboC+编写的DOS程序代码和用VC+编写的MFC应用程序的代码,看起来就几乎不像是同一种语言。这是由于它

3、们所依赖的开发包不相同的缘故。 在任何情况下都以写出避免依赖的代码为最佳。这样可以避免重复劳动。但是我们在学习一种开发包的使用时,必须习惯这个环境的编码方式,以便获得充分利用这个开发包的能力。 本书的代码几乎都依赖于WDK(Windows Driver Kit)。但是不限WDK的版本。WDK还在不断的升级中。这个开发包是由微软公司免费提供的。读者可以在微软的网站上下载。 当然读者必须把WDK安装的计算机上并配置好开发环境。具体的安装和配置方法本书没有提供。因为网上已经有非常多的中文文档介绍它们。 读完这本书之后,读者一定可以更轻松的阅读其他专门的驱动程序开发的文档和相关书籍。而不至于看到大量无

4、法理解的代码而中途放弃。如果有任何关于本书的内容的问题,读者可以随时发邮件到mfc_tan_wen或者walled_river。能够回答的问题我一般都会答复。 写本书的时候,我和wowocock合作的一本名为天书夜读(在网上有一个大约20%内容的缩减电子版本)正在电子工业出版社编辑。预计还有不到一个月左右就会出版。这也是我自己所见的唯一一本中文原创的从汇编和反汇编角度来学习Windows内核编程和信息安全软件开发的书。希望读者多多支持。有想购买的读者请发邮件给我。我会在本书出版的第一时间,回复邮件告知购买的方法。 此外我正在写另一本关于Windows安全软件的驱动编程的书。但是题目还没有拟好。

5、实际上,读者现在见到的免费版本的Windows驱动编程基础教程是从这本书的第一部分中节选出来的。这本书篇幅比较大,大约有600-800页。主要内容如下: 第一章驱动编程基础 第二章磁盘设备驱动 第三章磁盘还原与加密 第四章传统文件系统过滤 第五章小端口文件系统过滤 第六章文件系统保护与加密 第七章协议网络驱动 第八章物理网络驱动 第九章网络防火墙与安全连接 第十章打印机驱动与虚拟打印 第十一章视频驱动与过滤 附录A WDK的安装与驱动开发的环境配置 附录B 用WinDbg调试Windows驱动程序 这本书还没有完成。但是肯定要付出巨大的精力,所以请读者不要来邮件索取完整的免费的电子版本。希望读

6、者支持本书的纸版出版。因为没有完成,所以还没有联系出版商。有愿意合作出版本书的读者请发邮件与我联系。 凡是发送邮件给我的读者,我将会发送邮件提供本人作品最新的出版信息,以及最新发布的驱动开发相关的免费电子书。如果不需要这些信息的,请在邮件里注明,或者回复邮件给我来取消订阅。 谭文2008年6月9日目录版权声明 1作者信息 1前言 2目录 4第一章 字符串 61.1 使用字符串结构 61.2 字符串的初始化 71.3 字符串的拷贝 81.4 字符串的连接 81.5 字符串的打印 9第二章 内存与链表 112.1内存的分配与释放 112.2 使用LIST_ENTRY 122.3 使用长长整型数据

7、142.4使用自旋锁 15第三章 文件操作 183.1 使用OBJECT_ATTRIBUTES 183.2 打开和关闭文件 183.3 文件的读写操作 21第四章 操作注册表 254.1 注册键的打开操作 254.2 注册值的读 264.3 注册值的写 29第五章 时间与定时器 305.1 获得当前滴答数 305.2 获得当前系统时间 315.3 使用定时器 32第六章 内核线程 356.1 使用线程 356.2 在线程中睡眠 366.3 使用事件通知 37第七章 驱动与设备 417.1 驱动入口与驱动对象 417.2 分发函数与卸载函数 417.3 设备与符号链接 427.4 设备的生成安全

8、性限制 447.5 符号链接的用户相关性 46第八章 处理请求 478.1 IRP与IO_STACK_LOCATION 478.2 打开与关闭的处理 488.3 应用层信息传入 498.4 驱动层信息传出 51后记:我的闲言碎语 54第一章 字符串1.1 使用字符串结构常常使用传统C语言的程序员比较喜欢用如下的方法定义和使用字符串: char *str = “my first string” ; / ansi字符串 wchar_t *wstr = L”my first string” ; / unicode字符串 size_t len = strlen(str); / ansi字符串求长度 s

9、ize_t wlen = wcslen(wstr); / unicode字符串求长度 printf(“%s %ws %d %d”,str,wstr,len,wlen); / 打印两种字符串 但是实际上这种字符串相当的不安全。很容易导致缓冲溢出漏洞。这是因为没有任何地方确切的表明一个字符串的长度。仅仅用一个0字符来标明这个字符串的结束。一旦碰到根本就没有空结束的字符串(可能是攻击者恶意的输入、或者是编程错误导致的意外),程序就可能陷入崩溃。 使用高级C+特性的编码者则容易忽略这个问题。因为常常使用std:string和CString这样高级的类。不用去担忧字符串的安全性了。 在驱动开发中,一般不

10、再用空来表示一个字符串的结束。而是定义了如下的一个结构: typedef struct _UNICODE_STRING USHORT Length; / 字符串的长度(字节数) USHORT MaximumLength; / 字符串缓冲区的长度(字节数) PWSTR Buffer; / 字符串缓冲区 UNICODE_STRING, *PUNICODE_STRING; 以上是Unicode字符串,一个字符为双字节。与之对应的还有一个Ansi字符串。Ansi字符串就是C语言中常用的单字节表示一个字符的窄字符串。 typedef struct _STRING USHORT Length; USHOR

11、T MaximumLength; PSTR Buffer; ANSI_STRING, *PANSI_STRING; 在驱动开发中四处可见的是Unicode字符串。因此可以说:Windows的内核是使用Uincode编码的。ANSI_STRING仅仅在某些碰到窄字符的场合使用。而且这种场合非常罕见。 UNICODE_STRING并不保证Buffer中的字符串是以空结束的。因此,类似下面的做法都是错误的,可能会会导致内核崩溃: UNICODE_STRING str; len = wcslen(str.Buffer); / 试图求长度。 DbgPrint(“%ws”,str.Buffer); / 试

12、图打印str.Buffer。 如果要用以上的方法,必须在编码中保证Buffer始终是以空结束。但这又是一个麻烦的问题。所以,使用微软提供的Rtl系列函数来操作字符串,才是正确的方法。下文逐步的讲述这个系列的函数的使用。1.2 字符串的初始化 请回顾之前的UNICODE_STRING结构。读者应该可以注意到,这个结构中并不含有字符串缓冲的空间。这是一个初学者常见的出问题的来源。以下的代码是完全错误的,内核会立刻崩溃: UNICODE_STRING str; wcscpy(str.Buffer,L”my first string!”); str.Length = str.MaximumLength

13、 = wcslen(L”my first string!”) * sizeof(WCHAR); 以上的代码定义了一个字符串并试图初始化它的值。但是非常遗憾这样做是不对的。因为str.Buffer只是一个未初始化的指针。它并没有指向有意义的空间。相反以下的方法是正确的: / 先定义后,再定义空间UNICODE_STRING str; str.Buffer = L”my first string!”; str.Length = str.MaximumLength = wcslen(L”my first string!”) * sizeof(WCHAR); 上面代码的第二行手写的常数字符串在代码中形

14、成了“常数”内存空间。这个空间位于代码段。将被分配于可执行页面上。一般的情况下不可写。为此,要注意的是这个字符串空间一旦初始化就不要再更改。否则可能引发系统的保护异常。实际上更好的写法如下: /请分析一下为何这样写是对的:UNICODE_STRING str = sizeof(L”my first string!”) sizeof(L”my first string!”)0), sizeof(L”my first string!”), L”my first_string!” ; 但是这样定义一个字符串实在太繁琐了。但是在头文件ntdef.h中有一个宏方便这种定义。使用这个宏之后,我们就可以简单

15、的定义一个常数字符串如下: #include UNICODE_STRING str = RTL_CONSTANT_STRING(L“my first string!”); 这只能在定义这个字符串的时候使用。为了随时初始化一个字符串,可以使用RtlInitUnicodeString。示例如下: UNICODE_STRING str; RtlInitUnicodeString(&str,L”my first string!”); 用本小节的方法初始化的字符串,不用担心内存释放方面的问题。因为我们并没有分配任何内存。1.3 字符串的拷贝 因为字符串不再是空结束的,所以使用wcscpy来拷贝字符串是不

16、行的。UNICODE_STRING可以用RtlCopyUnicodeString来进行拷贝。在进行这种拷贝的时候,最需要注意的一点是:拷贝目的字符串的Buffer必须有足够的空间。如果Buffer的空间不足,字符串会拷贝不完全。这是一个比较隐蔽的错误。 下面举一个例子。 UNICODE_STRING dst; / 目标字符串 WCHAR dst_buf256; / 我们现在还不会分配内存,所以先定义缓冲区 UNICODE_STRING src = RTL_CONST_STRING(L”My source string!”); / 把目标字符串初始化为拥有缓冲区长度为256的UNICODE_ST

17、RING空串。 RtlInitEmptyString(dst,dst_buf,256*sizeof(WCHAR); RtlCopyUnicodeString(&dst,&src); / 字符串拷贝! 以上这个拷贝之所以可以成功,是因为256比L” My source string!”的长度要大。如果小,则拷贝也不会出现任何明示的错误。但是拷贝结束之后,与使用者的目标不符,字符串实际上被截短了。 我曾经犯过的一个错误是没有调用RtlInitEmptyString。结果dst字符串被初始化认为缓冲区长度为0。虽然程序没有崩溃,却实际上没有拷贝任何内容。 在拷贝之前,最谨慎的方法是根据源字符串的长度

18、动态分配空间。在1.2节“内存与链表”中,读者会看到动态分配内存处理字符串的方法。 1.4 字符串的连接 UNICODE_STRING不再是简单的字符串。操作这个数据结构往往需要更多的耐心。读者会常常碰到这样的需求:要把两个字符串连接到一起。简单的追加一个字符串并不困难。重要的依然是保证目标字符串的空间大小。下面是范例: NTSTATUS status; UNICODE_STRING dst; / 目标字符串 WCHAR dst_buf256; / 我们现在还不会分配内存,所以先定义缓冲区 UNICODE_STRING src = RTL_CONST_STRING(L”My source st

19、ring!”); / 把目标字符串初始化为拥有缓冲区长度为256的UNICODE_STRING空串 RtlInitEmptyString(dst,dst_buf,256*sizeof(WCHAR); RtlCopyUnicodeString(&dst,&src); / 字符串拷贝! status = RtlAppendUnicodeToString( &dst,L”my second string!”); if(status != STATUS_SUCCESS) NTSTATUS是常见的返回值类型。如果函数成功,返回STATUS_SUCCESS。否则的话,是一个错误码。RtlAppendUni

20、codeToString在目标字符串空间不足的时候依然可以连接字符串,但是会返回一个警告性的错误STATUS_BUFFER_TOO_SMALL。 另外一种情况是希望连接两个UNICODE_STRING,这种情况请调用RtlAppendUnicodeStringToString。这个函数的第二个参数也是一个UNICODE_STRING的指针。 1.5 字符串的打印 字符串的连接另一种常见的情况是字符串和数字的组合。有时数字需要被转换为字符串。有时需要把若干个数字和字符串混合组合起来。这往往用于打印日志的时候。日志中可能含有文件名、时间、和行号,以及其他的信息。 熟悉C语言的读者会使用sprint

21、f。这个函数的宽字符版本为swprintf。该函数在驱动开发中依然可以使用,但是不安全。微软建议使用RtlStringCbPrintfW来代替它。RtlStringCbPrintfW需要包含头文件ntstrsafe.h。在连接的时候,还需要连接库ntsafestr.lib。 下面的代码生成一个字符串,字符串中包含文件的路径,和这个文件的大小。 #include / 任何时候,假设文件路径的长度为有限的都是不对的。应该动态的分配 / 内存。但是动态分配内存的方法还没有讲述,所以这里再次把内存空间 / 定义在局部变量中,也就是所谓的“在栈中” WCHAR buf512 = 0 ; UNICODE_

22、STRING dst; NTSTATUS status; / 字符串初始化为空串。缓冲区长度为512*sizeof(WCHAR) RtlInitEmptyString(dst,dst_buf,512*sizeof(WCHAR); / 调用RtlStringCbPrintfW来进行打印 status = RtlStringCbPrintfW( dst-Buffer,L”file path = %wZ file size = %d rn”, &file_path,file_size); / 这里调用wcslen没问题,这是因为RtlStringCbPrintfW打印的 / 字符串是以空结束的。 d

23、st-Length = wcslen(dst-Buffer) * sizeof(WCHAR); RtlStringCbPrintfW在目标缓冲区内存不足的时候依然可以打印,但是多余的部分被截去了。返回的status值为STATUS_BUFFER_OVERFLOW。调用这个函数之前很难知道究竟需要多长的缓冲区。一般都采取倍增尝试。每次都传入一个为前次尝试长度为2倍长度的新缓冲区,直到这个函数返回STATUS_SUCCESS为止。 值得注意的是UNICODE_STRING类型的指针,用%wZ打印可以打印出字符串。在不能保证字符串为空结束的时候,必须避免使用%ws或者%s。其他的打印格式字符串与传统

24、C语言中的printf函数完全相同。可以尽情使用。 另外就是常见的输出打印。printf函数只有在有控制台输出的情况下才有意义。在驱动中没有控制台。但是Windows内核中拥有调试信息输出机制。可以使用特殊的工具查看打印的调试信息(请参阅附录1“WDK的安装与驱动开发的环境配置”)。 驱动中可以调用DbgPrint()函数来打印调试信息。这个函数的使用和printf基本相同。但是格式字符串要使用宽字符。DbgPrint()的一个缺点在于,发行版本的驱动程序往往不希望附带任何输出信息,只有调试版本才需要调试信息。但是DbgPrint()无论是发行版本还是调试版本编译都会有效。为此可以自己定义一个

25、宏: #if DBG KdPrint(a) DbgPrint#a #else KdPrint (a) #endif 不过这样的后果是,由于KdPrint (a)只支持1个参数,因此必须把DbgPrint的所有参数都括起来当作一个参数传入。导致KdPrint看起来很奇特的用了双重括弧: / 调用KdPrint来进行输出调试信息 status = KdPrint ( L”file path = %wZ file size = %d rn”, &file_path,file_size); 这个宏没有必要自己定义,WDK包中已有。所以可以直接使用KdPrint来代替DbgPrint取得更方便的效果。第

26、二章 内存与链表2.1内存的分配与释放 内存泄漏是C语言中一个臭名昭著的问题。但是作为内核开发者,读者将有必要自己来面对它。在传统的C语言中,分配内存常常使用的函数是malloc。这个函数的使用非常简单,传入长度参数就得到内存空间。在驱动中使用内存分配,这个函数不再有效。驱动中分配内存,最常用的是调用ExAllocatePoolWithTag。其他的方法在本章范围内全部忽略。回忆前一小节关于字符串的处理的情况。一个字符串被复制到另一个字符串的时候,最好根据源字符串的空间长度来分配目标字符串的长度。下面的举例,是把一个字符串src拷贝到字符串dst。 / 定义一个内存分配标记 #define M

27、EM_TAG MyTt / 目标字符串,接下来它需要分配空间。 UNICODE_STRING dst = 0 ; / 分配空间给目标字符串。根据源字符串的长度。 dst.Buffer = (PWCHAR)ExAllocatePoolWithTag(NonpagedPool,src-Length,MEM_TAG); if(dst.Buffer = NULL) / 错误处理 status = STATUS_INSUFFICIENT_RESOUCRES; dst.Length = dst.MaximumLength = src-Length; status = RtlCopyUnicodeStrin

28、g(&dst,&src); ASSERT(status = STATUS_SUCCESS); ExAllocatePoolWithTag的第一个参数NonpagedPool表明分配的内存是锁定内存。这些内存永远真实存在于物理内存上。不会被分页交换到硬盘上去。第二个参数是长度。第三个参数是一个所谓的“内存分配标记”。 内存分配标记用于检测内存泄漏。想象一下,我们根据占用越来越多的内存的分配标记,就能大概知道泄漏的来源。一般每个驱动程序定义一个自己的内存标记。也可以在每个模块中定义单独的内存标记。内存标记是随意的32位数字。即使冲突也不会有什么问题。 此外也可以分配可分页内存,使用PagedPoo

29、l即可。 ExAllocatePoolWithTag分配的内存可以使用ExFreePool来释放。如果不释放,则永远泄漏。并不像用户进程关闭后自动释放所有分配的空间。即使驱动程序动态卸载,也不能释放空间。唯一的办法是重启计算机。 ExFreePool只需要提供需要释放的指针即可。举例如下: ExFreePool(dst.Buffer); dst.Buffer = NULL; dst.Length = dst.MaximumLength = 0; ExFreePool不能用来释放一个栈空间的指针。否则系统立刻崩溃。像以下的代码: UNICODE_STRING src = RTL_CONST_ST

30、RING(L”My source string!”); ExFreePool(src.Buffer); 会招来立刻蓝屏。所以请务必保持ExAllocatePoolWithTag和ExFreePool的成对关系。2.2 使用LIST_ENTRY Windows的内核开发者们自己开发了部分数据结构,比如说LIST_ENTRY。 LIST_ENTRY是一个双向链表结构。它总是在使用的时候,被插入到已有的数据结构中。下面举一个例子。我构筑一个链表,这个链表的每个节点,是一个文件名和一个文件大小两个数据成员组成的结构。此外有一个FILE_OBJECT的指针对象。在驱动中,这代表一个文件对象。本书后面的章节会详细解释。这个链表的作用是:保存了文件的文件名和长度。只要传入FILE_OBJECT的指针,使用者就可以遍历链表找到文件名和文件长度。 typedef struct PFILE_OBJECT file_object; UNICODE_STRING file_name; LARGE_INTEGER file_length; MY_FILE_INFOR, *PMY_FILE_INFOR; 一些读

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1