Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Esecuzione di codice in un processo remoto senza usare una dll: esempio di FreeLibrary (scaricamento di una dll) eseguita su un processo remoto.
 

Nei giorni addietro abbiamo parlato di dll injection e via dicendo; ora però facciamo i tipi fighi e vediamo se siamo in grado di eseguire codice in un processo remoto senza ricorrere ad una dll esterna. Bene, bene, direi di passare direttamente ad un esempio pratico per presentare questa tecnica. Si è visto come eseguire l' unmapping di una dll dallo spazio di memoria di un processo remoto, determinandone l' indirizzo base di caricamento tramite le "Tool help api" ed eseguendo la FreeLibrary nel contesto del processo remoto (tramite CreateRemoteThread) passandogli come argomento l' indirizzo base precedentemente ottenuto. Bene, supponiamo di non voler usare le "tool help api": c'è una alternativa???? Cerrtooo. Anzitutto come dovrei procedere se volessi eliminare dalla memoria del mio processo una dll caricata precedentemente???
Beehhh

var Dll_handle: LongWord; begin DllHandle := GetModuleHandle('NomeDll.dll'); if DllHandle <> 0 then //la dll 'NomeDll.dll' è caricata nello spazio di memoria del mio processo FreeLibrary(DllHandle); end;


Bene; ora dovremmo eseguire tale procedura nel processo remoto (il processo in cui vogliamo eseguire il FreeLibrary della dll). Dovrei in sostanza copiare la procedura sopra nello spazio di memoria del processo remoto e poi eseguirla con il classico CreateRemoteThread. CreateRemoteThread prende in input (tra i vari argomenti) un puntatore alla procedura la cui implementazione definisce il codice di esecuzione del thread ed il parametro di input della procedura medesima (valore dword). Inizialmente il fatto di poter passare un solo parametro potrebbe sembrare una limitazione: e che cavolo, posso eseguire in remoto solo procedure con un solo parametro???? Ehhh si, però proviamo a pensare: dunque quel parametro potrebbe essere anche ad esempio un puntatore ad un record; se la procedura che voglio eseguire in remoto deve avere ad esempio 10 argomenti in input, allora posso crearmi un record con 10 campi ognuno dei quali corrisponde ad ognuno dei 10 argomenti in questione; passerò come argomento un puntatore a tale record ed il gioco è fatto. A questo punto bisogna dire che quando andremo ad eseguire il codice nel contesto del processo remoto, bisognerà specificare direttamente l' indirizzo delle varie api che vengono chiamate dalla procedura medesima. Facendo riferimento alla procedura sopra dovremo specificare direttamente l' indirizzo (naturalmente nel contesto del processo remoto) delle funzioni GetModuleHandle e FreeLibrary. Entrambe le funzioni sono implementate nella dll kernel32.dll: essa viene caricata in tutti i processi win32 ed in particolare non è soggetta a rilocazione, ossia viene caricata sempre allo stesso indirizzo in tutti i processi. Questo significa che, se nel mio processo vado a determinare l' indirizzo delle 2 funzioni in questione, tale indirizzo sarà valido anche nel contesto del processo remoto. Di conseguenza io posso determinare i 2 indirizzi e poi assegnarli ai campi di un record che andrò a passare come argomento della procedura da eseguire in remoto. Ci possiamo quindi creare un record

type TRemoteData = record //indirizzo della GetModuleHandle pGetModuleHandle: function (lpModuleName: PAnsiChar): LongWord; stdcall; //indirizzo della FreeLibrary pFreeLibrary: function (hLibModule: LongWord): LongBool; stdcall; end; PRemoteData = ^TRemoteData;


Ora dal mio processo scriverò

var RemoteData: TRemoteData; begin RemoteData.pGetModuleHandle := GetProcAddress(GetModuleHandle('kernel32'), 'GetModuleHandleA'); RemoteData.pFreeLibrary := GetProcAddress(GetModuleHandle('kernel32'), 'FreeLibrary'); ... ... ... end;


