`
isiqi
  • 浏览: 16588501 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

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

阅读更多

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

2006-01-09 09:48 作者: NorthTibet编译 出处: VCKBASE 责任编辑:方舟
  摘要

  本文讨论用各种不同的方法来获取系统中运行的进程信息,比如,进程列表,枚举列表中的进程,然后获取关于进程的详细信息。

  如何获取运行进程列表

  有三种方法来获取Win32运行进程的信息,参见表一:

  (表一)
方法 平台 备注
PSAPI Windows NT,Windows2000,Windows XP 获取进程,驱动器,模块,内存和工作集信息
性能计数器 Windows NT,Windows2000,Windows XP 提供除进程清单以外的关于进程的更多信息,可在远程机器上使用。
TOOLHELP32 Windows 9x,Windows2000,Windows XP 获取进程,线程,模块和堆信息

  本文不打算讨论 TOOLHELP32,因为 MSDN 中提供了很多使用 TOOLHELP32 函数的例子代码。性能计数器提供的信息更多,不仅仅是进程清单。如果你想获取远程机器的信息,那么性能计数器是再好不过的工具了。如果你总是想得到另外一台机器的进程列表信息,那么就用性能计数器吧!

  进程状态 API(PSAPI 全称是 Process Status API)是微软 SDK 中一个很有用的工具,在例子程序中有一个类 CProcessList,其实现文件 Process.cpp 对 PSAPI 进行了打包,用这个类可以获取进程清单。只要 Refresh 一被调用,通过某个进程的ID便可获得此进程的描述信息,并很容易用 GetFirst 和 GetNext 列举出其它进程:

   //用 CProcessList 列出运行进程 
   // 一个挨一个获取进程
   CProcess* pProcess = NULL;
   POSITION  Pos = 0;
   for (
         pProcess = ProcessList.GetFirst(Pos); 
         (pProcess != NULL); 
         pProcess = ProcessList.GetNext(Pos)
       )
   {
      if (pProcess != NULL)
      {
      // 对进程信息进行处理
      }
   }		
  Refresh 的实现用到了 EnumProcesses 函数(在PSAPI中):
//刷新进程列表 
void CProcessList::Refresh()
{
// 不要忘了重置和释放当前的进程列表
   DefaultReset();

// 存储当前进程列表
   DWORD aProcesses[MAX_PROCESS];
   DWORD cbNeeded = 0;

// 获取进程快照
   if (!g_PSAPI.EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
      return;
    
// 计算返回了多少个进程IDs
   DWORD cProcesses = cbNeeded / sizeof(DWORD);

// 将CProcess 对象捆绑到每一个进程ID
   DWORD     dwProcessID;
   CProcess* pProcess;
   for (
         DWORD dwCurrentProcess = 0; 
         dwCurrentProcess < cProcesses; 
         dwCurrentProcess++
       )
   {
      dwProcessID = aProcesses[dwCurrentProcess];

   // 将进程信息添加到映射
      pProcess = new CProcess(TRUE);
      if (pProcess != NULL)
      {
      // 填写当前进程ID的进程信息--捆绑
         if (!pProcess->AttachProcess(aProcesses[dwCurrentProcess]))
            delete pProcess;
         else
         // 存到映射表中
            m_ProcessMap[(LPVOID)dwProcessID] = pProcess;
      }
   }

// 第二次循环需要知道此进程的子进程清单
   SetChildrenList();
}		
  如果你至今还在支持 Windows 的 16 位代码,比如在任务管理器的 ntvdm.exe 项下列出的应用程序就属于此列,那么枚举进程更棘手。有关细节参见本文参考资料中的知识库文章,以及 Matt Pietrek 的专栏文章:August 1998 和 September 1998。

  如何获取进程信息

  有了运行进程的列表,接下来就是根据 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)来获取进程的宿主。

分享到:
评论

相关推荐

    别再掉进DLL地狱的陷阱里(DLL_Hell

    别再掉进DLL地狱的陷阱里(DLL_Hell)~.NET解决之道

    DLL.rar_dll地狱

    **DLL地狱:问题详解与解决方案** DLL(动态链接库)是Windows操作系统中一个重要的编程元素,它允许多个程序...通过良好的编程实践和利用现代编程框架,我们可以将DLL地狱带来的困扰降至最低,让开发过程更加顺畅。

    VC6获取主机信息,cpu信息,内存信息,硬盘信息,dll调用

    在VC6环境下,开发人员经常需要获取系统的各种硬件和软件信息,例如主机信息、CPU信息、内存信息、硬盘信息以及动态链接库(DLL)的调用情况。这些信息对于系统监控、性能分析以及软件优化至关重要。下面将详细介绍...

    c++获取进程信息列表和进程所调用的dll列表

    在C++编程中,获取进程信息列表以及进程所调用的DLL列表是系统监控和调试的重要环节。本文将详细介绍如何使用C++实现这一功能,并提供相关的代码示例。 首先,我们需要了解两个关键的Windows API函数:`...

    getprocessinfo 获取进程模块信息.rar_getprocessin_优化算法_获取_获取 进程_进程信息

    在Windows操作系统中,获取进程模块信息是系统管理和性能监控的重要环节。这涉及到对系统底层运行机制的理解,包括进程、线程、以及它们所载入的模块。本文将深入探讨`GetProcessInfo`函数以及与之相关的优化算法,...

    易语言创建进程注入DLL

    在创建进程注入DLL的过程中,可能会用到`CopyMemory`来复制数据到目标进程的内存空间,比如复制DLL的路径或者控制信息。 综合以上,创建进程注入DLL的基本流程如下: 1. 使用`SN_CreateProcessA`启动目标进程,并...

    C# DLL 进程注入示例。

    之后,使用`Kernel32.dll`库中的`OpenProcess`函数获取进程句柄。 3. **加载DLL**:接下来,使用`LoadLibrary`函数将DLL加载到目标进程中。由于这是系统级别的操作,我们需要使用P/Invoke(平台调用)来调用WinAPI...

    定制调试诊断工具和实用程序——摆脱DLL地狱(DLL Hell)的困扰VC源代码

    在 Windows 系统中,动态库版本冲突实在是一个老大难的问题了,为了解决这个问题,除了使用大量现有的工具外,你还可以利用丰富的 ...关键字:instrumentation, tool,utility,dll hell,dll,debug,调试,工具,实用程序

    易语言结束进程中的DLL

    易语言结束进程中的DLL源码,结束进程中的DLL,提升进程权限,列表,表项,取模块路径,列调用的模块,结束进程中DLL,Module32First,ExtractIconA,打开进程,获取线程,打开令牌,恢复权限,获取令牌特权,CreateToolhelp32...

    进程模块dll查看器dll查看器

    进程模块DLL查看器是一款强大的系统工具,主要用于查看操作系统中各个进程加载的动态链接库(Dynamic Link Library, DLL)信息。DLL是Windows操作系统中一个重要的组件,它包含可由多个程序共享的代码和数据,以此来...

    SetWindowsHookEx进程dll注入

    2. 获取进程句柄:通过OpenProcess函数获取目标进程的句柄,需要适当的访问权限,如 PROCESS_ALL_ACCESS 或者 PROCESS_VM_OPERATION。 3. 计算内存地址:确定DLL的入口点(例如DLLMain)在目标进程中的内存地址。这...

    exe将dll注入到explorer.exe资源管理器进程_DLL注入示例.injectdll

    2. 获取进程句柄:DLL注入的目标是explorer.exe进程。你需要获取这个进程的句柄,这通常通过`OpenProcess`函数完成,需要提供进程ID和适当的访问权限。 3. 加载DLL:然后,使用`VirtualAllocEx`在目标进程的地址...

    DLL进程注入器 把DLL文件注入到选定进程中

    1. **获取进程句柄**:首先,你需要知道目标进程的进程ID,然后使用`OpenProcess`函数获取该进程的句柄,这允许你对目标进程进行操作。 2. **加载DLL**:接下来,你需要使用`VirtualAllocEx`函数在目标进程的地址...

    PID获取进程用户名的软件源码

    这段代码首先通过`OpenProcess`函数获取指定PID的进程句柄,然后使用`GetProcessImageFileNameA`获取进程的完整路径,从中我们可以获取到进程的基本信息。接着,我们需要获取进程的SID(安全标识符),这通常涉及到...

    DLL注入源码(远程进程注入)

    - 如何获取和验证目标进程信息 - 如何正确分配和写入内存以加载DLL - 如何创建和管理远程线程 - 如何处理可能的错误和异常 在研究源码的过程中,还可以结合Windows API文档和调试工具(如Visual Studio Debugger或...

    易语言源码枚举句柄关闭进程DLL模块源码.rar

    2. **获取进程信息**:利用`GetProcessImageFileName`或`QueryFullProcessImageName`函数获取进程的可执行文件路径,进一步可以获取进程名称。 3. **枚举DLL模块**:对每个进程,使用`EnumProcessModules`函数获取...

    VC 6.0从Dll中获取 数据的实现方法.rar

    VC 6.0从Dll中获取 数据的实现方法,主要功能是调用Dll进程数,判断当前调用Dll的线程有多少个。显示当前调用了多少个进程:  CString strMessage = _T("");  strMessage.Format(_T("当前共%d个进程调用DLL。"), ...

    易语言创建进程注入DLL文件

    易语言创建进程注入DLL文件。@qq1347522182。

    C++进程注入(使用DLL)

    首先,你需要获取目标进程的句柄,然后在目标进程的地址空间中分配内存来存储DLL的路径。接着,创建一个新的远程线程,使其执行`LoadLibrary`函数,加载你之前分配内存的DLL路径。这样,目标进程就会执行DLL中的代码...

    驱动注入DLL指定进程.rar_dll注入_驱动 dll注入_驱动注入_驱动级注入dll_驱动进程

    驱动程序的主要任务是获取目标进程的信息,创建线程并执行DLL注入。 2. **获取目标进程信息**:驱动程序通过系统API函数,如 ZwQuerySystemInformation 或 NtQuerySystemInformation,获取目标进程的句柄、基地址等...

Global site tag (gtag.js) - Google Analytics