Работа с процессами в С/С++. Основные приемы

Работа с процессами в С/С++. Основные приемы

Тимур Хабибуллин

Данная статья рассказывает о работе с процессами,
модулями, кучами и потоками при помощи билиотеки TOOLHELP

Работа с процессами – основа, без которой заниматься
системным программированием так же бессмысленно, как без знания структуры
PE-файлов или организации памяти. Поэтому я поднимаю эту тему вновь и расскажу
о работе с процессами посредством функций TOOLHELP.

Язык программирования: я выбрал C (без плюсиков, т.к.
работы с классами в этой статье не будет – после прочтения вы сможете их без
труда составить сами) по многим причинам и в первую очередь из-за его
низкоуровнего взаимодействия с памятью…записал-считал, все просто и понятно.

Перечислить запущенные в системе процессы можно
по-разному, я привык пользоваться функциями TOOLHELP. Общая последовательность
действий при работе с этой библиотекой: делаем “снимок” (Snapshot)
системной информации, которая нам необходима, потом бегаем по процессам (а
также модулям и кучам). Поэтому начнем с простого – перечислим все процессы.

//Перечисление процессов

int EnumerateProcs(void)

{

//создаем “снимок” информации о процессах

//первый параметр функции – константа, определяющая,

//какую информацию нам нужно “снять”, а второй

//идентификатор процесса, к которому относится эта

//информация. В данном случае это 0 т.к. мы делаем

//снимок всех процессов

HANDLE pSnap =
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

bool bIsok = false;

//Структура, в которую будут записаны данные процесса

PROCESSENTRY32 ProcEntry;

//установим ее размер, это необходимое действие

ProcEntry.dwSize = sizeof(ProcEntry);

//теперь определим первый процесс

//первый параметр функции – хэндл “снимка”
информации

//второй – адрес структуры PROCESSENTRY32

//true – в случае удачи, false – в случае неудачи

bIsok = Process32First(pSnap, &ProcEntry);

//здесь можно было вставить роскошный цикл for(….) но
это

//не совсем удобочитаемо

//так что цикл while

while(bIsok)

{

//печатаем имя процесса, его идентификатор

//теперь, когда у нас есть структура ProcEntry

//То, какую информацию вы из нее возьмете, зависит

//только от задачи ))

printf(“%s 
%un”, ProcEntry.szExeFile, ProcEntry.th32ProcessID);

bIsok = Process32Next(pSnap, &ProcEntry);

}

//чистим память!

CloseHandle(pSnap);

return 1;

}

Вуаля, список всех процессов, аки в диспетчере задач.
Теперь мы сделаем кое-что, чего в диспетчере нет! В адресном пространстве
каждого процесса (в области памяти, выделенной ему системой) находятся
различные библиотеки, которые, собственно, состовляют ПРИЛОЖЕНИЕ. Это и
Kernel32 и GDI и еще множество различных. Наша задача – их все пересчитать и
переписать! Для этого действа напишем небольшую функцию.

//Перечисление модулей процесса

int EnumerateModules(DWORD PID)

{

//Входной параметр – идентификатор процесса, чьи модули
мы собираемся

//перечислять. Во первых создадим snapshot информации о
модулях

//теперь нам нужна информация о конкретном процессе –
процессе

//с идентификатором PID

HANDLE pMdlSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
PID);

bool bIsok = false;

//структура с информацией о модуле

MODULEENTRY32 MdlEntry;

//зададим размер

MdlEntry.dwSize = sizeof(MODULEENTRY32);

//и найдем первый модуль

bIsok = Module32First(pMdlSnap, &MdlEntry);

//и далее, как и с процессами

while(bIsok)

{

//печатаем имя модуля

printf(” %s n”, MdlEntry.szModule);

//и переходим к следующему

bIsok = Module32Next(pMdlSnap, &MdlEntry);

}

//чистим память!

CloseHandle(pMdlSnap);

return 1;

}

А теперь немного притормозим и посмотрим, какую еще
информацию о процессах и модулях мы получаем:

