Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Informazioni relative ad un processo remoto


In questo articolo andiamo ad analizzare le strutture fondamentali in User Mode che caratterizzano un processo. Verranno definite delle procedure per accedere ad informazioni relative a processi remoti.

1. Cominciamo

L'obiettivo di questo articolo, non è quello di descrivere nel dettaglio i passaggi (e conseguentemente le funzioni API utilizzate) che costituiscono la creazione di un processo; andremo ad esaminare alcune tra le strutture più importanti che si trovano nello spazio di memoria di ogni processo.

Come si può vedere dal grafico, lo spazio di memoria di un processo, contiene il modulo .exe, kernel32.dll e ntdll.dll (moduli presenti in ogni processo) più naturalmente tutte le altre dll necessarie nel caso specifico, più un insieme di strutture che raccolgono le caratteristiche del processo medesimo. Naturalmente la storia non finisce qui, si potrebbero scrivere biblioteche intere sull'argomento, ma per quel che dobbiamo dire nel seguito, basta e avanza. La cosa principale da dire è che in pratica il processo è un contenitore di threads. Ogni processo (anche se non specifico direttamente una chiamata all'api CreateThread) dispone di un thread: il thread è l'oggetto che esegue il codice. Il processo è un oggetto che serve al sistema operativo per raccogliere tutto ciò che è associato ad un determinato codice di esecuzione da fa girare sulla CPU. Il processo dispone di uno spazio di memoria in cui viene caricato il modulo .exe, ntdll.dll, kernel32.dll e tutte le altre dll presenti nella Import Table del .exe, più naturalmente tutte le dll che verranno caricate tramite ad esempio LoadLibrary. Nello stesso spazio di memoria vengono allocate delle strutture che raccolgono le caratteristiche del processo (linea di comando, elenco dei moduli dll caricati, con informazioni relative ad ognuno di essi, etc...). Questo solo per fare un breve panoramica di quello che andremo ad approfondire. La struttura di base che contiene poi i puntatori a tutto il resto, è il Process Environment Block (PEB); tutte le volte che verrà creato un thread verrà allocata una struttura analoga relativamente ad ogni thread chiamata Thread Environment Block (TEB). La struttura RTL_User_Process_Parameters (detta anche Process Parameter Block o più brevemente PPB), raccoglie informazioni importanti sul processo, tipo ad esempio il modulo .exe, la linea di comando con cui il processo è stato creato, etc... La struttura PEB_LDR_DATA (il cosidetto LOADER DATA) raccoglie informazioni relative ai moduli caricati: in pratica contiene soprattutto i puntatori alle liste linkate composte da oggetti di tipo LDR_MODULE. La struttura LDR_MODULE raccoglie le caratteristiche di un modulo caricato (es. indirizzo base di caricamento, nome modulo, etc...)

2. Process Environment Block (PEB)

Il PEB è il punto di partenza per analizzare le varie informazioni relative ad un processo: la sua composizione è decisamente complessa dal punto di vista quantitativo in quanto è composto da numerosi campi; inoltre trattandosi di una struttura non documentata ufficialmente, alla pari delle API Native può variare da una versione all'altra di Windows o anche da un service pack all'altro: intendiamoci, non è che all'uscita di un service pack ci troviamo un PEB che non c'entra nulla col precedente, le modifiche se ci sono possono riguardare l'aggiunta di alcuni campi in fondo, non si ha una modifica sostanziale; per avere la struttura del record che mi rappresenta il PEB si può far riferimento all'ottimo lavoro fatto da Marcel Van Brakel e Oliver Schneider  di traduzione in Pascal degli header .h relativi alle API Native in cui troviamo appunto anche la definizione delle varie strutture dati quali appunto PEB, PPB, etc... Nella unit JwaNative.pas troviamo le varie definizioni del PEB (Windows 2000, Windows XP, Windows 2003) determinate con l'ausilio di WinDbg. Come si può notare, fino all'indirizzo $1d4 incluso, i 3 record sono identici, poi ci sono i campi aggiuntivi che variano da versione a versione.

