`
fgm474ak
  • 浏览: 12727 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

c++在Windows下枚举所有进程

 
阅读更多

c++在Windows下枚举所有进程
2010年11月11日
  Windows下如何枚举所有进程
  Windows下如何枚举所有进程
  
要编写一个类似于 Windows任务管理器的软件,首先遇到的问题是如何实现枚举所有进程。暂且不考虑进入核心态去查隐藏进程一类的,下面提供几种方法。请注意每种方法的使用局限,比如使用这些 API 所需要的操作系统是什么(尤其是是否能在 Windows Mobile 下使用)。
    本文参考用户态枚举进程的几种方法,原文对于每一种方法都给出了完整的代码,被我照抄下来。还有一篇:如何用 Win32 APIs 枚举应用程序窗口和进程。基于我现学现卖的本质,对我演绎的部分请抱着批判的眼光来看,另外代码也没有充分验证。
  使用 ToolHelp API
  

     ToolHelp API 的功能就是为了获取当前运行程序的信息,从而编写适合自己需要的工具(@MSDN)。它支持的平台比较广泛,可以在Windows CE 下使用。在 Windows Mobile SDK 的 Samples 里面有一个 PViewCE的样例程序,就是用这个来查看进程和线程信息的。
    使用方法就是先用 CreateToolhelp32Snapshot 将当前系统的进程、线程、DLL、堆的信息保存到一个缓冲区,这就是一个系统快照。如果你只是对进程信息感兴趣,那么只要包含 TH32CS_SNAPPROCESS 标志即可。
    然后调用一次 Process32First 函数,从快照中获取第一个进程,然后重复调用 Process32Next,直到函数返回 FALSE为止。这样将遍历快照中进程列表。这两个函数都带两个参数,它们分别是快照句柄和一个 PROCESSENTRY32 结构。调用完Process32First 或 Process32Next 之后,PROCESSENTRY32 中将包含系统中某个进程的关键信息。其中进程ID 就存储在此结构的 th32ProcessID。此 ID 传给 OpenProcess API可以获得该进程的句柄。对应的可执行文件名及其存放路径存放在 szExeFile 结构成员中。在该结构中还可以找到其它一些有用的信息。
    需要注意的是:在调用 Process32First() 之前,要将 PROCESSENTRY32 结构的 dwSize 成员设置成sizeof(PROCESSENTRY32)。 然后再用 Process32First、Process32Next来枚举进程。使用结束后要调用 CloseHandle 来释放保存的系统快照。
    以下为参考代码:
  #include
  #include
  #include
  void useToolHelp()
  {
  HANDLE procSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if(procSnap == INVALID_HANDLE_VALUE)
  {
  printf("CreateToolhelp32Snapshot failed, %d ",GetLastError());
  return;
  }
  //
  PROCESSENTRY32 procEntry = { 0 };
  procEntry.dwSize = sizeof(PROCESSENTRY32);
  BOOL bRet = Process32First(procSnap,&procEntry);
  while(bRet)
  {
  wprintf(L"PID: %d (%s) ", procEntry.th32ProcessID, procEntry.szExeFile);
  bRet = Process32Next(procSnap, &procEntry);
  }
  CloseHandle(procSnap);
  }
  void main()
  {
  useToolHelp();
  getchar();
  }
  在 Windows SDK 中可以找到 PSAPI,通过 PSAPI 可以获取进程列表和设备驱动列表。通过EnumProcesses、EnumProcessModules、GetModuleFileNameEx 和GetModuleBaseName 来实现。
    首先使用 EnumProcesses 来枚举所有进程,它有三个参数:DWORD类型的数组指针 lpidProcess;该数组的大小尺寸 cb;以及一个指向 DWORD 的指针cbNeeded,它接收返回数据的长度。DWORD 数组用于保存当前运行的进程 IDs。cbNeeded返回数组所用的内存大小。下面算式可以得出返回了多少进程:nReturned = cbNeeded / sizeof(DWORD)。
    注意:虽然文档将返回的 DWORD 命名为“cbNeeded”,实际上是没有办法知道到底要传多大的数组的。EnumProcesses 根本不会在cbNeeded 中返回一个大于 cb 参数传递的数组值。所以,唯一确保 EnumProcesses 函数成功的方法是分配一个 DWORD数组,并且,如果返回的 cbNeeded 等于 cb,分配一个较大的数组,并不停地尝试直到 cbNeeded 小于 cb 。
    下面是参考代码:
  #include
  #include
  #include
  #include "psapi.h"
  #pragma comment(lib,"psapi.lib")
  void PrintProcessNameAndID(DWORD processID/*进程的PID值*/)
  {
  TCHAR szProcessName[MAX_PATH] = _T("");
  HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS/* | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ*/,FALSE,processID);
  //Process name.
  if(NULL!=hProcess)
  {
  HMODULE hMod;
  DWORD cbNeeded;
  if(EnumProcessModules(hProcess,&hMod,sizeof(hMod), &cbNeeded)) //通过PID获得进程句柄
  {
  GetModuleBaseName(hProcess,hMod,szProcessName,sizeof(szProcessName)/sizeof(TCHAR)); //通过进程句柄获得程序可执行文件名称
  }
  }
  wprintf(_T("PID: %d (%s) "),processID,szProcessName);
  CloseHandle(hProcess);//关闭句柄
  }
  void main( )
  {
  DWORD aProcesses[1024], cbNeeded, cProcesses;
  unsigned int i;
  if(!EnumProcesses(aProcesses,sizeof(aProcesses),&cbNeeded))//列出所有进程PID存入aProcesses数组中
  return;
  cProcesses = cbNeeded/sizeof(DWORD);
  for(i=0;iWindows XP 下可以用的相关结构和常量:
  typedef DWORD (WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);
  typedef struct _SYSTEM_PROCESS_INFORMATION
  {
  DWORD NextEntryDelta;
  DWORD ThreadCount;
  DWORD Reserved1[6];
  FILETIME ftCreateTime;
  FILETIME ftUserTime;
  FILETIME ftKernelTime;
  UNICODE_STRING ProcessName;
  DWORD BasePriority;
  DWORD ProcessId;
  DWORD InheritedFromProcessId;
  DWORD HandleCount;
  DWORD Reserved2[2];
  DWORD VmCounters;
  DWORD dCommitCharge;
  PVOID ThreadInfos[1];
  }SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
  #define SystemProcessesAndThreadsInformation 5
    然后动态加载 ntdll.dll,获得函数的地址。便可以进行进程的枚举相关代码如下:
  #include
  #include
  #include
  typedef DWORD (WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);
  typedef struct _SYSTEM_PROCESS_INFORMATION
  {
  DWORD NextEntryDelta;
  DWORD ThreadCount;
  DWORD Reserved1[6];
  FILETIME ftCreateTime;
  FILETIME ftUserTime;
  FILETIME ftKernelTime;
  UNICODE_STRING ProcessName;
  DWORD BasePriority;
  DWORD ProcessId;
  DWORD InheritedFromProcessId;
  DWORD HandleCount;
  DWORD Reserved2[2];
  DWORD VmCounters;
  DWORD dCommitCharge;
  PVOID ThreadInfos[1];
  }SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
  #define SystemProcessesAndThreadsInformation 5
  void main()
  {
  HMODULE hNtDll = GetModuleHandle(L"ntdll.dll");
  if(!hNtDll)
  return;
  ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll,"ZwQuerySystemInformation");
  ULONG cbBuffer = 0x10000;
  LPVOID pBuffer = NULL;
  pBuffer = malloc(cbBuffer);
  if(pBuffer == NULL)
  return;
  ZwQuerySystemInformation(SystemProcessesAndThreadsInformation,pBuffer,cbBuffer,NULL);
  PSYSTEM_PROCESS_INFORMATION pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
  for(;;)
  {
  wprintf(L"PID: %d (%ls) ",pInfo->ProcessId,pInfo->ProcessName.Buffer);
  if(pInfo->NextEntryDelta == 0)
  break;
  pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);
  }
  free(pBuffer);
  getchar();
  }
    
    对这个方法有问题的,可以参考我之前的那篇介绍 Native API 的文章。
    同样使用 ZwQuerySystemInformation 函数,查询类型如果设置为 SystemHandleInformation(第16号功能)也可以达到目的。它能获取系统中所有句柄,再加上进程 ID 的判断就可以枚举所有进程了。
  #include
  #include
  #include
  #include
  typedef NTSTATUS (WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);
  typedef struct _SYSTEM_HANDLE_INFORMATION
  {
  ULONG ProcessId;
  UCHAR ObjectTypeNumber;
  UCHAR Flags;
  USHORT Handle;
  PVOID Object;
  ACCESS_MASK GrantedAccess;
  }SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
  typedef struct _SYSTEM_HANDLE_INFORMATION_EX
  {
  ULONG NumberOfHandles;
  SYSTEM_HANDLE_INFORMATION Information[1];
  }SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
  #define SystemHandleInformation 0x10 //16
  void main()
  {
  HMODULE hNtDll = LoadLibrary(L"ntdll.dll");
  if(!hNtDll)
  return;
  ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll,"ZwQuerySystemInformation");
  ULONG cbBuffer = 0x4000;
  LPVOID pBuffer = NULL;
  NTSTATUS s;
  do
  {
  pBuffer = malloc(cbBuffer);
  if(pBuffer == NULL)
  return;
  memset(pBuffer,0,cbBuffer);
  s = ZwQuerySystemInformation(SystemHandleInformation,pBuffer,cbBuffer,NULL);
  if(s == STATUS_INFO_LENGTH_MISMATCH)
  {
  free(pBuffer);
  cbBuffer = cbBuffer * 2;
  }
  }while(s == STATUS_INFO_LENGTH_MISMATCH);
  PSYSTEM_HANDLE_INFORMATION_EX pInfo = (PSYSTEM_HANDLE_INFORMATION_EX)pBuffer;
  ULONG OldPID = 0;
  for(DWORD i = 0;iNumberOfHandles;i++)
  {
  if(OldPID != pInfo->Information.ProcessId)
  {
  OldPID = pInfo->Information.ProcessId;
  wprintf(L"PID: %d ",OldPID);
  }
  }
  free(pBuffer);
  FreeLibrary(hNtDll);
  getchar();
  }
    原文中提到,在进行进程“隐藏”工作的时候,此处的句柄是一件容易被忽略的地方,因此需要注意隐藏由程序打开的相关句柄。由于系统中句柄数量经常变换,所以没有什么必要修改其中的 NumberOfHandles域,因为如果修改此处的值,则需要不停对句柄的变化进行维护,开销比较大。在用户态下的进程枚举已经变得不可靠,因为一个内核级的 Rootkit很容易就能够更改这些函数的返回结果。所以进程的可靠枚举应在内核态中实现,可以通过编写驱动来实现。
  有关16位程序
    根据参考的第二篇文章:在 Windows 95,Windows 98 和 Windows ME 中,ToolHelp32对待16位程序一视同仁,它们与 Win32 程序一样有自己的进程 IDs。但是在 Windows NT,Windows 2000 或Windows XP 中情况并不是这样。在这些操作系统中,16位程序运行在所谓的 VDM 当中(也就是DOS机)。
    为了在Windows NT,Windows 2000 和 Windows XP 中枚举16位程序,必须使用一个名为 VDMEnumTaskWOWEx的函数。它的声明包含在 Windows SDK 中的 VDMDBG.h 中,并且需要在项目中链接 VDMDBG.lib 文件。
    微软的网上帮助里面有一篇介绍的文章:如何在 Windows NT、 Windows 2000 和 Windows XP 上使用 VDMDBG 函数。
分享到:
评论

相关推荐

    枚举进程示例,C++源代码

    枚举进程示例 关于如何列举windows中的进程列表显示出来 源代码

    VC++ 枚举所有进程信息

    摘要:VC/C++源码,系统相关,枚举进程 运行环境:Windows/Visual C/C++

    VC++枚举当前进程的一个小示例

    内容索引:VC/C++源码,系统相关,枚举,进程 VC++枚举当前进程的一个小示例,类似代码 已有很多,不过感觉这个挺简单,而且效果不错,比较实用,枚举效果如上图示。

    VC++枚举并杀死(Kill)当前指定进程

    VC/C++源码,系统相关,进程 VC++枚举出当前的所有进程,并彻底杀死(Kill)选中的进程,其实我们没有必要非要一个程序死那么彻底,编写这个小程序的目的是向大家示范如何利用 CreateToolhelp32Snapshot API函数枚举...

    用VC++枚举系统正在运行的程序

    摘要:VC/C++源码,系统相关,枚举进程 枚举系统正在运行的程序,用VC++枚举系统正在运行的程序,列出当前Windows运行的所有进程,具体的枚举过程,请参见源码包内的相关代码。程序实现的功能类似于Windows XP的任务...

    一个Cobalt Strike信标对象文件(BOF)项目,该项目使用直接系统调用来枚举特定已加载模块或进程句柄的进程。-C/C++开发

    一个Cobalt Strike信标对象文件(BOF)项目,该项目使用直接系统调用来枚举特定已加载模块或进程句柄的进程...在信标对象文件中使用直接系统调用来枚举特定进程句柄的进程(例如lsass.exe)。 避免使用Windows和本机API

    系统进程窗口对象查看器

    枚举系统所有进程,含子线程,dll文件模块和所有窗口对象

    用C语言开发手机软件 -Windows+CE+6.0开发者参考

    第2章 在屏幕上绘图 2.1 绘图基础 2.1.1 合法和非法区域 2.1.2 设备上下文 2.2 文本输出 2.2.1 设备上下文属性 2.2.2 TextDemo示例程序 2.2.3 字体 2.2.4 未完成的任务 2.3 位图 2.3.1 设备相关位图 2.3.2 设备无关...

    VB开发的进程暂停小工具

    自己使用VB开发的进程暂停和再开程序,受到codeproject上的一个...只在WindowXP下测试通过,如果有朋友在Windows7下遇到问题请联系我以便改进。如果有朋友发现bug或者想修改来实现其他功能,可以下载该工具的源代码。

    Visual C++开发经验技巧宝典(第9章)—1

    第9章 Windows相关技术 337 ...0629 枚举模块中的所有图标 417 0630 使用模块对话框资源 418 0631 替换应用程序中对话框资源 419 0632 可导出的动态链接库函数 420 0633 动态链接库动态加载 420

    Visual C++实践与提高-COM和COM+篇『PDF』

    5.3 在Vsual C++中使用ActiveX控件——例程VCActiveX 5.3.1 创建ActiveX控件包容器程序 5.3.2 加载ActiveX控件 5.3.3 设计时访问控件的属性 5.3.4 运行时访问控件的属性和方法 5.3.5 处理控件的事件 5.4 在Visual ...

    windows线程几种同步方式

    线程同步的几种方式的c++方法,Mutex,Event,Semaphore等

    WindowsDACLEnumProject:枚举和分析Windows DACL的工具集合

    Windows DACL枚举项目 枚举和分析Windows DACL的工具集合 NCC Group Plc作为开源发布-http: 由nccgroup dot com的ollie dot whitehouse开发,由Ollie Whitehouse开发 根据AGPL发布,请参阅许可以获取更多信息 ...

    VC进程模块枚举及查看管理器

    内容索引:VC/C++源码,系统相关,进程,枚举,远程卸载 VC++编写的进程模块查看器,用于枚举系统的所有进程及其所挂接的所有模块,并实现了枚举进程、远程卸载指定进程模块、搜索指定进程的功能。上边的截图。此外,这...

    Visual C++通用范例开发金典(第三卷/共三卷)

     Visual C++是Microsoft公司开发的可视化软件开发工具,具有简单、高效、功能强大等特点,是Windows环境下优秀的程序设计工具之一。本书系统全面地介绍了Visual C++软件——Microsoft Visual C++6.0的具体使用方法...

    Visual C++通用范例开发金典(第一卷、共两卷)

     Visual C++是Microsoft公司开发的可视化软件开发工具,具有简单、高效、功能强大等特点,是Windows环境下优秀的程序设计工具之一。本书系统全面地介绍了Visual C++软件——Microsoft Visual C++6.0的具体使用方法...

Global site tag (gtag.js) - Google Analytics