C/C++: Пишем процесс-менеджер
Николай "G" Андреев
Xakep, номер #056, стр. 056-076-2
Структура PROCESSENTRY32
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32;
typedef PROCESSENTRY32 *PPROCESSENTRY32;
Создадим снимок процесса и начнем его исследовать. Воспользуемся для этого функцией CreateToolhelp32Snapshot, о которой я уже говорил раньше. В первом параметре мы передадим ей константу TH32CS_SNAPPROCESS, говорящую о нашем желании получить снимок именно процессов, а не модулей, нитей или чего-нибудь другого в этом роде. Второй параметр нас совершенно не интересует, поэтому передаем NULL. Функция вернет значение типа HANDLE, которое мы сохраним в какую-нибудь переменную и будем впоследствии его использовать.
Получив снимок, можно начать перечислять процессы при помощи функции Process32First, в первом параметре которой мы передадим полученный HANDLE снимка, а во втором ссылку на переменную типа PROCESSENTRY32. Далее создадим цикл do - while, его условием является равенство нулю функции Process32Next. Там же в цикле будем заносить в массив DWORD значение члена th32ProcessID нашей структуры, увеличивать счетчик на 1 и ставить имя процесса в листбокс. В коде это выглядит так:
// process - наша структура
idarray[i++] = process.th32ProcessID;
SendDlgItemMessage(hwnd, IDC_LISTBOX1, LB_ADDSTRING,
0, (LPARAM)process.szExeFile);
SendDlgItemMessage(hwnd, IDC_LISTBOX1, LB_SETCURSEL,
(WPARAM)SendDlgItemMessage(hw, IDC_LISTBOX1, LB_GETCOUNT, 0, 0)-1, 0
где hwnd - описание нашего окна (формы), а IDC_LISTBOX1 - идентификатор листбокса. В итоге, для того, чтобы перечислить процессы и сообщить о них нашему таскменеджеру, необходимо всего лишь выполнить этот цикл. Но системные процессы не дадут доступа к своим свойствам, и мы не сможем ни закрыть их, ни даже посмотреть их имена. Как это исправить? Надо повысить привилегии процесса-менеджера до уровня отладчика. Звучит сложно, но на самом деле - раз плюнуть. Я написал небольшую функцию для изменения собственных привилегий. Запусти ее с параметром true, и получишь уровень отладчика, с false - ты его, соответственно, лишишься.
Листинг функции, дающей привилегии отладчика нашей программе
void EnableDebugPrivilege(bool fEnable){
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)){
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, false, &tp, sizeof(tp), NULL, NULL);
CloseHandle(hToken);
}
}
Убиваем процессы
Хорошо, какие процессы есть в системе - мы знаем, но как ими манипулировать? Для этого нам нужно получить хэндл процесса. Только получив его, можно с ним что-нибудь сотворить. Делается это с помощью функции OpenProcess, передав которой параметры PROCESS_ALL_ACCESS, false и идентификатор процесса (из массива), получим дескриптор. Поскольку мы пишем менеджер процессов, то первое, что он должен уметь - эти процессы убивать (завершать). Завершить их совсем не сложно. Достаточно запустить функцию TerminateProcess с параметрами: хэндл процесса и код завершения (я пишу 0). Функция завершит процесс, а так как привилегии у нас высокие, то убиваемый процесс умрет без лишних слов.