Inoltre dobbiamo anche copiare nello spazio di memoria del processo remoto il nome della dll da scaricare (nell' esempio iniziale 'NomeDll.dll'). L' indirizzo a partire dal quale (nel contesto del processo remoto) sarà presente la stringa con il nome della dll, verrà memorizzato in un ulteriore campo del record TRemoteData.
Avremo quindi

type TRemoteData = record //indirizzo della GetModuleHandle pGetModuleHandle: function (lpModuleName: PAnsiChar): LongWord; stdcall; //indirizzo della FreeLibrary pFreeLibrary: function (hLibModule: LongWord): LongBool; stdcall; //indirizzo del nome della dll da scaricare pNomeDll: pChar; end; PRemoteData = ^TRemoteData;

E' opportuno scrivere una funzione per copiare una stringa nello spazio di memoria di un altro processo

function InjectString(hProcess: LongWord; stringa: pChar): pChar; var BytesWritten: dword; begin //vado ad allocare nel processo remoto (di cui ho l' handle hProcess //opportunamente ottenuto tramite una chiamata ad OpenProcess) una striscia //di memoria di dimensione pari alla lunghezza della stringa incrementata di 1 //(carattere di terminazione stringa) Result := VirtualAllocEx(Process, nil, Length(stringa) + 1, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE); //scrivo, nell' area di memoria appena allocata, il nome della dll WriteProcessMemory(Process, Result, stringa, Length(stringa) + 1, BytesWritten); //l' output della funzione è appunto l' indirizzo di memoria a partire dal quale //è memorizzato (nel contesto del processo remoto) il nome della stringa end;


A questo punto dal mio processo scriverò

var RemoteData: TRemoteData; process: THandle; begin Process := OpenProcess(PROCESS_CREATE_THREAD + PROCESS_QUERY_INFORMATION + PROCESS_VM_OPERATION + PROCESS_VM_WRITE + PROCESS_VM_READ, False, PID ); RemoteData.pGetModuleHandle := GetProcAddress(GetModuleHandle('kernel32'), 'GetModuleHandleA'); RemoteData.pFreeLibrary := GetProcAddress(GetModuleHandle('kernel32'), 'FreeLibrary'); RemoteData.pNomeDll := InjectString(Process, 'NomeDll.dll'); ... ... ... end;


A questo punto la procedura da eseguire in remoto diventerà

function RemoteFreeLibrary(RemoteData: PRemoteData): Bool; stdcall; var Dll_handle:LongWord; begin Result := False; Dll_handle := RemoteData^.pGetModuleHandle(RemoteData^.pNomeDll); if Dll_handle <> 0 then //la dll in questione è caricata nello spazio di memoria del mio processo Result := RemoteData^.pFreeLibrary(Dll_handle); end;


Ho pensato bene di renderla una funzione che restituisce il valore restituito dalla FreeLibrary

Ok ora dobbiamo copiare sia la procedura (RemoteFreeLibrary) sia le informazioni puntate dal suo parametro di input (RemoteData) nello spazio di memoria del processo remoto; anzitutto scriviamoci una bella funzioncina (la sorella della InjectString) che mi consenta di copiare qualsiasi dato (e quindi anche il nostro record TRemoteData) nello spazio di memoria di un processo remoto.


function InjectDati(Process: LongWord; Dati: Pointer; Dimensione: dword): pointer; var BytesWritten: dword; begin Result := VirtualAllocEx(Process, nil, Dimensione, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE); WriteProcessMemory(Process, Result, Dati, Dimensione, BytesWritten); end;

La funzione è analoga alla precedente solo che in questo caso non è possibile determinare la dimensione del dato da copiare e quindi la dimensione va passata come parametro. Quando vado a chiamare InjectDati userò la funzione SizeOf per determinare la dimensione del dato da copiare.

Ora dobbiamo copiare la procedura: mmmhhh interessante ... il codice di implementazione di una procedura è di per sè una sequenza di byte e come tale può essere copiato nello spazio di memoria di un processo remoto tramite la InjectDati; l' unica cosa un pò tostina diventa la determinazione della dimensione della procedura: dunnnqueeeee ... si può fare in questo modo: si può definire subito dopo la definizione della funzione RemoteFreeLibrary un altra procedura che non esegue praticamente nulla e che chiameremo
RemoteFreeLibraryEnd;

procedure EndRemoteFreeLibrary();
begin end;

la dimensione della RemoteFreeLibrary sarà data dalla differenza tra l' indirizzo a partire dal quale è memorizzato il codice di implementazione di EndRemoteFreeLibrary (e cioè @EndRemoteFreeLibrary) e l' indirizzo a partire dal quale è memorizzato il codice di implementazione di RemoteFreeLibrary (e cioè @RemoteFreeLibrary); in sostanza la dimensione della RemoteFreeLibrary sarà:
dword(@EndRemoteFreeLibrary) - dword(@RemoteFreeLibrary

Ok, riassumiamo un attimo: sappiamo copiare una procedura in remoto, sappiamo copiare il suo input in remoto ... non resta altro da fare che chiamare CreateRemoteThread usando i 2 indirizzi (della procedura e del parametro relativamente al processo remoto) come argomenti: nel mio processo scriverò


function ExecuteRemoteFreeLibrary(PID: DWORD; Dll: pChar): Bool; var RemoteData: TRemoteData; process, thread: THandle; ptrInput, ptrProc: Pointer; ThreadId: DWORD; Coduscita: dword; begin //ottengo un handle al processo remoto Process := OpenProcess(PROCESS_CREATE_THREAD + PROCESS_QUERY_INFORMATION + PROCESS_VM_OPERATION + PROCESS_VM_WRITE + PROCESS_VM_READ, False, PID ); //definisco l' argomento di input della procedura da eseguire in remoto RemoteData.pGetModuleHandle := GetProcAddress(GetModuleHandle('kernel32'), 'GetModuleHandleA'); RemoteData.pFreeLibrary := GetProcAddress(GetModuleHandle('kernel32'), 'FreeLibrary'); RemoteData.pNomeDll := InjectString(Process, Dll); //copio RemoteData in remoto ptrInput := InjectDati(Process, @RemoteData, SizeOf(RemoteData)); //copio RemoteFreeLibrary in remoto ptrProc := InjectDati(Process, @RemoteData, SizeOfProcedure(RemoteFreeLibrary)); //eseguo RemoteFreeLibrary nel contesto del processo remoto passandogli //l' indirizzo di RemoteData come parametro Thread := CreateRemoteThread(Process, nil, 0, ptrProc, ptrInput, 0, ThreadId); //attendo la terminazione del thread remoto ossia lo scaricamento della dll //in questione dallo spazio di memoria del processo remoto WaitForSingleObject(Thread, INFINITE); //rilevo il codice di uscita del thread remoto che altro non è che il valore restituito //dalla RemoteFreeLibrary ossia False in caso di fallimento, True in caso di successo //essendo esso stesso l' output della FreeLibrary. Essendo il secondo parametro della // GetExitCodeThread un dword allora si avrà 0 per False ed un valore <> 0 per True GetExitCodeThread(Thread, CodUscita); if CodUscita = 0 then Result := False else Result := True; //chiudo l' handle al thread creato in remoto (Thread) CloseHandle(Thread); end;

Come ulteriore rifinitura è opportuno abilitare il privilegio di debug prima dell' OpenProcess tramite la procedura ModificaPrivilegio già esaminata nei precedenti articoli; anche il PID può essere ottenuto a partire dal nome dell' eseguibile tramite la funzione PidProcesso già esposta precedentemente. In definitiva questo è il codice per scaricare una dll dallo spazio di memoria di un processo remoto:

unit RemoteFreeUnit; interface uses Windows, Sysutils, TlHelp32; function PidProcesso(NomeProcesso: string): LongWord; function ExecuteRemoteFreeLibrary(PID: DWORD; Dll: pChar): bool; implementation type TRemoteData = record pGetModuleHandle: function (lpModuleName: PAnsiChar): LongWord; stdcall; pFreeLibrary: function (hLibModule: LongWord): LongBool; stdcall; pNomeDll: pChar; end; PRemoteData = ^TRemoteData; procedure ModificaPrivilegio(szPrivilege: pChar; fEnable: Boolean); var NewState: TTokenPrivileges; luid: TLargeInteger; hToken: THandle; ReturnLength: DWord; begin OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken); LookupPrivilegeValue(nil, szPrivilege, luid); NewState.PrivilegeCount := 1; NewState.Privileges[0].Luid := luid; if fEnable then NewState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED else NewState.Privileges[0].Attributes := 0; AdjustTokenPrivileges( hToken, FALSE, NewState, sizeof(NewState), nil, ReturnLength); CloseHandle(hToken); end; function PidProcesso(NomeProcesso: string): LongWord; var pe: TProcessEntry32; hSnap: THandle; begin hSnap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); pe.dwSize := sizeof(TProcessEntry32); //Prelevo informazioni sul primo processo nello snapshot di sistema Process32First(hSnap, pe); repeat //loop sui processi Result := pe.th32ProcessID; if (pe.szExeFile = NomeProcesso) then begin break; end; until (not (Process32Next(hSnap, pe)) ) ; CloseHandle(hSnap); end; function InjectString(Process: LongWord; Text: pchar): pchar; var BytesWritten: dword; begin Result := VirtualAllocEx(Process, nil, Length(Text) + 1, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE); WriteProcessMemory(Process, Result, Text, Length(Text) + 1, BytesWritten); end; function InjectDati(Process: LongWord; Dati: Pointer; Dimensione: dword): pointer; var BytesWritten: dword; begin Result := VirtualAllocEx(Process, nil, Dimensione, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE); WriteProcessMemory(Process, Result, Dati, Dimensione, BytesWritten); end; function RemoteFreeLibrary(RemoteData: PRemoteData): Bool; stdcall; var Dll_handle:LongWord; begin Result := False; Dll_handle := RemoteData^.pGetModuleHandle(RemoteData^.pNomeDll); if Dll_handle <> 0 then //la dll in questione è caricata nello spazio di memoria del mio processo Result := RemoteData^.pFreeLibrary(Dll_handle); end; procedure EndRemoteFreeLibrary(); stdcall; begin end; function ExecuteRemoteFreeLibrary(PID: DWORD; Dll: pChar): bool; var RemoteData: TRemoteData; process, thread: THandle; ptrInput, ptrProc: Pointer; ThreadId: DWORD; CodUscita: dword; begin ModificaPrivilegio('SeDebugPrivilege', TRUE); Process := OpenProcess(PROCESS_CREATE_THREAD + PROCESS_QUERY_INFORMATION + PROCESS_VM_OPERATION + PROCESS_VM_WRITE + PROCESS_VM_READ, False, PID ); RemoteData.pGetModuleHandle := GetProcAddress(GetModuleHandle('kernel32'), 'GetModuleHandleA'); RemoteData.pFreeLibrary := GetProcAddress(GetModuleHandle('kernel32'), 'FreeLibrary'); RemoteData.pNomeDll := InjectString(Process, Dll); ptrInput := InjectDati(Process, @RemoteData, SizeOf(RemoteData)); ptrProc := InjectDati(Process, @RemoteFreeLibrary, dword(@EndRemoteFreeLibrary) - dword(@RemoteFreeLibrary) ); Thread := CreateRemoteThread(Process, nil, 0, ptrProc, ptrInput, 0, ThreadId); WaitForSingleObject(Thread, INFINITE); GetExitCodeThread(Thread, CodUscita); if CodUscita = 0 then Result := False else Result := True; CloseHandle(Thread); end; end.



 
 
 
Your Ad Here