Process, Thread Enumeration은 Injection 같은 기법을 수행할 때 필수적입니다.
Process Enumeration
BOOL GetRemoteProcessHandle(IN LPWSTR szProcessName, OUT DWORD* dwProcessId,
OUT HANDLE* hProcess) {
HANDLE hSnapShot = NULL;
PROCESSENTRY32 Proc = {
.dwSize = sizeof(PROCESSENTRY32)
};
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (hSnapShot == INVALID_HANDLE_VALUE) {
printf("\t[!] CreateToolhelp32Snapshot Failed with Error : %d \n",
GetLastError());
goto _EndOfFunction;
}
if (!Process32First(hSnapShot, &Proc)) {
printf("\n\t[!] Process32First Failed with Error : %d\n", GetLastError());
goto _EndOfFunction;
}
do {
WCHAR LowerName[MAX_PATH * 2];
if (Proc.szExeFile) {
DWORD dwSize = lstrlenW(Proc.szExeFile);
DWORD i = 0;
RtlSecureZeroMemory(LowerName, MAX_PATH * 2);
if (dwSize < MAX_PATH * 2) {
for (; i < dwSize; i++) {
LowerName[i] = (WCHAR)toLower(Proc.szExeFile[i]);
}
LowerName[i++] = '\0';
}
}
if (wcscmp(LowerName, szProcessName) == 0) {
*dwProcessId = Proc.th32ProcessID;
*hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Proc.th32ProcessID);
if (*hProcess == NULL) {
printf("\n\t[!] OpenProcess Failed with Error : %d\n", GetLastError());
}
break;
}
} while (Process32Next(hSnapShot, &Proc));
_EndOfFunction:
if (hSnapShot != NULL)
CloseHandle(hSnapShot);
if (*dwProcessId == NULL || *hProcess == NULL)
return FALSE;
return TRUE;
}
이 코드는 크게 3가지 단계로 나눠집니다.
1. Snapshot
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
일단 프로세스 의 Snapshot을 찍어 저장합니다.
이렇게 찍은 Snapshot에서 프로세스 목록을 훑어 원하는 프로세스의 Handle을 가지기 위함입니다.
2. Case Sensitive
if (Proc.szExeFile) {
DWORD dwSize = lstrlenW(Proc.szExeFile);
DWORD i = 0;
RtlSecureZeroMemory(LowerName, MAX_PATH * 2);
if (dwSize < MAX_PATH * 2) {
for (; i < dwSize; i++) {
LowerName[i] = (WCHAR)toLower(Proc.szExeFile[i]);
}
LowerName[i++] = '\0';
}
}
프로세스 이름을 Lower case로 바꿉니다. 이렇게 함으로서 프로세스 이름 비교를 보다 수월하게 만듭니다.
3. Compare Name
if (wcscmp(LowerName, szProcessName) == 0) {
*dwProcessId = Proc.th32ProcessID;
*hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Proc.th32ProcessID);
if (*hProcess == NULL) {
printf("\n\t[!] OpenProcess Failed with Error : %d\n", GetLastError());
}
break;
}
Lower case 로 바꾼 프로세스의 이름을 입력받은 이름과 비교하여 일치하는 프로세스를 찾습니다.
이렇게 찾은 프로세스의 Handle을 반환합니다.
이름으로 비교하지 않고, ProcessID로 비교할 수도 있습니다. Case Sensitive 부분을 없애고 Compare Name 부분을
if (Proc.th32ProcessID == dwProcessId) {
// Open Process AND Break
}
이렇게 바꾸면 됩니다. 추가로, 함수 인자를 szProcessName 이 아닌 dwProcessId 만 받으면 됩니다(IN OUT DWORD* dwProcessId).
이렇게 해서 프로세스의 Handle을 획득했습니다.
이제 획득한 프로세스의 Thread의 handle을 얻어보겠습니다.
Thread Handle
BOOL GetRemoteThreadHandle(IN DWORD dwProcessId, OUT DWORD* dwThreadId, OUT HANDLE* hThread) {
HANDLE hSnapShot = NULL;
THREADENTRY32 Thr = {
.dwSize = sizeof(THREADENTRY32)
};
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL);
if (hSnapShot == INVALID_HANDLE_VALUE) {
printf("\n\t[!] CreateToolhelp32Snapshot Failed with Error : %d \n", GetLastError());
goto _EndOfFunction;
}
if (!Thread32First(hSnapShot, &Thr)) {
printf("\n\t[!] Thread32First Failed with Error : %d \n");
goto _EndOfFunction;
}
do {
if (Thr.th32OwnerProcessID == dwProcessId) {
*dwThreadId = Thr.th32ThreadID;
*hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, Thr.th32ThreadID);
if (*hThread == NULL) {
printf("\n\t[!] OpenThread Failed with Error : %d \n", GetLastError());
break;
}
}
} while (Thread32Next(hSnapShot, &Thr));
_EndOfFunction:
if (hSnapShot != NULL)
CloseHandle(hSnapShot);
if (*dwThreadId == NULL || *hThread == NULL)
return FALSE;
return TRUE;
}
이 함수는 프로세스의 첫번째 ThreadID 를 반환합니다.
동일하게 Snapshot을 찍어 enumerating 을 합니다.
앞으로 쓸 포스팅에 자주 나올 함수들이므로 잘 메모해두시면 용이할 것 같습니다.
'IT' 카테고리의 다른 글
| Syscall - 커널의 소통 창구 (0) | 2026.06.03 |
|---|---|
| IAT Hiding (0) | 2026.05.30 |
| Classic APC Injection (0) | 2026.05.25 |
| API Hooking with Custom structure (0) | 2026.05.24 |
| API Hooking feat.DF (0) | 2026.05.21 |