Home | Chi sono | Contattami
 

Progr. lineare

Delphi
 
Componenti
  Database
 
Miei articoli

Windows

Miei articoli 

 

Dll Auto Injection: Applet del Pannello di Controllo (.cpl)


Nell'articolo sul Dll Injection vengono esposte le basi di questa tecnica; quello che abbiamo è una dll che vogliamo mappare nello spazio di memoria di un processo ed un eseguibile che va ad eseguire il mapping della medesima. In questo articolo vediamo come creare una dll che si mappa da sola senza la necessità di un eseguibile di supporto. Dunnnqueee ... pensiamoci un attimo, non mi risulta che facendo doppio click su una dll, questa vada in esecuzione ... Behh una dll no ma una cpl si. Vediamo nel seguito di cosa si tratta

1. Applet del Pannello di Controllo

Chiunque abbia un minimo di dimestichezza con Windows, si sarà trovato almeno una volta nella vita a fare doppio click su una delle icone che si trovano nel Pannello di Controllo. Una delle icone che attirano più il doubleclick del mouse è quella relativa ad Installazione Applicazioni; ma cosa c'è dietro ad Installazione Applicazioni? Si tratta di appwiz.cpl situata nella cartella di sistema (\windows\system32). Sempre nella cartella di sistema si trovano altri file con estensione cpl che fanno riferimento sempre ad Applet del Pannello di Controllo. Cos'è in sostanza un .cpl? Si tratta semplicemente di una dll, niente di più. Come fa a partire con il doppio click? Basta aprire il registro di Windows con regedit.exe: come al solito un'immagine ha un elevato rendimento

Quindi per eseguire un .cpl viene usata la seguente linea di comando:

rundll32.exe shell32.dll,Control_RunDLL <nome.cpl>

Una linea di comando relativa all'eseguibile rundll32.exe ha le seguenti caratteristiche:

rundll32.exe <nome.dll>,<nomefunzione> <sequenza di parametri separati tra loro dal carattere ",">

In pratica rundll32.exe va in esecuzione, carica nel proprio spazio di memoria la dll <nome.dll> e chiama la funzione esportata da quest'ultima di nome <nomefunzione> passandogli i parametri in questione.

Nel caso specifico di un file .cpl si ha il seguente comportamento

  1. rundll32.exe va in esecuzione
  2. il processo rundll32 carica nel proprio spazio di memoria la dll shell32.dll
  3. il processo rundll32 chiama la funzione Control_RunDLL esportata dalla dll shell32.dll passandogli il nome del .cpl
  4. la funzione Control_RunDLL carica il file .cpl nello spazio di memoria del processo chiamante (quindi rundll32): quindi in questo frangente verrà chiamato l'entrypoint del .cpl con parametro DLL_PROCESS_ATTACH come avviene per qualsiasi dll.
  5. effettuato il caricamento del file .cpl, viene invocata una funzione esportata dal .cpl di nome CPLApplet; se tale funzione non esiste, allora il .cpl viene scaricato e rundll32 termina; in caso contrario viene chiamata la funzione CPLApplet assegnando determinati valori ai suoi parametri. Nel seguito ci focalizziamo un attimo sulle caratteristiche che deve avere questa funzione, soffermandoci su ciò che ci interessa in questo contesto (non è questo il luogo adatto per una descrizione dettagliata dei meccanismi comportamentali di un file .cpl).

Bene, eccoci qua ad esporre la dichiarazione della funzione CPLApplet

function CPlApplet(hwndCPl: Cardinal; uMsg : Cardinal; lParam1: Integer; lParam2: Integer ): Integer; stdcall;

Il parametro che ci interessa di più è uMsg: la prima chiamata alla CPLApplet avviene col valore 1 (costante CPL_INIT, vedi Platform SDK per approfondimenti) assegnato al parametro uMsg; in corrispondenza di questo valore, la funzione può eseguire procedure di inizializzazione varie, allocazioni iniziali di memoria, etc... Se, in corrispondenza di questo parametro, si fa restituire 0 alla funzione, allora il .cpl viene scaricato dalla memoria e rundll32 termina. Ed è proprio questo che vogliamo, come vedremo nei passaggi sucessivi.

