venerdì 19 ottobre 2012

Sticking fingers in the socket...

Una delle primissime cose che mi son sentito ripetere sin da bambino, è stata che non si devono MAI mettere le dita nelle prese di corrente. Il mio problema, però, è che sin da bambino adoravo divertirmi scoprendo dei modi nuovi ed inusuali di utilizzare ciò che mi trovavo per le mani.
Un giorno, mentre stavo ascoltando con il mio fido mangiacassette un nastro con una fiaba, inciampai nel cavo di alimentazione, staccandolo dalla presa. Mi divertì molto sentire la voce del narratore storpiarsi lentamente prima di cascare nel silenzio. Mi divertì talmente tanto, che decisi che il mio passatempo per il resto del pomeriggio sarebbe stato quello di sentire la fiaba storpiata dal mio continuo staccare e riattaccare la spina dalla presa di corrente. Ricordo che mia madre era al telefono in camera sua, quando accadde il fattaccio: le mie ditine di bambino finirono troppo vicine ai contatti della spina mentre la reinserivo nella presa. Vidi un leggero lampo azzurrino e sentii un dolore istantaneo e fortissimo alla mano ed al braccio. Fortunatamente il salvavita intervenne e tutto si concluse solo con un grosso spavento.
Cosa c'entra tutto ciò con l'informatica? Beh, in inglese le prese elettriche si chiamano "socket" ed io oggi metterò le dita dentro le prese elettriche dell'informatica distribuita: i network socket.
Mi servirò dei seguenti strumenti: uno sniffer ed  analizzatore di protocollo (Wireshark), un linguaggio di scripting ad alto livello (Ruby) ed un paio di computer connessi in rete. Tutto può essere simulato anche su un computer singolo, dato che i network socket operano perfettamente anche sull'interfaccia loopback ed anzi, sono usatissimi proprio per gestire la comunicazione IPC.
Cominciamo dalla teoria:

Cos'è un network socket?

Un network socket è uno strumento concettuale di alto livello, che serve ad identificare il punto finale di una comunicazione. Esso in sostanza è una astrazione della struttura e dei processi necessari a trasmettere un pacchetto da un punto ad un altro di una rete. 
Se andiamo ad analizzare un pacchetto dati, scopriremo che esso è in realtà molto più complesso di quanto si possa immaginare. Esso è composto da tutta una serie di headers che, di fatto, "avvolgono" i nostri dati nei vari livelli OSI.
Non intendo mettermi qui a trattare del modello OSI. Se non lo conoscete, qui troverete più di ciò che vi serve sapere. Ad ogni modo, capirne qualcosa è fondamentale per andare avanti a seguire questo post.
Quando io voglio trasmettere qualcosa ad un altro sistema, devo specificare almeno chi sono (sorgente), dove voglio trasmettere (destinazione), a chi voglio trasmettere (porta) ed il contenuto della trasmissione (Dati). Questo è tutto ciò che normalmente mi serve per poter avviare una comunicazione.

Quello che segue è un semplicissimo client scritto in ruby che consente di aprire una connessione TCP, inviare dei dati e ricevere una risposta:

require 'socket'
hostname = (if ARGV.length == 2; ARGV.shift; else "10.136.128.90"; end)
puts hostname
comando = ""
s = TCPSocket.open(hostname, ARGV.shift)
while comando != "chiudi"
 comando = gets.chomp
 s.puts(comando)
 buffer = ""
 while buffer
  if buffer.chomp != ".term"
   buffer = s.gets
   puts buffer
  else
   buffer = nil
  end
 end
end
s.close

Questo qui sotto invece è un semplice server che "emula" in maniera molto rozza un servizio telnet, in quanto riceve i comandi dal client, li esegue in una shell locale e trasmette l'output del comando eseguito al client:

require 'socket' # prende socket dalla libreria

server = TCPServer.open(34444) # apre il socket server in ascolto sulla porta 19000

