王朝网络
分享
 
 
 

《Undocumented Windows 2000 Secrets》翻译 --- 第三章(3)

王朝system·作者佚名  2006-01-09
宽屏版  字体: |||超大  

第三章 编写内核模式驱动程序

翻译:Kendiv

更新:Thursday, February 10, 2005

设备I/O控制

就像在本章开头的简介中提到的,在本书中,我们不会构建某一具体硬件的驱动程序。替代的是,我们将利用功能强大的内核驱动程序来研究Windows 2000的秘密。从实际结果来看,驱动程序的强大之处在于它们能在CPU的最高特权级别上运行。这意味着内核驱动可以访问所有的系统资源,可以读取所有的内存空间,而且也被允许执行CPU的特权指令,如,读取CPU控制寄存器的当前值等。而处于用户模式下的程序如果试图从内核空间中读取一个字节或者试图执行像MOV EAX,CR3这样的汇编指令都会被立即终止掉。不过,这种强大的底线是驱动程序的一个很小的错误就会让整个系统崩溃。即使是非常小的错误发生,也会让系统蓝屏,因此开发内核程序的人员必须比Win32应用程序或DLL的开发人员更加仔细的处理错误。还记得我们在第一章里使用的导致系统蓝屏的Windows 2000 Killer device driver吗?它所作的一切只是触及了虚拟内存地址0x00000000,然后就---Boom!!!你应该意识到在开发内核驱动时,你会比以往更频繁的重启你的机器。

在随后章节中,我给出的驱动程序代码将采用称为设备I/O控制(IOCTL)的技术,以允许用户模式下的代码实现一定程序的“远程控制”。如果应用程序需要访问在用户模式下无法触及的系统资源,那内核驱动程序将可很好的完成此项工作,而IOCTL则是联系二者的桥梁。事实上,IOCTL并不是Windows 2000采用的新技术。即使旧的操作系统---DOS 2.11也具有这种能力,0x44函数及其子函数构成了DOS的IOCTL。基本上,IOCTL是通过控制通路和设备通讯的一中手段,控制通路在逻辑上独立于数据通路。想象一个硬盘设备通过其主数据通路传递磁盘扇区中的内容。如果客户想获取当前设备使用的媒体信息,它就必须使用另一个不同的通路。例如,DOS函数0x44,其子函数0x0d、0x66构成了DOS的IOCTL,调用这些函数就可读取磁盘的32位连续数据(参考

Brown and Kyle 1991,1993)。

设备I/O控制根据要控制的设备,可以有多种实现方式。就其一般形式来说,IOCTL有如下几类:

l 客户端通过一个特殊的进入点来控制设备。在DOS中,这个进入点为INT 21h、函数号0x44。在Windows 2000中,则通过Kernel32.dll导出的Win32函数DeviceIoControl()。

l 客户端通过提供设备的唯一标识符、控制代码以及一个存放输入数据的缓冲区、一个存放输出数据的缓冲区来调用IOCTL的进入点。对于Windows 2000,设备标识符是成功打开的设备的句柄(HANDLE)。

l 控制代码用于告诉目标设备的IOCTL分派器(dispatcher),客户端请求的是哪一个控制函数。

l 输入缓冲区中可包含任意地附加数据,设备可能需要这些数据来完成客户所请求的操作。

l 客户所请求的操作产生的任何数据,都会保存在客户端提供的输出缓冲区中。

l IOCTL操作的整体结果通过返回给客户端的状态代码来表示

很显然这是一种强大的通用机制,这种机制可以适用于很大范围的控制请求。例如,应用程序在访问系统内核所占用的内存空间时会被禁止,这是因为当程序触及该内存空间时会立即抛出一个异常,不过程序可以通过加载一个内核驱动程序来完成此项工作,这样就可避免出现异常。交互的两个模块都需遵循IOCTL协议来管理数据的传输。例如,程序可能通过给驱动程序发送控制代码0x80002000来读取内存或发送0x80002001来向内存中写入数据。对于读取请求,IOCTL输入缓冲区或许要提供基地址和要读取的字节数。内核驱动程序能获取这些请求并通过控制代码来判断是读取操作还是写入操作。对于读取请求,内核驱动程序会将请求的内存范围内的数据复制到调用者提供的输出缓冲区中,如果输出缓冲区足够容纳这些数据,则返回成功代码。对于写入请求,驱动程序会将输入缓冲区中的数据复制到指定的内存中(该内存的起始位置也由输入缓冲区指定)。在第四章,我将提供一个Memory Spy的示列代码。

