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