while true
 Thread.new(server.accept) do |connection|
  puts "connessione in ingresso da: #{connection.peeraddr[2]}"
  begin
   while connection
    c = connection.gets()
    if c != nil
     c = c.chomp
    end
    puts "dati in ingresso: #{c}"
    if c == "chiudi"
     puts "ricevuto: chiudi, connessione terminata"
     connection.close
     break
    else
     if c != ""
      # connection.puts "#{c}"
      uscita = %x(#{c})
      puts uscita
      connection.puts "eseguito: #{c}"
      connection.puts "#{uscita}"
      uscita = nil
      connection.puts ".term"
      connection.flush
     else
      connection.puts "nessun comando eseguito"
      connection.puts ".term"
     end
    end
   end
  rescue Exception => e
   puts "#{ e } (#{ e.class})"
  ensure
   connection.close
   puts "assicura: chiusura!"
  end
 end
end

Prima di far partire il server o il client, ho avviato il mio sniffer per catturare tutti i pacchetti scambiati tra il client ed il server.
A questo punto, lancio lo script server sulla mia macchina linux, quindi mi troverò con il seguente terminale attivo:


Il client lo avvio dalla mia macchina windows, pertanto mi si presenterà il seguente terminale:


Appena avviata la connessione, noto che il mio terminale mi segnala l'avviamento di una connessione in ingresso dal client:


Lo sniffer nel frattempo ha raccolto 3 pacchetti.

Andando a vedere il contenuto di questi pacchetti, scopriamo che non contengono dati. Ed allora perchè sono 3? Qui si deve entrare nella specificità del protocollo Internet e dei socket TCP. I socket TCP sono utili a stabilire delle trasmissioni orientate alla connessione (connection-oriented), pertanto per parlarsi, i due sistemi devono essere reciprocamente certi di poterlo fare. Come lo fanno? Con quello che si chiama normalmente un Three Way Handshacking.
Chi chiama manda un segnale (SYN) al server, come per dire:"Ciao! sei in ascolto?".. Il server risponde con un segnale (SYN, ACK), che dice "Ciao! Si, sono in ascolto.." A questo punto, il client manda un altro segnale (ACK) che "conferma" che adesso lui è al corrente del fatto che il server è effettivamente attivo e che è disposto a ricevere dei dati.
Dato che lo script server che ho creato è un emulatore "molto grezzo" di Telnet, proviamo allora a listare il contenuto della directory con il comando bash

ls -l

Sul teminale server apparirà l'output del comando...


...che verrà quindi trasmesso al client:


Ma cosa è successo sulla rete? Andiamo ad analizzare i pacchetti trasmessi:
Se osserviamo la colonna "Info" della nostra cattura, notiamo che il quarto pacchetto della nostra cattura è flaggato PSH, ACK. PSH sta per "push". I pacchetti PSH sono quei pacchetti che "spingono" i dati verso il server.



Osservando la finestra centrale, vedremo il nostro pacchetto scomposto secondo i vari layer OSI: Fisico (Frame), Data Link (Ethernet), Network (IPV4), Trasporto (TCP) e Sessione. In fondo vediamo anche un campo Data, con l'indicazione che contiene 5 bytes. Nella finestra più in basso, ci viene mostrato il nostro pacchetto per intero, sia in esadecimale che in ASCII. Guardate un po' quali sono gli ultimi 5 caratteri? Il nostro comando: "ls -l", più un terminatore (0x00) che ci viene mostrato in ASCII come un punto (.).
In pratica, per trasmettere 5 bytes di dati, abbiamo messo "sul filo" la bellezza di 60 bytes.
A questo punto, però, ci manca di capire cosa fa il server dopo che ha ricevuto il nostro pacchetto dati contenente la stringa "ls -l".
Lo scopriamo, ovviamente, col pacchetto successivo che, come si può ben vedere, è un ACK. Lascio a chi lo vorrà fare, il gusto di analizzare il resto della sessione.
Ci tengo a parlare però di altri due pacchetti particolari: FIN e RST.
Se per avviare e gestire una comunicazione abbiamo visto che esistono i pacchetti SYN, ACK e PSH, per terminare una sessione occorre un altro segnale. Questo segnale è il FIN, a cui si aggiunge l'ACK per chiedere al server di confermare di aver ricevuto l'intezione di cessare il socket. Il segnale FIN ACK dice al server:"Grazie di tutto, non ho più bisogno di mandarti o di ricevere dati". Ovviamente, il server, risponderà al FIN ACK con un ACK, per dire "Ok, grazie a te, addio!"
Ma cosa succede se a questo punto proviamo ancora a "spingere" un pacchetto dati? Qui entra il segnale RST.
RST sta per Reset e, sostanzialmente è il " 'zzo vuoi?" della comunicazione in rete. Dato che ogni sessione deve cominciare con un SYN, se io mando un pacchetto senza aver avviato educatamente la sessione con SYN, il server risponderà sgarbatamente con un RST, troncando la connessione.
Il pacchetto RST, vedremo prossimamente, è molto prezioso perchè consente di capire se un servizio è attivo senza necessariamente avviare una sessione, evitando così di lasciare una propria "impronta" nei log delle sessioni dei server analizzati.

giovedì 11 ottobre 2012

How to dismantle a Logic Bomb


Anni fa gli U2 pubblicarono un album, dal titolo "How to dismantle an Atomic bomb".
Era il 2004, ed all'epoca io ero molto impegnato a godermi la mia nuova moto...Ma questo non mi impediva certo di continuare a combinare le mie belle marachelle informatiche in casa. A dire il vero, questa che vi racconto oggi è una delle primissime sciocchezze che mi sia mai capitato di fare ed è strettissimamente collegata con il post precedente. Se nel 1982 i loop si facevano in BASIC, a partire dal 1991 cominciai a farli un po' più seri con Ms-DOS.
All'epoca ero fortunato possessore di un brillantissimo (all'epoca) 80386DX40, ovviamente affiancato dal coprocessore FPU 80387. In quegli anni i PC venivano anche chiamati "cloni", in quanto era appena partito alla grande il mercato del personal computing ed era pertanto facile trovare assemblatori ad ogni angolo delle strade. Più o meno chiunque avesse a disposizione quattro mura si metteva a comprare ed a mettere insieme motherboards, schede video, case, alimentatori per poi venderli. Il mio sistema usava un 80386 prodotto da AMD (quando ancora la AMD era una dittarella), saldato sulla scheda madre perchè, all'epoca, l'idea di bruciare una CPU non passava per l'anticamera del cervello di nessuno, ed i produttori di CPU si facevano pagare soldoni per confezionare i chip in voluminosi e complicati package flip chip dotati di centinaia di pin. Come conseguenza di ciò, anche le mainboard dotate di socket costavano betoniere di quattrini! All'epoca i processor socket erano ad incastro "semplice" e sulle mainboard normalmente non ce n'erano. C'era la CPU saldata sulla scheda e basta. Su quelle un po' più "sofisticate" se ne trovava solo uno idoneo al montaggio del famigerato coprocessore 80X87. Fino alla serie 486, infatti, non tutte le CPU erano dotate nativamente di una Floating Point Unit integrata. Effettuavano le operazioni in virgola mobile "emulando" le operazioni con una serie di istruzioni più elementari. Paradossalmente, ciò che all'epoca costava di più, in un computer, non era la CPU, nè la scheda video. Il vero capitale era la RAM. I sistemi impiegavano dei costosissimi moduli SIMM che venivano venduti a tagli da 256KB, 512KB, 1MB, 2MB e 4MB. I primi, ovviamente costavano relativamente poco, ma dato che le mainboard supportavano solo 8 slot (utilizzabili tassativamente a coppie), era logico che venissero usati tipicamente 8 moduli da 256KB per avere la bellezza di 2 MB di RAM. Io ero all'epoca un privilegiato, perchè comprai il computer fornito di 4 moduli da 1MB. pertanto potevo vantare la bellezza di 4 Mega di RAM. E vi lascio immaginare la mia gioia estrema quando un giorno, in ufficio da mio padre, trovai 2 moduli da 2MB che erano stati dismessi da una loro titolatrice CHYRON per far spazio a 2 moduli da 4 MB. Li portai a casa e li montai nel PC con la stessa cura ed attenzione che un neurochirurgo adopera quando ficca le mani in un cranio umano. Ora ero il fortunato possessore di un PC 386DX40 con coprocessore 387, 8 MB di RAM, scheda video VESA, Hard Disk da 40 MB, Ms-DOS 5.0 e Windows 3.0!
Credetemi, all'epoca era davvero un pezzo forte, che suscitava l'invidia di tutti i miei compagni di scuola. Vi basti sapere che ci potevo persino giocare a Wolf3D a tutto schermo!
Al tempo, Windows era per me sostanzialmente inutile. Di fatto non lo caricavo mai, dato che tutto quello che mi serviva fare lo potevo tranquillamente e molto più efficacemente fare rimanendo in DOS mode. L'unico vero limite del DOS mode era il fatto che la memoria "visibile" ai programmi era limitata a 640KB, perchè MS-DOS era un sistema a 16 bit. Era ovviamente disponibile un driver dos extended mode che permetteva di indirizzare la memoria a 32 bit tramite paging, ma dato che di norma nessun programma occupava più di 2MB di RAM, difficilmente si poteva sentirne l'esigenza. Ad ogni modo, in quegli anni io andavo a scuola e stavo imparando a programmare in Turbo Pascal. Il principale problema di MS-DOS era che non era affatto un sistema operativo multitasking, pertanto era impossibile sviluppare software che operasse tramite IPC (Inter Process Communication). Come si poteva ovviare a tutto ciò? Il modo più semplice (ed abusato) era quello di avviare i programmi tramite dei batch script (i famosi file con estensione .bat) e creando per quanto possibile dei moduli indipendenti che, tramite un menù ed il trasferimento dei dati via filesystem, permettesse di alternare l'elaborazione. Concettualmente semplice, praticamente un casino.
Fu proprio in occasione della produzione di un batch file per lanciare un set di 2 programmi che innescai la mia prima bomba logica.
A scuola ci insegnavano la programmazione Top-Down, dal macro al micro, dagli algoritmi in metalinguaggio al codice compilabile...
A casa si apriva l'IDE e si scriveva direttamente il codice, sviluppando per debug incrementale.
Produssi pertanto due exe: il primo, chiamato menu.exe, non faceva altro che disegnare un menù sullo schermo, raccogliere l'input, metterlo in un file ed uscire. Il secondo, chiamato elabora.exe, apriva il file, leggeva cosa c'era scritto, faceva una operazione in funzione di quanto aveva letto, cancellava il contenuto del file ed usciva. Il tutto veniva lanciato da un batch file, chiamato avvio.bat, che conteneva il seguente codice:

:inizio
menu.exe
elabora.exe
goto inizio
:end

Ovviamente il tutto, messo così, non darebbe alcun problema se non fosse che, come è facile immaginare, si tratta di un loop infinito. Manca la condizione di uscita. Certo, sarebbe bastato un semplice IF nel file batch. Il problema è che io volevo che la condizione d'uscita fosse selezionabile nel menù del programma e non dipendente dallo script di avvio.
Introdussi quindi nel codice di elabora.exe una routine che, nella mia mente di programmatore ragazzino, avrebbe dovuto risolvere il problema. Avrei aperto il file batch dall'interno dell'eseguibile ed avrei modificato la riga del goto cambiando la label, da "inizio" a "end". Stavo per addentrarmi nel terreno inesplorato del polimorfismo...
Feci un po' di prove e sembrava che tutto funzionasse bene...decisi quindi di impaccare il tutto e di lanciare il mio batch
_______________________

C:\Programma> Avvio.bat

                                    MENU'
   Digita un numero per selezionare l'opzione desiderata, quindi premi invio:

                              1) Opzione 1
                              2) Esci dal programma

Scelta> 2

Mi aspettavo che il programma sarebbe uscito al prompt, invece tornò al menù...
Mi venne pertanto il dubbio che non avesse modificato come desiderato il file batch, pertanto mi domandai come fosse possibile cambiare il programma durante l'esecuzione.
Ve la faccio breve: non riuscivo a capire che il file batch che chiamava gli eseguibili rimaneva "bloccato" dal sistema, rendendone impossibile la modifica. Cercate di capire una cosa: all'epoca non c'era Internet in casa, pertanto trovare la soluzione al mio problema "online" non era una opzione praticabile. Tutto il supporto che potevo avere era quello fornitomi da un manuale di Ms-DOS.
Lì dentro trovai la fatidica risposta ai miei quesiti: la funzione "CALL".
la funzione Call permette ad un batch di richiamare un altro batch, eseguirlo, quindi ripassare l'esecuzione al batch chiamante.
Ecco allora l'idea: il batch di avvio chiama il programma del menù tramite una call ad un batch che chiama menù.exe
Quindi menù.exe oltre a scrivere il file contenente l'opzione per elabora.exe, scrive anche il batch che lo chiama, in maniera tale che se l'opzione scelta non è quella di uscita, allora il batch conterrà una call al batch che lancia il menù.
Tralascio ogni considerazione sul fatto che, come è evidente, un programmatore motivato è sempre capace di trovare una soluzione che aggira i limiti imposti dal sistema. Sta di fatto che avevo creato un loop potenzialmente devastante.
Ed infatti alla prima esecuzione il sistema non fece una piega, salvo cominciare a dare un po' i numeri dopo una trentina di volte che rieseguivo il ciclo senza uscire dal programma...
Avevo creato una bomba logica che dopo un certo numero di cicli saturava la memoria del computer.
La cosa paradossale è che ancora oggi il problema delle bombe logiche non è affatto risolto.
Se non ci credete, provate voi stessi, con il seguente semplicissimo batch file.