现在,可以看出IOCTL是Win32应用程序的一种后门,通过IOCTL,程序可以执行几乎所有的操作,而在此之前,这些操作仅允许特权模块执行。当然,这需要首先编写一个特权级的模块,但是,一旦你拥有一个运行于系统中的Spy模块,一切就变得很简单了。本书的两个目标是:详细展示如何编写内核模式的驱动程序以及一个可以完成很多让人惊异的事的驱动程序的示例代码。

Windows 2000的Killer Device

在开始更高级的驱动程序工程之前,让我们先看看一个非常简单的驱动程序。在第一章中,我介绍了Windows 2000的Killer Device----w2k_kill.sys,它被设计为引发一个良性的系统崩溃。这个驱动程序并不需要示例3-3中的大多数代码,因为它在有机会收到第一个I/O请求包之前就会使系统崩溃。示例3-7给出了它的实现代码。这里没有给出w2k_kill.h文件,因为它不不包含任何我们感兴趣的代码。

示列3-7中的代码没有在DriverEntry()中执行初始化操作,因为系统会在DriverEntry()返回前就崩溃,所以没有必要进行这些额外的工作。

#define _W2K_KILL_SYS_

#include <ddk\ntddk.h>

#include "w2k_kill.h"

// =================================================================

// DISCARDABLE FUNCTIONS

// =================================================================

NTSTATUS DriverEntry (PDRIVER_OBJECT pDriverObject,

PUNICODE_STRING pusRegistryPath);

#ifdef ALLOC_PRAGMA

#pragma alloc_text (INIT, DriverEntry)

#endif

// =================================================================

// DRIVER INITIALIZATION

// =================================================================

NTSTATUS DriverEntry (PDRIVER_OBJECT pDriverObject,

PUNICODE_STRING pusRegistryPath)

{

return *((NTSTATUS *) 0);

}

// =================================================================

// END OF PROGRAM

// =================================================================

示列3-7. 一个小巧的系统崩溃者

加载/卸载驱动程序

在完成一个内核驱动程序之后,你可能会想立即执行它。怎么做呢?典型的做法是,在系统启动时加载驱动程序并执行之。但这是不是就意味着我们每次更新驱动程序后,都必须重新启动系统呢?很幸运,这并不是必须的。Windows 2000的一个特色就是提供了一个Win32接口以允许在运行时加载或卸载驱动程序。这是由服务控制管理器(Service Control Manager,SCM)完成的,下面的将详细介绍它的用法。

服务控制管理器

“服务控制管理器”这个名字容易让人误解,因为它暗示该组件仅用于服务的管理。服务(Service)是Windows 2000的一类非常强大的模块,它们在后台运行配套的程序,并且不需要用户交互(也就是说没有常见的用户界面或者控制台)。换句话说,一个服务就是一个始终运行于系统中的Win32进程,即使没有用户登陆进来也如此。尽管开发服务是一个令人兴奋的话题,但它并不属于本书的范畴。想进一步了解服务的开发,请阅读Windows Developer’s Journal(WDJ)(Tomlinson 1996a)中Paula Tomlinson提供的非常不错的教程,以及随后在她的WDJ专栏----Understanding NT中发表的有关服务的论文。

SC管理器(即服务控制管理器)可以控制服务和驱动程序。为了简单起见,我在这里使用“服务”一词来代表SC管理器控制的所有对象,这包括严格意义上的服务和内核驱动程。SC的接口对于Win32程序是可用的,它由Win32子系统组件----advapi32.dll提供,这个DLL还提供了很多有趣的API函数。表3-3给出了用于加载、控制和卸载服务的API函数的名称,同时还给出了简单的描述。在你可以加载或访问任何服务之前,你必须获取SC管理器的句柄(通过调用OpenSCManager()),在随后的讨论中,该句柄将被称为:管理器句柄。CreateService()和OpenService()都需要此句柄,而这些函数返回的句柄将被称为:服务句柄。这种类型的句柄可以传递给需要引用一个服务的函数,如ControlService()、DeleteService()和StartService()。这两种类型的SC句柄都通过CloseServiceHandle()函数来释放。

名 称

描 述

CloseServiceHandle

关闭来自OpenSCManager()、CreateService()或OpenService()的句柄

ControlService

停止、暂停、继续、查询或通知已加载的服务/驱动程序

CreateService

加载一个服务/驱动程序

DeleteService

卸载一个服务/驱动程序

OpenSCManager

获取SC管理器的句柄

OpenService

获取一个已加载的服务/驱动程序的句柄

QueryServiceStatus

查询一个服务/驱动程序的属性和当前状态

StartService

启动一个已加载的服务/驱动程序

表3-3. 基本的服务控制函数

加载和运行一个服务需要执行的典型操作步骤:

1. 调用OpenSCManager()以获取一个管理器句柄

2. 调用CreateService()来向系统中添加一个服务

3. 调用StartService()来运行一个服务

