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

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

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


Данная статья рассказывает о работе с процессами, модулями, кучами и потоками при помощи билиотеки 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 приложений" (имхо эта книга - настольная для системного программиста).

Сохранить в соц. сетях:
Обсуждение:
comments powered by Disqus

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

Слов:1197
Символов:12715
Размер:24.83 Кб.