分类: C/C++
2008-03-14 14:52:45
|
列举加载的模块 //
//通过取得ToolHelp32 进程快照,枚举此进程的模块列表
//
HANDLE hSnapshotModule;
hSnapshotModule = pfnCreateToolhelp32Snapshot( TH32CS_SNAPMODULE,
procEntry.th32ProcessID );
if ( !hSnapshotModule )
continue;
// 迭代快照中每一个模块
MODULEENTRY32 modEntry = { sizeof(MODULEENTRY32) };
BOOL fModWalkContinue;
for (fModWalkContinue = pfnModule32First(hSnapshotModule,&modEntry);
fModWalkContinue;
fModWalkContinue = pfnModule32Next(hSnapshotModule,&modEntry) )
{
// 确定是否为EXE文件本身,如果是,则不将它加入模块列表
if ( 0 == stricmp( modEntry.szExePath, procEntry.szExeFile ) )
continue;
// 确定是否为我们已有的DLL
PModuleInstance pModInst = modList.Lookup(modEntry.hModule,
modEntry.szExePath );
// 如果以前没有见过,则将它加入列表
if ( !pModInst )
pModInst = modList.Add( modEntry.hModule, modEntry.szExePath );
// 将此进程加入到使用此DLL的进程列表
pModInst->AddProcessReference( procEntry.th32ProcessID );
}
CloseHandle( hSnapshotModule ); // 完成模块列表快照
其实并不是程序有什么瑕疵,主要是时过境迁,导致代码中一个if语句的使用无效,毕竟 Matt Pietrek 写那篇文章的时候(其代码是1998.9 在 MSJ 上发布的),Windows 2000 还不知道在哪里呢!那个无效的 if 语句是这样的:由于 CreateToolhelp32Snapshot 调用失败时不会返回 NULL,所以下面的错误处理代码是无效的: if ( !hSnapshotModule ) continue;实际上,如果失败,hSnapshotModule的值为INVALID_HANDLE_VALUE或-1,并且这个if语句是捕获不到它的,这到没什么,关键是如何发现这个bug。当我在Windows 2000上测试ProcessSpy时,一切运行正常,只是当列表框即便为空的时候,程序也没有返回某些进程的出错信息。由于错误处理代码本身是错的,执行跳过了循环,Module32First调用失败,但没有任何实质性的错误。如果你在Windows 2000环境用Matt Pietrek的这篇文章提供的ModuleList工具,你将得到不正确的结果。 为了搞清楚代码运行中发生的事情,用本文实例代码包含的Helpers.cpp 文件中提供的GetLastErrorMessage辅助函数可以有助于你看得更清楚。他调用GetLastError 和 FormatMessage以纯文本形式获取相应的失败原因。失败原因都一样:Access Denied,也就是拒绝存取。但是使用PSAPI函数时,当获得相同进程的模块列表时不存在存取问题。 之所以发生存取问题,是由于缺乏优先级。使用TOOLHELP32 的代码要正常工作必须得有 SE_DEBUG_NAME 优先级。有关这个问题的详细信息,请参考 1998.3 MSJ 的 专栏以及 1999.8 的 Security Briefs 专栏 关于 DLL 的方方面面 用 PSAPI 和 TOOLHELP32 两种途径获得的某个进程所加载的模块列表只反映地址,在这个地址处,DLL被映射到地址空间。下一步便是尽可能完整地获取关于DLL的描述。我的实现并不象在CProcess中所做的那样提供单独的 AttachModule 方法。因为要获取某些细节信息代价实在太高,因此我选择将它们分割成不同的函数。最不值钱的信息从 CModule 的构造函数获得,其它信息的获取要到相应的存取器方法被调用(通过 Refresh 函数)。实现细节请参考 Module.cpp 文件。其 Refresh 方法模仿了 Matt Pietrek 的 CModuleList 中的Refresh/RefreshTOOLHELP32 方法。表三列出了 CModule 的存取器方法:
我前面提到过,想要获取模块的全路径名需要一点诀窍。由于一些原因,GetModuleFilenameEx 或 TOOLHELP32 模块函数返回的模块名很奇怪,它们不遵循 Win32 的命名标准。例如以smss为例,返回的名字是"\SystemRoot\System32\smss.exe",这里"\SystemRoot"必须用Windows文件夹的实际名字来替换。又如 wonlogon.程序,返回的名字是"\??\C:\WINNT\system32\winlogon.exe",应该转换成"C:\WINNT\system32\winlogon.exe"。"\??\"前缀是 Windows NT 名字空间的残留物,是 kernel 模式中的东西,即便是Win32编程也很少用到它。我写了一个辅助函数 TranslateFilename 用于将这些文件名转换成更标准的形式。此函数的细节请参考下载源代码中的Helpers.cpp 文件。
SHELLEXECUTEINFO sei;
ZeroMemory(&sei,sizeof(sei));
sei.cbSize = sizeof(sei);
sei.lpFile = szFilename;
sei.lpVerb = _T("properties");
sei.fMask = SEE_MASK_INVOKEIDLIST;
ShellExecuteEx(&sei);
在 ProcessSpy 程序界面底部窗格中任何一个模块记录上双击鼠标便可以调出文件的属性对话框,相应模块文件的描述信息一目了然。注意 Windows XP 中不支持多个 ShellExecuteEx 调用,当你调用第二次时,线程冻结,也不会有任何提示。正如你所看到的,有许多方法都可以获得加载 DLLs 以及活动进程的信息。我在本文中提供的几个工具可以作为一个很好的学习开端,你完全可以借鉴文本描述的方法以及所提供的 C++ 类来定制满足自己需要的调试工具。 参考资料 在后续文章中,我将介绍从系统获取信息的新方法。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
作者简介Christophe Nasarre 是法国 Business Objects 公司的技术经理(technical manager)。他在 Windows 平台上(3.0 以后的版本)编写了若干个低级工具。他的联系方式:cnasarre@montataire.net. . | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 本文出自 的 期刊,可通过当地报摊获得,或者最好是 |