获取系统中所有进程&线程信息

时间:2024-01-11 10:00:56

读书笔记--[计算机病毒解密与对抗]

目录:

  1. 遍历进程&线程程序
  2. 终止进程
  3. 获取进程信息
  4. 获取进程内模块信息
  5. 获取进程命令行参数

代码运行环境:Win7 x64

VS2012 Update3

遍历系统中所有进程

  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include <TlHelp32.h>
  4. int main()
  5. {
  6. // 为进程的所有线程拍个快照
  7. HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  8. if( hSnapshort==INVALID_HANDLE_VALUE )
  9. {
  10. printf("CreateToolhelp32Snapshot调用失败!\n");
  11. return -1;
  12. }
  13. // 获得线程列表,里面记录了线程的详细信息,再使用Thread32First和Thread32Next遍历快照中记录的每个线程信息
  14. PROCESSENTRY32 stcProcessInfo;
  15. stcProcessInfo.dwSize = sizeof(stcProcessInfo);
  16. BOOL  bRet = Process32First(hSnapshort, &stcProcessInfo);
  17. printf("进程名\t\t\t 进程ID\t 线程数\t 父进程ID\n");
  18. while (bRet)
  19. {
  20. printf("%ls\t\t %d\t %d\t %d\n", stcProcessInfo.szExeFile, stcProcessInfo.th32ProcessID, stcProcessInfo.cntThreads, stcProcessInfo.th32ParentProcessID);
  21. bRet = Process32Next(hSnapshort, &stcProcessInfo);
  22. }
  23. CloseHandle(hSnapshort);
  24. system("pause");
  25. return 0;
  26. }
获取系统中所有进程&线程信息
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h> int main()
{
// 为进程的所有线程拍个快照
HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if( hSnapshort==INVALID_HANDLE_VALUE )
{
printf("CreateToolhelp32Snapshot调用失败!\n");
return -1;
} // 获得线程列表,里面记录了线程的详细信息,再使用Thread32First和Thread32Next遍历快照中记录的每个线程信息
PROCESSENTRY32 stcProcessInfo;
stcProcessInfo.dwSize = sizeof(stcProcessInfo); BOOL bRet = Process32First(hSnapshort, &stcProcessInfo); printf("进程名\t\t\t 进程ID\t 线程数\t 父进程ID\n"); while (bRet)
{
printf("%ls\t\t %d\t %d\t %d\n", stcProcessInfo.szExeFile, stcProcessInfo.th32ProcessID, stcProcessInfo.cntThreads, stcProcessInfo.th32ParentProcessID); bRet = Process32Next(hSnapshort, &stcProcessInfo);
} CloseHandle(hSnapshort); system("pause");
return 0;
}

遍历系统中所有线程

  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include <TlHelp32.h>
  4. int main()
  5. {
  6. // 为进程的所有线程拍个快照
  7. HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  8. if( hSnapshort==INVALID_HANDLE_VALUE )
  9. {
  10. printf("CreateToolhelp32Snapshot调用失败!\n");
  11. return -1;
  12. }
  13. // 获得线程列表,里面记录了线程的详细信息,再使用Thread32First和Thread32Next遍历快照中记录的每个线程信息
  14. THREADENTRY32 stcThreadInfo;
  15. stcThreadInfo.dwSize = sizeof(stcThreadInfo);
  16. BOOL  bRet = Thread32First(hSnapshort, &stcThreadInfo);
  17. DWORD dwProId = -1;  // 保存上一个线程的进程ID
  18. unsigned unCount=0;
  19. while (bRet)
  20. {
  21. if( dwProId!=stcThreadInfo.th32OwnerProcessID )
  22. {
  23. // 记录PID与所属PID不同,遍历至不属于同一进程的线程
  24. if( dwProId!=-1 )
  25. {
  26. // 不是第一次遍历
  27. printf("\n进程%d的线程总数:%d\n", dwProId, unCount);
  28. unCount = 0;
  29. printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n\n");
  30. }
  31. dwProId = stcThreadInfo.th32OwnerProcessID;
  32. printf("进程%d:\n\n ", dwProId);
  33. printf("线程TID\t\t线程所属进程PID\t\t线程优先级\n");
  34. }
  35. printf("  %d\t\t\t%d\t\t\t %d\n",stcThreadInfo.th32ThreadID, stcThreadInfo.th32OwnerProcessID, stcThreadInfo.tpBasePri);
  36. unCount++;
  37. bRet = Thread32Next(hSnapshort, &stcThreadInfo);
  38. }
  39. printf("\n进程%d的线程总数:%d\n", dwProId, unCount);
  40. unCount = 0;
  41. printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n\n");
  42. CloseHandle(hSnapshort);
  43. system("pause");
  44. return 0;
  45. }