Dunqueeee .... vediamo di chiarire bene cosa vogliamo fare: vogliamo che quando faccio doppio click sul .cpl, tale file venga mappato nello spazio di memoria di un processo remoto; behh ... se la matematica non è un opinione, allora... l'entrypoint del mio .cpl verrà chiamato ben 2 volte: una volta quando il .cpl viene caricato da rundll32 ed una seconda volta quando viene effettivamente caricato nello spazio di memoria del processo remoto. Nel primo caso dovrò eseguire la procedura di dll injection, mentre nel secondo caso dovrò eseguire il codice che voglio mandare in esecuzione nello spazio di memoria del processo remoto (tipo ad esempio l'Hook su alcune API). Come faccio a distinguere le 2 situazioni? La prima soluzione che mi è venuta in mente è stata quella di controllare la linea di comando (tramite GetCommandLine), a quel punto il cervello ha cominciato a rodermi un pò, i pensieri si intrecciavano, poi alla fine ... la luce: usiamo la CPLApplet per creare un Global Atom tramite l' API win32 GlobalAddAtom, e tale valore servirà per controllare se siamo in Fase 1 o in Fase 2. Vediamo la sequenza temporale degli eventi per capirci meglio

1) CPL caricata nello spazio di memoria di rundll32:  chiamata all'entrypoint della CPL (Fase 1)

2) chiamata alla funzione CPLApplet con valore 1 del parametro uMsg; tale funzione crea il Global Atom, poi esegue il caricamento della CPL nello spazio di memoria del processo remoto: chiamata all'entrypoint della CPL (Fase 2); eseguito il dll injection, la funzione elimina il Global Atom, poi restituisce 0 causando l'immediato scaricamento della CPL dallo spazio di memoria di rundll32 e la terminazione di rundll32 medesimo.

Quindi:

Fase 1: il Global Atom non esiste

Fase 2: il Global Atom esiste

Behh, visto e considerato che le procedure di dll injection sono già state abbondantemente approfondite negli articoli precedenti, non resta altro da fare che implementare il tutto.

2. Implementazione

Nell'implementazione facciamo uso delle procedure già esposte ed approfondite nell'articolo articolo12.htm

library Applet; uses Windows, SysInit, uUtils; {$E cpl} var atom: integer; procedure InjectSelf(); var PID: dword; lpFileName: array [0..MAX_PATH - 1] of char; baseAddr, err: Cardinal; begin // //scegliamo notepad come al solito; basta cambiare il nome dell'eseguibile //per ottenere il self injection nello spazio di memoria di un altro processo PID := PidProcesso('notepad.exe'); GetModuleFileName(hInstance, @lpFileName, MAX_PATH); if not remoteloadlibrary(PID, (lpFileName), baseAddr, err, True) then begin MessageBox(0, pchar('errore nell''injecting'), 'Errore!', 0); end else begin if err <> 0 then MessageBox(0, pchar('Errore nel caricamento; Cod errore: '), 'Errore!', 0) else MessageBox(0, pchar('dll caricata all''indirizzo: ' + IntToHex(baseAddr, 8)), 'Eureka!', 0); end; end; function CPlApplet(hwndCPl: Cardinal; uMsg : Cardinal; lParam1: Integer; lParam2: Integer ): Integer; stdcall; begin case uMsg of 1: begin atom := GlobalAddAtom('cpl_auto_injection') ; InjectSelf; GlobalDeleteAtom(atom); Result := 0; end; end; end; procedure EntryPointProc(reason: integer); begin case reason of DLL_PROCESS_ATTACH: //1 begin DisableThreadLibraryCalls(HInstance); //MessageBox(0, pchar(ParamStr(0)), 'Ciauzz', 0); //se l'atom è già stato creato allora siamo nella fase di caricamento //nello spazio di memoria del processo remoto if GlobalFindAtom('cpl_auto_injection') <> 0 then begin //inserisco qui la chiamata alle procedure implementate nella cpl //come ad esempio procedure di settaggio di Hook, etc... end; end; DLL_THREAD_ATTACH: //2 begin end; DLL_PROCESS_DETACH: //3 begin end; DLL_THREAD_DETACH: //0 begin end; end; end; exports CPlApplet index 1; begin //codice di inizializzazione della dll DllProc := @EntryPointProc; DllProc(DLL_PROCESS_ATTACH); end.

Come si può vedere, basta la direttiva {$E cpl} per far sì che l'output della compilazione e del linking sia un file con estensione .cpl; in ogni caso si potrebbe anche rinomiare successivamente l'estensione da dll in cpl.

cpl_AutoInjection.7z

 

 

 

 

 

 

 
 
Your Ad Here