Windows驱动编程基础教程
时间:2025-05-16
时间:2025-05-16
新手入门学习的好资料
第一章 字符串 6
1.1 使用字符串结构 6
1.2 字符串的初始化 7
1.3 字符串的拷贝 8
1.4 字符串的连接 8
1.5 字符串的打印 9
第二章 内存与链表 11
2.1内存的分配与释放 11
2.2 使用LIST_ENTRY 12
2.3 使用长长整型数据 14
2.4使用自旋锁 15
第三章 文件操作 18
3.1 使用OBJECT_ATTRIBUTES 18
3.2 打开和关闭文件 18
3.3 文件的读写操作 21
第四章 操作注册表 25
4.1 注册键的打开操作 25
4.2 注册值的读 26
4.3 注册值的写 29
第五章 时间与定时器 30
5.1 获得当前滴答数 30
5.2 获得当前系统时间 31
5.3 使用定时器 32
第六章 内核线程 35
6.1 使用线程 35
6.2 在线程中睡眠 36
6.3 使用事件通知 37
第七章 驱动与设备 41
7.1 驱动入口与驱动对象 41
7.2 分发函数与卸载函数 41
7.3 设备与符号链接 42
7.4 设备的生成安全性限制 44
7.5 符号链接的用户相关性 46
第八章 处理请求 47
8.1 IRP与IO_STACK_LOCATION 47
8.2 打开与关闭的处理 48
8.3 应用层信息传入 49
8.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字符串求长度
size_t wlen = wcslen(wstr); // unicode字符串求长度
printf(“%s %ws %d %d”,str,wstr,len,wlen); // 打印两种字符串
Kernel Debugging Tutorial © 2005 Microsoft Corporation 1
新手入门学习的好资料
但是实际上这种字符串相当的不安全。很容易导致缓冲溢出漏洞。这是因为没有任何地方确切的表明一个字符串的长度。仅仅用一个’\0’字符来标明这个字符串的结束。一旦碰到根本就没有空结束的字符串(可能是攻击者恶意的输入、或者是编程错误导致的意外),程序就可能陷入崩溃。
使用高级C++特性的编码者则容易忽略这个问题。因为常常使用std::string和CString这样高级的类。不用去担忧字符串的安全性了。
在驱动开发中,一般不再用空来表示一个字符串的结束。而是定义了如下的一个结构:
typedef struct _UNICODE_STRING {
USHORT Length; // 字符串的长度(字节数)
USHORT MaximumLength; // 字符串缓冲区的长度(字节数)
PWSTR Buffer; // 字符串缓冲区
} UNICODE_STRING, *PUNICODE_STRING;
以上是Unicode字符串,一个字符为双字节。与之对应的还有一个Ansi字符串。Ansi字符串就是C语言中常用的单字节表示一个字符的窄字符串。
typedef struct _STRING {
USHORT Length;
USHORT 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); // 试图打印str.Buffer。
如果要用以上的方法,必须在编码中保证Buffer始终是以空结束。但这又是一个麻烦的问题。所以,使用微软提供的Rtl系列函数来操作字符串,才是正确的方法。下文逐步的讲述这个系列的函数的使用。
1.2 字符串的初始化
请回顾之前的UNICODE_STRING结构。读者应该可以注意到,这个结构中并不含有字符串缓冲的空间。这是一个初学者常见的出问题的来源。以下的代码是完全错误的,内核会立刻崩溃:
UNICODE_STRING str;
wcscpy(str.Buffer,L”my first string!”);
str.Length = str.MaximumLength = 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); 2 Kernel Debugging Tutorial © 2005 Microsoft Corporation
新手入门学习的好资料
上面代码的第二行手写的常数字符串在代码中形成了“常数”内存空间。这个空间位于代码段。将被分配于可执行页面上。一般的情况下不可写。为此,要注意的是这个字符串空间一旦初始化就不要再更改。否则可能引发系统的保护异常。实际上更好的写法如下:
//请分析一下为何这样写是对的:
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中有一个宏方便这种定义。使用这个宏之后,我们就可以简单的定义一个常数字符串如下:
上一篇:套筒窑维修技术标准
下一篇:肖马杨1#特大桥施工组织设计