获取系统中所有进程&线程信息
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h> int main()
{
// 为进程的所有线程拍个快照
HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if( hSnapshort==INVALID_HANDLE_VALUE )
{
printf("CreateToolhelp32Snapshot调用失败!\n");
return -1;
} // 获得线程列表,里面记录了线程的详细信息,再使用Thread32First和Thread32Next遍历快照中记录的每个线程信息
THREADENTRY32 stcThreadInfo;
stcThreadInfo.dwSize = sizeof(stcThreadInfo); BOOL bRet = Thread32First(hSnapshort, &stcThreadInfo);
DWORD dwProId = -1; // 保存上一个线程的进程ID
unsigned unCount=0; while (bRet)
{
if( dwProId!=stcThreadInfo.th32OwnerProcessID )
{ // 记录PID与所属PID不同,遍历至不属于同一进程的线程
if( dwProId!=-1 )
{
// 不是第一次遍历
printf("\n进程%d的线程总数:%d\n", dwProId, unCount);
unCount = 0;
printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n\n");
} dwProId = stcThreadInfo.th32OwnerProcessID;
printf("进程%d:\n\n ", dwProId);
printf("线程TID\t\t线程所属进程PID\t\t线程优先级\n");
} printf(" %d\t\t\t%d\t\t\t %d\n",stcThreadInfo.th32ThreadID, stcThreadInfo.th32OwnerProcessID, stcThreadInfo.tpBasePri);
unCount++; bRet = Thread32Next(hSnapshort, &stcThreadInfo);
} printf("\n进程%d的线程总数:%d\n", dwProId, unCount);
unCount = 0;
printf("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n\n"); CloseHandle(hSnapshort); system("pause");
return 0;
}

在win程序设计中除了使用ToolHelp函数可以完成进程的遍历操作之外(程序需要包含tlhelp32.h),还可以使用Psapi函数EnumProcess函数,但是它只能获取进程的ID。

进程相关操作--终止进程

  1. void  WINAPI  ExitProcess(
  2. UINT  uExitCode;  // 进程退出码
  3. );
获取系统中所有进程&线程信息
void  WINAPI  ExitProcess(
UINT uExitCode; // 进程退出码
);

在程序的任意一个地方调用这个函数都会立即终止自身进程的运行。在C/C++中应避免直接调用这个函数,因为C/C++运行期库得不到通知,∴没有机会去调用全局或静态C/C++对象的析构函数。

ExitProcess不能终止其他进程,应使用TerminateProcess函数。
  1. BOOL WINAPI TerminateProcess(
  2. HANDLE hProcess,  // 要结束的进程句柄
  3. UINT uExitCode    // 指定目标进程的退出码
  4. );
获取系统中所有进程&线程信息
BOOL WINAPI TerminateProcess(
HANDLE hProcess, // 要结束的进程句柄
UINT uExitCode // 指定目标进程的退出码
);

我们必须通过获取目标进程的ID,然后再获取这个进程句柄,然后才可以使用这个句柄操作此进程。可以使用如下函数:

  1. HANDLE WINAPI OpenProcess(
  2. DWORD dwDesiredAccess,  // 指定得到的句柄具有的访问权限
  3. BOOL  bInheritHandle,   // 指定返回的句柄是否可被继承
  4. DWORD dwProcessId       // 指定要打开的进程ID
  5. );
获取系统中所有进程&线程信息
HANDLE WINAPI OpenProcess(
DWORD dwDesiredAccess, // 指定得到的句柄具有的访问权限
BOOL bInheritHandle, // 指定返回的句柄是否可被继承
DWORD dwProcessId // 指定要打开的进程ID
);

这个函数打开一个存在的进程,返回具有指定权限的句柄。

获取进程的其他信息

  • 获取进程对应的可执行程序路径
  1. DWORD  WINAPI  GetProcessImageFileNameA (
  2. _In_ HANDLE hProcess,                       // 要获得文件路径的进程句柄
  3. _Out_writes_(nSize) LPSTR lpImageFileName,  // 保存文件路径的内存首地址
  4. _In_ DWORD nSize<span style="white-space:pre">              </span>// 保存文件路径的内存大小
  5. );
获取系统中所有进程&线程信息
DWORD  WINAPI  GetProcessImageFileNameA (
_In_ HANDLE hProcess, // 要获得文件路径的进程句柄
_Out_writes_(nSize) LPSTR lpImageFileName, // 保存文件路径的内存首地址
_In_ DWORD nSize// 保存文件路径的内存大小
);

