寒江独钓Windows内核编程与信息安全免费试读版.docx
- 文档编号:11844921
- 上传时间:2023-04-05
- 格式:DOCX
- 页数:43
- 大小:353.70KB
寒江独钓Windows内核编程与信息安全免费试读版.docx
《寒江独钓Windows内核编程与信息安全免费试读版.docx》由会员分享,可在线阅读,更多相关《寒江独钓Windows内核编程与信息安全免费试读版.docx(43页珍藏版)》请在冰豆网上搜索。
寒江独钓Windows内核编程与信息安全免费试读版
寒江独钓——Windows内核编程与信息安全(免费试读版)
内容简介:
这本书是一本专门介绍实时扫描的防毒软件、虚拟磁盘、硬盘还原、硬盘透明加密、文件透明加密、防火墙、反外挂、反窃取密码等软件的Windows内核模块开发的编程技术书。
这本书的读者需要有C语言的基础。
作者:
楚狂人
联系方式:
(,MSN:
Email:
)
前言
这本书是一本专门介绍实时扫描的防毒软件、虚拟磁盘、硬盘还原、硬盘透明加密、文件透明加密、防火墙、反外挂、反窃取密码等软件的Windows内核模块开发的编程技术书。
这本书的读者需要有C语言的基础。
我大学的时候,在Windows平台上,最初学的是VB,然后是Delphi。
我的感觉是,无论想实现任何功能,都有工具的开发者,早就已经给我们准备了良好的接口和文档,让我们学习和使用都非常的方便。
因此我的学习到了我以为的终点。
如果仅仅从“能实现功能”的角度讲,我没有必要再学习了,剩下的事情,只是去很舒适的使用那些接口就可以了。
那又何必再学习Windows编程呢。
工作之后我遇到了障碍。
我的第一个任务是实现一个网络的虚拟磁盘。
我虽然自以为无所不能,但是也找不到在Windows系统里增加一个虚拟磁盘的API在哪里。
我每天都在使用虚拟光驱、杀毒软件、防火墙。
但是我从未想过他们如何实现。
不是因为我懂,而是因为我自以任何功能的实现一定是简单而舒适的。
等需要的时候再去研究,绝不会有什么困难。
但是实际编码的时候才明白。
良好的接口,舒适的编码过程,绝对不是天生之道。
天地万物自混沌而起。
那些美好的表面,不过是在残酷的现实上重重包裹的包装纸罢了。
一辆新车的表面自然光彩照人,操作接口也人性而美好。
但是一旦需要打开车身去修理内部某根漏油的管子,就没有那么容易和舒适了。
绝不是Windows的底层开发者们天生没有美学观念。
那些多年积累和维护着并不断改进的无数行代码,已经是人类工程史上的奇迹了。
如今要打开它的外壳去肆意修理,当然不是一件轻松的事情。
但这正是Windows内核编程的魅力所在。
只有极少的程序员会需要参与微软的Windows内核的开发。
也只有极少的读者会试图去自己从头去开发一个类Windows的操作系统内核(有这方面兴趣的读者,建议参考开源项目ReactOS)。
单纯的讲解Windows内核编程对大多数读者都没有意义。
但是,信息安全类的软件是内核编程的极好的应用实例。
病毒实时监控、防火墙、入侵检测、数据保护还原、数据即时备份、数据加密、数据防止泄密、反外挂,都不同程度的涉及到内核编程,或者,内核编程可以让它们工作得更好。
这就是本书的内容。
因此本书的副标题为《Windows内核编程与信息安全》。
“寒江独钓”则表明了这个领域的寒冷与寂寥。
这本书适合有志于成为软件程序员的学生,也适合希望加强自己的技术实力的Windows程序员。
同时更精确的适合从事信息安全行业的Windows软件的开发者。
本书假定读者了解C语言,能理解C语言的基本语法。
本书的读者未来很可能会从事底层编码的工作,而不是一个上层的设计和管理人员。
从事底层编程的程序员,常常被同事称为“牛人”。
这个牛人不是“牛皮哄哄的人”的意思,而是“像牛一样辛苦工作的人”的意思。
想从事这个行业的读者,本人抄古人《代牛言》一首献给您:
渴饮颍水流,饿喘吴门月。
黄金如可种,我力终不竭。
谭文
2008年10月30日
第一章内核上机指导
请注意,因为大部分Windows驱动程序都是内核程序,所以本书中,不区分“驱动编程”与“内核编程”。
同时,也不区分“内核模块”与“驱动程序”。
这两个词汇都指本书中编译出的.sys可执行文件。
但是本书和一般“驱动开发”的书籍不同的是,本书专注与较通用的内核程序的开发。
并不介绍针对某种类硬件的,比如声卡、显卡、USB等的等各种驱动程序的开发。
本书的许多内容涉及到各种不同的内核驱动程序,比如文件系统驱动、存储设备驱动以及网络驱动程序。
但是开发目的,并不是为了提供来驱动某个硬件。
而是在通用的Windows上实现我们的某种功能。
Windows上内核编程和应用程序编程有很多不同的地方。
初次学习,很多读者会关心如何开始动手实践。
为此本章专门讲述如何在Windows的PC上,下载和安装必要的工具,并动手开始内核编程。
值得庆幸的是,在Windows上进行内核编程、编译、调试、安装、显示Debug信息的全部必要工具(不包括为了编程方便而使用的VisualStudio的话),都是免费的。
因此任何读者都可以不花费任何金钱的情况下开始学习这一章节。
对实际上机暂时没有兴趣,或者已经做过驱动开发的读者则可以跳过第一章。
1-1下载和使用WDK
1-1-1下载安装WDK
就像应用程序使用开发包SDK一样。
内核编程使用“WindowsDriverKit”。
简称WDK。
WDK已经自带所有的需要的头文件、库、C/C++语言以及汇编语言的编译器与连接器。
所以完全可以在不安装VisualStudio的情况下进行编程。
只是功能管理会不太方便。
读者可以使用记事本或者自己喜欢的其他文本编辑器进行编程。
习惯使用VisualStudio的读者会感觉这很酷,因为只有传说中的骨灰级程序员才使用记事本编程。
有些读者可能听说过DDK或者IFSDDK。
但是那已经是历史了,请遗忘它。
同时下面的描述也可能成为历史,所以请读者进入网页后灵机应变。
首先请打开网页:
这个网页必须首先登录。
登录拦住了不少人。
有些人以为是收费注册的。
其实使用WindowsLive账号就可以登录。
(更通俗一点的说,用你的MSN账号登录)。
如果没有,可以去免费注册一个。
如何注册请询问使用MSN聊天的朋友。
有了Live账号之后,还必须用这个账号向connect注册,才能下载WDK。
用Live账号登录之后,下面出现“立即注册connect!
”的连接。
注册很简单,只要填个名字、地区和邮箱就可以了。
登录之后出现一个“配置控制面板”的页面。
但是我没有使用它。
再次点击上面的连接,回到主页,能看见显示自己已经登录了。
大致画面如下:
请点击那个红色圈圈住的“查看所有站点”。
下面就比较简单了,左边会显示类别。
类别中请选择“开发人员工具”。
选择之后左边就有“WindowsDriverKit”可以下载。
请按网页的提示逐步下载即可。
这个开发包非常的大,下载之后有好几G的内容。
安装过程没有什么需要特别注意的地方,只有两点:
1.安装到一个简单一点的路径,避免特殊情况需要配置路径的时候麻烦。
比如C:
\WinDDK。
2.一定要选择“完全安装”。
否则可能错过一些代码例子。
图表I的页面
1-1-2编写第一个C文件
现在请打开记事本(或者读者喜欢的任何工具)来创建一个文件。
我们把这个文件命名为first.c,以表示这是我们编写的第一个内核编程的文件。
在内核编程的时候,读者必须打开着WDK的帮助。
在WDK安装之后,点击开始菜单->“所有程序”,会发现增加了“WindowsDriverKits”和“WindowsDriverKitsDocumentation”两个子菜单。
选择“WindowsDriverKitsDocumentation”下面的子菜单来打开帮助。
在使用任何一个函数之前,请在帮助里查询这个函数是否存在、使用的环境要求以及输入输出。
因为这不是应用程序编程,所以所有的Win32API函数都不能使用。
部分CRuntime函数也不能使用。
但是文档中说明的函数则都可以使用。
本书称在文档中有说明的在内核下调用的SystemRoutine为内核API函数。
以便和Win32API函数区分。
我们编写first.c内容如下:
///
///@filefirst.c
///@authorcrazy_chu
///@date2008-11-1
///
#include
//提供一个Unload函数只是为了让这个程序能动态卸载,方便调试。
VOIDDriverUnload(PDRIVER_OBJECTdriver)
{
//但是实际上我们什么都不做,只打印一句话:
DbgPrint("first:
Ourdriverisunloading…\r\n");
}
//DriverEntry,入口函数。
相当于main。
NTSTATUSDriverEntry(PDRIVER_OBJECTdriver,PUNICODE_STRINGreg_path)
{
//这是我们的内核模块的入口,可以在这里写入我们想写的东西。
//我在这里打印一句话。
因为”Hello,world”常常被高手耻笑,所以
//我们打印一点别的。
DbgPrint("first:
Hello,mysalary!
");
//设置一个卸载函数便于这个函数能退出。
driver->DriverUnload=DriverUnload;
returnSTATUS_SUCCESS;
}
上面的DriverEntry是每个内核模块的入口。
在这个模块被加载的被系统进程System调用一次。
在其中我们设置了DriverUnload的函数指针。
那么这个模块可以被动态的卸载(这将方便我们调试程序)。
否则的话一个内核模块一旦被加载就不能卸载了。
如果读者对那些代码的实际内容有兴趣,可以先查阅帮助。
但是,现在不必太当真。
请建立新目录叫first,然后把first.c保存在下面。
1-1-2编译一个工程
前面已经建立了工程first,虽然这个工程只有一个first.c作为代码文件。
现在必须在这个目录下增加两个文件以便WDK的build工具可以build它。
其中一个文件的名字必须为makefile。
这个文件的内容很无聊,永远也不需要改动,内容如下:
!
IF0
Copyright(C)MicrosoftCorporation,1999-2002
ModuleName:
makefile.
Notes:
DONOTEDITTHISFILE!
!
!
Edit.\sources.ifyouwanttoaddanewsource
filetothiscomponent.Thisfilemerelyindirectstotherealmakefile
thatissharedbyallthecomponentsofWindowsNT(DDK)
!
ENDIF
!
INCLUDE$(NTMAKEENV)\
或者读者不想自己写这个文件。
请到WDK的src目录下随便寻找一个例子,然后拷贝一个出来。
大部分例子的makefile都和这个一样。
另外还需要一个名字为SOURCES的文件。
这个文件的内容关系到这个模块要编译哪些文件,以及编译出来的.sys文件的名字。
举例内容如下:
TARGETNAME=first
TARGETTYPE=DRIVER
SOURCES=first.c
其中TARGETNAME是名字。
编译出来之后,模块的名字为。
SOURCES表示要编译的.c文件。
(对于初学者,必须提醒一点不要加入.h文件。
因为.h是被包含在.c文件中编译的。
)如果.c文件有多个,请用空格分隔。
下面请从开始菜单中打开WDK的build环境配置。
图表II从开始菜单中打开WDK的环境配置
从开始菜单中选所有程序,然后选择WindowsDriverKits.然后选择你的WDK的版本,选BuildEnvirements,再选择WindowsXP,点击LaunchWindowsx86CheckedBuildEnvirment。
这样的结果是出现一个控制台。
这个控制台已经配置好编译环境。
现在请不断的输入cd命令来进入我们先建立的first目录。
进入之后,敲入build。
作者本人这里测试的结果如下:
图表IIIfirst编译的结果
现在编译结束。
出现在\first\objchk_wxp_x86\i386下。
这个东西并不像普通exe一样可以直接双击执行。
需要一个安装工具进行安装。
下面再来讲解如何安装执行它。
1-2安装与运行
1-2-1下载一个安装工具
有很多工具可以安装内核模块。
但是我不大敢随便下载推荐给读者。
因为现在网站上病毒实在太多了。
好吧,我暂时相信华军软件园。
请搜索。
这个小软件居然还有汉化版。
我找到的链接如下:
如果这个链接已经失效了,请读者自己在网上搜索这个小工具。
然后执行它来把我们编写的安装到自己的计算机上。
刚开始打开的界面如下:
图表IV执行的开始画面
选择“安装服务”并下一步。
然后选择“本地计算机”,然后选择下一步,这时要输入服务名称。
输入“first”。
这个名称可以随意输入,但是不能和已经有的服务冲突。
下面要输入我们的sys文件所在的路径。
这里只能手工输入,而不要去点下面的浏览。
因为浏览只能看见.exe文件,却无法找到.sys文件。
这一点上我不得不说这个工具(也许是汉化版的问题?
)非常的糟糕。
再下一步,选择“设备驱动”。
下一步要输入“NT驱动器目标名”,不需要输入任何东西,直接点下一步即可。
再下一步要选择启动类型,请一定选择“手动”(见后面的图V)。
选择手动让我们方便调试,可以在我们所需要的时间进行启动和停止。
这一步过去之后安装就成功了。
1-2-2运行与查看输出信息
现在这个内核模块已经可以运行了。
但是,我们不会看到任何现象。
虽然前面的代码中,调用了DbgPrint来打印一些信息。
但是因为这是内核模块,不能指望它弹出一个窗口,或者显示一个
图表V安装到最后一步选择“手动”
示一个控制台来输出那些信息。
内核模块的输出(也就是调用DbgPrint)的输出可以在WinDebug中看到。
但是我们目前还没有讲到如何调试内核代码。
现在用来查看输出是个不错的选择。
可以从微软的网站上下载。
我下载的时候使用的地址如下:
如果失效,请读者自己搜索。
打开之后,界面如图VI。
注意DebugView可以捕获各种输出。
所以你要把那个CaptureKernel勾勾上,才能看到内核输出。
图表VIDebugView的界面
前面已经安装了服务。
所以,现在打开控制台,输入netstartfirst就可以启动。
反之可以输入netstopfirst来停止它的运行。
请注意这里netstart之后跟的是服务名,而不是文件名。
如果前面输入了不同的服务名,哪里就必须用不同的名字。
在我这里,控制台输入输出的结果如图VII。
图表VIIfirst的运行和停止
与此同时,我在DebugView中看到的输出结果如图VIII。
图表VIIIfirst运行的输出结果
1-2-3在虚拟机中运行
虽然按照前面的设置,读者已经可以编译一个内核程序并在自己的计算机上运行,但是在本机上直接加载刚刚编写的内核模块是非常不智的。
如果模块中有错误,很容易导致操作系统立刻蓝屏。
这时我们的工作文件可能还没有保存,导致代码丢失。
因此我个人一般都安装一个虚拟机。
把编译好的sys文件放到虚拟机中运行。
这样的话,即使操作系统崩溃,也不会导致我手忙脚乱。
请在VMWare的官方主页上去下载VMWare的免费版本。
官方网站网址如下:
没有使用过虚拟机的读者可能觉得很有意思,因为我们在自己的XP系统上又运行了另一个XP系统(也可以运行别的,比如Linux),就好像我们有了两台电脑一样。
打开VMWare之后,选文件->新建虚拟机即可。
之后有请按照向导操作。
用于调试的虚拟机和一般的虚拟机没什么区别。
读者需要一张WindowsXP的安装光盘,像安装普通电脑一样在虚拟机上安装WindowsXP。
读者安装完毕之后,碰到的另一个问题可能是如何从外面(我们的本机)拷贝文件到虚拟机中。
这有很多方法。
一种方法是让虚拟机也正常上网,这样两台电脑之间可以用网络邻居访问。
另一种是可以从外面用鼠标直接拖文件到虚拟机中。
或者从里面拖到外面。
这个操作类似段誉的六脉神剑,强大但是时灵时不灵。
最后的方法是选“编辑该虚拟机设置”,然后选“Options”。
左边选中“SharedFolders”,在右边增加一个共享目录。
这个目录在本机上。
选中之后,在虚拟机中也可以访问这个目录。
但是访问的方法比较离奇:
读者必须在虚拟机中的我的电脑中输入“\\.host”回车后才能看见外面共享的目录。
第二章内核数据类型与函数
初次进行Windows内核编程的读者应该仔细阅读这一章。
而进行过驱动开发的读者则可以直接跳过这一章节了。
这一章介绍在Windows内核编程中,所习惯遵守的“WDK编码习惯”。
主要是其中所用的新定义的数据类型。
以及在内核编程中所常用的一些函数调用等等。
阅读此章有助于初次进行内核编程的读者消除对不认识的数据类型以及陌生函数的迷茫感,便于熟悉后面的内核代码。
2-1数据类型
2-1-1基本数据类型
一般的说,在进行内核编程的时候,应当遵守WDK的编码习惯。
虽然这并不是必须的。
举个例子,读者可能很容易的像这样定义一个无符号长整型的数据:
unsignedlonglength;
这样是简单而方便的。
但是有时候会带来问题。
比如说,不同的C语言编译器或者为不同的目标平台编译,有可能unsignedlong表示的实际字节长度并不一样。
假设一个32位的环境下认为unsignedlong是4个字节而一个64位环境下认为unsignedlong为8个字节(但是x64环境下unsignedlong依然是4字节),那么在这种情况下,当我们把一个unsignedlong数据存入磁盘,那么磁盘上到底是写了4个字节还是8个字节呢。
这个问题就很大了。
这直接关系到数据在硬盘上保存的格式的问题。
为此WDK编程规范中不直接使用unsignedlong,而使用了已经重新定义过的ULONG。
重新定义的好处是,万一有了什么问题,再重新定义一下就行。
这些代码不至于产生不可控的问题。
为此请习惯下面的数据类型:
unsignedlong重定义为ULONG。
unsignedchar重定义为UCHAR。
unsignedint重定义为UINT。
void重定义为VOID。
unsignedlong*重定义为PULONG。
unsignedchar(重定义为PUCHAR。
unsignedint*重定义PUINT。
void*重定义为PVOID。
读者将编译的目标平台一般都是x86和x64两种平台。
从x86到x64,除了所有的指针从4字节变成了8字节之外,上述其他几种类型的字节宽度都没有什么变化。
2-1-2返回状态
绝大部分内核API的返回都是一个状态,也就是一个错误码。
这个类型为NTSTATUS。
我们自己编写的函数也将这样做。
比如说:
NTSTATUSMyFunction()
{
NTSTATUSstatus;
//打开一个文件…
status=ZwCreateFile(…);
if(!
NT_SUCCESS(status))
{
//如果出错了就直接返回错误即可。
returnstatus;
}
…
}
从上面的例子可以看到,使用宏NT_SUCCESS()可以判断一个返回值是否成功。
如果在编写一个函数的过程中,检测到了错误,编写者可以返回任何一个错误码(没有任何法律规定必须返回什么)。
但是这些错误码有一些约定俗成但是又并不是非常明确的含义。
如果最终成功,则毫无疑问的返回STATUS_SUCCESS。
下表列出作者本人常用的一些错误值:
STATUS的值
意义
STATUS_SUCCESS
成功
STATUS_INVALID_PARAMETER
错误参数。
一般表示发送到设备的IRP或者调用某个函数的时候提供了错误的参数。
在给文件系统发送请求的时候常常得到这样回答。
得到这样的回答一般等于没有回答:
太多的错误会返回这个错误码。
所以等于没有信息。
STATUS_INSUFFICIENT_RESOURCES
资源不足。
这是最明确的错误,一般都发生于分配内存失败。
系统资源枯竭时。
但是遗憾的是这也是出现得最少的错误之一。
除非内存泄漏一般不会到这个境地。
STATUS_PENDING
请求未决。
这不能算一个错误。
指发送的请求尚未完成。
一般的在完成之后会提供一个回调或者一个事件。
这常见于异步的文件读写操作。
STATUS_BUFFER_OVERFLOW
缓冲长度不够。
可能需要给予更长的输出缓冲。
STATUS_BUFFER_TOO_SMALL
缓冲长度不够。
可能需要给予更长的输出缓冲。
这个和STATUS_BUFFER_OVERFLOW有细微的差别,但是读者没有必要注意这些细节。
NTSTATUS的值还有很多。
但是读者暂时只需要了解这几个就可以了。
如果碰到某个函数返回了奇特的NTSTATUS的值,请注意在帮助里往往是搜索不到的。
正确的方法是在WDK的头文件(比如ntstatus.h)中去寻找答案。
2-1-3字符串
驱动里字符串一般用一个结构来容纳。
这个结构的定义如下:
这个字符串的字符是宽字符,是双字节的。
很多情况下不直接使用字符的指针,而使用这个结构作为字符串使用。
这个结构的指针可以直接在DbgPrint中打印。
读者可以回忆前面第一章我们的“Helloworld”的例子:
//这是我们的内核模块的入口,可以在这里写入我们想写的东西。
//我在这里打印一句话。
因为”Hello,world”常常被高手耻笑,所以
//我们打印一点别的。
DbgPrint("first:
Hello,mysalary!
");
这里直接打印了一个常数字符串。
当然,UNICODE_STRING也是可以直接打印的。
使用UNICODE_STRING就可以这样写:
UNICODE_STRINGstr=RTL_CONSTANT_STRING(L”first:
Hello,mysalary!
”);
DbgPrint(“%wZ”,&str);
和int用%d来打印,char用%c来打印一样,UNICODE_STRING的指针(注意是指针,结构本身不能打)可以用%wZ来打印。
UNICODE_STRING除了可以初始化和打印之外,还可以像普通字符串一样做很多操作,比如拷贝、连接、转换等等。
在后面实际应用的时候再详细举例。
现在读者只要见到UNICODE_STRING知道这是宽字符串,不觉得迷惑就可以了。
2-2重要的数据结构
2-2-1驱动对象
Windows内核采用面向对象的编程方式,但是使用的却是C语言。
所以说到所谓的“Windows内核对象”,并不是一个C++类的对象。
而是Windows的内核程序员使用C语言对面向对象编程方式的一种模拟。
首先,Windows内核认为许多东西都是“对象”。
比如一个驱动、一个设备、一个文件、甚至一些其他的东西。
“对象”相当于一个基类。
在Windows启动之后,这些内核对象都在内存中。
如果我们在内核中写我们的代码,则可以随意访问它们。
每个种类的内核对象用一个结构体来表示。
一个驱动对象代表了一个驱动程序。
或者说一个内核模块。
驱动对象的结构如下(这个结构的定义取自WDK中的wdm.h)。
下面有一些
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 寒江独钓 Windows 内核 编程 信息 安全 免费 试读