Guida Introduzione ai Socket e programmazione

TheWorm91

Helper
31 Marzo 2022
424
49
203
317
Ultima modifica:
Ho scritto questa "guida" per comprendere in maniera basilare il funzionamento dei socket, argomento fondamentale non solo per programmare applicazioni di rete ma anche per capire le reverse shell e le backdoor.

Per scriverla ho preso spunto da diversi libri e materiale vario che studiavo alle superiori anni fa e che sto studiando anche ora, ho cercato di essere il più preciso possibile nella scrittura, se in certi passaggi avessi sbagliato qualcosa accetto consigli per migliorare la qualità della discussione per il bene di tutti i frequentatori del forum.


1    Introduzione


Il nucleo di un'applicazione di rete consiste in due programmi: un programma cliente uno server.
Queste sono dette applicazioni client/server cioè un tipo di applicazione distribuita divisa in due parti:
un'applicazione server che offre servizi a un'applicazione client.

Questi due programmi quando sono in esecuzione creano un processo client e un processo server che dovranno comunicare tra loro attraverso la rete tramite le rispettive socket,i rispettivi indirizzi IP e un determinato protocollo di trasporto (solitamente TCP o UDP).
Un socket detto semplicemente è la porta tra il processo dell'applicazione e il protocollo di rete che si trovano sia sui server che sui client, i quali sono identificati in rete tramite il rispettivo indirizzo IP.
I socket rappresentano un punto di connessione di una rete TCP/IP; sono un punto a cui collegarsi per comunicare con altre applicazioni sulla rete.
Per fare un esempio pratico il processo è come se fosse una casa e il socket del processo è la porta d'ingresso.​
I socket si dividono in tre categorie:
- TCP socket detti Stream socket
- UDP socket detti Datagram socket
- Raw Socket utilizzati per lo sviluppo di particolari protocolli.
La scelta del tipo di socket dipende dall'applicazione che si intende realizzare, se abbiamo bisogno di un servizio affidabile oppure di un servizio che punta sulla velocità di trasmissione a discapito dell'affidabilità.
Quindi un socket identifica un computer e un processo in esecuzione su quel determinato computer ed è formato da hostname (o indirizzo IP che indentifica l'host in rete) e da un numero di porta TCP (che identifica un processeo all'interno della macchina).

2    Comunicazione client / server


La comunicazione in rete tra un server e un client avviene secondo questi passaggi:
- Server
1.Crea il socket socket() e gli assegna un local address bind();
2.Setta il socket all'ascolto e rimane in attesa di una richiesta listen();
3.Accetta una nuova connessione accept() , invia e riceve i dati read() write() ;
4.Chiude la connessione close().

- Client
1.Crea il socket socket() e si connette al server connect();
2.Invia e riceve i dati write() read();
3.Chiude la connessione close();

socket_diagram.png

Il client ha il compito di iniziare il contatto con il server e poichè il server possa rispondere, deve essere pronto.
Quindi sul server deve essere attivo e in ascolto un processo con il relativo socket altrimenti sarà impossibile per il client instaurare la connessione, riprendendo l'esempio iniziale della casa e della porta potremmo riferirci al contatto iniziale del client come al bussare alla porta.​
Dopo la creazione di un oggetto socket(), il client inizia un handshake a tre vie e stabilisce una connessione TCP con il server.
Essendo la connessione TCP va da se che questa sia instaurata con un three way handshake (completamente trasparente ai programmi client e server).
Una volta instaurata la connessione verranno scambiati i dati tramite le funzioni read() e write() finchè questa non verrà chiusa close().

3    Programmazione java dei socket con protocollo TCP


I socket possono essere implementati tramite vari linguaggi di programmazione (c++,python,java ecc...).
Preferisco spiegare il funzionamento in Java perché si hanno a disposizione classi già predefinite che permettono la realizzazione dei socket sia della macchina client che della macchina server in maniera più semplice.
Per spiegare le istruzioni riporto l'esempio di una semplice applicazione client che invia una stringa al server, il quale risponde inviando la stessa stringa ma modificata con i caratteri maiuscoli.
Per creare un'applicazione client/server che usa TCP si usano le classi Socket e ServerSocket del package java.net.
socket.png