typedef struct tagPROCESSENTRY32 {

DWORD dwSize; 
//Рамер структуры

DWORD cntUsage; //Число ссылк на процесс. Процесс
уничтожается, //когда число ссылок становится 0

DWORD th32ProcessID; //Идентификатор процесса – необходим

  //во многих
функциях

DWORD th32DefaultHeapID; //Идентификатор основной кучи –
имеет

  //смысл только в
функциях toolhelp

DWORD th32ModuleID; //идентификатор модуля – имеет

  //смысл только в
функциях toolhelp

DWORD cntThreads;  //Число
потоков

DWORD th32ParentProcessID; //Идентификатор родителя –
возвращается

   //Даже если
родителя уже нет

LONG pcPriClassBase; //приоритет по умолчанию всех
//создаваемых процессом потоков

DWORD dwFlags;   //Зарезервировано

CHAR 
szExeFile[MAX_PATH]; //Собственно имя процесса

} PROCESSENTRY32,*PPROCESSENTRY32,*LPPROCESSENTRY32;

typedef struct tagMODULEENTRY32 {

DWORD dwSize;  
//размер структуры

DWORD th32ModuleID; //идентификатор
модуля

DWORD th32ProcessID; //идентификатор процесса, к которому
относится

  //модуль

DWORD GlblcntUsage; 
//общее число ссылок на этот модуль

DWORD ProccntUsage; 
//число ссылко в контексте процесса,

  //по
идентификатору которого был создан

  //снэпшот. Если
равен 65535 – модуль подгружен

  //неявно

BYTE *modBaseAddr; 
//адрес модуля в контексте процесса

DWORD modBaseSize; 
//размер проекции

HMODULE hModule;  //ссылка
на модуль

char szModule[MAX_MODULE_NAME32 + 1]; //Имя модуля

char szExePath[MAX_PATH];  //Полный путь к модулю

} MODULEENTRY32,*PMODULEENTRY32,*LPMODULEENTRY32;

Обратите внмание: ссылка на модуль (параметр hModule) – это первый байт ДОС-заголовка! Таким образом, мы получаем возможность работать с проекцией при некотором
знании структуры PE-файлов. В частности мы можем прочиатать таблицу импорта, и,
как правило, – даже переписать ее (это используется при перехвате АПИ).
Параметр szExePath имеет свой “заскок” – иногда полный путь к модулю
возвращается со странными вставками и, например, всесто
“c:windowssystem32advapi32.dll” я иногда получаю
“c:x86_proc_winsyspathadvapi32.dll”. Как правило для системных задач
средней сложности (перехват апи, или, наоборот, перехват стелсов) всего
вышеописанного хватает. Но на этом возможности toolhelp не исчерпываются и
теперь мы побегаем по потокам! Работа с потоками несколько отличается от работы
с модулями – даже если мы сделаем снимок, задав идентификатор какого-либо
процесса, функция Thread32Next не остановится, пока не пробежится по ВСЕМ
потокам в системе. Поэтому мы должны проверять, к какому процессу принадлежит
поток – благо, в структуре THREADENTRY32 есть член th32OwnerProcessID –
идентификатор породившего поток процесса. Таким образом:

int EnumerateThreads(DWORD PID)

{

//Начнем с создания снимка

HANDLE pThreadSnap = 
CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, PID);

bool bIsok = false;

//Структура, описывающая поток

THREADENTRY32 ThrdEntry;

//ставим размер

ThrdEntry.dwSize = sizeof(THREADENTRY32);

//Берем первый поток

bIsok = Thread32First(pThreadSnap, &ThrdEntry);

//и бегаем по всем потокам…

while (bIsok)

{

//проверяем, тому ли процессу принадлежит поток

if (ThrdEntry.th32OwnerProcessID == PID)

{

//Если да, то выводим некотурую информацию…

//Хоть она никому нафиг не нужна :о)

printf(“%u 
%un”, ThrdEntry.th32OwnerProcessID, ThrdEntry.th32ThreadID);

}

bIsok = Thread32Next(pThreadSnap, &ThrdEntry);

}

//не забываем чистить память

CloseHandle(pThreadSnap);