注意使用上述函数获取的路径并不是磁盘驱动器的路径,而是内核路径,形如: " \Device\HarddiskVolume1\WINDOWS\System32\notepad.exe ",通常此路径中的Device表示硬盘驱动器,HarddiskVolume表示分区数字表示具体分区号(1表C 2表D。。。),所以对路径需要进行转换。

  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include <tchar.h>
  4. #include <TlHelp32.h>
  5. #include <Psapi.h>
  6. #pragma comment(lib, "psapi.lib")
  7. void TransPath(PTCHAR pPath)
  8. {
  9. // 获取驱动器字符串位置
  10. PTCHAR pFind = _tcsstr(pPath, _T("HarddiskVolume"));
  11. if( pFind==NULL )
  12. return;
  13. TCHAR tcDriver[5] = {'C', 'D', 'E', 'F', 'G'};  // 可写满24个字符,这里只是一个demo
  14. int nNum = pFind[_tcslen(_T("HarddiskVolume"))] - 0x30;
  15. PTCHAR pTemp = _tcsstr(pFind, _T("\\"));
  16. // 拼接字符串的缓冲区
  17. TCHAR tcBuffer[MAX_PATH];
  18. memset(tcBuffer, 0, MAX_PATH*sizeof(TCHAR));
  19. _stprintf_s(tcBuffer, _T("%c:%s"), tcDriver[nNum-1], pTemp);
  20. memset(pPath, 0, _tcslen(pPath));
  21. _tcscpy_s(pPath, 256, tcBuffer);
  22. }
  23. int main()
  24. {
  25. // 传入\Device\HarddiskVolume1\WINDOWS\System32\notepad.exe
  26. DWORD dwNeed;
  27. PDWORD pdwMem = new DWORD[4000];
  28. BOOL bRet = EnumProcesses(pdwMem, 4000, &dwNeed);
  29. if( !bRet )
  30. {
  31. if(pdwMem!=NULL)
  32. {
  33. delete[] pdwMem;
  34. pdwMem = nullptr;
  35. return -1;
  36. }
  37. }
  38. int nNumProc = dwNeed/4;
  39. HANDLE hProcess = NULL;
  40. DWORD dwPathLength = 0;
  41. TCHAR tcBuffer[256] = {0};
  42. for( int i=0; i<nNumProc; i++ )
  43. {
  44. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pdwMem[i]);
  45. if( hProcess==NULL )
  46. continue;
  47. memset(tcBuffer, 0, 256*sizeof(TCHAR));
  48. dwPathLength = GetProcessImageFileName(hProcess, tcBuffer, 256);
  49. if( dwPathLength!=0 )
  50. {
  51. TransPath(tcBuffer);
  52. printf("%08d %ls\n",pdwMem[i], tcBuffer);
  53. }
  54. CloseHandle(hProcess);
  55. }
  56. if( pdwMem!=NULL )
  57. {
  58. delete[] pdwMem;
  59. pdwMem = NULL;
  60. }
  61. system("pause");
  62. return 0;
  63. }
获取系统中所有进程&线程信息
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <TlHelp32.h>
#include <Psapi.h> #pragma comment(lib, "psapi.lib") void TransPath(PTCHAR pPath)
{
// 获取驱动器字符串位置
PTCHAR pFind = _tcsstr(pPath, _T("HarddiskVolume"));
if( pFind==NULL )
return; TCHAR tcDriver[5] = {'C', 'D', 'E', 'F', 'G'}; // 可写满24个字符,这里只是一个demo int nNum = pFind[_tcslen(_T("HarddiskVolume"))] - 0x30; PTCHAR pTemp = _tcsstr(pFind, _T("\\")); // 拼接字符串的缓冲区
TCHAR tcBuffer[MAX_PATH];
memset(tcBuffer, 0, MAX_PATH*sizeof(TCHAR)); _stprintf_s(tcBuffer, _T("%c:%s"), tcDriver[nNum-1], pTemp);
memset(pPath, 0, _tcslen(pPath));
_tcscpy_s(pPath, 256, tcBuffer);
} int main()
{
// 传入\Device\HarddiskVolume1\WINDOWS\System32\notepad.exe
DWORD dwNeed;
PDWORD pdwMem = new DWORD[4000];
BOOL bRet = EnumProcesses(pdwMem, 4000, &dwNeed); if( !bRet )
{
if(pdwMem!=NULL)
{
delete[] pdwMem;
pdwMem = nullptr;
return -1;
}
} int nNumProc = dwNeed/4;
HANDLE hProcess = NULL;
DWORD dwPathLength = 0;
TCHAR tcBuffer[256] = {0}; for( int i=0; i<nNumProc; i++ )
{
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pdwMem[i]); if( hProcess==NULL )
continue; memset(tcBuffer, 0, 256*sizeof(TCHAR));
dwPathLength = GetProcessImageFileName(hProcess, tcBuffer, 256); if( dwPathLength!=0 )
{
TransPath(tcBuffer);
printf("%08d %ls\n",pdwMem[i], tcBuffer);
} CloseHandle(hProcess);
} if( pdwMem!=NULL )
{
delete[] pdwMem;
pdwMem = NULL;
} system("pause");
return 0;
}
  • 获取进程内模块信息
