65.9K
CodeProject 正在变化。 阅读更多。
Home

进程查看器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (99投票s)

2007 年 5 月 8 日

CPOL

9分钟阅读

viewsIcon

486375

downloadIcon

22107

列出了系统中正在运行的进程的详细信息、加载的驱动程序、加载的 DLL、每个 DLL 和进程的版本、进程时间、命令行、所有者、优先级、GDI 资源使用情况、权限、加载的符号、窗口层次结构、自动启动应用程序查找等。

下载 ProcViewer.zip - 195.9 KB
下载 Exe ProcessViewer.zip - 118.1 KB

Screenshot - ProcessViewer.jpg

选项对话框

Screenshot - Options.jpg

权限管理器对话框

PrivilegeMgr.JPG

引言

一个实用程序,列出了系统中所有正在运行的进程以及它们加载的 DLL,还显示了加载 DLL 的路径、它们的加载地址、DLL 的基址、映像大小以及每个进程的信息。

此工具的功能

  1. 显示进程加载的所有 DLL
    • 每个 DLL 中的符号,您可以点击其中任何一个
      • 目录
      • 名称
      • 地址
      • 大小
    • 每个 DLL 的版本
    • DLL 的描述
    • 公司名称
    • 如果启用了依赖关系查看,则使用依赖关系查看器查看每个 DLL
    • 完整模块路径,即它从何处加载?
    • 加载地址
    • 入口点
    • 映像大小
    • 加载顺序
  2. 如果您点击主内核进程,将显示系统中加载的所有驱动程序
  3. 进程 ID
  4. 进程所有者
  5. 进程命令行
  6. 进程自动启动信息
  7. 应用程序类型,即应用程序是控制台、Windows 还是 MS-DOS 应用程序。
  8. 进程优先级
  9. GUI 资源使用情况统计(未来版本将扩展)
  10. 每个进程的大小
  11. 每个进程中的符号
  12. 窗口详细信息(如果有)
    • 以正确的层次结构显示窗口,即父窗口的所有子窗口都包含在其节点下。
    • 文本或标题
    • HWND
    • 窗口过程
    • 图标(如果有)
    • 光标
    • 窗口类,例如工具栏的 Toolbarwindow32。
    • 拥有该窗口的线程
    • 窗口状态,即最大化、最小化、还原或隐藏
    • 窗口边界
    • 窗口 ID
    • 父窗口 HWND
    • 窗口类样式
    • 窗口样式
    • 所有子窗口将以层次结构显示
  13. 进程时间
    • 创建时间
    • 退出时间,即杀死一个进程然后您会看到此时间已更新
    • 内核模式时间,使用了多少内核时间
    • 用户模式时间
  14. 文件时间
    • 创建时间
    • 上次访问时间
    • 上次写入时间
  15. IO 计数器
    • 输入和输出计数器值
  16. 内存详细信息,页面错误计数等
  17. 进程版本,您还可以通过工具提示获得一些信息
  18. 进程的权限以及权限管理器
  19. 视图排序
  20. 布局切换
  21. 杀死一个进程并杀死该进程的所有实例
  22. 搜索进程或加载的 DLL/驱动程序
  23. 选项对话框用于关闭不需要的详细信息
  24. 您有一个关于框。 ;)
  25. 还有一个退出按钮。 ;))

使用代码

我使用了 Microsoft 提供的 PSAPI 函数来枚举进程加载的模块。

使用的 API 是:

  1. EnumProcesses -- 获取系统中所有正在运行的进程
  2. EnumProcessModules -- 获取进程加载的所有模块/DLL
  3. GetModuleBaseName -- 获取模块的基本名称...例如:mydll.dll 而不是 C:\AnyLongPath\mydll.dll。
  4. GetModuleFileNameEx -- 获取模块的长路径
  5. GetModuleInformation -- 检索有关模块的信息
  6. GetProcessMemoryInfo -- 获取进程的内存详细信息。
  7. EnumDeviceDrivers -- ID 为 4 (System) 的进程是加载驱动程序的进程,因此我们使用此函数来枚举这些设备驱动程序
  8. GetDeviceDriverBaseName -- 获取设备驱动程序的基本名称
  9. GetDeviceDriverFileName -- 获取设备驱动程序的完整路径
  10. GetProcessTimes -- 获取每个进程花费的时间、启动时间、内核模式和用户模式时间。
  11. SearchPath -- 搜索 PATH 环境变量。文档详细描述了搜索过程。
  12. GetFileVersionInfo -- 用于提取版本。
  13. GetFileVersionInfoSize -- 版本大小
  14. VerQueryValue -- 查询版本组件。有一个名为 FileVersionInfo 的类为此目的而编写。查阅该类以获取有关这些版本函数及其使用方法的更多信息。