Process Environment Block

Behh, per maggiori informazioni relative ai singoli campi rimando all'ottimo documento prelevabile all'indirizzo

Processes_Thread_Fibers_Jobs__By_Alex_Ionescu.pdf

che contiene poi informazioni dettagliate anche su tutte le altre strutture precedentemente descritte e molto (ma mooolto) di più.

A noi interessano principalmente i valori in posizione $0C (12) e $10 (16) che contengono rispettivamente l'indirizzo del Process Parameter Block (PPB ossia la struttura RTL_USER_PROCESS_PARAMETER contenente informazioni sul processo quali linea di comando, etc...) e l'indirizzo del LOADER DATA (ossia la struttura PEB_LDR_DATA che contiene i puntatori alle strutture LDR_MODULE che raccolgono informazioni sui moduli mappati nello spazio di memoria del processo). A questo punto rimane però una cosa: a che indirizzo si trova il PEB? Lo vediamo nel paragrafo successivo.

2.1 Indirizzo del PEB

Con la seguente funzione vado a determinare l'indirizzo del PEB nello spazio di memoria di un processo

function NtQueryInformationProcess( ProcessHandle : Cardinal; ProcessInformationClass : Byte; ProcessInformation : Pointer; ProcessInformationLength : Cardinal; ReturnLength : PCardinal ): Integer; stdcall; external 'ntdll.dll'; ... function GetPEBptr( hProcess: Cardinal; //handle del processo var addr: Cardinal //indirizzo del PEB ): Boolean; type TProcessBasicInformation = record ExitStatus: Integer; PebBaseAddress: Cardinal; AffinityMask: Integer; BasePriority: Integer; UniqueProcessID: Integer; InheritedFromUniqueProcessID: Integer; end; var ProcInfo: TProcessBasicInformation; status: Integer; begin result := False; //prelevo le informazioni di base relative al processo status := NtQueryInformationProcess(hProcess, 0, @ProcInfo, SizeOf(TProcessBasicInformation), nil); if status <> 0 then begin ErrStrNative('NtQueryInformationProcess', status); Exit; end; addr := ProcInfo.PebBaseAddress; Result := True; end;

come si può vedere, basta usare l' api Native NtQuerySystemInformation (già trattata in precedenti articoli). A questo punto posso determinare l'indirizzo del Process Parameter Block e del Loader Data come vedremo nei paragrafi successivi.

3. Process Parameter Block (PPB)

Il PPB può essere rappresentato dal seguente record _RTL_USER_PROCESS_PARAMETERS

PUNICODE_STRING = ^UNICODE_STRING; UNICODE_STRING = record Length: Word; MaximumLength: Word; Buffer: PWideChar; end; PANSI_STRING = ^ANSI_STRING; ANSI_STRING = record Length: Word; MaximumLength: Word; Buffer: PAnsiChar; end; CURDIR = record (*000*)DosPath: UNICODE_STRING; (*008*)Handle: Cardinal; end; _RTL_DRIVE_LETTER_CURDIR = record (*000*)Flags: Word; (*002*)Length: Word; (*004*)TimeStamp: Cardinal; (*008*)DosPath: ANSI_STRING; end; _RTL_USER_PROCESS_PARAMETERS = record (*000*)MaximumLength: Cardinal; (*004*)Length: Cardinal; (*008*)Flags: Cardinal; (*00c*)DebugFlags: Cardinal; (*010*)ConsoleHandle: Cardinal; (*014*)ConsoleFlags: Cardinal; (*018*)StandardInput: Cardinal; (*01c*)StandardOutput: Cardinal; (*020*)StandardError: Cardinal; (*024*)CurrentDirectory: CURDIR; (*030*)DllPath: UNICODE_STRING; (*038*)ImagePathName: UNICODE_STRING; (*040*)CommandLine: UNICODE_STRING; (*048*)Environment: Pointer; (*04c*)StartingX: Cardinal; (*050*)StartingY: Cardinal; (*054*)CountX: Cardinal; (*058*)CountY: Cardinal; (*05c*)CountCharsX: Cardinal; (*060*)CountCharsY: Cardinal; (*064*)FillAttribute: Cardinal; (*068*)WindowFlags: Cardinal; (*06c*)ShowWindowFlags: Cardinal; (*070*)WindowTitle: UNICODE_STRING; (*078*)DesktopInfo: UNICODE_STRING; (*080*)ShellInfo: UNICODE_STRING; (*088*)RuntimeData: UNICODE_STRING; (*090*)CurrentDirectories: array[0..31] of _RTL_DRIVE_LETTER_CURDIR; end;

3.1 Indirizzo del PPB

Come già detto in precedenza, l'indirizzo del PPB si trova nel PEB (4 byte a partire dal 16° byte)

function GetRTLProcessParametersPtr( hProcess: Cardinal; //handle del processo var addr: Cardinal //indirizzo del PPB ): Boolean; var PEBptr: Cardinal; BytesRead: dword; Usr: Pointer; begin result := False; //determino l'indirizzo del PEB con GetPEBPtr //definita in precedenza if not GetPEBptr(hProcess, PEBptr) then begin Exit; end; if not ReadProcessMemory( hProcess, pointer(PEBptr + 16), @addr, 4, BytesRead ) then begin ErrStr('ReadProcessMemory'); Exit; end; result := True; end;

4. Loader Data

Il blocco Loader Data può essere rappresentato dal seguente record PEB_LDR_DATA

PEB_LDR_DATA = record (*000*)Length: Cardinal; (*004*)Initialized: BOOLEAN; (*008*)SsHandle: Pointer; (*00c*)InLoadOrderModuleList: LIST_ENTRY; (*014*)InMemoryOrderModuleList: LIST_ENTRY; (*01c*)InInitializationOrderModuleList: LIST_ENTRY; (*024*)EntryInProgress: Pointer; end;

4.1 Indirizzo del Loader Data

Come già detto in precedenza, l'indirizzo del Loader Data si trova nel PEB (4 byte a partire dal 12° byte)

function GetPebLdrData( hProcess: Cardinal; //handle del processo var addr: Cardinal //indirizzo del Loader Data ): Boolean; var PEBptr: Cardinal; BytesRead: dword; begin Result := False; //determino l'indirizzo del PEB //con GetPEBPtr definita in precedenza if not GetPEBptr(hProcess, PEBptr) then begin Exit; end; if not ReadProcessMemory( hProcess, pointer(PEBptr + 12), @addr, 4, BytesRead ) then begin ErrStr('ReadProcessMemory'); Exit; end; result := True; end;

5. LDR_MODULE

LDR_MODULE definisce le caratteristiche di ogni modulo mappato nello spazio di memoria di un processo; esiste una istanza di LDR_MODULE per ogni modulo mappato; la struttura è la seguente

LDR_MODULE = record InLoadOrderLinks: LIST_ENTRY; InMemoryOrderLinks: LIST_ENTRY; InInitializationOrderLinks: LIST_ENTRY; DllBase: Cardinal; EntryPoint: Cardinal; SizeOfImage: Cardinal; FullDllName: UNICODE_STRING; BaseDllName: UNICODE_STRING; Flags: Cardinal; LoadCount: SmallInt; TlsIndex: SmallInt; HashLinks: LIST_ENTRY; TimeDateStamp: Cardinal; LoadedImports: Pointer; EntryPointActivationContext: Pointer; // PACTIVATION_CONTEXT PatchInformation: Pointer; end;

Mi soffermerei sui primi 3 campi di tipo LIST_ENTRY; cosa esprime il tipo LIST_ENTRY? Questo tipo è un tipo usato da Windows nella gestione di tutte le più importanti informazioni di tipo "lista linkata" (come è il caso appunto delle informazioni relative all'elenco dei moduli mappati): dalla unit Windows di Delphi abbiamo:

type PListEntry = ^TListEntry; TListEntry = record Flink: PListEntry; //puntatore al successivo Blink: PListEntry; //puntatore al precedente end;

In pratica quando voglio definire una lista linkata di oggetti di un determinato tipo basta che vado ad inserire nella definizione del tipo un campo di tipo TListEntry. Ma come funziona esattamente il tipo TListEntry? Come devo settare i suoi 2 campi Flink e Blink? La cosa migliore è passare direttamente ad un esempio, ma anzitutto definiamo meglio cosa significano i primi 3 campi di LDR_MODULE:

InLoadOrderLinks: consente di collegare le varie istanze di LDR_MODULE nell' ordine di caricamento dei moduli associati; l'anello iniziale e finale della lista è il campo InLoadOrderModuleList di PEB_LDR_DATA (anch'esso chiaramente di tipo TListEntry)

InMemoryOrderLinks: consente di collegare le varie istanze di LDR_MODULE nell' ordine di posizione in memoria dei moduli associati; l'anello iniziale e finale della lista è il campo InMemoryOrderModuleList di PEB_LDR_DATA (anch'esso chiaramente di tipo TListEntry

InInitializationOrderLinks: consente di collegare le varie istanze di LDR_MODULE nell' ordine di inizializzazione dei moduli associati; l'anello iniziale e finale della lista è il campo InInitOrderModuleList di PEB_LDR_DATA (anch'esso chiaramente di tipo TListEntry)

Bene. Ora passiamo al seguente grafico che ci rappresenta la situazione ipotetica in cui siano mappati in memoria 5 moduli: dll1, dll2, dll3, dll4, dll5

Ok. Ora esaminiamo ognuno dei 3 ordinamenti:

InLoadOrderLinks: esaminiamo l'ordine di caricamento; supponiamo che sia il seguente:

dll4, dll2, dll5, dll1, dll3

avremo le seguenti impostazioni (in pseudocodice):

PEB_LDR_DATA.InLoadOrderModuleList.FLink = ptr4; PEB_LDR_DATA.InLoadOrderModuleList.BLink = ptr3; LDRM4.LoadOrder.FLink = ptr2; LDRM4.LoadOrder.BLink = (@(PEB_LDR_DATA) + 12); LDRM2.LoadOrder.FLink = ptr5; LDRM2.LoadOrder.BLink = ptr4; LDRM5.LoadOrder.FLink = ptr1; LDRM5.LoadOrder.BLink = ptr2; LDRM1.LoadOrder.FLink = ptr3; LDRM1.LoadOrder.BLink = ptr5; LDRM3.LoadOrder.FLink = (@(PEB_LDR_DATA) + 12); LDRM3.LoadOrder.BLink = ptr1;

InMemoryOrderLinks: esaminiamo l'ordine di posizione in memoria; supponiamo che sia il seguente:

dll3, dll2, dll4, dll1, dll5

avremo le seguenti impostazioni (in pseudocodice):

PEB_LDR_DATA.InMemoryOrderModuleList.FLink = ptr3 + 8; PEB_LDR_DATA.InMemoryOrderModuleList.BLink = ptr5 + 8; LDRM3.MemoryOrder.FLink = ptr2 + 8; LDRM3.MemoryOrder.BLink = (@(PEB_LDR_DATA) + 20); LDRM2.MemoryOrder.FLink = ptr4 + 8; LDRM2.MemoryOrder.BLink = ptr3 + 8; LDRM4.MemoryOrder.FLink = ptr1 + 8; LDRM4.MemoryOrder.BLink = ptr2 + 8; LDRM1.MemoryOrder.FLink = ptr5 + 8; LDRM1.MemoryOrder.BLink = ptr4 + 8; LDRM5.MemoryOrder.FLink = (@(PEB_LDR_DATA) + 20); LDRM5.MemoryOrder.BLink = ptr1 + 8;

InInitOrderLinks: esaminiamo l'ordine di inizializzazione; supponiamo che sia il seguente:

dll2, dll5, dll4, dll3, dll1

avremo le seguenti impostazioni (in pseudocodice):

PEB_LDR_DATA.InInitOrderModuleList.FLink = ptr2 + 16; PEB_LDR_DATA.InInitOrderModuleList.BLink = ptr1 + 16; LDRM2.InitOrder.FLink = ptr5 + 16; LDRM2.InitOrder.BLink = (@(PEB_LDR_DATA) + 28); LDRM5.InitOrder.FLink = ptr4 + 16; LDRM5.InitOrder.BLink = ptr2 + 16; LDRM4.InitOrder.FLink = ptr3 + 16; LDRM4.InitOrder.BLink = ptr5 + 16; LDRM3.InitOrder.FLink = ptr1 + 16; LDRM3.InitOrder.BLink = ptr4 + 16; LDRM1.InitOrder.FLink = (@(PEB_LDR_DATA) + 28); LDRM1.InitOrder.BLink = ptr3 + 16;

Uhmm.. si, bisogna far un pò di verifiche, comunque è una str****ta una volta capita.

6. Un pò di codice

E' arrivato il momento di scrivere un pò di codice. Partiremo dal Process Parameter Block e andremo a leggere un pò di informazioni relative al processo.

6.1 Informazioni sul processo

Andremo a prelevare le informazioni da RTL_USER_PROCESS_PARAMETERS. Come prima cosa però è opportuno definire una procedura di supporto che ci consenta di ottenere una stringa corrispondente ad un valore UNICODE_STRING ad un determinato indirizzo; infatti alcuni dei parametri di RTL_USER_PROCESS_PARAMETERS sono di tipo UNICODE_STRING.

6.2 Procedura per leggere una UNICODE_STRING dallo spazio di memoria di un processo

function GetUnicodeStringRemoteProcess( hProcess: Cardinal; addr: Cardinal; var remotestring: string ): Boolean; var Length: Cardinal; BytesRead: Cardinal; Buf: Cardinal; Len: Word; Buffer: PWideChar; begin result := False; //leggo i primi 2 byte di _PROCESS_PARAMETER.ImageFile //(che è Unicode_String e quindi i primi 2 byte sono //il campo Length e quindi la lunghezza in byte della stringa) if not ReadProcessMemory( hProcess, pointer(addr), @Len, 2, BytesRead ) then begin ErrStr('ReadProcessMemory'); Exit; end; GetMem(Buffer, Len); try //leggo gli ultimi 4 byte di _PROCESS_PARAMETER.ImageFile //e quindi il puntatore alla stringa if not ReadProcessMemory( hProcess, pointer(addr + 4), @Buf, 4, BytesRead ) then begin ErrStr('ReadProcessMemory'); Exit; end; //leggo la stringa if not ReadProcessMemory( hProcess, pointer(Buf), Buffer, Len, BytesRead ) then begin ErrStr('ReadProcessMemory'); Exit; end; //converto da WideChar a string remotestring := WideCharToString(Buffer); //Len è la lunghezza in byte della wideString //--> la dimensione della stringa sarà la metà SetLength(remotestring, Len div 2); finally FreeMem(Buffer); end; Result := True; end;

a questo punto possiamo scrivere una bella funzioncina che preleva i valori di tutti i campi di RTL_USER_PROCESS_PARAMETERS di tipo UNICODE_STRING

6.3 Funzione per leggere i valori dei campi di RTL_USER_PROCESS_PARAMETERS di tipo UNICODE_STRING

//preleva informazioni di tipo UNICODE_STRING dal processo // (* Valori del parametro infoType 0: offset := 36; //CurrentDirectory 1: offset := 48; //DLLPath 2: offset := 56; //ImagePathName 3: offset := 64; //CommandLine 4: offset := 112; //WindowTitle 5: offset := 120; //DesktopInfo 6: offset := 128; //ShellInfo 7: offset := 136; //RuntimeData *) function GetRemoteProcessInfo( Pid: Cardinal; infoType: byte; var Info: string ): Boolean; var Process: Cardinal; BytesRead: Cardinal; pRTLProcParams, Buf: Cardinal; offset: Cardinal; begin Result := False; Process := OpenProcessEx( PROCESS_VM_READ or PROCESS_QUERY_INFORMATION, False, Pid ); if Process = 0 then begin ErrStr('OpenProcess'); Exit; end; //metto in pRTLProcParams l'indirizzo //del RTL_PROCESS_PARAMETERS if not GetRTLProcessParametersPtr( Process, pRTLProcParams ) then begin Exit; end; case infoType of 0: offset := 36; //CurrentDirectory 1: offset := 48; //DLLPath 2: offset := 56; //ImagePathName 3: offset := 64; //CommandLine 4: offset := 112; //WindowTitle 5: offset := 120; //DesktopInfo 6: offset := 128; //ShellInfo 7: offset := 136; //RuntimeData end; if not GetUnicodeStringRemoteProcess( Process, pRTLProcParams + offset, Info ) then begin Exit; end; CloseHandle(Process); Result := True; end;

7. Elenco dei moduli mappati con elenco delle informazioni relative ad ognuno

Siamo ora arrivati alla stesura di un pò di codice relativo alle strutture PEB_LDR_DATA e LDR_MODULE. Di seguito una funzione che fa appunto tutto questo:

//informazioni che vogliamo raccogliere //relativamente ad ogni modulo mappato type ModuleInfo = record BaseAddress: Cardinal; EntryPoint: Cardinal; SizeOfImage: Cardinal; NomeDll: string; NomeDllCompleto: string; LoadCount: SmallInt; TlsIndex: SmallInt; end; //funzione di callback TElencoModuliCallBackFunc = procedure(var info: ModuleInfo); ... ... (* ListType: tipo di ordinamento richiesto 0: InLoadOrderModuleList 1: InMemoryOrderModuleList 2: InInitializationOrderModuleList *) function ElencoModuli( Pid: Cardinal; ListType: Byte; cbFunc: TElencoModuliCallBackFunc //funzione di callback ): Boolean; var pPeb_ldr_data: Cardinal; LdrData: PEB_LDR_DATA; LdrModule: LDR_MODULE; dwSize: Cardinal; readAddr, readAddrHead: Pointer; loadCount: SHORT; DllName: PWideChar; cnt: Cardinal; Offset: Byte; hProcess: Cardinal; mInfo: ModuleInfo; begin result := False; ModificaPrivilegio('SeDebugPrivilege', TRUE); hProcess := OpenProcessEx( PROCESS_VM_READ or PROCESS_QUERY_INFORMATION, False, Pid ); if hProcess = 0 then begin ErrStr('OpenProcess'); Exit; end; Offset := 8 * ListType; GetPebLdrData(hProcess, pPeb_ldr_data); // Now read the PEB_LDR_DATA structure if not ReadProcessMemory( hProcess, Pointer(pPeb_ldr_data), @LdrData, sizeof(LdrData), dwSize ) then begin ErrStr('ReadProcessMemory'); Exit; end; readAddrHead := Pointer(pPeb_ldr_data + 12 + Offset); case ListType of 0: readAddr := Pointer(LdrData.InLoadOrderModuleList.Flink); 1: readAddr := Pointer(LdrData.InMemoryOrderModuleList.Flink); 2: readAddr := Pointer(LdrData.InInitializationOrderModuleList.Flink); end; cnt := 0; repeat Inc(cnt); // readAddr := Pointer(Cardinal(readAddr) - Offset); //estraggo il modulo if not ReadProcessMemory( hProcess, readAddr, @LdrModule, sizeof(LdrModule), dwSize ) then begin ErrStr('ReadProcessMemory'); Exit; end; //inizializzo il record che conterrà //le caratteristiche del Modulo esaminato mInfo.BaseAddress := 0; mInfo.EntryPoint := 0; mInfo.SizeOfImage := 0; mInfo.NomeDll := ''; mInfo.NomeDllCompleto := ''; mInfo.LoadCount := 0; mInfo.TlsIndex := 0; // //leggo i dati mInfo.BaseAddress := LdrModule.DllBase; mInfo.EntryPoint := LdrModule.EntryPoint; mInfo.SizeOfImage := LdrModule.SizeOfImage; mInfo.LoadCount := LdrModule.LoadCount; mInfo.TlsIndex := LdrModule.TlsIndex; //leggo il nome della DLL GetMem(DllName, LdrModule.BaseDllName.MaximumLength); if ReadProcessMemory( hProcess, LdrModule.BaseDllName.Buffer, DllName, LdrModule.BaseDllName.MaximumLength, dwSize ) then begin mInfo.NomeDll := DllName; end; FreeMem(DllName); //leggo il nome completo della DLL GetMem(DllName, LdrModule.FullDllName.MaximumLength); if ReadProcessMemory( hProcess, LdrModule.FullDllName.Buffer, DllName, LdrModule.FullDllName.MaximumLength, dwSize ) then begin mInfo.NomeDllCompleto := DllName end; FreeMem(DllName); //chiamo la funzione di callback cbFunc(mInfo); //* Jump to next LDR_MODULE structure */ case ListType of 0: readAddr := Pointer(LdrModule.InLoadOrderLinks.Flink); 1: readAddr := Pointer(LdrModule.InMemoryOrderLinks.Flink); 2: readAddr := Pointer(LdrModule.InInitializationOrderLinks.Flink); end; // until readAddr = readAddrHead; CloseHandle(hProcess); result := True; end;

8. GetModuleHandle remoto

Ebbene si: per finire andiamo ad eseguire GetModuleHandle su un processo remoto, ossia scriviamo una funzione che preleva l'indirizzo di base di caricamento di un modulo nello spazio di memoria di un processo remoto: il risultato sarà appunto l'indirizzo del modulo nello spazio di memoria del processo specificato

function RemoteGetModuleHandle( hProcess: Cardinal; //handle del processo nomeModulo: PAnsiChar; //nome del modulo var BaseAddr: Cardinal //indirizzo di base ): Boolean; var pPeb_ldr_data: Cardinal; LdrData: PEB_LDR_DATA; LdrModule: LDR_MODULE; dwSize: Cardinal; readAddr, readAddrHead: Pointer; loadCount: SHORT; BaseDllName: PWideChar; cnt: Cardinal; Offset: Byte; begin result := False; if not GetPebLdrData( hProcess, pPeb_ldr_data ) then begin Exit; end; //prelevo PEB_LDR_DATA if not ReadProcessMemory( hProcess, Pointer(pPeb_ldr_data), @LdrData, sizeof(LdrData), dwSize ) then begin ErrStr('ReadProcessMemory'); Exit; end; readAddrHead := Pointer(pPeb_ldr_data + 12); readAddr := Pointer(LdrData.InLoadOrderModuleList.Flink); cnt := 0; repeat Inc(cnt); // readAddr := Pointer(Cardinal(readAddr)); //estraggo il modulo if not ReadProcessMemory( hProcess, readAddr, @LdrModule, sizeof(LdrModule), dwSize ) then begin ErrStr('ReadProcessMemory'); Exit; end; //leggo il nome della DLL GetMem(BaseDllName, LdrModule.BaseDllName.MaximumLength); if ReadProcessMemory( hProcess, LdrModule.BaseDllName.Buffer, BaseDllName, LdrModule.BaseDllName.MaximumLength, dwSize ) then begin if lstrcmpiA( nomeModulo, PAnsiChar(WideCharToString(BaseDllName)) ) = 0 then begin BaseAddr := LdrModule.DllBase; Result := True; Break; end; end; FreeMem(BaseDllName); //* passo al successivo LDR_MODULE */ readAddr := Pointer(LdrModule.InLoadOrderLinks.Flink); // until readAddr = readAddrHead; end;

chiaramente la funzione è il risultato della semplice modifica della funzione vista al paragrafo precedente: per ogni modulo incontrato si va a confrontarne il nome col nome desiderato ed in caso di match (abbiamo trovato il modulo desiderato) si va a salvare il BaseAddress e si esce dal loop; chiaramente non abbiamo bisogno di gestire tutte e 3 le liste linkate, così facciamo riferimento alla prima semplificando così le operazioni.

Behh, un esempio in allegato è quello che ci vuole

InfoProc.7z

 

 

 

 

 

 
 
Your Ad Here