|
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
|