0%

Process Search

CreateToolhelp32Snapshot() API 와 EnumProcesses() API를 통해서 시스템에 실행 중인 모든 프로세스를 검색하는 방법을 정리했습니다.

CreateToolhelp32Snapshot()

CreateToolhelp32Snapshot() API를 이용해서 프로세스에서 사용되는 힙, 모듈, 및 스레드의 스냅샷을 찍어서 프로세스에 대한 정보를 추출할 때 사용하는 함수입니다. 프로세스와 관련된 모든 부분을 검색할 수도 있고, 일부분만 검색 할 수도 있습니다.

CreateToolhelp32Snapshot() API는 Tlhelp32.h 헤더 파일에 정의되어 있습니다. 정의는 다음과 같습니다.

1
2
3
4
HANDLE CreateToolhelp32Snapshot(
DWORD dwFlags,
DWORD th32ProcessID
);
  • dwFlags

dwFlags 파라미터는 스냅샷을 찍을 때 어떻게 찍을지를 정합니다. 시스템의 모든 프로세스와 스레드의 정보를 가져올지, 아니면 프로세스만 가져올지, 아니면 모듈만 가져올지, 아니면 스레드만 가져올지 그러한 것들을 지정할 수 있읍니다.

해당 값에 자세한 정보는 MSDN에서 확인할 수 있습니다.

  • th32ProcessID

th32ProcessID 파라미터는 스냅샷에 포함될 프로세스의 식별자를 정합니다. 현재 프로세스를 나타낼 때는 NULL을 입력하면 됩니다. 만약에 TH32CS_SNAPHEAPLIST, TH32CS_SNAPMODULE, TH32CS_SNAPMODULE32, TH32CS_SNAPALL 값이 dwFlasgs 파라미터에 지정된 경우에는 th32ProcessID 파라미터를 사용하지만 그외의 경우에는 사용하지 않습니다.

예제

CreateToolhelp32Snapshot() API를 사용해봅시다. 해당 API를 사용해서 얻고 싶은 정보에 따라서 Heap32First(), Heap32Next(), Module32First(), Module32Next(), Process32First(), Process32Next() 들중 한가지를 골라서 사용을 해야 합니다. 여기 있는 것만이 다가 아니고, MSDN에 들어가면 사용할 수 있는 API들이 더 있습니다.

해당 예제에서는 Process에 대한 정보를 추출하고 싶은 것이기 때문에 Process32First() 와 Process32Next()를 선택했습니다.

Process32First()와 Process32Next()의 구조체 정의는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
BOOL Process32First(
HANDLE hSnapshot,
LPPROCESSENTRY32 lppe
);

BOOL Process32Next(
HANDLE hSnapshot,
LPPROCESSENTRY32 lppe
);

파라미터로 CreateToolhelp32Snapshot() 함수를 통해서 얻은 스냅샷 핸들과 PROCESSENTRY32 구조체의 주소를 넘겨주면 됩니다. Process32First()와 Process32Next() 통해서 얻을 수 있는 정보는 PROCESSENTRY32 라는 구조체에 들어있는 데이터입니다. PROCESSENTRY32 구조체에서는 실행 파일 이름, 프로세스 ID, 부모 프로세스 ID와 같은 프로세스 정보들을 얻을 수 있습니다.

PROCESSENTRY32 구조체는 정의는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
CHAR szExeFile[MAX_PATH];
} PROCESSENTRY32;
  • dwSize

    구조체의 크기입니다. Process32First 함수를 호출하기 전에 sizeof(PROCESSENTRY32)으로 초기화 시켜줘야합니다. 초기화 시키지 않으면 Process32First 함수 호출을 실패합니다.

  • cntUsage

    이 멤버는 더 이상 사용하지 않으며 항상 0으로 설정됩니다.

  • th32ProcessID

    프로세스 ID

  • th32DefaultHeapID

    이 멤버는 더 이상 사용하지 않으며 항상 0으로 설정됩니다.

  • th32ModuleID

    이 멤버는 더 이상 사용하지 않으며 항상 0으로 설정됩니다.

  • cntThreads

    프로세스가 시작한 실행 스레드 개수

  • th32ParentProcessID

    이 프로세스를 생성한 프로세스의 ID(부모 프로세스)

  • pcPriClassBase

    이 프로세스에서 작성된 스레드의 기본 우선 순위

  • dwFlags

    이 멤버는 더 이상 사용되지 않으며 항상 0으로 설정됩니다.

  • szExeFile

    프로세스의 실행 파일 이름입니다. 실행 파일의 전체 경로를 검색하려면 Module32First 함수를 호출하고 리턴 되는 MODULEENTRY32 구조 의 szExePath 멤버를 확인하십시오 . 그러나 호출 프로세스가 32 비트 프로세스 인 경우 QueryFullProcessImageName 함수를 호출하여 64 비트 프로세스에 대한 실행 파일의 전체 경로를 검색 해야합니다.