!!!!ATTENZIONE!!!!
Il codice qui sotto è una VERA bomba logica. Se lo lanciate ben difficilmente sarete in grado di fermare il computer dal saturarsi e l'unico modo che avrete per interrompere il loop sarà quello di spegnere fisicamente il computer. Pertanto accertatevi di avere salvato tutto e di aver creato un punto di ripristino, dato che lo spegnimento "brutale" del sistema, per quanto completamente innocuo nel 99.99% dei casi, ha sempre uno 0.01% di probabilità di rovinare il filesystem da qualche parte.

aprite un command prompt e digitate il comando:

echo start bomb.bat > bomb.bat

A questo punto vi è sufficiente lanciare bomb.bat per far partire una cascata di finestre di CMD che in breve tempo renderanno il computer inutilizzabile.


Le bombe logiche, nelle loro più disparate varianti, sono tecnicamente da ascrivere alla categoria dei Denial of Service (DoS).


mercoledì 3 ottobre 2012

Very first

Agli albori della mia vita informatica andava di moda il B.A.S.I.C.
Chiunque iniziava con il suo primissimo programma:

dopo il fatidico RUN, l'output faceva brillare gli occhi di soddisfazione:



Subito dopo, si cominciava a "complicarsi la vita", creando il primo loop:



di nuovo RUN e..


