用户名: 密码: 免费注册 忘记密码? 网站地图 | 加入收藏 | 设为首页
首页 | 新闻 | 工具 | 系统 | 办公 | 聊天 | 多媒体 | 网页 | 运营 | 平面 | 欣赏 | 数据库 | 程序 | 服务器 | 组网
网页 | 3dmax | Ghost | Windows Xp| Dreamweaver | photoshop | Flash | office | Alexa | Css | QQ | Asp | PHP | Jsp | Access
Flash MX 2004入门 | 网站推广策略 | CorelDRAW入门 | ASP学习 | 网站建设大师功 | Word入门
  iTbulo.com > 学院 > 操作系统教程 > Windows2000教程 > 文章正文
《Undocumented Windows 2000 Secrets》翻译 --- 3
iTbulo.COM 2005-7-28 ccxunmeng()

EnumProcessModules()返回指定进程所有模块的句柄的引用。在Windows 2000中,一个HMODULE只是简单的模块映像基址。在SDK头文件windef.h中,HMODULE被定义为HINSTANCE的别名,二者都是HANDLE类型。严格的来讲HMODULE并不是一个句柄。通常,句柄是系统管理的一个表的索引,可通过此表来查找对象属性。系统返回的所有句柄都有一个与特定对象相关的计数器,在一个对象的所有句柄没有返回系统时,该对象不能从内存中被移除。Win32 API提供了CloseHandle()函数用于关闭句柄。该函数与Native API NtClose()等价。有关HMODULEs最重要的事情是,这些“handles”不需要关闭。

另一件让人困惑的事是,事实上,模块句柄通常并不被保证是一直有效的。SDKGetModuleHandle()函数文档提示到,在多线程程序中必须更加注意模块句柄,因为一个线程可以通过卸载HMODULE引用的模块而让另一个线程拥有的HMODULE无效。在多任务环境下,一个程序(如调试器)使用另一程序的模块句柄时也许注意这一点。这似乎使HMODULEs没有多大用处了,但是,在下面两种情况中,HMODULE的有效性会保持足够长的时间:

1.由LoadLibrary()LoadLibraryEx()返回的HMODULE在进程调用FreeLibrary()之前都会一直有效,由于这些函数包含了模块引用计数,所以即使在多线程程序中,这也会阻止模块被意外卸载。

2.如果HMODULE指向的模块会永久的存在,那么它也会一直有效。例如,所有Windows 2000内核组件(不包括内核模式的驱动程序)总是被映射到每个进程的相同固定地址上,并且在进程生命期里一直在那里。

不幸地是,这些情况并不适用于EnumProcessModules()函数返回的模块句柄,至少通常不行。复制到调用者提供的缓冲区中的HMODULE,在获取进程快照那一刻其所表示映像基址是有效的。稍后,进程可能调用FreeLibrary()来释放一个或多个模块,并将其从内存中移除,此时它们的句柄将无效,随后进程很有可能立即调用LoadLibrary()加载了另一个DLL,而此新模块恰好映射到了前面释放的地址上。这看上去是不是很熟悉?是的,同样的问题也存在于EnumDeviceDrivers()的指针数组和EnumProcesses()函数的ID数组。不过,这些问题是可以避免的。psapid.dll通过调用未文档化的API函数来完成数据收集工作后,考虑这些数据的完整性,可返回一个完整的请求对象的快照,其中应包括所有感兴趣的属性信息。这样就没有必要在稍后调用另一个函数来获取附加的信息了。我的观点是,psapi.dll的设计过于简单,因为它忽略了数据的完整性,这也是我不会将此DLL作为一个专业调试工具的基础的原因。

EnumDeviceDrivers()EnunProcesses()函数相比EnumProcessModules()函数算是个好公民了,因为如果调用者提供的缓冲区不能放下全部的输出数据,它会准确地提示有多少字节没有复制。注意列表1-7没有包括一个循环,在那里缓冲区会不断增大直到足够的大。然而,仍然需要trial-and-error循环,因为在下一次调用时,EnumProcessModules报告的所需大小可能已经无效了(如果指定进程在两次调用之间又加载了新的模块)。因此,列表1-7中的代码将不断枚举模块直到EnumProcessModules()报告需要的缓冲区等于或小于实际可用大小,或者出现了错误。

