Подписка

добавить на Яндекс

Наши проекты

Delphi+Google

Google API

Google API в Delphi - проект с открытым исходным кодом.

Chrono

Chrono

Хронометр - программа для ведения списка задач.

ODFProc

ODFProc

ODFProc - работа с документами OpenOffice в Lazarus и FreePascal.

Поддержка блога

А тут я коплю на лицензию Delphi XE на iPad =).
Сумма пожертвования не фиксирована.

Публикации

Год назад

Случайный пост

Последние

Сообщения форума

Комментарии

Социальные сети

Google

Facebook

Twitter

Опрос

Вы сейчас или в ближайшем обозримом будущем планируете разрабатывать кроссплатформенное приложение с использованием Firemonkey?



Loading ... Loading ...

Блоги и сообщества

Статьи по Delphi DelphiFeeds.ru - Все Delphi-блоги Рунета Сообщество умных людей VR-Online.RU Бесплатный журнал для программистов и всех, кто интересуется IT Статьи и уроки по Delphi Новостной блог о высоких технологиях
Система Orphus
Опубликовал Vlad 5 января 2010 в 15:22.
Категории: Моя работа, Основы Delphi.


Вообще заинтересоваться темой работой с процессами в 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.

------------------------------
Хотие снизить трудозатраты по управлению? Лучший вариан - это внедрение управленческого учета в Вашей организации.

Ну, а если у Вас нет собственного сервера для хранения и обработки информации, то возьмите выделенный сервер в аренду - недорого и очень эффективно!

Опять проблемы? Не на чем ехать на работу? Иди на авто базар и выбери себе автомобиль по корману. Автобазар Украины к Вашим услугам!

Если твой четвероногий друг заболел и тебе не хочется его тревожить? Ветврач на дом - лучший вариант в этом случае. Приедут, вылечат, поставят на лапы :)


------------------------------

Понравилась статья? Тогда:
Делись! Загружай! Плюсуй!
   Отправить PDF на   
Читай ещё статьи на WebDelphi.ru

Комментарии (8)

WP_Cloudy
  • 33-й пишет:

    Список создали

    List:=TStringList.Create;

    А освобождать?

    List.Free

    И вообще

    try finally

    надо использовать

  • Владимир пишет:

    Попал сюда случайно, искамши программные таймеры и делители частот. Сам я очень далек от программирования, но с подобной проблемой касательно вируса тоже сталкивался. Все было решено радикально просто — файлы, поступившие на компьютер с соответствующей датой были выброшены в корзину а затем удалены.

  • Vlad пишет:

    Ну месяц-два назад такое действие было вполне приемлемым, а сейчас говорят, что сей вирус стал практически Ai :) меняет даты создания файлов, «косит» под антивирусы и т.д.

  • Сергей пишет:

    Если взять этот код, и в каждой итерации выводить ph (дескриптор процесса), то на выходе одно и тоже значение?! Но при этом я по этому дескриптору получаю различные имена модулей.
    А вообще я не могу остановить текущее приложение WaitForSingleObject(ph, 5000); Не работает..

  • Игорь пишет:

    GetModuleFileNameEx(hProcess: THandle; hModule: HMODULE;
    lpFilename: PWideChar; nSize: DWORD): DWORD; — не возвращает полный путь к exe…. ну только к текущему процессу….

  • bne пишет:

    Я (после экспериментов) просто убил зараженного пользователя и потом создал нового

  • Володько пишет:

    Странно конечно но при запуске в среде разработки отображаються все процессы, а если запустить чисто exe то тупо большую часть не видит.
    если использовать GetModuleBaseName то получим чисто имя запущенного процесса

  • Саня пишет:

    «if Not EnumProcesses(@procesess,SizeOf(procesess),count) then
        Exit(здесь двоеточие на хрена, с ним не запустится!)
      else
        begin»

Ваш ответ

Внимание: Все комментарии модерируются, и это может вызвать задержку их публикации. Отправлять комментарий заново не требуется.

Пожалуйста, заключайте исходный код в тэги [code][/code].
Если код большой, то воспользуйтесь Вставкой кода на отдельной странице и оставьте в комментарии ссылку на исходник

   


оборудование для балансировки ротора