Seguendo i passaggi descritti sopra, per creare l'applicazione server bisogna:
  1. Creare una istanza della classe ServerSocket() indicando la porta su cui il server è in ascolto ServerSocket server = new ServerSocket(int numero_porta);
  2. Usare il metodo accept() per bloccare il programma in attesa di una richiesta di connessione, quando un client si connette il metodo restituisce il socket usato per la connessione dal lato del server: Socket connectionSocket = welcomeSocket.accept();
  3. Usare i metodi getInputStream() e getOutputStream() della classe Socket per ottenere dal socket un InputStream per ricevere dati e un OutputStream per inviare dati: connectionSocket.getInputStream() e connectionSocket.getOutputStream();
  4. Usare i metodi degli stream per gestire la comunicazione: outToClient.writeBytes(capitalizedSentence);
  5. Chiudere il socket al termine dopo avere chiuso gli stream;
Mentre per l'applicazione client, assumendo il processo server in esecuzione, il processo client può iniziare la connessione TCP verso il server.
Per fare ciò bisogna:​
  1. Creare un socket indicando l'indirizzo del server (hostname o IP) del server e la porta su cui il server è in ascolto: Socket clientSocket = new Socket(String hostname, int porta);
  2. Usare i metodi getInputStream() e getOutputStream() della classe Socket per ottenere dal socket un InputStream per ricevere dati e un OutputStream per inviare dati;
  3. Usare i metodi degli stream per gestire la comunicazione:
    outToServer.writeBytes(sentence + '\n');
    modifiedSentence = inFromServer.readLine();;
  4. Chiudere il socket al termine: clientSocket.close();;

4    Codici java degli applicativi Server e Client


Codice server:
Java:
//dichiaro i package java per usare i metodi delle classi Socket e String
import java.io.*; // metodi e classi per le stringhe
import java.net.*; // metodi e classi per il socket
class TCPServer{
    public static void main(String argv[]) throws Exception //programma main
    {
        String clientSentence; //dichiaro la variabile clientSentence di tipo String
        String capitalizedSentence; //dichiaro la variabile capitalizedSentence di tipo String
        //creo l'oggetto welcomeSocket di tipo ServerSocket con numero di porta 6789
        ServerSocket welcomeSocket = new ServerSocket(6789);
        while(true){
            //creo un nuovo Socket in ascolto e accetto la connessione quando si connette un client
            Socket connectionSocket = welcomeSocket.accept();
            //variabile inFromClient per leggere la stringa inviata dal client
            BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
            //variabile outToClient per inviare la stringa trasformata in maiuscolo al client
            DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
            //leggo lo stream in arrivo dal client e lo metto nella variabile clientSentence
            clientSentence = inFromClient.readLine();
            /*Trasformo i caratteri della stringa in maiuscolo con la funzione toUpperCase()
             e metto il risultato nella variabile capitalizedSentence*/
            capitalizedSentence = clientSentence.toUpperCase() + '\n';
            outToClient.writeBytes(capitalizedSentence); //invio la stringa elaborata al client
        }
    }
}

Di seguito l'esempio di codice in Java per la parte client:
Java:
import java.io.*;
import java.net.*;
class TCPClient{
    public static void main(String argv[]) throws Exception
    {
        String sentence; //dichiaro la variabile sentence di tipo String
        String modifiedSentece; //dichiaro la variabile modifiedSentence di tipo String
        //variabile inFromUser per leggere lo stream dalla tastiera
        BufferedReader inFromuser = new BufferedReader(new InputStreamReader(System.in));
        //creo il socket con i parametri hostname e porta per identificare il server e fare la connessione
        // n.b. il port number è uguale a quello del codice server
        Socket clientSocket = new Socket("hostname", 6789); //sostituire hostname con l'ip o il nome del server a cui connettersi
        //variabile per inviare la stringa acquisita da tastiera AL server
        DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
        //variabile per ricevere la stringa convertita in maiuscolo DAL server
        BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        sentence = inFromUser.readLine(); //leggo l'input della tastiera e lo metto nella variabile sentence
        outToServer.writeBytes(sentence + '\n'); //invio la stringa ricevuta in input dalla tastiera al server
        modifiedSentence = inFromServer.readLine();//leggo il risultato che arriva dal server e lo metto in modifiedSentece
        system.out.println("FROM SERVER : " + modifiedSentence);//stampo a video il risultato
        clientSocket.close();//chiudo la connessione
    }  
}

Per ora mi fermo qui, se trovate il materiale interessante potrei procedere per scrivere una seconda parte descrivendo la comunicazione con il protocollo UDP.
Se qualcuno ha da aggiungere un contributo o correggere degli errori ben venga.
Spero abbiate gradito quanto ho scritto finora.