这是主函数...

void GetProcessDetails()
{
   // Clear previous associated item data if any 


   ClearItemData();
   // Clear tree view 


   m_ctvProcess.DeleteAllItems(); 
   // Clear list view 


   m_clvProcessDetails.DeleteAllItems(); 
 
   #define MAX 10000
   DWORD dwSize = 0;
   DWORD dwProcIdentifiers[MAX] = { 0 };
   EnumProcesses( dwProcIdentifiers, sizeof( dwProcIdentifiers ), &dwSize );
  
   if( !dwSize )
   {
       return;
   }
 
   // Process count 


   dwSize /= sizeof( DWORD ); 
   HTREEITEM hItemRoot = m_ctvProcess.InsertItem( _T( "Process list" ), 60, 60 );
   TCHAR szName[MAX_PATH] = { 0 };
  
   for( DWORD dwIndex = 0; dwIndex < dwSize; ++ dwIndex )
   {
       HANDLE hProcModule = 
            OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 
                        FALSE, 
                        dwProcIdentifiers[dwIndex] );
       if( !hProcModule )
       {
           continue;
       }

       DWORD dwModuleSize = 0;
       HMODULE hModules[MAX] = { 0 };

       // Get all modules for a process 


       EnumProcessModules( hProcModule, 
                           hModules, 
                           sizeof( hModules ), 
                           &dwModuleSize );
       dwModuleSize /= sizeof( HMODULE );
       if( !dwModuleSize )
       {
          continue;
       }

       // Structure for storing process information 


       PPROC_INFO_t pstProcInfo = new PROC_INFO_t;
       if( !pstProcInfo )
       {
           return;
       }
 
       // Fill out process information 


       pstProcInfo->dwProcId = dwProcIdentifiers[dwIndex];
       GetModuleFileNameEx( hProcModule, hModules[0], szName, MAX_PATH ); 
       pstProcInfo->csFullPath = szName;
       GetModuleBaseName( hProcModule, hModules[0], szName, MAX_PATH );
       pstProcInfo->csBaseName = szName;

       // Get full path display status, if checked then we will display full path 


       const BOOL bFullPathChecked = 
       m_ToolBar.GetToolBarCtrl().IsButtonChecked( ID_OPTIONS_SHOWPATH ); 

       CString csProc;
       csProc.Format( _T( "%s, PID: %lu" ), 
              SCAST( LPCTSTR , 
               ( bFullPathChecked ? 
                 pstProcInfo->csFullPath : pstProcInfo->csBaseName )), 
              dwProcIdentifiers[dwIndex] );

       HTREEITEM hItem = m_ctvProcess.InsertItem( csProc, 20, 20, hItemRoot );
       m_ctvProcess.SetItemData( hItem, RCAST( DWORD, pstProcInfo ));

       // Process details 


       PROCESS_MEMORY_COUNTERS& pmcProcMemCounter = pstProcInfo->stpmcMemCounters;
       GetProcessMemoryInfo( hProcModule, 
                 &pmcProcMemCounter, 
                 sizeof( pmcProcMemCounter ));

       // Insert process memory related values 


       HTREEITEM hPInfoItem = m_ctvProcess.InsertItem( _T( "Details" ), 
                               7, 7, hItem );
       CString csTempStr;
       const int nIconIndex = 63;

       // Page fault count 


       csTempStr.Format( _T( "Page fault count: %lu" ), 
             pmcProcMemCounter.PageFaultCount );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );

       // Page file usage 


       csTempStr.Format( _T( "Page file usage: %.02lf KB" ), 
             pmcProcMemCounter.PagefileUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
       
       // Peak page file usage 


       csTempStr.Format( _T( "Peak page file usage: %.02lf KB" ), 
             pmcProcMemCounter.PeakPagefileUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );

       // Peak working set size 


       csTempStr.Format( _T( "Peak working set size: %.02lf KB" ), 
             pmcProcMemCounter.PeakWorkingSetSize/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );

       // Quota non pages pool usage 


       csTempStr.Format( _T( "Quota non paged pool size: %.02lf KB" ), 
             pmcProcMemCounter.QuotaNonPagedPoolUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );

       // Quota pages pool usage 


       csTempStr.Format( _T( "Quota paged pool size: %.02lf KB" ), 
             pmcProcMemCounter.QuotaPagedPoolUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
 
       // Quota peak non pages pool usage 


       csTempStr.Format( _T( "Quota peak non paged pool size: %.02lf KB" ), 
             pmcProcMemCounter.QuotaPeakNonPagedPoolUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
 
       // Quota peak pages pool usage 


       csTempStr.Format( _T( "Quota peak paged pool size: %.02lf KB" ), 
             pmcProcMemCounter.QuotaPeakPagedPoolUsage/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
 
       // Working set size 


       csTempStr.Format( _T( "Working set size: %.02lf KB" ), 
             pmcProcMemCounter.WorkingSetSize/1024.0f );
       m_ctvProcess.InsertItem( csTempStr, nIconIndex, nIconIndex, hPInfoItem );
 
       // Prepare module list 


       PMODULEINFO_LIST_t* ppModuleInfo = &pstProcInfo->pstModuleInfoList;
       for( DWORD dwModIndex = 1; dwModIndex < dwModuleSize; ++ dwModIndex )
       {
            // Allocate new module 


            *ppModuleInfo = new MODULEINFO_LIST_t;;
            if( !*ppModuleInfo )
            {
                continue;
            }

            // Get module related information 


            GetModuleInformation( hProcModule, 
                  hModules[dwModIndex], 
                  &( *ppModuleInfo )->stmiModInfo, 
                  sizeof( MODULEINFO ));
 
            // Get full path of module 


            GetModuleFileNameEx( hProcModule, 
                 hModules[dwModIndex], 
                 szName, 
                 MAX_PATH );

            ( *ppModuleInfo )->csModuleFullPath = szName;

            // Get base name of module 


            GetModuleBaseName( hProcModule, 
                   hModules[dwModIndex], 
                               szName, 
                               MAX_PATH );
        ( *ppModuleInfo )->csModuleBaseName = szName;
 
            // Move forward 


            ppModuleInfo = &( *ppModuleInfo )->pstNextModuleInfo;

       }// End for 



       // Close process handle 


       CloseHandle( hProcModule );
    }// End for 



    // Expand root node


    m_ctvProcess.Expand( hItemRoot, TVE_EXPAND );
}


PSAPI 函数用法

EnumProcesses

// A large array of DWORDs for storing process ids of processes


DWORD dwProcIds[2048];
DWORD dwProcRunning = 0;
 
// Get all running processes


EnumProcesses( dwProcIds, sizeof( dwProcIds ) /sizeof( dwProcIds[0] ), 
               &dwNeeded );

// To get the count of processes running use


dwProcRunning /= sizeof( DWORD );

EnumProcessModules

// Pass in a large array of modules just to be safe


HMODULE hModuleArray[2048];
DWORD dwModuleCount = 0;
 
// Open process to get it's handle, for some processes 


// we may be denied access.


HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 
                               FALSE, 
                               dwAnyProcessId );
 
// Get processes 


EnumProcessModules( hProcess, hModuleArray, 
                    sizeof( hModuleArray )/ sizeof( hModuleArray[0] ), 
                    &dwModuleCount );
 
// Now get actual count of modules loaded


dwModuleCount /= sizeof( HMODULE );

返回时,数组中的第一个模块是进程的可执行文件。其余模块是实际加载的模块。

GetModuleBaseName

返回模块的基本名称,例如:如果模块路径是 C:\MyPath\Averylongpath\Basename.dll,则此 API 将返回 Basename.dll

TCHAR szName[MAX_PATH] = { 0 }
GetModuleBaseName( hProcess, hModule, szName, MAX_PATH )

GetModuleFileNameEx

与上述函数非常相似,只是它返回所请求模块的完整路径。

GetModuleInformation 和 GetProcessMemoryInfo

这两个 API 易于使用。只需传入一个结构,它就会返回信息。

// Get module related information


MODULEINFO miModInfo = { 0 };
GetModuleInformation( hProcModule, hModule, &miModInfo, sizeof( MODULEINFO ));

// Get process related information


PROCESS_MEMORY_COUNTERS pmcProcMemCounter = { 0 };
GetProcessMemoryInfo( hProcModule, 
                      &pmcProcMemCounter, 
                      sizeof( pmcProcMemCounter ));

这些是我使用的 API。主代码可以在 ProcessViewerDlg.cppProcessViewerDlg.h 中找到

EnumDeviceDrivers

枚举由 System 进程加载的设备驱动程序。尽管进程名称显示为 System,但其真实名称是 %systemroot%\System32\ntoskrnl.exe

const int nSize = 2048;
DWORD dwCount = 0;
LPVOID lpvImageBases[nSize] = { 0 };
 
// Get drivers


EnumDeviceDrivers( lpvImageBases, nSize, &dwCount );
 

// Get count of drivers loaded


dwCount /= sizeof( LPVOID );

请注意,数组中的第一个项目是进程的名称,其余是驱动程序。

GetDeviceDriverBaseName

提取设备驱动程序的基本名称。

TCHAR szName[MAX_PATH] = { 0 };
 
// Returns main process name, since we are accessing the first element of the 


// array


GetDeviceDriverBaseName( lpvImageBases[0], szName, MAX_PATH );

GetDeviceDriverFileName

提取设备驱动程序的完整路径。不过,这对我的帮助不大。返回的大部分路径是 \Windows\System32\Some.sys 等或 \??\C:\..\Some.sys。所以我不得不使用 SearchPath API 来找出这些设备驱动程序的确切位置,当然,我修改了 PATH 变量以迫使此 API 也搜索 Drivers 文件夹。

TCHAR szName[MAX_PATH] = { 0 };
 
// Returns full path of device driver loader process, since we are accessing 


// first element of the array.


GetDeviceDriverFileName( lpvImageBases[0], szName, MAX_PATH );

GetProcessTimes

返回指定进程的时间信息。

HANDLE hProcess; // Handle returned by OpenProcess


 
// Prepare filetime structures



FILETIME ftStartTime = { 0 }, 
         ftExitTime = { 0 }, 
         ftKernelTime = { 0 }, 
         ftUserTime = { 0 }
 
// Get time


GetProcessTimes( hProcess, 
                 &ftStartTime, 
                 &ftExitTime, 
                 &ftKernelTime, 
                 &ftUserTime );

您可以直接使用 FILETIME, 但应小心,除了开始时间和退出时间之外,其他时间可能无法按预期工作(请阅读文档)。您需要对内核时间和用户时间做一些额外的工作。

最好的方法是调用 FileTimeToSystemTime 或将其放入 COleDateTimeDavidCrow 撰写了一篇关于此主题的精彩 文章。我建议您阅读它。

SearchPath

仔细阅读此函数的文档。在这里描述此 API 的工作原理是浪费时间,因为它已经有了很好的文档。

const int nAllocLength = MAX_PATH * 2;
TCHAR szPathBuffer[ nAllocLength ];
 

// Search for file 


if( SearchPath( 0, _T( "Anyfile.anytype" ), 0<;/span>, nAllocLength, szPathBuffer, 0 ))
{
     csFilePath_o = szPathBuffer;
     return true;
}// End if

ProcessPriority

要更改进程的优先级,请调用 SetPriorityClass,要检索进程的优先级,请调用 GetPriorityClass

六个优先级类如下...

  1. IDLE_PRIORITY_CLASS
  2. BELOW_NORMAL_PRIORITY_CLASS
  3. NORMAL_PRIORITY_CLASS
  4. ABOVE_NORMAL_PRIORITY_CLASS
  5. HIGH_PRIORITY_CLASS
  6. REALTIME_PRIORITY_CLASS
Usage:

// For setting priority use


VERIFY( SetPriorityClass( hProcess, ABOVE_NORMAL_PRIORITY_CLASS ));

// For retrieving priority use


VERIFY( GetPriorityClass( hProcess ) != 0 );

搜索

进程查看器支持搜索加载的模块。您可以搜索特定进程或 DLL。搜索不支持通配符搜索,只进行“StrStrI”搜索。因此,如果您搜索“shell”,则会命中“shell32.dll”、“someshell.dll”、“myShelldll.dll”等。

按 Ctrl + F 进行搜索。

依赖关系查看

双击任何模块或进程即可查看其依赖关系。您应该在系统上安装了依赖关系查看器。只有在启用依赖关系查看时才会发生这种情况。工具栏按钮之一(第四个)就是为此目的。

您可以从 这里 下载依赖关系查看器。

完整路径

要查看加载进程的完整路径,只需单击工具栏上的绿色勾号按钮(第三个)即可启用完整路径。

刷新

要刷新,请单击工具栏上的第一个按钮或按 F5。

交换视图

要从垂直视图切换到水平视图,或反之亦然,请按 F6 或单击工具栏上的第二个按钮。

杀死进程

选择要杀死的特定进程。按 F8 或单击杀死按钮。当进程被杀死时,它不会从进程列表中移除,而是将进程名称设置为粗体。按 F5/刷新以从树中删除已杀死的进程。主要是为了保留该进程的快照。如果您有木马在运行,并且您想查看有关该木马的详细信息而不必运行木马,则很有帮助。

注意:不要杀死内核进程,我尝试过,结果整个系统崩溃了。 :)