我不想描述EnumProcessModules()的等价函数,因为该函数要比EnumDeviceDriversEnumProcesses稍微复杂些,它涉及几个未文档化的数据结构。基本上,它还是通过调用NtQuerySystemInformation()函数(当然,该函数也没有文档化)来获取目标进程环境块(PEB)的地址,通过该地址可获取一个模块信息链表。因为不管是PEB还是这个链表在调用进程的地址空间都是无法直接使用的,EnumProcessModules调用Win32 API ReadProcessMemory()(该函数有文档记载)来遍历目标进程的地址空间。顺便说一下,PEB结构的布局将在第7章讨论,在附录C中,可以找到该结构的定义。

调整进程特权

回忆一下稍早讨论过的有关EnumProcessModules所需的进程句柄。通常,你首先得到的是进程ID---可能是EnumProcesses返回的进程ID集中的一个。Win32 API OpenProcess()可通过进程ID来获取其句柄。这个函数期望一个访问标志符作为其第一个参数。假定进程ID存放在一个DWORD类型的变量dId中,你以最大访问权限来调用OpenProcess,如下:

OpenProcess(PROCESS_ALL_ACCESSFALSEdId)

以获取该进程的句柄,此时你会收到一个针对几个低ID进程的错误代码。这并不是bug---这是安全特性!这些进程都是保持系统活动的系统服务。一个普通用户进程不允许执行针对系统服务的所有操作。例如,允许所有进程都可以杀死系统中其余进程并不是个好主意。如果一个程序意外终止了一个系统服务,那么整个系统都将崩溃。因此,一个进程只有拥有确切的访问权限才会有适当的特权。

由于多种原因,调试器必须拥有大量的权限来完成他的工作。改变进程的特权可通过以下三个简单的基本步骤:

1.首先,必须打开进程的访问令牌(access token),使用advapi32.dll中的函数OpenProcessToken()

    2.如果上一步正确完成,接下来就是准备TOKEN_PRIVILEGES结构,该结构包含有关要请求的特权的信息。这个工作需要advapi32.dll中的另一个函数LookupPrivilegeValue()的帮助。特权通过名称来指定。SDK文档winnt.h定义了27中特权名称和其对应的符号名称。例如,调试权限的符号名称为:SE_DEBUG_NAME,该名称和字符串“SeDebugPrivilege”等效。

3.如果上一步正确完成,就可以使用进程的令牌句柄(Token Handle)来调用AdjustTokenPrivileges()函数以初始化TOKEN_PRIVILEGES结构。该函数也是advapi32.dll导出的。

如果OpenProcessToken()调用成功,要记得关闭其返回的令牌句柄(Token Handle)。w2k_dbg.dll包含一个dbgPrivilegeSet()函数,该函数合并了这几个步骤,下面的列表1-8列出了该函数和w2k_dbg.dll中的另一个函数:dbgPrivilegeDebug()。此函数是dbgPrivilegeSet()的一个外包函数,为了便于设定调试特权。顺便说一下,Windows NT Server资源工具集中的kill.exe也使用了同样的技巧。Kill.exe需要调试特权来剔除内存中饿死的服务(starved services)。这是NT Server管理员不可缺少的一个工具,这对于重起一个挂掉的系统服务十分有用,而且可以避免不必要的系统重启。对于使用IISInternet Information Server)的人,在他们的紧急工具箱中可能都有这个工具,以便重起偶尔挂掉的inerinfo.exe

BOOL WINAPI dbgPrivilegeSet(PWORD pwName)

{

       HANDLE           hToken;

       TOKEN_PRIVILEGES tp;

       BOOL             fOk = FALSE;

       if ( (pwName != NULL) &&

              OpenProcessToken(GetCurrentProcess(),

                               TOKEN_ADJUST_PRIVILEGES,

                               &hToken) )

       {

              if ( LookupPrivilegeValue(NULL,pwName,&tp,Privileges->Luid) )

              {

                     tp.Privileges->Attributes = SE_PRIVILEGE_ENABLED;

                     tp.PrivilegeCount         = 1;

                     fOk = AdjustTokenPrivileges(hToken,FALSE,&tp,0,NULL,NULL)

                              && (GetLastError() == ERROR_SUCCESS);

              }

              CloseHandle(hToken);

       }

       return fOk;

}

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

BOOL WINAPI dbgPrivilegeDebug(void)

{

       return dbgPrivilegeSet(SE_DEBGU_NAME);

}

列表1-8  Requesting a Privilege for a Process

上一页  [1] [2] [3] 

文章搜索
相关资讯
相关文章 相关下载
没有相关文章
焦点信息