正 文

摆脱DLL地狱的困扰之获取进程信息


www.7dspace.com  更新日期:2006-1-10 1:20:57  七度空间



  如何获取进程信息

  有了运行进程的列表,接下来就是根据 EnumProcesses 返回的进程IDs尽可能多的获取每一个进程的详细信息,然后根据这些信息建立有用的工具。用PROCESS_QUERY_INFORMATION | PROCESS_VM_READ作为参数,调用OpenProcess获取进程句柄,然后用AttachProcess(参见Process.cpp文件中的CProcess类实现)方法创建进程描述。表二中列出的是CProcess用于获取进程细节信息的函数:

  (表二)
方法 描述
GetName 以NULL作为参数,调用 GetModuleBaseName ,最后去掉扩展名 “.EXE”
GetFileName 以NULL作为参数,调用 GetModuleFileNameEx
GetMainWindowHandle 参见GetMainWindowHandle
GetMainWindowTitle
GetParentProcessID 用ProcessBasicInformation作为参数调用NtQueryInformationProcess
GetKERNELHandleCount 用ProcessHandleCount作为参数调用NtQueryInformationProcess
GetUSERHandleCount 用GR_USEROBJECTS作为参数调用GetGuiResources
GetGDIHandleCount 用GR_GDIOBJECTS作为参数调用GetGuiResources
GetWorkingSet 调用GetProcessMemoryInfo
GetCmdLine 参见GetProcessCmdLine
GetOwner 参见GetProcessOwner的细节
GetSessionID ProcessIdToSessionId (参见对快速用户转换的讨论部分——Windows XP的一个新特性)
GetModuleList CModuleList是一个对EnumProcessModules 和GetModuleFileNameEx的打包类
GetChildrenCount 以及子进程清单 要获取某个进程的子进程列表,目前还没这样的API(即便有也未公开)可供使用。但是,因为某个进程的父进程是已知的,所以将某个进程加到其父进程的子进程列表中不难(参见SetChildrenList的实现)

  这里有几个关于AttachProcess的细节问题需要解释一下。首先,为了避免与PSAPI或 NTDLL这样的操作系统特有的DLLs进行静态链接,编写一个类对我们需要的从这些DLLs中输出的函数进行打包是值的得--有关细节可以参考例子代码中的Wrappers.h和Wrappers.cpp文件。这样的话,你只需要定义一个CPSAPIWrapper对象并调用它的 GetModuleFileNameEx方法即可,不用链接到PSAPI库。另外,你应该调用IsValid方法来检查这些DLLs在你运行系统中是可用的。如此一来你的代码便可以运行在任何Windows平台而不会产生诸如某某函数未定义之类的链接错误。注意在使用某个专门的特性之前,你应该检查一下 Windows的版本或IsValid的返回结果(参见DllSpy例子代码中的DllSpyApp::InitInstance部分)。

  注意PSAPI中的GetModuleFileNameEx函数返回的文件名很奇怪:如:"\SystemRoot\ System32\ smss.exe"或者"\??\C:\WINNT\system32\winlogon.exe"等等。谁知道这是什么意思?在Helper.cpp中有一个函数TranslateFilename专门对此进行转换,将这些文件名转换成更容易理解的名字。稍候会我们还会谈到这个函数。

  接下来我们讨论如何寻找某个进程的主窗口,EnumWindows有一个参数是回调函数,此回调函数的作用是接收顶层窗口句柄,在这个问调函数中,我们要调用GetWindowThreadProcessId来获取创建相应窗口的进程ID,如果找到这个窗口(可见的)便停止枚举(详情请参见 GetMainWindow实现)。函数GetWindowText可以被用来获取某个不同进程的窗口标题。

  在Windows NT 和Windows 2000里,为了获取与创建某个特定窗口的进程对应的文件名字,不能像以前那样用GetWindowModuleFileName函数,你会毫无所获,这个函数总是返回当前运行进程的路径名。

  获取某个进程主窗口的详细过程描述可以参考Jeff Prosise在MSJ Aug99上的Wicked Code专栏文章。现在你已经知道了如何通过某个已知的进程ID,调用PSAPI函数来获取全路径名。然后利用这个路径名并调用 GetWindowThreadProcessID函数获取创建某个特定窗口的进程文件名。

  在AttachProcess中必须调用 OpenProcess来获取大多数的进程信息,但是有可能出现拒绝访问的错误。如果出现这种情况,我用了一个Keith Brown给出的方法,参见他在MSJ Aug99中的"Security Briefs"专栏文章,其中详细讨论了如何用高级别权限获取进程句柄。细节请参见例子代码的Helpers.cpp文件,其中有一个函数名叫 GetProcessHandleWithEnoughRights,就是出自Keith Brown之手。

  当某个进程是作为另外一个用户账号计划任务而运行的时候,就会出现上述提到的拒绝访问问题。即便是Windows任务管理器都无法终止这样的进程,它只显示一个象下面这样的对话框。如图九:


图九 无法终止的进程

  如果你在ProcessSpy例子程序里的某个进程上双击鼠标按键,它将终止这个进程。你看一下在例子代码中的SlayProcess函数就会知道。此辅助函数调用了GetProcessHandleWithEnoughRights来获取进程句柄,但访问权限参数是 PROCESS_TERMINATE,而不是在AttachProcess里所用的PROCESS_QUERY_INFORMATION | PROCESS_VM_READ。

  最后是用GetProcessOwner获取进程运行的用户账号(格式为\\Domain\ User),通过用TokenUser作为参数调用GetTokenInformation,然后用LookupAccountSid将返回的用户SID 转换为人可读的域名和用户名。有时OpenProcessToken会因为遇到象System这样的进程而调用失败,甚至是Windows 2000 资源开发包中的PULIST.EXE遇到这种情况都无法显示出拥有进程的用户。只有ProcessExplorer(Sysinterals公司开发的一个工具软件)能成功找到此"安全的"应用的所有者。本文稍后会讨论Windows XP中如何用WTS APIs(也就是Windows Terminal Services API--Windows终端服务API)来获取进程的宿主。
2页,页码:[1] [2] 

上一篇:摆脱DLL地狱困扰之目前的困境
下一篇:Photoshop制作宣纸底纹与墨韵特效
作者:NorthTibet编译  来源:VCKBASE ( 责任编辑:7dspace )
收藏此页】【打印】【关闭
站 内 搜 索
 

热 点 导 读
特 别 推 荐