Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1333839
  • 博文数量: 953
  • 博客积分: 52320
  • 博客等级: 大将
  • 技术积分: 13090
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-05 13:42
文章分类

全部博文(953)

文章存档

2011年(1)

2008年(952)

我的朋友

分类: C/C++

2008-08-05 14:00:20

下载本文示例代码
下载源代码:Debugsrc0206.exe (583KB)
原文出处:Windows XP:Escape from DLL Hell with Custom Debugging and Instrumentation Tools and Utilities


本文假设你熟悉 Win32,DLL
  • 定制调试诊断工具和实用程序——摆脱DLL"地狱"(DLL Hell)的困扰(一)
  • 定制调试诊断工具和实用程序——摆脱DLL"地狱"(DLL Hell)的困扰(二)
  • 定制调试诊断工具和实用程序——摆脱DLL"地狱"(DLL Hell)的困扰(三)
  • 定制调试诊断工具和实用程序——摆脱DLL"地狱"(DLL Hell)的困扰(四)

列举加载的模块

  任何时候通过 PSAPI 或 TOOLHELP32 都可以列出某个进程加载的 DLLs 列表。在写此文前的调研过程中,我研究了 Matt Pietrek 以前在 MSJ Under The Hood 专栏中的一篇文章,其内容是讨论如何使用 TOOLHELP32 来实现前述的功能,我发现在 Windows 2000 和 Windows XP 环境中是有问题的,代码不能正常工作,现将其代码摘录如下:

用TOOLHELP32遍历模块


//

//通过取得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 的 Q&A Win32 专栏以及 1999.8 的 Security Briefs 专栏


关于 DLL 的方方面面


  用 PSAPI 和 TOOLHELP32 两种途径获得的某个进程所加载的模块列表只反映地址,在这个地址处,DLL被映射到地址空间。下一步便是尽可能完整地获取关于DLL的描述。我的实现并不象在CProcess中所做的那样提供单独的 AttachModule 方法。因为要获取某些细节信息代价实在太高,因此我选择将它们分割成不同的函数。最不值钱的信息从 CModule 的构造函数获得,其它信息的获取要到相应的存取器方法被调用(通过 Refresh 函数)。实现细节请参考 Module.cpp 文件。其 Refresh 方法模仿了 Matt Pietrek 的 CModuleList 中的Refresh/RefreshTOOLHELP32 方法。表三列出了 CModule 的存取器方法:

  下载本文示例代码

存取器

说明

HMODULE GetModuleHandle

DLL被映射的地址

CString& GetFullPathName

源自TOOLHELP32::Module32xxx PSAPI::GetModuleFilenameEx

CString& GetPathName

GetFullPathName

CString& GetModuleName

GetFullPathName

阅读(107) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~