Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Enumerare i processi con l' api nativa NtQuerySystemInformation


In questo documento andiamo ad affrontare l' utilizzo delle API Native di Windows: sono api non documentate nel Platform SDK e di cui viene sconsigliato l' utilizzo per il fatto che possono essere soggette a modifiche strutturali da una versione all' altra del sistema operativo (del resto non essendo fornita una documentazione ufficiale non viene nemmeno perseguito l' obiettivo della compatibilità retroattiva). Tuttavia non è che poi ci siano tutte queste modifiche: anzi il programma seguente funziona correttamente su un Windows 2000 sp4 ed un Windows xp sp2 entrambi aggiornati con le patch fornite alla data 22/12/2004. Utilizzeremo l' api NtQuerySystemInformation implementata nella dll ntdll.dll per enumerare i processi: altri metodi (perfettamente documentati nel Platform SDK) per l' enumerazione dei processi consistono nell' utilizzo della "Tool Help Library", della "Process Status Helper Library" (PSAPI), della "Performance Data Helper Library". Lo stesso Task Manager utilizza NtQuerySystemInformation per enumerare i processi. Per le dichiarazioni e le definizioni delle varie strutture utilizzeremo la unit Native.pas creata da Marcel Van Brakel e liberamente scaricabile all' indirizzo http://members.chello.nl/m.vanbrakel2/. Vediamo ora di esaminare la funzione NtQuerySystemInformation

function NtQuerySystemInformation(SystemInformationClass: SYSTEM_INFORMATION_CLASS; SystemInformation: PVOID; SystemInformationLength: ULONG; ReturnLength: PULONG ): NTSTATUS; stdcall;

con

type _SYSTEM_INFORMATION_CLASS = ( SystemBasicInformation, SystemProcessorInformation, SystemPerformanceInformation, SystemTimeOfDayInformation, SystemNotImplemented1, SystemProcessesAndThreadsInformation, SystemCallCounts, SystemConfigurationInformation, SystemProcessorTimes, SystemGlobalFlag, SystemNotImplemented2, SystemModuleInformation, SystemLockInformation, SystemNotImplemented3, SystemNotImplemented4, SystemNotImplemented5, SystemHandleInformation, SystemObjectInformation, SystemPagefileInformation, SystemInstructionEmulationCounts, SystemInvalidInfoClass1, SystemCacheInformation, SystemPoolTagInformation, SystemProcessorStatistics, SystemDpcInformation, SystemNotImplemented6, SystemLoadImage, SystemUnloadImage, SystemTimeAdjustment, SystemNotImplemented7, SystemNotImplemented8, SystemNotImplemented9, SystemCrashDumpInformation, SystemExceptionInformation, SystemCrashDumpStateInformation, SystemKernelDebuggerInformation, SystemContextSwitchInformation, SystemRegistryQuotaInformation, SystemLoadAndCallImage, SystemPrioritySeparation, SystemNotImplemented10, SystemNotImplemented11, SystemInvalidInfoClass2, SystemInvalidInfoClass3, SystemTimeZoneInformation, SystemLookasideInformation, SystemSetTimeSlipEvent, SystemCreateSession, SystemDeleteSession, SystemInvalidInfoClass4, SystemRangeStartInformation, SystemVerifierInformation, SystemAddVerifier, SystemSessionProcessesInformation); SYSTEM_INFORMATION_CLASS = _SYSTEM_INFORMATION_CLASS;

