Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Terminazione "pulita" di un processo remoto


In questo articolo viene proposta una procedura basata sul Code Injection tramite la quale viene eseguita l'api win32 ExitProcess

1. ExitProcess

L'api win32 ExitProcess termina un processo e tutti i suoi thread

procedure ExitProcess(uExitCode: Cardinal); stdcall;

La funzione consente di specificare un codice di uscita come parametro: tale valore può essere poi prelevato da un qualsiasi processo che detiene un handle al processo terminato, chiamando l'api win32 GetExitCodeProcess.

Entrando più nel dettaglio, riguardo al comportamento di ExitProcess, si ha:

  • Tutti gli handles aperti dal processo, vengono chiusi
  • Tutti i thread nel processo, fatta eccezione per il thread che esegue la chiamata all'api ExitProcess, terminano la loro esecuzione. Inoltre (e questa è una cosa molto importante) viene chiamato l'entry-point di tutte le dll caricate nello spazio di memoria del processo chiamante, usando come parametro il valore DLL_PROCESS_DETACH. A questo punto viene terminato il processo corrente, incluso naturalmente il thread chiamante.
  • Lo stato del processo viene settato a Signaled, sbloccando quindi l'esecuzione di tutti i thread in attesa della terminazione del processo medesimo (ad esempio tramite l'api win32 WaitForSingleObject
  • Lo stato di tutti i thread nel processo viene settato a Signaled, sbloccando quindi l'esecuzione di tutti i thread in attesa della terminazione di uno o più di essi (ad esempio tramite l'api win32 WaitForSingleObject)
  • Lo stato di terminazione del processo viene modificato dal valore STILL_ACTIVE al valore specificato come parametro della ExitProcess.

Il comportamento della ExitProcess è quindi molto più certosino rispetto all'api win32 TerminateProcess che provoca una terminazione incondizionata di qualsiasi processo (tralasciando ad esempio la chiamata all'entrypoint delle dll caricate nello spazio di memoria del processo che si vuole terminare). Mentre la ExitProcess coinvolge solo il processo chiamante, la TerminateProcess coinvolge qualsiasi processo. L'obiettivo di questo articolo è estendere la ExitProcess a tutti i processi e l'implementazione si baserà sulla già ben collaudata tecnica di code injection

2. RemoteExitProcess

Facendo riferimento all'articolo Code Injection e soprattutto allo scheletro di procedura in esso esposto, otteniamo una procedura che consente di chiamare la ExitProcess su qualsiasi processo remoto

function RemoteExitProcess(PID: Cardinal; //PID del processo remoto var outValue: Cardinal; //valore importante ottenuto dalla funzione remota var codError: Cardinal; //errore riscontrato dalla funzione remota synch: Boolean): Boolean; //esecuzione sincrona del thread remoto type TRemoteExitProcessData = record errorcod: Cardinal; //codice di errore rilevato nella funzione remota output: Cardinal; //risultato significativo nella funzione remota //Indirizzi delle API usate: elenco puntatori a funzioni; N.B. rigorosamente stdcall //Es. pLoadLibraryA: function(pLibFileName: PAnsiChar): Cardinal; stdcall; pExitThread: procedure (dwExitCode: Cardinal); stdcall; //API obbligatoria pExitProcess: procedure (uExitCode: Cardinal); stdcall; //Puntatori a dati; Es. tutte le stringhe vanno messe qui dentro //Es. pNomeDll: Pointer; end; var hProcess: Cardinal; //handle al processo remoto RemoteExitProcessData: TRemoteExitProcessData; //indirizzo del record nello spazio di memoria del processo remoto pRemoteExitProcessData: Pointer; //indirizzo della funzione del thread nello spazio di memoria del processo remoto pRemoteExitProcessThread: Pointer; output_value: Cardinal; error_code: Cardinal; functionSize, parameterSize: Cardinal; procedure RemoteExitProcessThread(lpParameter: pointer); stdcall; //var //qui posso definire delle variabili locali begin with TRemoteExitProcessData(lpParameter^) do begin //implementazione: tutte le API vengono chiamate tramite i puntatori nel record pExitProcess(1); pExitThread(1); //N.B. chiamare sempre pExitCodeThread per definire il codice di uscita del thread //Es. pExitThread(1) significa successo // pExitThread(0) significa fallimento end; end; //dealloco la memoria in remoto function RemoteExitProcessUnloadData(): Boolean; begin Result := False; with RemoteExitProcessData do begin //chiamo UnloadData su tutti i puntatori a dati inclusi nel record end; //deallocazione spazio per il parametro UnloadData(hProcess, pRemoteExitProcessData); //deallocazione spazio per la funzione UnloadData(hProcess, pRemoteExitProcessThread); Result := True; end; //definisco i valori dei campi del record e copio i dati in remoto function RemoteExitProcessInjectData(): Boolean; begin Result := False; try //inizializzazione valori campi del record: with RemoteExitProcessData do begin errorcod := 0; output := 0; //assegno i valori ai puntatori a funzione //(tramite GetModuleHandle e GetProcAddres) //Es. pLoadLibraryA := GetProcAddress(GetModuleHandle('kernel32'), 'LoadLibraryA'); pExitThread := GetProcAddress(GetModuleHandle('kernel32'), 'ExitThread'); //API Obbligatoria pExitProcess := GetProcAddress(GetModuleHandle('kernel32'), 'ExitProcess'); //API Obbligatoria //assegno i valori ai puntatori ai dati (tramite InjectData); //N.B. se una InjectData ritorna False allora bisogna chiamare Exit end; //copio il parametro parameterSize := SizeOf(RemoteExitProcessData); if not InjectData(hProcess, @RemoteExitProcessData, parameterSize, False, pRemoteExitProcessData) then begin Exit; end; //copio la funzione functionSize := SizeOfProc(@RemoteExitProcessThread); if not InjectData(hProcess, @RemoteExitProcessThread, functionSize, True, pRemoteExitProcessThread) then begin Exit; end; Result := True; finally if not Result then begin RemoteExitProcessUnloadData; end; end; end; begin //inizializzo a zero le variabili locali hProcess := 0; pRemoteExitProcessData := nil; pRemoteExitProcessThread := nil; output_value := 0; error_code := 0; try hProcess := OpenProcessEx(PROCESS_CREATE_THREAD + PROCESS_QUERY_INFORMATION + PROCESS_VM_OPERATION + PROCESS_VM_WRITE + PROCESS_VM_READ, False, PID ); if hProcess = 0 then begin ErrStr('OpenProcessEx'); Exit; end; if not RemoteExitProcessInjectData() then Exit; if not InjectThread(hProcess, pRemoteExitProcessThread, pRemoteExitProcessData, output_value, error_code, synch) then Exit; //output_value e error_code sono stati inizializzati a 0. //sono stati modificati dalla InjectThread solo se synch = true; //se synch=false sono rimasti uguali a zero outvalue := output_value; codError := error_code; Result := True; finally if synch or (not Result) then begin RemoteExitProcessUnloadData; end; if hProcess <> 0 then begin if not CloseHandle(hProcess) then begin ErrStr('CloseHandle'); end; end; end; end;

RemoteExit

 

 

 

 

 

 

 

 

 

 

 

 
 
Your Ad Here