Слежение за процессами

На первый взгляд, задача кажется малорешаемой. На второй - после поиска в MSDN - понимаешь, что она не решаема в User-mode в том смысле, что нет соответствующих API. А впрочем, когда это было проблемой для настоящих программистов?

В режиме ядра задача решается тривиально - в драйвере регистрируешь callback функцией PsSetCreateProcessNotifyRoutine и он будет вызван при создании/удалении процесса. Но нам нужна реализация в user-mode...

Ограничимся тем, что будем отлавливать создание процессов. Первое, что приходит на ум, это следующий алгоритм:

получить список процессов
просмотреть его на предмет появления новых процессов
переход на 1)
Реализуем его с помощью NATIVE API.

Отступление: Native API - это набор API, не документированный Microsoft (или документированный частично). С его помощью можно сделать все то же что можно сделать с помощью обычных API и многое другое. Мне в Native API нравится его структура, например с помощью одной функции ZwQuerySystemInformation можно получить очень большое количество информации (перечислить хэндлы, получить информацию о процессе и многое другое).

Для Delphi порт заголовочных файлов существует в нескольких вариантах, наиболее распространен вариант JEDI. Только, при использовании JEDI, придется все Zw-функции заменить на их Nt-аналоги. Впрочем, в режиме пользователя эти функции абсолютно идентичны.


	program process_seeker;

	{$APPTYPE CONSOLE}

	uses
	  SysUtils, windows, tintlist;

	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; // Windows 2000 only

	   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: tintegerlist;
	begin
	  process_id := TIntegerList.Create;

	  //СОЗДАЕМ СПИСОК ПРОЦЕССОВ НА МОМЕНТ СОЗДАНИЯ НАШЕГО ПРОЦЕССА
	    info := GetInfoTable(SystemProcessesAndThreadsInformation);
	    info2 := info;

	    while (info2^.NextEntryDelta <> 0) do

	    begin
	      if (process_id.IndexOf(info2^.ProcessId)=-1)
	        then process_id.Add(info2^.ProcessId);

	      info2 := Pointer(dword(info2)+info2^.NextEntryDelta);
	    end;

	    VirtualFree(info, 0, MEM_RELEASE);

	    //А теперь смотрим что добавилось

	  while true do
	  begin
	    Sleep(200);
	    info := GetInfoTable(SystemProcessesAndThreadsInformation);
	    info2 := info;

	    while (info2^.NextEntryDelta <> 0) do

	    begin
	      if (process_id.IndexOf(info2^.ProcessId)=-1)
	        then
	          begin
	            writeln(info2^.ProcessId, ' - created');
	            process_id.Add(info2^.ProcessId);
	          end;
	      info2 := Pointer(dword(info2)+info2^.NextEntryDelta);
	    end;
	    VirtualFree(info, 0, MEM_RELEASE);
	  end;

	end.
	

Вот и всё, Удачи!

    No results found.
Отменить.