选项

按 F7 显示选项对话框。您可以设置各种进程查看选项。选项将包含更多内容。

CSV 模块列表和符号列表

要保存进程的模块列表,请按 Ctrl + A 选择所有模块,然后按 Ctrl + C。需要记住的事情...

  1. 如果光标停在某个项目上,则该子项(或子项)将被复制到剪贴板。因此,要复制多个子项,请先选择所需的子项,然后将光标放在该子项上以复制到剪贴板。
  2. 要复制所有内容,请将光标移出列表视图。
  3. 打开记事本并粘贴。
  4. 另存为任意文件名.csv。
  5. 使用 CSV 查看器(如 Excel)打开。

符号列表也是如此。

视图方面保存

将您的设置保存到 ini 文件。例如:列顺序、分隔窗格大小、主窗口大小。这使您可以根据自己的喜好更改列表视图的顺序。甚至列的大小也会被保存。

实用程序

有一个 FileVersionInfo 类和一个 DividerWnd 类可以重用。一个 WindowCollection 类用于枚举所有打开的窗口。

FileVersionInfo 从模块中提取版本信息。DividerWnd 是一个简单轻量级的分割器。

致谢

  1. 您在此应用程序的树状视图和列表视图中看到的位图来自 DominikKeepass 应用。
  2. 嘿 Ralph,感谢您提供的 评论。我已经实现了其中大部分,但重写(可重用性)正在进行中。再次感谢。
  3. 感谢 DavidCrow 撰写的关于 进程时间 的文章。
  4. 感谢 Todd C Wilson 撰写的关于 激进优化 的文章,我正在使用他的头文件(我已注释掉合并节的链接器命令)。
  5. 感谢 CodeProject。感谢所有通过发布精彩文章帮助过我的开发人员。如果没有他们,这一切都不会发生。 :)