return 1;

}

Ну вот, у нас есть потоки. Что еще осталось? Правильно,
остались кучи. Здесь тоже все очень просто:

int EnumerateHeaps(DWORD PID)

{

//Первый параметр – идентификатор процесса

//а второй – основная куча

//Теперь делаем снимок, чтоб перечислить кучки…

HANDLE pSnapHeaps =
CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, PID);

bool bIsok = false;

bool bIsokHeap = false;

//Структура, в которую будут записываться данные списка
кучи

HEAPLIST32 HpLst;

//Структура, в которую будут записываться данные

//непосредствнно БЛОКОВ КУЧИ

HEAPENTRY32 HpEntry;

//Ставим размеры…

HpLst.dwSize = sizeof(HEAPLIST32);

HpEntry.dwSize = sizeof(HEAPENTRY32);

bIsok = Heap32ListFirst(pSnapHeaps, &HpLst);

while (bIsok)

{

//Теперь перечисляем блоки кучи

//этот код я привел, чтобы стало ясно

//как получить данные по блокам

//но он жрет много времени

//так что я его закомментирую – если вам интересно

//можете погонять…

/*bIsokHeap = Heap32First(&HpEntry, PID,
HpLst.th32HeapID);

while(bIsokHeap)

{

//Выводим немного информации

printf(“%u n”, HpEntry.dwBlockSize);

//Шагаем дальше

bIsokHeap = Heap32Next(&HpEntry);

}*/

//выводим инфу о куче в общем

printf(“%u n”, HpLst.dwSize);

//шагаем дальше

bIsok = Heap32ListNext(pSnapHeaps, &HpLst);

}

CloseHandle(pSnapHeaps);

return 1;

}

Ну вот, теперь тока осталось написать о структурах THREADENTRY32, HEAPENTRY32 и HEAPLIST32:

typedef struct tagTHREADENTRY32{

DWORD dwSize;  //размер структуры

DWORD cntUsage; //число ссылок

DWORD th32ThreadID; //идентификатор

DWORD th32OwnerProcessID; //родительский процесс

LONG tpBasePri;  //основной
приоритет (при инициализации)

LONG tpDeltaPri; //изменение приоритета

DWORD dwFlags; //зарезервировано

} THREADENTRY32;

typedef THREADENTRY32 * PTHREADENTRY32;

typedef THREADENTRY32 * LPTHREADENTRY32;

typedef struct tagHEAPENTRY32

{

DWORD  dwSize;
//размер структуры

HANDLE hHandle;    
// хэндл этого блока

DWORD 
dwAddress;   // линейный адрес начала блока

DWORD  dwBlockSize;
// размер блока в байтах

DWORD  dwFlags; //флаги

/*

LF32_FIXED Блок памяти имеет фиксированную позицию

LF32_FREE  Блок
памяти не используется

LF32_MOVEABLE  Блок
памяти может перемещаться

*/

DWORD  dwLockCount;
число “замков”

DWORD  dwResvd;  // зарезервировано

DWORD 
th32ProcessID;   // родительский процесс

DWORD 
th32HeapID;      // идентификатор кучи

} HEAPENTRY32;

typedef HEAPENTRY32 * 
PHEAPENTRY32;

typedef HEAPENTRY32 * 
LPHEAPENTRY32;

typedef struct tagHEAPLIST32

{

DWORD  dwSize;
//размер структуры

DWORD 
th32ProcessID;   // родительский
процесс

DWORD 
th32HeapID;      //куча в
контексте процесса

DWORD  dwFlags; //флаг.
Значение всегда одно:

// HF32_DEFAULT – основная куча процесса

} HEAPLIST32;

вызовы функций EnumerateHeaps, EnumerateThreads и
EnumerateModules можно проводить из EnumerateProcs. Все скомпилино в Visual C++
6.0. В тексте использована информация из MSDN и книги Джеффри Рихтера
“Создание эффективных win32 приложений” (имхо эта книга – настольная
для системного программиста).
Список литературы

Для подготовки данной работы были использованы материалы
с сайта http://www.realcoding.net