为了正常的体验网站,请在浏览器设置里面开启Javascript功能!
首页 > 《寒江独钓:Windows内核编程与信息安全》

《寒江独钓:Windows内核编程与信息安全》

2010-08-13 49页 pdf 1016KB 52阅读

用户头像

is_569018

暂无简介

举报
《寒江独钓:Windows内核编程与信息安全》 书名: 寒江独钓——Windows内核编程与信息安全(免费试读版) 内容简介: 这本书是一本专门介绍实时扫描的防毒软件、虚拟磁盘、硬盘还原、硬盘透明加密、 文件透明加密、防火墙、反外挂、反窃取密码等软件的 Windows 内核模块开发的编程技 术书。这本书的读者需要有 C语言的基础。 作者: 楚狂人 联系方式: (QQ 16191935, MSN: walled_river@hotmail.com, Email: mfc_tan_wen@163.com) 广告 此书的...
《寒江独钓:Windows内核编程与信息安全》
书名: 寒江独钓——Windows内核编程与信息安全(免费试读版) 内容简介: 这本书是一本专门介绍实时扫描的防毒软件、虚拟磁盘、硬盘还原、硬盘透明加密、 文件透明加密、防火墙、反外挂、反窃取密码等软件的 Windows 内核模块开发的编程技 术书。这本书的读者需要有 C语言的基础。 作者: 楚狂人 联系方式: (QQ 16191935, MSN: walled_river@hotmail.com, Email: mfc_tan_wen@163.com) 广告 此书的读者,请点击 http://www.china-pub.com/209258 本文作者的第一本正式出版的书《天书夜读:从汇编语言到Windows内核编程》。您 的点击和评论可以提高此书在 china-pub上的关注度排名,以便更多圈外的读者可以看到 此书。 版权信息 本书为楚狂人原创,保留一切权利。 欢迎勘误、或者对大纲提出建议、或是修改某些章节(请用 doc修订模式修改,改后 发邮件到:mfc_tan_wen@163.com) 欢迎转载未修改过的版本。但必须保证全文完整、广告、版权声明和作者信息完整。 严禁拷贝全部或部分用于商业出版物。严禁不经作者同意的情况下,剽窃本文全部或 部分内容到其他文章、著作中。 前言 这本书是一本专门介绍实时扫描的防毒软件、虚拟磁盘、硬盘还原、硬盘透明加密、 文件透明加密、防火墙、反外挂、反窃取密码等软件的Windows内核模块开发的编程技术 书。这本书的读者需要有 C语言的基础。 我大学的时候,在Windows平台上,最初学的是 VB,然后是 Delphi。我的感觉是,无 论想实现任何功能,都有工具的开发者,早就已经给我们准备了良好的接口和文档,让我 们学习和使用都非常的方便。因此我的学习到了我以为的终点。如果仅仅从“能实现功能” 的角度讲,我没有必要再学习了,剩下的事情,只是去很舒适的使用那些接口就可以了。 那又何必再学习Windows编程呢。 工作之后我遇到了障碍。我的第一个任务是实现一个网络的虚拟磁盘。我虽然自以为 无所不能,但是也找不到在 Windows 系统里增加一个虚拟磁盘的 API 在哪里。我每天都 在使用虚拟光驱、杀毒软件、防火墙。但是我从未想过他们如何实现。不是因为我懂,而 是因为我自以任何功能的实现一定是简单而舒适的。等需要的时候再去研究,绝不会有什 么困难。 但是实际编码的时候才明白。良好的接口,舒适的编码过程,绝对不是天生之道。天 地万物自混沌而起。那些美好的面,不过是在残酷的现实上重重包裹的包装纸罢了。 一辆新车的表面自然光彩照人,操作接口也人性而美好。但是一旦需要打开车身去修 理内部某根漏油的管子,就没有那么容易和舒适了。绝不是Windows的底层开发者们天生 没有美学观念。那些多年积累和维护着并不断改进的无数行代码,已经是人类史上的 奇迹了。如今要打开它的外壳去肆意修理,当然不是一件轻松的事情。 但这正是Windows内核编程的魅力所在。 只有极少的程序员会需要参与微软的Windows内核的开发。也只有极少的读者会试图 去自己从头去开发一个类Windows的操作系统内核(有这方面兴趣的读者,建议参考开源 项目 ReactOS)。单纯的讲解 Windows 内核编程对大多数读者都没有意义。但是,信息安 全类的软件是内核编程的极好的应用实例。病毒实时监控、防火墙、入侵检测、数据保护 还原、数据即时备份、数据加密、数据防止泄密、反外挂,都不同程度的涉及到内核编程, 或者,内核编程可以让它们工作得更好。这就是本书的内容。因此本书的副标为《Windows 内核编程与信息安全》。“寒江独钓”则表明了这个领域的寒冷与寂寥。 这本书适合有志于成为软件程序员的学生,也适合希望加强自己的技术实力的 Windows程序员。同时更精确的适合从事信息安全行业的Windows软件的开发者。本书假 定读者了解 C语言,能理解 C语言的基本语法。 本书的读者未来很可能会从事底层编码的工作,而不是一个上层的设计和管理人员。 从事底层编程的程序员,常常被同事称为“牛人”。这个牛人不是“牛皮哄哄的人”的意 思,而是“像牛一样辛苦工作的人”的意思。想从事这个行业的读者,本人抄古人《代牛 言》一首献给您: 渴饮颍水流,饿喘吴门月。黄金如可种,我力终不竭。 谭文 2008年 10月 30日 目录 前言 ............................................................................................................................................... 3 目录 ............................................................................................................................................... 5 第一章 内核上机指导 ................................................................................................................. 7 1-1 下载和使用WDK ......................................................................................................... 7 1-1-1下载安装WDK ................................................................................................... 7 1-1-2编写第一个 C文件 ............................................................................................. 8 1-1-2编译一个工程.................................................................................................... 10 1-2 安装与运行 .................................................................................................................. 12 1-2-1下载一个安装工具............................................................................................ 12 1-2-2运行与查看输出信息........................................................................................ 13 1-2-3在虚拟机中运行................................................................................................ 15 第二章 内核数据类型与函数 ................................................................................................... 17 2-1 数据类型 ...................................................................................................................... 17 2-1-1 基本数据类型................................................................................................... 17 2-1-2 返回状态........................................................................................................... 18 2-1-3 字符串............................................................................................................... 19 2-2 重要的数据结构 .......................................................................................................... 20 2-2-1 驱动对象........................................................................................................... 20 2-2-2 设备对象........................................................................................................... 21 2-2-3 请求................................................................................................................... 23 2-3 函数调用 ...................................................................................................................... 25 2-3-1 查阅帮助........................................................................................................... 25 2-3-2 帮助中有的几类函数....................................................................................... 26 2-3-3 帮助中没有的函数........................................................................................... 29 第三章 过滤简单设备——串口 ............................................................................................... 30 2-1过滤的概念 ................................................................................................................... 30 2-1-1设备绑定的内核 API之一 ............................................................................... 30 2-1-2设备绑定的内核 API之二 ............................................................................... 31 2-1-3生成过滤设备并绑定........................................................................................ 32 2-1-4从名字获得设备对象........................................................................................ 34 2-1-5绑定所有串口.................................................................................................... 36 2-2获得实际数据 ............................................................................................................... 37 2-2-1 请求的区分....................................................................................................... 37 2-2-2 请求的结局....................................................................................................... 38 2-2-3 写请求的数据................................................................................................... 39 2-3完整的代码 ................................................................................................................... 40 2-3-1 分发和卸载....................................................................................................... 40 2-3-2 完整的代码....................................................................................................... 43 第四章 防止键盘的过滤 ........................................................................................................... 45 2-1 技术原理 ...................................................................................................................... 45 2-1-1 键盘的不安全性............................................................................................... 45 2-1-3 中断描述符表................................................................................................... 46 第五章 虚拟的磁盘 ................................................................................................................... 49 第六章 磁盘的过滤 ................................................................................................................... 49 第七章 磁盘加密 .......................................................................................错误!未定义书签。 第八章 文件系统过滤 ............................................................................................................... 49 第九章 捕获打印图片文件 ....................................................................................................... 49 第十章 文件系统透明加密 ....................................................................................................... 49 第十一章 文件系统微过滤器 ................................................................................................... 49 第十二章 TDI网络过滤............................................................................................................ 49 第十三章 NDIS网络过滤 ......................................................................................................... 49 第十四章 平台网络过滤 ........................................................................................................... 49 第十五章 内核 HOOK............................................................................................................... 49 第十六章 nprotect技术 ............................................................................................................. 49 第一章 内核上机指导 请注意,因为大部分Windows驱动程序都是内核程序,所以本书中,不区分“驱动编 程”与“内核编程”。同时,也不区分“内核模块”与“驱动程序”。这两个词汇都指本书 中编译出的.sys可执行文件。 但是本书和一般“驱动开发”的书籍不同的是,本书专注与较通用的内核程序的开发。 并不介绍针对某种类硬件的,比如声卡、显卡、USB等的等各种驱动程序的开发。 本书的许多内容涉及到各种不同的内核驱动程序,比如文件系统驱动、存储设备驱动 以及网络驱动程序。但是开发目的,并不是为了提供来驱动某个硬件。而是在通用的 Windows上实现我们的某种功能。 Windows上内核编程和应用程序编程有很多不同的地方。初次学习,很多读者会关心 如何开始动手实践。为此本章专门讲述如何在Windows的 PC 上,下载和安装必要的工具, 并动手开始内核编程。值得庆幸的是,在Windows上进行内核编程、编译、调试、安装、 显示 Debug信息的全部必要工具(不包括为了编程方便而使用的 Visual Studio的话),都 是免费的。因此任何读者都可以不花费任何金钱的情况下开始学习这一章节。 对实际上机暂时没有兴趣,或者已经做过驱动开发的读者则可以跳过第一章。 1-1 下载和使用WDK 1-1-1下载安装WDK 就像应用程序使用开发包 SDK一样。内核编程使用“Windows Driver Kit”。简称WDK。 WDK已经自带所有的需要的头文件、库、C/C++语言以及汇编语言的编译器与连接器。所 以完全可以在不安装 Visual Studio的情况下进行编程。只是功能管理会不太方便。读者可 以使用记事本或者自己喜欢的其他文本编辑器进行编程。习惯使用 Visual Studio的读者会 感觉这很酷,因为只有传说中的骨灰级程序员才使用记事本编程。 有些读者可能听说过 DDK 或者 IFSDDK。但是那已经是历史了,请遗忘它。同时下 面的描述也可能成为历史,所以请读者进入网页后灵机应变。 首先请打开网页: https://connect.microsoft.com/default.aspx 这个网页必须首先登录。登录拦住了不少人。有些人以为是收费注册的。其实使用 Windows Live账号就可以登录。(更通俗一点的说,用你的MSN账号登录)。如果没有, 可以去免费注册一个。如何注册请询问使用MSN聊天的朋友。 有了 Live账号之后,还必须用这个账号向 connect注册,才能下载WDK。用 Live账 号登录之后,下面出现“立即注册 connect!”的连接。 注册很简单,只要填个名字、地区和邮箱就可以了。 登录之后出现一个“配置控制面板”的页面。但是我没有使用它。再次点击上面的连 接,回到主页,能看见显示自己已经登录了。大致画面如下: 请点击那个红色圈圈住的“查看所有站点”。 下面就比较简单了,左边会显示类别。类别中请选择“开发人员工具”。 选择之后左边就有“Windows Driver Kit”可以下载。请按网页的提示逐步下载即可。 这个开发包非常的大,下载之后有好几 G的内容。 安装过程没有什么需要特别注意的地方,只有两点: 1. 安装到一个简单一点的路径,避免特殊情况需要配置路径的时候麻烦。比如 C:\WinDDK。 2. 一定要选择“完全安装”。否则可能错过一些代码例子。 图表 I connect.microsoft.com的页面 1-1-2编写第一个 C文件 现在请打开记事本(或者读者喜欢的任何工具)来创建一个文件。我们把这个文件命 名为 first.c,以表示这是我们编写的第一个内核编程的文件。在内核编程的时候,读者必 须打开着WDK的帮助。在WDK安装之后,点击开始菜单->“所有程序”,会发现增加了 “Windows Driver Kits”和“Windows Driver Kits Documentation”两个子菜单。 选择“Windows Driver Kits Documentation”下面的子菜单来打开帮助。在使用任何一 个函数之前,请在帮助里查询这个函数是否存在、使用的环境要求以及输入输出。 因为这不是应用程序编程,所以所有的Win32 API函数都不能使用。部分 C Runtime 函数也不能使用。但是文档中说明的函数则都可以使用。本书称在文档中有说明的在内核 下调用的 System Routine为内核 API函数。以便和Win32 API函数区分。 我们编写 first.c内容如下: /// /// @file first.c /// @author crazy_chu /// @date2008-11-1 /// #include // 提供一个 Unload函数只是为了让这个程序能动态卸载,方便调试。 VOID DriverUnload(PDRIVER_OBJECT driver) { // 但是实际上我们什么都不做,只打印一句话: DbgPrint("first: Our driver is unloading…\r\n"); } // DriverEntry,入口函数。相当于 main。 NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path) { // 这是我们的内核模块的入口,可以在这里写入我们想写的东西。 // 我在这里打印一句话。因为”Hello,world” 常常被高手耻笑,所以 // 我们打印一点别的。 DbgPrint("first: Hello, my salary!"); // 设置一个卸载函数便于这个函数能退出。 driver->DriverUnload = DriverUnload; return STATUS_SUCCESS; } 上面的 DriverEntry 是每个内核模块的入口。在这个模块被加载的被系统进程 System 调用一次。在其中我们设置了 DriverUnload的函数指针。那么这个模块可以被动态的卸载 (这将方便我们调试程序)。否则的话一个内核模块一旦被加载就不能卸载了。 如果读者对那些代码的实际内容有兴趣,可以先查阅帮助。但是,现在不必太当真。 请建立新目录叫 first,然后把 first.c保存在下面。 1-1-2编译一个工程 前面已经建立了工程 first,虽然这个工程只有一个 first.c 作为代码文件。现在必须在 这个目录下增加两个文件以便WDK的 build工具可以 build它。其中一个文件的名字必须 为 makefile。这个文件的内容很无聊,永远也不需要改动,内容如下: !IF 0 Copyright (C) Microsoft Corporation, 1999 - 2002 Module Name: makefile. Notes: DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source file to this component. This file merely indirects to the real make file that is shared by all the components of Windows NT (DDK) !ENDIF !INCLUDE $(NTMAKEENV)\makefile.def 或者读者不想自己写这个文件。请到 WDK 的 src 目录下随便寻找一个例子,然后拷 贝一个出来。大部分例子的 makefile都和这个一样。 另外还需要一个名字为 SOURCES的文件。这个文件的内容关系到这个模块要编译哪 些文件,以及编译出来的.sys文件的名字。举例内容如下: TARGETNAME=first TARGETTYPE=DRIVER INCLUDE=$(INCLUDE);$(BASEDIR) \inc\ddk SOURCES=first.c 其中 TARGETNAME 是名字。编译出来之后,模块的名字为 first.sys。SOURCES 表 示要编译的.c 文件。(对于初学者,必须提醒一点不要加入.h 文件。因为.h 是被包含在.c 文件中编译的。)如果.c文件有多个,请用空格分隔。 下面请从开始菜单中打开WDK的 build环境配置。 图表 II 从开始菜单中打开WDK的环境配置 从开始菜单中选所有程序,然后选择Windows Driver Kits.然后选择你的WDK的版本, 选 Build Envirements,再选择 Windows XP,点击 Launch Windows x86 Checked Build Envirment。 这样的结果是出现一个控制台。这个控制台已经配置好编译环境。现在请不断的输入 cd 命令来进入我们先建立的 first 目录。进入之后,敲入 build。作者本人这里测试的结果 如下: 图表 III first编译的结果 现在编译结束。first.sys出现在\first\objchk_wxp_x86\i386下。这个东西并不像普通 exe 一样可以直接双击执行。需要一个安装工具进行安装。下面再来讲解如何安装执行它。 1-2 安装与运行 1-2-1下载一个安装工具 有很多工具可以安装内核模块。但是我不大敢随便下载推荐给读者。因为现在网站上 病毒实在太多了。好吧,我暂时相信华军软件园。请搜索 Srvinstw.exe。这个小软件居然还 有汉化版。我找到的链接如下: http://www.onlinedown.net/soft/36059.htm 如果这个链接已经失效了,请读者自己在网上搜索这个小工具。 然后执行它来把我们编写的 first.sys安装到自己的计算机上。刚开始打开的界面如下: 图表 IV srvinstw.exe执行的开始画面 选择“安装服务”并下一步。然后选择“本地计算机”,然后选择下一步,这时要输 入服务名称。输入“first”。这个名称可以随意输入,但是不能和已经有的服务冲突。 下面要输入我们的 sys文件所在的路径。这里只能手工输入,而不要去点下面的浏览。 因为浏览只能看见.exe 文件,却无法找到.sys 文件。这一点上我不得不说这个工具(也许 是汉化版的问题?)非常的糟糕。 再下一步,选择“设备驱动”。 下一步要输入“NT驱动器目标名”,不需要输入任何东西,直接点下一步即可。 再下一步要选择启动类型,请一定选择“手动”(见后面的图 V)。选择手动让我们方 便调试,可以在我们所需要的时间进行启动和停止。 这一步过去之后安装就成功了。 1-2-2运行与查看输出信息 现在这个内核模块已经可以运行了。但是,我们不会看到任何现象。虽然前面的代码 中,调用了 DbgPrint来打印一些信息。但是因为这是内核模块,不能指望它弹出一个窗口, 或 者 显 示 一 个 图表 V srvinstw.exe安装到最后一步选择“手动” 示一个控制台来输出那些信息。 内核模块的输出(也就是调用 DbgPrint)的输出可以在 WinDebug中看到。但是我们 目前还没有讲到如何调试内核代码。现在用 DbgView.exe 来查看输出是个不错的选择。 DebugView.exe可以从微软的网站上下载。我下载的时候使用的地址如下: http://www.microsoft.com/taiwan/technet/sysinternals/utilities/debugview.mspx 如果失效,请读者自己搜索。 打开 DebugView.exe 之后,界面如图 VI。注意 DebugView 可以捕获各种输出。所以 你要把那个 Capture Kernel勾勾上,才能看到内核输出。 图表 VI DebugView的界面 前面已经安装了服务。所以,现在打开控制台,输入 net start first就可以启动 first.sys。 反之可以输入 net stop first来停止它的运行。请注意这里 net start之后跟的是服务名,而不 是文件名。如果前面输入了不同的服务名,哪里就必须用不同的名字。在我这里,控制台 输入输出的结果如图 VII。 图表 VII first的运行和停止 与此同时,我在 DebugView中看到的输出结果如图 VIII。 图表 VIII first 运行的输出结果 1-2-3在虚拟机中运行 虽然按照前面的设置,读者已经可以编译一个内核程序并在自己的计算机上运行,但 是在本机上直接加载刚刚编写的内核模块是非常不智的。如果模块中有错误,很容易导致 操作系统立刻蓝屏。这时我们的工作文件可能还没有保存,导致代码丢失。 因此我个人一般都安装一个虚拟机。把编译好的 sys 文件放到虚拟机中运行。这样的 话,即使操作系统崩溃,也不会导致我手忙脚乱。 请在 VMWare的官方主页上去下载 VMWare的免费版本。官方网站网址如下: http://www.vmware.cn/soft/ 没有使用过虚拟机的读者可能觉得很有意思,因为我们在自己的 XP 系统上又运行了 另一个 XP系统(也可以运行别的,比如 Linux),就好像我们有了两台电脑一样。 打开 VMWare之后,选文件->新建虚拟机即可。之后有请按照向导操作。用于调试的 虚拟机和一般的虚拟机没什么区别。读者需要一张Windows XP的安装光盘,像安装普通 电脑一样在虚拟机上安装Windows XP。 读者安装完毕之后,碰到的另一个问题可能是如何从外面(我们的本机)拷贝文件到 虚拟机中。这有很多方法。一种方法是让虚拟机也正常上网,这样两台电脑之间可以用网 络邻居访问。 另一种是可以从外面用鼠标直接拖文件到虚拟机中。或者从里面拖到外面。这个操作 类似段誉的六脉神剑,强大但是时灵时不灵。 最后的方法是选“编辑该虚拟机设置”,然后选“Options”。左边选中“Shared Folders”, 在右边增加一个共享目录。这个目录在本机上。选中之后,在虚拟机中也可以访问这个目 录。但是访问的方法比较离奇:读者必须在虚拟机中的我的电脑中输入“\\.host”回车后 才能看见外面共享的目录。 下面是一张图,展示了在虚拟机中安装了一个 WindowsXP 进行调试的情景。如何使 用WinDbg进行代码级的调试见本书后面的附录。 图表 IX 在一台 PC上用WinDbg调试虚拟机中的WindowsXP 第二章 内核数据类型与函数 初次进行Windows内核编程的读者应该仔细阅读这一章。而进行过驱动开发的读者则 可以直接跳过这一章节了。这一章介绍在Windows内核编程中,所习惯遵守的“WDK编 码习惯”。主要是其中所用的新定义的数据类型。以及在内核编程中所常用的一些函数调 用等等。 阅读此章有助于初次进行内核编程的读者消除对不认识的数据类型以及陌生函数的 迷茫感,便于熟悉后面的内核代码。 2-1 数据类型 2-1-1 基本数据类型 一般的说,在进行内核编程的时候,应当遵守WDK的编码习惯。虽然这并不是必须 的。举个例子,读者可能很容易的像这样定义一个无符号长整型的数据: unsigned long length; 这样是简单而方便的。但是有时候会带来问题。比如说,不同的 C语言编译器或者为 不同的目标平台编译,有可能 unsigned long 表示的实际字节长度并不一样。假设一个 32 位的环境下认为 unsigned long是 4个字节而一个 64位环境下认为 unsigned long为 8个字 节(但是 x64 环境下 unsigned long 依然是 4 字节),那么在这种情况下,当我们把一个 unsigned long数据存入磁盘,那么磁盘上到底是写了 4个字节还是 8个字节呢。这个问题 就很大了。这直接关系到数据在硬盘上保存的格式的问题。 为此 WDK 编程规范中不直接使用 unsigned long,而使用了已经重新定义过的 ULONG。重新定义的好处是,万一有了什么问题,再重新定义一下就行。这些代码不至 于产生不可控的问题。 为此请习惯下面的数据类型: unsigned long重定义为 ULONG。 unsigned char重定义为 UCHAR。 unsigned int重定义为 UINT。 void重定义为 VOID。 unsigned long *重定义为 PULONG。 unsigned char(重定义为 PUCHAR。 unsigned int *重定义 PUINT。 void *重定义为 PVOID。 读者将编译的目标平台一般都是 x86和 x64两种平台。从 x86到 x64,除了所有的指 针从 4字节变成了 8字节之外,上述其他几种类型的字节宽度都没有什么变化。 2-1-2 返回状态 绝大部分内核 API的返回都是一个状态,也就是一个错误码。这个类型为 NTSTATUS。 我们自己编写的函数也将这样做。比如说: NTSTATUS MyFunction() { NTSTATUS status; // 打开一个文件… status = ZwCreateFile(…); if(!NT_SUCCESS(status)) { // 如果出错了就直接返回错误即可。 return status; } … } 从上面的例子可以看到,使用宏 NT_SUCCESS ()可以判断一个返回值是否成功。如 果在编写一个函数的过程中,检测到了错误,编写者可以返回任何一个错误码(没有任何 法律规定必须返回什么)。但是这些错误码有一些约定俗成但是又并不是非常明确的含义。 如果最终成功,则毫无疑问的返回 STATUS_SUCCESS。下表列出作者本人常用的一些错 误值: STATUS的值 意义 STATUS_SUCCESS 成功 STATUS_INVALID_PARAMETER 错误。一般表示发送到设备的 IRP或者调用 某个函数的时候提供了错误的参数。在给文件系 统发送请求的时候常常得到这样回答。得到这样 的回答一般等于没有回答:太多的错误会返回这 个错误码。所以等于没有信息。 STATUS_INSUFFICIENT_RESOURCES 资源不足。这是最明确的错误,一般都发生于分 配内存失败。系统资源枯竭时。但是遗憾的是这 也是出现得最少的错误之一。除非内存泄漏一般 NTSTATUS的值还有很多。但是读者暂时只需要了解这几个就可以了。如果碰到某个 函数返回了奇特的 NTSTATUS 的值,请注意在帮助里往往是搜索不到的。正确的方法是 在WDK的头文件(比如 ntstatus.h)中去寻找。 2-1-3 字符串 驱动里字符串一般用一个结构来容纳。这个结构的定义如下: 这个字符串的字符是宽字符,是双字节的。 很多情况下不直接使用字符的指针,而使用这个结构作为字符串使用。这个结构的指 针可以直接在 DbgPrint中打印。读者可以回忆前面第一章我们的“Hello world”的例子: // 这是我们的内核模块的入口,可以在这里写入我们想写的东西。 // 我在这里打印一句话。因为”Hello,world” 常常被高手耻笑,所以 // 我们打印一点别的。 DbgPrint("first: Hello, my salary!"); 这里直接打印了一个常数字符串。当然,UNICODE_STRING也是可以直接打印的。 使用 UNICODE_STRING就可以这样写: UNICODE_STRING str = RTL_CONSTANT_STRING(L” first: Hello, my salary!”); DbgPrint(“%wZ”,&str); 和 int 用%d 来打印,char 用%c 来打印一样,UNICODE_STRING 的指针(注意是指 针,结构本身不能打)可以用%wZ来打印。 UNICODE_STRING除了可以初始化和打印之外,还可以像普通字符串一样做很多操 作,比如拷贝、连接、转换等等。在后面实际应用的时候再详细举例。现在读者只要见到 UNICODE_STRING知道这是宽字符串,不觉得迷惑就可以了。 不会到这个境地。 STATUS_PENDING 请求未决。这不能算一个错误。指发送的请求尚 未完成。一般的在完成之后会提供一个回调或者 一个事件。这常见于异步的文件读写操作。 STATUS_BUFFER_OVERFLOW 缓冲长度不够。可能需要给予更长的输出缓冲。 STATUS_BUFFER_TOO_SMALL 缓冲长度不够。可能需要给予更长的输出缓冲。 这个和 STATUS_BUFFER_OVERFLOW 有细微 的差别,但是读者没有必要注意这些细节。 2-2 重要的数据结构 2-2-1 驱动对象 Windows 内核采用面向对象的编程方式,但是使用的却是 C 语言。所以说到所谓的 “Windows内核对象”,并不是一个 C++类的对象。而是Windows的内核程序员使用 C语 言对面向对象编程方式的一种模拟。首先,Windows 内核认为许多东西都是“对象”。比 如一个驱动、一个设备、一个文件、甚至一些其他的东西。“对象”相当于一个基类。 在 Windows启动之后,这些内核对象都在内存中。如果我们在内核中写我们的代码, 则可以随意访问它们。每个种类的内核对象用一个结构体来表示。 一个驱动对象代表了一个驱动程序。或者说一个内核模块。驱动对象的结构如下(这 个结构的定义取自WDK中的 wdm.h)。下面有一些域被我用省略号代替。 typedef struct _DRIVER_OBJECT { // 结构的类型和大小。 CSHORT Type; CSHORT Size; // 设备对象,这里实际上是一个设备对象的链表的开始。因为 DeviceObject // 中有相关链表信息。读下一小节“设备对象”会得到更多的信息。 PDEVICE_OBJECT DeviceObject; …… // 驱动的名字 UNICODE_STRING DriverName; …… // 快速 IO分发函数 PFAST_IO_DISPATCH FastIoDispatch; …… // 驱动的卸载函数 PDRIVER_UNLOAD DriverUnload; // 普通分发函数 PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; } DRIVER_OBJECT; 实际上,如果我们写一个驱动程序,或者说编写一个内核模块,要在Windows中加载, 则必须填写这样一个结构,来告诉Windows,我们的程序提供一些什么功能。 这样一来,和我们编写一个应用程序,Windows直接从 main()函数开始执行来生成一 个进程就不同了。内核模块并不生成一个进程。只是填写一组回调函数让Windows来调用。 而且这组回调函数是必须符合Windows内核所规定的 这一组回调函数包括上面的“普通分发函数”,以及“快速 IO 分发函数”。这些函数 的原型细节,将后面书中的例子里详述。一个内核模块所有的功能都由它们提供给 Windows。 Windows中很多的组件都拥有自己的 DRIVER_OBJECT。比如说:所有的硬件驱动程 序,所有的类驱动(Disk,Cdrom…)、文件系统(NTFS 和 FastFat,有各自的 DRIVER_OBJECT)、以及许多其他的内核组件。有一个软件叫做 WinObj,可以显示所有 的内核对象。下面图 X是一幅截图,显示所有看到的驱动对象,读者可以取得感性的认识。 图表 X 展示内核中的驱动对象 有些读者可能想到,如果我们编写内核程序,能找到这些关键的驱动对象结构(比如 NTFS 文件系统),然后改写下面的分发函数,替换成我们自己的函数,可能就能捕获 Windows的文件操作,让我们的内核程序处理完毕后,再交给 NTFS文件系统处理。这样 就可以加入我们自己的功能,比如说,扫描病毒,或者是文件加密等等。这虽然不是很标 准,但是是可行的。这就是所谓的分发函数 Hook技术。在本书后面的内容中会有所描述。 2-2-2 设备对象 设备对象是内核中的重要对象。其重要性不亚于在WindowsGUI编程中的窗口(Wnd)。 进行过Windows窗口应用程序开发的读者知道,窗口是唯一可以接收消息的东西。任何消 息都是发送给一个窗口的。而在内核的世界里,大部分“消息”以“请求”(IRP)的方式 传递。而设备对象(DEVICE_OBJECT)是唯一可以接受请求的的实体。任何一个“请求” (IRP)都是发送给某个设备对象的。 设备对象的结构是 DEVICE_OBJECT,常常被简称为 DO。一个 DO可能代表许多东 西。浅显的例子是:一个 DO可以代表一个实际的硬盘。这很明显:硬盘可以被读,或者 被写。所以这个 DO 将接受读和写两种请求(实际还有更多)。但是一个 DO 也可能代表 一个和硬件毫无关系的东西。比如说内核中可能有一个设备,实现类似“管道”的功能。 一个进程打开这个设备对象进行读,另一个进程打开这个设备进行写,就把数据从一个进 程传递到了另一个进程。为了接受来自用户进程的请求,我们不得不生成了一个 DO。这 个 DO和实际硬件没什么关系。 因为我们总是在内核程序中生成一个 DO,而一个内核程序是用一个驱动对象表示的。 所以一个设备对象总是属于一个驱动对象。 在WDK中的 wdm.h中可以看到结构定义如下(我省略了许多现在读者不需要了解的 域): typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _DEVICE_OBJECT { // 和驱动对象一样 CSHORT Type; USHORT Size; // 引用计数 ULONG ReferenceCount; // 这个设备所属的驱动对象 struct _DRIVER_OBJECT *DriverObject;
/
本文档为【《寒江独钓:Windows内核编程与信息安全》】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索