历史

  1. 创建于 2007 年 5 月 8 日。
  2. 于 2007 年 5 月 14 日添加了“搜索”。
  3. 于 2007 年 5 月 16 日添加了应用程序描述。
  4. 更新了应用程序以显示加载的设备驱动程序、版本信息、大小、GUI 更改。
  5. 于 2007 年 5 月 28 日添加了进度条、与进程相关的图标显示、进程类型(控制台、Windows、MS-DOS)。
  6. 于 2007 年 5 月 31 日添加了排序、列重新排序和保存此顺序的支持。
  7. 于 2007 年 6 月 2 日添加了从正在运行的进程中提取 HWND。更改了工具栏格式。
  8. 于 2007 年 6 月 5 日添加了 HWND 样式列表、父列表。
  9. 于 2007 年 6 月 7 日使工具栏透明,启用窗口样式、ID 查看。
  10. 由于某些问题禁用了窗口样式查看,正在修复中。于 2007 年 6 月 8 日。
  11. 于 2007 年 6 月 9 日添加了选项对话框和各种不同的调整。
  12. 于 2007 年 6 月 10 日添加了杀死进程选项,修复了一些保存设置的问题。
  13. 于 2007 年 6 月 13 日添加了进程优先级和 IO 计数器。
  14. 现在正确显示窗口的子项。于 2007 年 6 月 18 日。
  15. 于 2007 年 6 月 25 日为进程的每个加载模块添加了符号列表。
  16. 于 2007 年 6 月 26 日添加了进程权限显示。
  17. 于 2007 年 10 月 14 日添加了进程所有者、进程命令行和总窗口计数显示。
  18. 于 2007 年 10 月 24 日添加了动态进程优先级更新支持。
  19. 于 2007 年 10 月 26 日向窗口详细信息添加了窗口图标、字符集、启用/禁用状态,并添加了进程优先级提升支持。
  20. 将根节点文本更改为机器名和用户名 ;)。于 2007 年 11 月 2 日。
  21. 于 2007 年 11 月 12 日找出进程是否已注册自动启动。
  22. 在修复了一些 bug 后重新启用了窗口样式查看。于 2007 年 11 月 27 日。
  23. 修复了 VS2005 中的编译错误(注意:在 VS2005 中您会遇到清单错误,作为修复,请在资源编辑器中删除清单资源)。
  24. 添加了杀死应用程序所有实例的支持。还为进程添加了“打开父文件夹”、“属性”支持。
  25. 添加了权限管理器,可帮助动态启用/禁用/删除权限(尽管需要微调)。于 2008 年 1 月 1 日(新年快乐!)。
  26. 针对速度进行了一些优化!
© . All rights reserved.