4. 调用CloseServiceHandle()来释放管理器或服务句柄

要确保当一个错误发生时,要回滚到最后一个成功的调用,然后再开始。例如,你在调用StartService()时SC管理器报告了一个错误,你就需要调用DeleteService()。否则,服务将保持在一个非预期的状态。另一个使用SC管理器API易犯的错误是,必须为CreateService()函数提供可执行文件的全路径名,否则,如果该函数在当前目录中没有找到可执行文件的话,就会失败。因此,你应该使用Win32函数---GetFullPathName()来规格化传递给CreateService()的所有文件名,除非可以保证它们已经是全路径的。

高层的驱动程序管理函数

为了更容易的和SC管理器进行交互,本书附带的CD提供了多个更高级的外包函数,这些函数屏蔽了原有的一些不方便的特殊要求。这些函数是本书提供的庞大的Windows 2000工具库(位于随书CD中的\src\w2k_lib)中的一部分。w2k_lib.dll导出的所有函数都有一个全局的名字前缀w2k,服务和驱动程序管理函数都使用w2kService前缀。列表3-8给出了本书提供的工具库中实现的加载、控制和卸载服务/驱动程序的函数的细节。

// =================================================================

// SERVICE/DRIVER MANAGEMENT

// =================================================================

SC_HANDLE WINAPI w2kServiceConnect (void)

{

return OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);

}

// -----------------------------------------------------------------

SC_HANDLE WINAPI w2kServiceDisconnect (SC_HANDLE hManager)

{

if (hManager != NULL) CloseServiceHandle (hManager);

return NULL;

}

// -----------------------------------------------------------------

SC_HANDLE WINAPI w2kServiceManager (SC_HANDLE hManager,

PSC_HANDLE phManager,

BOOL fOpen)

{

SC_HANDLE hManager1 = NULL;

if (phManager != NULL)

{

if (fOpen)

{

if (hManager == NULL)

{

*phManager = w2kServiceConnect ();

}

else

{

*phManager = hManager;

}

}

else

{

if (hManager == NULL)

{

*phManager = w2kServiceDisconnect (*phManager);

}

}

hManager1 = *phManager;

}

return hManager1;

}

// -----------------------------------------------------------------

SC_HANDLE WINAPI w2kServiceOpen (SC_HANDLE hManager,

PWORD pwName)

