Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Processo che elimina il file eseguibile associato immediatamente dopo la propria terminazione (self deleting executable)


In questo articolo viene esposto un ulteriore esempio di utilizzo della tecnica di Code Injection esaminata nel dettaglio nell'articolo Code Injection

1. Analisi della problematica

Un processo non può eliminare l'immagine .exe su disco ad esso associata: questo è un dato di fatto. Se voglio terminare un processo e poi eliminare il .exe, ... uhmmm non è una operazione triviale. Certo si può creare un altro programma che lancia il mio programma e poi, una volta terminato, ne elimina il .exe. Una soluzione che non richiede programmi esterni è quella di ricorrere al Code Injection, iniettando del codice nello spazio di memoria di un altro processo (tipo explorer.exe tanto per citarne uno che è sempre presente): tale codice si mette in attesa della terminazione del mio processo e, una volta rilevata la terminazione, elimina il .exe associato.

2. Implementazione

function DeleteAfterExit(PID: Cardinal; //PID del processo remoto TargetPid: Cardinal; TargetExeName: PAnsiChar; 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 TDeleteAfterExitData = 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 pGetLastError: function(): Cardinal; stdcall; pOpenProcess: function (dwDesiredAccess: Cardinal; bInheritHandle: Boolean; dwProcessId: Cardinal): Cardinal; stdcall; pWaitForSingleObject: function (hHandle: Cardinal; dwMilliseconds: Cardinal): Cardinal; stdcall; pDeleteFileA: function (lpFileName: PAnsiChar): Boolean; stdcall; pCloseHandle: function (hObject: Cardinal): Boolean; stdcall; //Puntatori a dati; Es. tutte le stringhe vanno messe qui dentro //Es. pNomeDll: Pointer; pTargetPID: Cardinal; pTargetExeName: Pointer; end; var hProcess: Cardinal; //handle al processo remoto DeleteAfterExitData: TDeleteAfterExitData; //indirizzo del record nello spazio di memoria del processo remoto pDeleteAfterExitData: Pointer; //indirizzo della funzione del thread nello spazio di memoria del processo remoto pDeleteAfterExitThread: Pointer; output_value: Cardinal; error_code: Cardinal; functionSize, parameterSize: Cardinal; procedure DeleteAfterExitThread(lpParameter: pointer); stdcall; var //qui posso definire delle variabili locali hProcess: Cardinal; begin with TDeleteAfterExitData(lpParameter^) do begin //implementazione: tutte le API vengono chiamate //tramite i puntatori nel record hProcess := pOpenProcess(SYNCHRONIZE, False, pTargetPID); if hProcess = 0 then begin errorcod := pGetLastError; pExitThread(0); end; case pWaitForSingleObject(hProcess, INFINITE) of WAIT_FAILED: begin errorcod := pGetLastError; pExitThread(0); end; end; pCloseHandle(hprocess); if not pDeleteFileA(PAnsiChar(pTargetExeName)) then begin errorcod := pGetLastError; pExitThread(0); end; pExitThread(1); //N.B. chiamare sempre pExitCodeThread per //definire il codice di uscita del thread //Es. pExitCodeThread(1) significa successo // pExitCodeThread(0) significa fallimento end; end; //dealloco la memoria in remoto function DeleteAfterExitUnloadData(): Boolean; begin Result := False; with DeleteAfterExitData do begin //chiamo UnloadData su tutti i puntatori a dati inclusi nel record UnloadData(hProcess, pTargetExeName); end; //deallocazione spazio per il parametro UnloadData(hProcess, pDeleteAfterExitData); //deallocazione spazio per la funzione UnloadData(hProcess, pDeleteAfterExitThread); Result := True; end; //definisco i valori dei campi del record e copio i dati in remoto function DeleteAfterExitInjectData(): Boolean; begin Result := False; try //inizializzazione valori campi del record: with DeleteAfterExitData 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 pGetLastError := GetProcAddress(GetModuleHandle('kernel32'), 'GetLastError'); pOpenProcess := GetProcAddress(GetModuleHandle('kernel32'), 'OpenProcess'); pWaitForSingleObject := GetProcAddress(GetModuleHandle('kernel32'), 'WaitForSingleObject'); pDeleteFileA := GetProcAddress(GetModuleHandle('kernel32'), 'DeleteFileA'); pCloseHandle := GetProcAddress(GetModuleHandle('kernel32'), 'CloseHandle'); pTargetPID := TargetPid; //assegno i valori ai puntatori ai dati (tramite InjectData); //N.B. se una InjectData ritorna False allora bisogna chiamare Exit if not InjectData(hProcess, TargetExeName, Length(TargetExeName), False, pTargetExeName) then begin Exit; end; end; //copio il parametro parameterSize := SizeOf(DeleteAfterExitData); if not InjectData(hProcess, @DeleteAfterExitData, parameterSize, False, pDeleteAfterExitData) then begin Exit; end; //copio la funzione functionSize := SizeOfProc(@DeleteAfterExitThread); if not InjectData(hProcess, @DeleteAfterExitThread, functionSize, True, pDeleteAfterExitThread) then begin Exit; end; Result := True; finally if not Result then begin DeleteAfterExitUnloadData; end; end; end; begin //inizializzo a zero le variabili locali hProcess := 0; pDeleteAfterExitData := nil; pDeleteAfterExitThread := 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 DeleteAfterExitInjectData() then Exit; if not InjectThread(hProcess, pDeleteAfterExitThread, pDeleteAfterExitData, 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 DeleteAfterExitUnloadData; end; if hProcess <> 0 then begin if not CloseHandle(hProcess) then begin ErrStr('CloseHandle'); end; end; end; end;

e di seguito un semplice esempio di utilizzo

procedure TfrmMain.btnTerminateAndDeleteClick(Sender: TObject); var output: Cardinal; err: Cardinal; exeName: PAnsiChar; begin // GetMem(exeName, 1000); GetModuleFileNameA(0, exeName, 1000); if not DeleteAfterExit(PidProcesso(edtNomeEXE.Text), GetCurrentProcessId, exeName, output, err, False) then begin mmoLog.Lines.Add('errore nella procedura'); end else begin if err <> 0 then mmoLog.Lines.Add('Errore nel thread; Cod errore: ' + IntToStr(err)) else MessageDlg('Perfetto: hai attivato con successo il monitoraggio' + #13#10 + 'Il processo ' + edtNomeEXE.Text + ' ha un thread che' + #13#10 + 'sorveglia la terminazione di questo processo' + #13#10 + 'Quando questo processo terminerà (in qualsiasi modo questo succeda)' + #13#10 + 'il file .exe di questo processo verrà eliminato', mtInformation, [mbOK], 0); end; FreeMem(exeName); end;

EliminaSeTerminato

 

 

 

 

 
 
Your Ad Here