Si premeva continuamente "Y" per continuare a vedere sempre la stessa videata...
Dopo una serie di almeno 10 "Y", finalmente si premeva "N" ed il loop si interrompeva, con grande soddisfazione...
Quello è stato l'inizio, anche per me, nell'ormai lontano 1982, all'età di 8 anni, hands-on su un Sinclair ZX Spectrum 48K.
Molti ragazzini oggi non sanno che 30 anni fa serviva un sacco di ingegno per non saturare la memoria di un computer. 48K, alla fine della fiera, equivalgono a 49152 caratteri. Non si può certo mettere chissà quanto codice, in così poca memoria. Eppure all'epoca pareva pure tanta, dato che lo Spectrum "di serie" montava solo 16K.
Ad ogni modo, da lì in poi tutti gli informatici del mondo si sono distinti in due grandi gruppi: quelli che da subito si sono ingegnati per cercare di far stare tutto il loro lavoro entro quei fatidici 48K e quelli che invece, da subito, hanno voluto provare a vedere cosa succedeva provando a spingersi oltre.
I primi si sono pertanto messi con carta e penna a cercare di capire esattamente quanti caratteri avrebbero potuto memorizzare, tenendo conto dello spazio di memoria già usato dal programma, ecc..ecc..
I secondi invece hanno prodotto immediatamente un codice come questo, o perlomeno mooooooolto simile:


e dopo il RUN...


"Y".."Y"..."Y"..."Y"... e finalmente:



Ecco..quella è la differenza fondamentale. Da lì nasce tutto. Da una "fork filosofica".
I sistemisti più "estremi" sono soliti dire che, dopotutto, anche UNIX è una fork.

Questo blog mi nasce così, dall'idea di parlare di informatica nel modo in cui l'ho conosciuta e la conosco io.
Troverete tutorials, suggerimenti, curiosità o anche solo dei racconti.
Dipende da cosa mi passerà per la testa volta per volta.