소스코드

이렇게 CreateToolhelp32Snapshot() API, Process32First() API, Process32Next() API를 적절하게 사용하면 프로세스 ID와 프로세스 이름을 구할 수 있습니다. 아래의 코드는 프로세스 ID와 프로세스 이름을 구하는 소스코드 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "stdio.h"
#include "windows.h"
#include "tlhelp32.h"
#include "tchar.h"

int main(int argc, char* argv[])
{
HANDLE hSnapShot;
PROCESSENTRY32 pe;

pe.dwSize = sizeof(PROCESSENTRY32);
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);

Process32First(hSnapShot, &pe);
while (1)
{
_tprintf(_T("PID: %u \tProcess Name : %s\n"), pe.th32ProcessID, pe.szExeFile);

if (!Process32Next(hSnapShot, &pe))
{
CloseHandle(hSnapShot);
return 0;
}
}
}

EnumProcesses()

EnumProcesses() API 이용해서 프로세스 ID의 정보를 얻어낼 수 있습니다. 이를 통해서 프로세스의 정보를 알아낼 수 있습니다.

EnumProcesses() API는 Psapi.h 헤더 파일에 정의되어 있습니다. 정의는 다음과 같습니다.

1
2
3
4
5
BOOL EnumProcesses(
DWORD *lpidProcess,
DWORD cb,
LPDWORD lpcbNeeded
);
  • lpidProcess

lpidProcess 파라미터는 프로세스 ID 목록을 받는 배열에 대한 포인터를 지정하면 됩니다.

  • cb

lpidProcess 배열의 크기를 지정하면 됩니다.

  • lpcbNeeded

lpidProcess 배열에 반환된 바이트 수입니다.


EnumProcess() API를 호출 할 때 얼마나 많은 프로세스가 있을지 예측하기 어렵기 때문에 큰 배열을 사용해서 프로세스의 ID를 구하는 것이 바람직합니다.

프로세스 목록을 확인하려면 lpcbNeeded 파라미터에 입력된 변수의 값을 sizeof(DWORD)로 나누면 프로세스의 개수를 얻을 수 있습니다. 만약에 버퍼가 작아서 모든 프로세스를 식별을 하지 못했다면, 더 큰 배열로 다시 시도하면 됩니다.

배열에는 함수의 프로세스의 ID를 얻었으면, 프로세스 ID가 들어가게 됩니다. 배열안에 프로세스 ID가 있다는 것을 확인했으면, OpenProcess 함수를 호출해서 프로세스 핸들을 얻고, 프로세스의 정보를 추출하면 됩니다.
프로세스 정보를 추출할 수 있는 API들은 psapi.h 헤더 파일을 참고 하시면 됩니다.


예제

이때, 프로세스의 이름을 얻기 위해 사용된 API는 EnumProcessModules() 과 GetModuleBaseName() 입니다. EnumProcessModule()을 이용해서 프로세스에서 사용되는 모듈 핸들을 얻고, GetModuleBaseName()을 이용해서 프로세스 이름을 얻는 방식입니다. 자세한 정보는 MSDN에서 검색하면 됩니다.

소스코드

아래의 코드는 EnumProcesses() API, EnumProcessModule() API, GetModuleBaseName() API를 이용해서 프로세스의 ID와 프로세스 이름을 얻는 소스 코드입니다. 해당 코드는 MSDN의 예제 소스 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "stdio.h"
#include "windows.h"
#include "tchar.h"
#include "psapi.h"

void ProcessSearch(DWORD dwPID)
{
TCHAR szProcName[MAX_PATH] = _T("<unknown>");
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);

if (NULL != hProcess)
{
HMODULE hMod;
DWORD cbNeeded;

if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
{
GetModuleBaseName(hProcess, hMod, szProcName, sizeof(szProcName) / sizeof(TCHAR));
}
}
_tprintf(_T("PID: %u \tProcess Name : %s\n"), dwPID, szProcName);

CloseHandle(hProcess);
}
int main(int argc, char* argv[])
{
DWORD Processes[1024], cbNeeded, cProcesses;

if (!EnumProcesses(Processes, sizeof(Processes), &cbNeeded))
return 1;

cProcesses = cbNeeded / sizeof(DWORD);
for (int i = 0; i < cProcesses; ++i)
{
if (Processes[i] != 0)
{
ProcessSearch(Processes[i]);
}
}

return 0;
}

참고자료

CreateToolhelp32Snapshot()

PROCESSENTRY32 구조체

EnumProcess()

EnumProcessModule()

GetModuleBaseName()

Process Search using EnumProcess