一个可执行程序要执行就必须运行起来形成一个进程,即把自身加载到内存,我们把这段内存称为该进程的主模块。but并不是程序的所有功能都位于主模块中,也有部分功能位于动态链接库中,程序运行后也会把辅助功能的DLL加载到内存形成其他的模块。摒弃一个进程中可以存在多个模块用于辅助主模块完成各种任务。有些病毒把自身代码以模块形式加载到正常进程中去然后偷偷执行。
使用ToolHelp系列函数获取进程模块和遍历进程模块
首先给某个进程的模块拍个快照,这样即可得到这个进程的模块列表,里面记录了每个模块的详细信息。然后使用Module32FirstModule32Next即可遍历快照列表中记录的模块信息。
使用Psapi函数操作
使用EnumProcessModules函数。这个函数只能得到每个模块的句柄,不能得到模块的名称和路径,所以需要使用GetModuleFileNameEx函数通过模块句柄去获得模块的路径。在枚举进程中的模块时,第一个模块是进程的主模块,用主模块句柄得到的模块路径就是进程文件的路径。
  • 获取进程命令行参数
命令行参数即进程启动时传递给它的参数,通过这个参数可知道金城个启动情况。例如系统服务中,凡是服务对应模块是DLL文件情况,都需要svchost.exe去加载启动,这样系统中就有很多svchost进程,每个svchost进程对应一类服务,这时就是通过命令行参数来区分他们。获取本进程命令行参数API:
获取系统中所有进程&线程信息

  1. // 返回命令行参数字符串的首地址
  2. LPTSTR  GetCommandLine(VOID);
获取系统中所有进程&线程信息
// 返回命令行参数字符串的首地址
LPTSTR GetCommandLine(VOID);

获取指定进程的命令行参数,可以使用OD随意调试一个Win程序,如notepad.exe。在command窗口输入follow GetCommandLineA命令即可定位到该函数的实现代码。可以看到实现代码仅一行

  1. mov  ax, dword ptr [7C8835F4]
  2. // 个人做测试的时候地址好像是7C8855F4
获取系统中所有进程&线程信息
mov  ax, dword ptr [7C8835F4]
// 个人做测试的时候地址好像是7C8855F4

也就是说进程的命令行参数被保存在内存中的固定位置,只要定位到GetCommandLineA的入口地址,跳过一个字节后取出一个dword值,这个值是一个指针,指向的内容即命令行参数的地址。现在的问题即得到远程进程中GetCommandLineA函数的入口地址即可得到该进程的命令行参数。获取指定模块中某个Win32API函数地址的函数是GetProcAddress。but这个函数仅可获取当前进程中API函数的地址,要获得远程进程中某个函数的地址就必须在远程进程中执行这个函数,需要使用远线程方法,本文暂时不涉及这个内容。换个思路:GetCommandLineA使用Kernel32.dll提供,这个库是系统基本库,几乎所有程序都会加载这个模块。而且对于NT架构所有进程加载Kernel32.dll的模块基址基本都是一致的,而GetCommandLineA在其库中的地址也是固定的。所以我们在本进程中得到的此函数地址和远程进程中该函数的地址是一致的(这个方法在我的电脑Win7x64中实验失败,应该因为程序的加载基址不固定了)。获取远程进程中数据使用ReadProcessMemory函数。

  1. BOOL  WINAPI  ReadProcessMemory(
  2. _In_ HANDLE hProcess, <span style="white-space:pre">                    </span>         // 远程进程句柄
  3. _In_ LPCVOID lpBaseAddress,<span style="white-space:pre">                       </span> // 目标进程内存空间首地址
  4. _Out_writes_bytes_to_(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer,  // 本进程内存空间首地址,存储读出的数据
  5. _In_ SIZE_T nSize,<span style="white-space:pre">                            </span> // 要读取的字节数
  6. _Out_opt_ SIZE_T * lpNumberOfBytesRead<span style="white-space:pre">                </span> // 实际读取的字节数
  7. );
获取系统中所有进程&线程信息
BOOL  WINAPI  ReadProcessMemory(
_In_ HANDLE hProcess, // 远程进程句柄
_In_ LPCVOID lpBaseAddress, // 目标进程内存空间首地址
_Out_writes_bytes_to_(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer, // 本进程内存空间首地址,存储读出的数据
_In_ SIZE_T nSize, // 要读取的字节数
_Out_opt_ SIZE_T * lpNumberOfBytesRead // 实际读取的字节数
);

为保证此方法成功,应判断当前系统是否是NT系统,使用GetVersion函数获取当前系统的版本。返回值表示系统的版本信息,若是NT/2k/xp则返回值小于0x80000000,否则大于该值。虚拟机中XP实验成功。

jpg改rar 获取系统中所有进程&线程信息