|
Nell'articolo Elimina se terminato
viene descritto come eliminare il .exe associato ad un processo in esecuzione
immediatamente dopo la terminazione del processo medesimo. In maniera analoga,
in questo articolo viene mostrato come riavviare un processo subito dopo che
questo č stato terminato. E' un ennesimo esempio di applicazione della tecnica
di "Code Injection" esposta nell'articolo
Code Injection. Rispetto all'articolo sull'eliminazione del .exe, viene
chiamata l'api win32 CreateProcess (creazione di un processo) al posto
dell'api win32 DeleteFile (eliminazione di un file su disco).
1. Implementazione
function RestartIfTerminated(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
TRestartIfTerminatedData = 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;
pCreateProcessA: function (lpApplicationName: PAnsiChar;
lpCommandLine: PAnsiChar;
lpProcessAttributes: PSecurityAttributes;
lpThreadAttributes: PSecurityAttributes;
bInheritHandles: Boolean;
dwCreationFlags: Cardinal;
lpEnvironment: Pointer;
lpCurrentDirectory: PAnsiChar;
const lpStartupInfo: TStartupInfo;
var lpProcessInformation: TProcessInformation): 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;
//Puntatori a dati; Es. tutte le stringhe vanno messe qui dentro
//Es. pNomeDll: Pointer;
end;
var
hProcess: Cardinal; //handle al processo remoto
RestartIfTerminatedData: TRestartIfTerminatedData;
//indirizzo del record nello
//spazio di memoria del processo remoto
pRestartIfTerminatedData: Pointer;
//indirizzo della funzione del thread nello
//spazio di memoria del processo remoto
pRestartIfTerminatedThread: Pointer;
output_value: Cardinal;
error_code: Cardinal;
functionSize, parameterSize: Cardinal;
procedure RestartIfTerminatedThread(lpParameter: pointer); stdcall;
var
hProcess: Cardinal;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
//qui posso definire delle variabili locali
begin
with TRestartIfTerminatedData(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);
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.lpReserved := nil;
StartupInfo.lpDesktop := pAnsiChar('winsta0\default');
StartupInfo.lpTitle := nil;
StartupInfo.dwX := 0;
StartupInfo.dwY := 0;
StartupInfo.dwXSize := 0;
StartupInfo.dwYSize := 0;
StartupInfo.dwXCountChars := 0;
StartupInfo.dwYCountChars := 0;
StartupInfo.dwFillAttribute := 0;
StartupInfo.dwFlags := 1;
StartupInfo.wShowWindow := 5;
StartupInfo.cbReserved2 := 0;
StartupInfo.lpReserved2 := nil;
StartupInfo.hStdInput := 0;
StartupInfo.hStdOutput := 0;
StartupInfo.hStdError := 0;
if not pCreateProcessA(nil,
pTargetExeName,
nil,
nil,
False,
CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,
nil,
nil,
StartupInfo,
ProcessInfo) then
begin
errorcod := pGetLastError;
pExitThread(0);
end
else
begin
//come output metto il PID del processo creato
output := ProcessInfo.dwProcessId;
pCloseHandle(ProcessInfo.hProcess);
pExitThread(1);
end;
//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 RestartIfTerminatedUnloadData(): Boolean;
begin
Result := False;
with RestartIfTerminatedData do
begin
//chiamo UnloadData su tutti i puntatori a dati inclusi nel record
UnloadData(hProcess, pTargetExeName);
end;
//deallocazione spazio per il parametro
UnloadData(hProcess, pRestartIfTerminatedData);
//deallocazione spazio per la funzione
UnloadData(hProcess, pRestartIfTerminatedThread);
Result := True;
end;
//definisco i valori dei campi del record e copio i dati in remoto
function RestartIfTerminatedInjectData(): Boolean;
begin
Result := False;
try
//inizializzazione valori campi del record:
with RestartIfTerminatedData 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');
pCreateProcessA := GetProcAddress(GetModuleHandle('kernel32'),
'CreateProcessA');
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;
//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(RestartIfTerminatedData);
if not InjectData(hProcess,
@RestartIfTerminatedData,
parameterSize,
False,
pRestartIfTerminatedData) then
begin
Exit;
end;
//copio la funzione
functionSize := SizeOfProc(@RestartIfTerminatedThread);
if not InjectData(hProcess,
@RestartIfTerminatedThread,
functionSize,
True,
pRestartIfTerminatedThread) then
begin
Exit;
end;
Result := True;
finally
if not Result then
begin
RestartIfTerminatedUnloadData;
end;
end;
end;
begin
//inizializzo a zero le variabili locali
hProcess := 0;
pRestartIfTerminatedData := nil;
pRestartIfTerminatedThread := 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 RestartIfTerminatedInjectData() then
Exit;
if not InjectThread(hProcess,
pRestartIfTerminatedThread,
pRestartIfTerminatedData,
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
RestartIfTerminatedUnloadData;
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.btnRestartIfterminatedClick(Sender: TObject);
var
output: Cardinal;
err: Cardinal;
exeName: PAnsiChar;
begin
//
GetMem(exeName, 1000);
GetModuleFileNameA(0, exeName, 1000);
if not RestartIfTerminated(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 +
'verrā automaticamente riavviato', mtInformation, [mbOK], 0);
end;
FreeMem(exeName);
end;
RiavviaSeTerminato
|