{

SC_HANDLE hManager1;

SC_HANDLE hService = NULL;

w2kServiceManager (hManager, &hManager1, TRUE);

if ((hManager1 != NULL) && (pwName != NULL))

{

hService = OpenService (hManager1, pwName,

SERVICE_ALL_ACCESS);

}

w2kServiceManager (hManager, &hManager1, FALSE);

return hService;

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServiceClose (SC_HANDLE hService)

{

return (hService != NULL) && CloseServiceHandle (hService);

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServiceAdd (SC_HANDLE hManager,

PWORD pwName,

PWORD pwInfo,

PWORD pwPath)

{

SC_HANDLE hManager1, hService;

PWORD pwFile;

WORD awPath [MAX_PATH];

DWORD n;

BOOL fOk = FALSE;

w2kServiceManager (hManager, &hManager1, TRUE);

if ((hManager1 != NULL) && (pwName != NULL) &&

(pwInfo != NULL) && (pwPath != NULL) &&

(n = GetFullPathName (pwPath, MAX_PATH, awPath, &pwFile)) &&

(n < MAX_PATH))

{

if ((hService = CreateService (hManager1, pwName, pwInfo,

SERVICE_ALL_ACCESS,

SERVICE_KERNEL_DRIVER,

SERVICE_DEMAND_START,

SERVICE_ERROR_NORMAL,

awPath, NULL, NULL,

NULL, NULL, NULL))

!= NULL)

{

w2kServiceClose (hService);

fOk = TRUE;

}

else

{

fOk = (GetLastError () ==

ERROR_SERVICE_EXISTS);

}

}

w2kServiceManager (hManager, &hManager1, FALSE);

return fOk;

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServiceRemove (SC_HANDLE hManager,

PWORD pwName)

{

SC_HANDLE hService;

BOOL fOk = FALSE;

if ((hService = w2kServiceOpen (hManager, pwName)) != NULL)

{

if (DeleteService (hService))

{

fOk = TRUE;

}

else

{

fOk = (GetLastError () ==

ERROR_SERVICE_MARKED_FOR_DELETE);

}

w2kServiceClose (hService);

}

return fOk;

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServiceStart (SC_HANDLE hManager,

PWORD pwName)

{

SC_HANDLE hService;

BOOL fOk = FALSE;

if ((hService = w2kServiceOpen (hManager, pwName)) != NULL)

{

if (StartService (hService, 1, &pwName))

{

fOk = TRUE;

}

else

{

fOk = (GetLastError () ==

ERROR_SERVICE_ALREADY_RUNNING);

}

w2kServiceClose (hService);

}

return fOk;

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServiceControl (SC_HANDLE hManager,

PWORD pwName,

DWORD dControl)

{

SC_HANDLE hService;

SERVICE_STATUS ServiceStatus;

BOOL fOk = FALSE;

if ((hService = w2kServiceOpen (hManager, pwName)) != NULL)

{

if (QueryServiceStatus (hService, &ServiceStatus))

{

switch (ServiceStatus.dwCurrentState)

{

case SERVICE_STOP_PENDING:

case SERVICE_STOPPED:

{

fOk = (dControl == SERVICE_CONTROL_STOP);

break;

}

case SERVICE_PAUSE_PENDING:

case SERVICE_PAUSED:

{

fOk = (dControl == SERVICE_CONTROL_PAUSE);

break;

}

case SERVICE_START_PENDING:

case SERVICE_CONTINUE_PENDING:

case SERVICE_RUNNING:

{

fOk = (dControl == SERVICE_CONTROL_CONTINUE);

break;

}

}

}

fOk = fOk ||

ControlService (hService, dControl, &ServiceStatus);

w2kServiceClose (hService);

}

return fOk;

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServiceStop (SC_HANDLE hManager,

PWORD pwName)

{

return w2kServiceControl (hManager, pwName,

SERVICE_CONTROL_STOP);

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServicePause (SC_HANDLE hManager,

PWORD pwName)

{

return w2kServiceControl (hManager, pwName,

SERVICE_CONTROL_PAUSE);

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServiceContinue (SC_HANDLE hManager,

PWORD pwName)

{

return w2kServiceControl (hManager, pwName,

SERVICE_CONTROL_CONTINUE);

}

// -----------------------------------------------------------------

SC_HANDLE WINAPI w2kServiceLoad (PWORD pwName,

PWORD pwInfo,

PWORD pwPath,

BOOL fStart)

{

BOOL fOk;

SC_HANDLE hManager = NULL;

if ((hManager = w2kServiceConnect ()) != NULL)

{

fOk = w2kServiceAdd (hManager, pwName, pwInfo, pwPath);

if (fOk && fStart)

{

if (!(fOk = w2kServiceStart (hManager, pwName)))

{

w2kServiceRemove (hManager, pwName);

}

}

if (!fOk)

{

hManager = w2kServiceDisconnect (hManager);

}

}

return hManager;

}

// -----------------------------------------------------------------

SC_HANDLE WINAPI w2kServiceLoadEx (PWORD pwPath,

BOOL fStart)

{

PVS_VERSIONDATA pvvd;

PWORD pwPath1, pwInfo;

WORD awName [MAX_PATH];

DWORD dName, dExtension;

SC_HANDLE hManager = NULL;

if (pwPath != NULL)

{

dName = w2kPathName (pwPath, &dExtension);

lstrcpyn (awName, pwPath + dName,

min (MAX_PATH, dExtension - dName + 1));

pwPath1 = w2kPathEvaluate (pwPath, NULL);

pvvd = w2kVersionData (pwPath1, -1);

pwInfo = ((pvvd != NULL) && pvvd->awFileDescription [0]

? pvvd->awFileDescription

: awName);

hManager = w2kServiceLoad (awName, pwInfo, pwPath1, fStart);

w2kMemoryDestroy (pvvd);

w2kMemoryDestroy (pwPath1);

}

return hManager;

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServiceUnload (PWORD pwName,

SC_HANDLE hManager)

{

SC_HANDLE hManager1 = hManager;

BOOL fOk = FALSE;

if (pwName != NULL)

{

if (hManager1 == NULL)

{

hManager1 = w2kServiceConnect ();

}

if (hManager1 != NULL)

{

w2kServiceStop (hManager1, pwName);

fOk = w2kServiceRemove (hManager1, pwName);

}

}

w2kServiceDisconnect (hManager1);

return fOk;

}

// -----------------------------------------------------------------

BOOL WINAPI w2kServiceUnloadEx (PWORD pwPath,

SC_HANDLE hManager)

{

DWORD dName, dExtension;

WORD awName [MAX_PATH];

PWORD pwName = NULL;

if (pwPath != NULL)

{

dName = w2kPathName (pwPath, &dExtension);

lstrcpyn (pwName = awName, pwPath + dName,

min (MAX_PATH, dExtension - dName + 1));

}

return w2kServiceUnload (pwName, hManager);

}

// -----------------------------------------------------------------

列表3-8. 服务和驱动管理库函数

....................待续.........................

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有