Come si può vedere il tipo enumerativo _SYSTEM_INFORMATION_CLASS (tipologia del primo parametro) definisce il tipo di informazione di sistema che la NtQuerySystemInformation deve prelevare. Nel caso in cui si vogliano enumerare i processi si utilizzerà il valore SystemProcessesAndThreadsInformation (intero 5 come si può vedere dal fatto che è al 6° posto nell' enumerazione (enumerazione che è in base 0)). Il parametro SystemInformation è un puntatore che definisce l' indirizzo del risultato della query sul sistema effettuata dalla NtQuerySystemInformation: nel caso in cui vogliamo enumerare i processi, tale risultato consisterà in una sequenza di blocchi di byte ognuno dei quali raccoglie le informazioni di ogni singolo processo; ognuno di questi blocchi di byte può essere rappresentato dal seguente record

_SYSTEM_PROCESSES = record // Information Class 5 NextEntryDelta: ULONG; //offset all' inizio del blocco successivo ThreadCount: ULONG; //numero di thread Reserved1: array [0..5] of ULONG; CreateTime: LARGE_INTEGER; //l' istante di creazione del processo espresso come il numero di intervalli //da 100 nanosecondi a partire dal 1 Gennaio 1601 UserTime: LARGE_INTEGER; //tempo totale di esecuzione in modalità utente da parte dei thread del processo //espresso in numero di intervalli da 100 nanosecondi KernelTime: LARGE_INTEGER; //tempo totale di esecuzione in modalità kernel da parte dei thread del processo //espresso in numero di intervalli da 100 nanosecondi ProcessName: UNICODE_STRING; //nome del processo: dalla definizione di UNICODE_STRING si ha che il nome del processo //è dato da ProcessName.Buffer (di tipo PWideChar) BasePriority: KPRIORITY; //Priorità di esecuzione con la quale vengono creati i thread del processo ProcessId: ULONG; //PID del processo InheritedFromProcessId: ULONG; //PID del processo padre HandleCount: ULONG; //numero di handle aperti dal processo SessionId: ULONG; //Id della sessione in cui viene eseguito il processo Reserved2: ULONG; VmCounters: VM_COUNTERS; //struttura che raccoglie statistiche sull' utilizzo della memoria virtuale da //parte del processo IoCounters: IO_COUNTERSEX; // Windows 2000 only //struttura che raccoglie statistiche sulle operazioni di I/O effettuate dal processo Threads: array [0..0] of SYSTEM_THREADS; //array in cui ogni elemento rappresenta le caratteristiche di ognuno dei thread del processo end; SYSTEM_PROCESSES = _SYSTEM_PROCESSES; PSYSTEM_PROCESSES = ^SYSTEM_PROCESSES; TSystemProcesses = SYSTEM_PROCESSES; PSystemProcesses = PSYSTEM_PROCESSES;

Non mi sto a dilungare a questo punto sulla definizione dei tipi dei vari campi del record in questione (ad esempio Threads, IoCounters, etc...) in quanto sono presenti anch' essi nella unit Native.pas creata da Marcel Van Brakel. Il campo principale è NextEntryDelta: per ogni blocco che mi rappresenta le caratteristiche di un processo, il valore di NextEntryDelta indica a quanti byte di distanza, dall' inizio del blocco, incomincia il blocco successivo. Tale valore ci consente di enumerare tutti i blocchi: l' ultimo blocco avrà NextEntryDelta = 0 (questo sarà quindi il criterio di arresto dell' enumerazione). Il parametro SystemInformationLength indica la dimensione che diamo alla memoria puntata da SystemInformation; ReturnLength indica invece la dimensione effettiva dell' informazione ottenuta come risultato della query sul sistema. Come fare per determinare la dimensione della memoria da allocare e da far puntare al parametro SystemInformation (ossia il valore da dare al parametro SystemInformationLength)? Bisogna osservare che nel caso delle tipologia di informazione SystemCallCounts (valore intero 6) e SystemModuleInformation (valore intero 11), se assegnamo a SystemInformationLength un valore inferiore alla dimensione effettiva del risultato, allora ReturnLength restituisce la dimensione effettiva del risultato. In questo modo possiamo procedere eseguendo una prima chiamata con SystemInformationLength = 0, ReturnLength avrà come valore la dimensione effettiva del risultato, allochiamo un blocco di memoria di dimensione pari a ReturnLength e chiamiamo nuovamente NtQuerySystemInformation passandogli come SystemInformation un puntatore alla memoria appena allocata. Purtroppo nel caso dell' enumerazione dei processi e dei thread (tipologia di informazione SystemProcessesAndThreadsInformation corrispondente al valore intero 5) questo meccanismo non funziona. Siamo quindi costretti ad andare a spanne partendo da una dimensione iniziale ed incrementando tale dimensione fino a quando la NtQuerySystemInformation viene eseguita senza restituire il valore $C0000004 che indica appunto fallimento per dimensione insufficiente della memoria allocata. Per dovere di cronaca la NtQuerySystemInformation può restituire i seguenti valori:

$00000000: successo
$C0000002: operazione non implementata
$C0000003: classe di informazione non valida: è stato dato al primo parametro un valore che non rientra in nessuno di quelli specificati dall' enumerazione SYSTEM_INFORMATION_CLASS
$C0000004: dimensione della memoria allocata inferiore alla dimensione effettiva del risultato
 

Bene mi sembra tutto ed è ora di passare ad un esempio: una form, un button ed un memo

procedure ElencoProcessi; var i,rl,cp : dword; pinfo : PSystemProcesses; buf : Pointer; dim: dword; begin dim := 10000; GetMem(buf, dim); rl := 0; //N.B. se il primo parametro è 6 (SystemCallCounts) o 11 (SystemModuleInformation): //se il valore assegnato a dim non contiene tutto il risultato restituito dalla funzione, //(e quindi viene restituito il valore STATUS_INFO_LEN_MISMATCH che equivale a $C0000004 //come è possibile vadere nel file NTSTATUS.h presente nel DDK), allora il parametro rl //conterrà la dimensione effettiva da assegnare a buf; in tutti gli altri casi bisogna //andare a spanne come di seguito (SystemProcessesAndThreadsInformation equivale a 5) i := NtQuerySystemInformation(SystemProcessesAndThreadsInformation, buf, dim, @rl); while (i = $C0000004) do begin dim := dim + 10000; FreeMem(buf); GetMem(buf, dim); i := NtQuerySystemInformation(SystemProcessesAndThreadsInformation, buf, dim, @rl); end; if i = 0 then begin cp := 0; repeat pinfo := PSystemProcesses(Pointer(dword(buf) + cp)); cp := cp + pinfo.NextEntryDelta; with pinfo^ do begin if (ProcessName.Buffer <> nil) then begin Form1.Memo1.Lines.Add(WideCharToString(ProcessName.Buffer)) end else begin Form1.Memo1.Lines.Add('System Idle'); end; Form1.Memo1.Lines.Add(' ' + 'Numero di thread: ' + IntToStr(ThreadCount)); Form1.Memo1.Lines.Add(' ' + 'PID: ' + IntToStr(ProcessId)); Form1.Memo1.Lines.Add(' ' + 'PID processo padre: ' + IntToStr(InheritedFromProcessId)); Form1.Memo1.Lines.Add(' ' + 'Numero di handle: ' + IntToStr(HandleCount)); Form1.Memo1.Lines.Add(' ' + 'Id di sessione: ' + IntToStr(SessionId)); end; until (pinfo.NextEntryDelta = 0); end; FreeMem(buf); end;

 

 

 
 
Your Ad Here