Страницы

Поиск по вопросам

воскресенье, 9 июня 2019 г.

Отслеживание появления нового процесса Windows

Добрый день. Надо отследить появление новых процессов. Есть ли в Windows слот, на который поступает сигнал при появлении нового процесса? Иначе как это можно реализовать? В psapi подобного не нашёл. Есть предположение через определённые интервалы времени сверять список PID, но надеюсь найти более простое решение.


Ответ

Можно сделать трейсинг через системную функцию ZwQuerySystemInformation. Внимание! Microsoft не рекомендует, начиная с Windows 8, её использовать, предлагая замену, но, как проверено только что на Windows 10, она работает.
Пример реализации и использования привожу на Delphi, в виде консольного приложения. Поскольку речь касается функций WinAPI, переписать на C/C++ - легко.
program ListOfProcess;
{$APPTYPE CONSOLE}
uses SysUtils, windows, System.Generics.Collections;
// поскольку подход писался очень давно (во времена Windows 2000), // типы и константы определены, поскольку в то время их ещё не было в // системных модулях Delphi. Хотя и сейчас, возможно, некоторых нет.
type NTStatus = cardinal; PVOID = pointer; USHORT = WORD; UCHAR = byte; PWSTR = PWideChar;
const
STATUS_SUCCESS = NTStatus($00000000); STATUS_ACCESS_DENIED = NTStatus($C0000022); STATUS_INFO_LENGTH_MISMATCH = NTStatus($C0000004);
const SystemProcessesAndThreadsInformation = 5;
type PClientID = ^TClientID; TClientID = packed record
UniqueProcess:cardinal; UniqueThread:cardinal; end;
PUnicodeString = ^TUnicodeString; TUnicodeString = packed record Length: Word; MaximumLength: Word; Buffer: PWideChar; end;
PVM_COUNTERS = ^VM_COUNTERS; VM_COUNTERS = packed record PeakVirtualSize, VirtualSize, PageFaultCount, PeakWorkingSetSize, WorkingSetSize, QuotaPeakPagedPoolUsage, QuotaPagedPoolUsage, QuotaPeakNonPagedPoolUsage, QuotaNonPagedPoolUsage, PagefileUsage, PeakPagefileUsage: dword; end;
PIO_COUNTERS = ^IO_COUNTERS; IO_COUNTERS = packed record
ReadOperationCount, WriteOperationCount, OtherOperationCount, ReadTransferCount, WriteTransferCount, OtherTransferCount: LARGE_INTEGER; end;
PSYSTEM_THREADS = ^SYSTEM_THREADS; SYSTEM_THREADS = packed record KernelTime, UserTime, CreateTime: LARGE_INTEGER; WaitTime: dword; StartAddress: pointer; ClientId: TClientId; Priority, BasePriority, ContextSwitchCount: dword; State: dword; WaitReason: dword; end;
PSYSTEM_PROCESSES = ^SYSTEM_PROCESSES; SYSTEM_PROCESSES = packed record NextEntryDelta, ThreadCount: dword; Reserved1 : array [0..5] of dword; CreateTime, UserTime, KernelTime: LARGE_INTEGER; ProcessName: TUnicodeString; BasePriority: dword; ProcessId, InheritedFromProcessId, HandleCount: dword; Reserved2: array [0..1] of dword; VmCounters: VM_COUNTERS; IoCounters: IO_COUNTERS;
Threads: array [0..0] of SYSTEM_THREADS; end;
// основная функция, которую мы будем использовать
Function ZwQuerySystemInformation(ASystemInformationClass: dword; ASystemInformation: Pointer; ASystemInformationLength: dword; AReturnLength:PCardinal): NTStatus; stdcall;external 'ntdll.dll';
// Функция получения буфера с системной информацией Function GetInfoTable(ATableType:dword):Pointer; var mSize: dword; mPtr: pointer; St: NTStatus; begin Result := nil; mSize := $4000; //начальный размер буфера
repeat // подгонка размера буфера mPtr := VirtualAlloc(nil, mSize, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE); if mPtr = nil then Exit; St := ZwQuerySystemInformation(ATableType, mPtr, mSize, nil); if St = STATUS_INFO_LENGTH_MISMATCH then
begin VirtualFree(mPtr, 0, MEM_RELEASE); mSize := mSize * 2; end; until St <> STATUS_INFO_LENGTH_MISMATCH; if St = STATUS_SUCCESS then Result := mPtr else VirtualFree(mPtr, 0, MEM_RELEASE);
end; // приведу пример использовании фунции: var info, info2: PSystem_Processes; i, j, k: integer; t, t1: LARGE_INTEGER; process_id:TDictionary; // здесь будем хранить информацию begin process_id := TDictionary.Create;
// Список процессов на момент старта программы info := GetInfoTable(SystemProcessesAndThreadsInformation); info2 := info;
while (info2^.NextEntryDelta <> 0) do
begin if not process_id.ContainsKey(info2^.ProcessId) then process_id.Add(info2^.ProcessId, info2^); info2 := Pointer(dword(info2)+info2^.NextEntryDelta); end;
VirtualFree(info, 0, MEM_RELEASE);
while true do // нужно вставить вариант выхода из цикла. Разумеется, Ctrl+C всегда сработает :) begin Sleep(2000); // будем проверять наличие новых процессов каждые 2 секунды info := GetInfoTable(SystemProcessesAndThreadsInformation); info2 := info;
while (info2^.NextEntryDelta <> 0) do
begin if not process_id.ContainsKey(info2^.ProcessId) then begin writeln(info2^.ProcessId, ' - появился'); process_id.Add(info2^.ProcessId,info2^); // если будем постоянно добавлять, словарь разрастётся, // надо бы подумать и об удалении элементов, оставляю ТС :) end; info2 := Pointer(dword(info2)+info2^.NextEntryDelta); end; VirtualFree(info, 0, MEM_RELEASE); end; process_id.Free; end.

Комментариев нет:

Отправить комментарий