Тут много тем было по этому поводу. Вот,решил нембольшой мануал написать... Как тулхелпом процессы, кучи, модули и потоки перечислить. все-таки работа с процессами не ограничивается их перечислением)))
Работа с процессами. Основные приемы.
Раюота с процессами – основа, без которой заниматься системным программированием так же бессмысленно, как без знания структуры 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 %u\n", 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; }
|
А теперь немного притормозим и посмотрим, какую еще информацию о процессах и модулях мы получаем:
Код |
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:\windows\system32\advapi32.dll» я иногда получаю «c:\x86_proc_winsyspath\advapi32.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 %u\n", 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 приложений» (имхо эта книга – настольная для системного программиста).
Это сообщение отредактировал(а) Kergan - 21.12.2005, 12:21
Присоединённый файл ( Кол-во скачиваний: 140 )
ProcArticle.rar 8,86 Kb