Статья: Работа с процессами в С/С++. Основные приемы
Название: Работа с процессами в С/С++. Основные приемы Раздел: Рефераты по информатике, программированию Тип: статья |
Тимур Хабибуллин Данная статья рассказывает о работе с процессами, модулями, кучами и потоками при помощи билиотеки 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 приложений" (имхо эта книга - настольная для системного программиста). |