Вообще заинтересоваться темой работой с процессами в Windows XP меня заставило не любопытство, а острая необходимость решения мелкой, но очень нехорошей проблемы.
Началось всё с того, что моя жена блуждая по просторам Рунета, приволокла в свой комп нехороший вирус. Не знаю какое уж название прилепил этому вирусу Касперский, н согласно его методам именования, но вирус, мягко говоря козлячий. Смысл его работы заключался в следующем: примерно через 1-2 минуты после запуска ОС на рабочем столе появлялось окно с сообщением, типа «Тестовый период использования программы … закончился. Отошлите СМС на номер … » и до кучи предупреждение о том, что не стоит вмешиваться работы системы иначе будет мега-коллапс. В общем развод на денюжки гордых пользователей нелегального софта честных граждан. Проблемка заключалась в том, что окно это висело аккурат по центру рабочего стола, поверх всех окон и напрочь отказывалось сменить свое положение, т.е. просто так взять и запустить диспетчер задач было нереально (кстати, он и не помог бы особо в решении проблемы). В безопасном режиме — то же самое.
Делать нечего — пошел Гуглить на тему как с этой гадостью бороться. Как оказалось вирь этот уже имел тучу модификаций и скрывался в разных файлах системы. Как назло у меня оказалась последняя модификация. Как вариант, на одном из форумов нашел совет по «ручному» обезвреживанию — как-нибудь убить процесс winlogon.exe (в нем скрывалось жирное, безобразное тело вируса), скачать скрипт (прилагался) и выполнить его, тем самым убив файлы вируса пока тот отключен.
Если кто не в курсе — winlogon.exe средствами Windows просто так не убить. Обычно убийство заканчивается сообщением:
Вот тут и началось мое ускоренное изучение работы с процессами в Windows XP. И первая задача, которая передо мной встала — узнать имена всех запущенных процессов в Windows. И тут нам как нельзя кстати пригодиться модуль PsAPI.pas.
Назначение PsAPI (Process status Application Programming Interface): вспомогательная библиотека, облегчающая доступ к информации о запущенных процессах и драйверах системы.
Открываем Delphi, создаем новый проект и подключаем в uses модуль psapi. Теперь открываем сам модуль и смотрим, что он нам может предложить:
function EnumProcesses(lpidProcess: LPDWORD; cb: DWORD; var cbNeeded: DWORD): BOOL;
Получает идентификаторы для каждого процесса, запущенного в системе.
lpidProcess — указатель на массив, в котором будут храниться идентификаторы.
cb — размер массива в байтах
cbNeeded — количество байт, записанных в массив.
Сам по себе идентификатор процесса нам много информации не принесет, хотелось бы получить в распоряжение Handle процесса. Сделать это можно, воспользовавшись функцией:
function OpenProcess; external kernel32 name 'OpenProcess';
параметры функции следующие:
dwDesiredAccess: DWORD — права доступа к объекту. Далеко не углубляясь в дебри прав доступа, скажу, что нам необходимы права PROCESS_QUERY_INFORMATION (0x0400) и PROCESS_VM_READ (0x0010)
bInheritHandle: bool — Если это значение равно true, то процесс, созданный в результате выполнения функции будет наследовать Handle.
dwProcessId — идентификатор процесса. Тот самый, который мы получим, выполнив EnumProcesses.
Хэндл процесса получили. Что с ним делать? Первое, что пришло на ум — заглянуть опять в PsAPI.pas и узнать в каких функциях используется значение Handle процесса. Вот, что мы можем использовать, применительно к нашей задаче:
function EnumProcessModules(hProcess: THandle; lphModule: LPDWORD; cb: DWORD; var lpcbNeeded: DWORD): BOOL;
Возвращает хэндл для каждого модуля процесса. Параметры функции:
hProcess — хэндл процесса.
lphModule — указатель на массив для записи хэндлов модулей
cb — размер массива в байтах
lpcbNeeded — число байт, записанных в массив.
Ну, а дальше можно получать и имя файла, используя функцию:
function GetModuleFileNameEx(hProcess: THandle; hModule: HMODULE; lpFilename: PWideChar; nSize: DWORD): DWORD;
Возвращает полный путь к файлу, содержащему указанный модуль.
lpFilename — указатель на буфер, который получает полный путь к модулю. Если размер файла больше, чем значение параметра nSize, функция завершается успешно, но имя файла обрезается.
nSize — размер lpFilename в символах.
Вот пожалуй все функции, которые необходимы для получения списка всех запущенных процессов в системе. Попробуем реализовать всё вышесказанное на примере. Пусть в результате выполнения процедуры в список TStringList будут записываться все запущенные процессы. У меня процедура получилась следующая:
procedure TForm4.Button3Click(Sender: TObject); var procesess: array [0..$FFF] of DWORD; i,count, cm: cardinal; ph: THandle; //дескриптор процесса ModName:array[0..max_path] of char;//имя модуля mh:hmodule;//дескрипrтор модуля List: TStringList; begin if Not EnumProcesses(@procesess,SizeOf(procesess),count) then Exit; else begin List:=TStringList.Create; for i:=0 to count div 4-1 do begin ph:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, procesess[i]); if ph>0 then begin EnumProcessModules(ph, @mh, 4, cm); GetModuleFileNameEx(ph, mh, ModName, sizeof(ModName)); List.Add(string(ModName)); CloseHandle(ph); end; end; end; end;
Здесь, в числе прочего, видимо следует обратить внимание на строку
for i:=0 to count div 4-1 do
и, соответственно на переменную
procesess: array [0..$FFF] of DWORD;
т.к. может показаться, что деление на 4 и сам размер массива взяты «с потолка». Дело в том, что согласно рекомендациям с msdn необходимо изначально задавать большой размер массива, т.к. неизвестно сколько процессов запущено на данный момент в системе.
Далее, чтобы узнать количество запущенных процессов нам необходимо разделить cbNeeded на sizeof(DWORD), а это как раз и есть 4. Далее, если значение cb оказывается равным cbNeeded, то размер массива увеличивается и выполнение функции повторяется. Поэтому я изначально задал большой размер массива, чтобы не повторять выполнение EnumProcesses. Конечно, в идеале стоило бы провести проверку всё ли мы записали в массив, но для примера, процедуры, рассмотренной выше, думаю, будет вполне достаточно.
Итак, в результате выполнения функций из модуля PsAPI.pas мы можем получить:
- идентификаторы процессов, запущенных в системе
- дескрипторы каждого модуля для процесса
- пути к файлам, содержащим модули
Остается решить вторую задачу — убить критический процесс. Здесь можно, опять же воспользоваться функцией из kernel32.dll — TerminateProcess, например вот так:
[...] var Ret: BOOL; [...] Ret := TerminateProcess(Ph, 0); if Integer(Ret) = 0 then begin //процесс по каким-либо причинам не был убит end; [...]
По крайней мере с использованием функции мне удалось завершить начатой — завершить процесс winlogon.exe.
В целом могу сказать, что работа с PsAPI мне показалась более удобной, нежели с TLHelp32, который, как оказалось, рекомендуется использовать для Win98-Me, хотя функции из этого модуля возвращают такие же результаты в XP.
Список создали
[code]List:=TStringList.Create;[/code]
А освобождать?
[code]List.Free[/code]
И вообще[code] try finally[/code] надо использовать
Попал сюда случайно, искамши программные таймеры и делители частот. Сам я очень далек от программирования, но с подобной проблемой касательно вируса тоже сталкивался. Все было решено радикально просто — файлы, поступившие на компьютер с соответствующей датой были выброшены в корзину а затем удалены.
Ну месяц-два назад такое действие было вполне приемлемым, а сейчас говорят, что сей вирус стал практически Ai :) меняет даты создания файлов, «косит» под антивирусы и т.д.
Если взять этот код, и в каждой итерации выводить ph (дескриптор процесса), то на выходе одно и тоже значение?! Но при этом я по этому дескриптору получаю различные имена модулей.
А вообще я не могу остановить текущее приложение WaitForSingleObject(ph, 5000); Не работает..
GetModuleFileNameEx(hProcess: THandle; hModule: HMODULE;
lpFilename: PWideChar; nSize: DWORD): DWORD; — не возвращает полный путь к exe…. ну только к текущему процессу….
Я (после экспериментов) просто убил зараженного пользователя и потом создал нового
Странно конечно но при запуске в среде разработки отображаються все процессы, а если запустить чисто exe то тупо большую часть не видит.
если использовать GetModuleBaseName то получим чисто имя запущенного процесса
«if Not EnumProcesses(@procesess,SizeOf(procesess),count) then
Exit(здесь двоеточие на хрена, с ним не запустится!)
else
begin»
Влад, столкнулся с такой проблемой, в Delphi 7 (х32)собранное приложение не определяет EXE запущенного приложения по его хендлу в Win7 x64, используя вышеуказанную методику. Как это можно побороть?
Буду признателен за помощь.
Hobit, извини не могу помочь пока ни чем — у меня под рукой только XE2 под win7 х64 — просто не на сем проверять :(