Tutorial Linux: Come aggiungere una System Call

In questa guida imparerai ad aggiungere una system call, cioè una chiamata di sistema, al kernel Linux. Le chiamate di sistema o system call, permettono ai programmi utente di poter richiamare i servizi principali del sistema operativo. Sono solitamente disponibili come funzioni scritte in linguaggio C, e sono l’interfaccia principale tra, programma utente e sistema operativo.

Questo tipo di architettura consente una separazione che è fondamentale ai fini della sicurezza, tra due aree del sistema operativo, l’area utente e l’area dedicata esclusivamente al kernel.

Nell’area utente, i processi non possono accedere direttamente, a risorse importanti come la memoria o l’hardware del computer.

Nell’area kernel, i processi che gestiscono la memoria o l’hardware del computer sono gestiti direttamente tramite le system call, le quali hanno un accesso a livelli privilegiati a questo tipo di risorse.

Tutto questo fa sì che, il kernel mantenga il computer quanto più stabile e sicuro possibile da attacchi di malintenzionati, o da esecuzione di codice maligno.

Essendo il kernel Linux, opensource, il programmatore ha la possibilità di effettuare modifiche al kernel, a volte necessarie, per garantire l’esecuzione di codice, di accesso a risorse hardware, e lasciare al kernel la gestione dei processi coinvolti in questa fase.

Vediamo passo passo, le fasi coinvolte nella modifica del kernel Linux, e cioè, nell’aggiunta di eventuali system call al kernel.

corso javascript per principianti

1. Preparazione ambiente di sviluppo

Innanzitutto abbiamo bisogno di scaricare tutti gli strumenti necessari per aggiungere una chiamata di sistema al kernel Linux ed eseguirla. Questa è l’unica fase dell’intero processo in cui è necessaria una connessione attiva ad internet.

Innanzitutto è necessario, aggiornare completamente il tuo sistema operativo. Questa operazione è molto semplice, e per eseguirla devi aprire il famoso terminale e digitare il seguente comando:

sudo apt update && sudo apt upgrade -y

Dopo aver aggiornato il sistema operativo, sempre da terminale, devi scaricare e installare i pacchetti essenziali per poter compilare il kernel in totale sicurezza. Questa operazione, anch’essa molto semplice, viene eseguita digitando il seguente comando:

sudo apt install build-essential libncurses-dev libssl-dev libelf-dev bison flex -y

Da questo punto in poi, abbiamo bisogno di utilizzare un editor di testi. Linux ne ha più di uno già preinstallati. Se preferisci usare vim o qualsiasi altro editor di testo, ad esempio nano, lo puoi installare digitano questo comando da terminale:

sudo apt install nano -y

A questo punto è conveniente fare un po’ di pulizia di eventuali pacchetti installati, non più necessari, e mettere il sistema nella condizione migliore.

sudo apt clean && sudo apt autoremove -y

1.4 – Scarica il codice sorgente dell’ultima versione stabile del kernel Linux nella tua cartella home.

aggiungere una system call
credit: kernel.org

Da questo sito, scarica l’ultima versione stabile del kernel Linux. come puoi vedere in figura, clicca sulla voce tarball, oppure memorizza l’indirizzo del file tar e con il seguente comando lo scarichi direttamente nella tua home.

wget -P ~/ https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.8.1.tar.xz

Se hai scaricato una versione più recente del kernel Linux, fai riferimento a questa documentazione per conoscere qualsiasi modifica rilevante apportata alle chiamate di sistema.

1.5 – Decomprimi il tarball che hai appena scaricato nella tua cartella home.

Una volta terminato il processo di download del file tar, devi decomprimerlo, questa operazione e eseguibile digitando il seguente comando nella finestra del terminale: tar -xvf ~/linux-5.8.1.tar.xz -C ~/

Terminato il processo di decompressione del file, riavvia il computer.

2. Creazione della system call

In questa sezione, progetteremo una system call in C e la integreremo nel nuovo kernel.

2.1 – Controlla la versione del tuo kernel attuale.

Prima di procedere è necessario capire la versione installata del kernel sul computer. quindi digita il seguente comando: uname -r

Il kernel a mia disposizione e installato sul mio computer risulta essere:

5.4.0-42-generic

Successivamente, nella sezione 4, il numero di versione che visualizzeremo sarà diverso.

2.2 – Cambia la tua directory di lavoro nella directory principale del codice sorgente recentemente decompresso.

Adesso cambia la directory corrente e vai alla cartella dove abbiamo precedentemente decompresso il kernel quindi digita questo comando: cd ~/linux-5.8.1/

2.3 – Crea la home directory della tua chiamata di sistema.

Una volta deciso il nome della tua system call, io ho scelto identity, prosegui creando la cartella che conterrà i file in linguaggio C della nuova system call, quindi digita il seguente comando:

mkdir identity

2.4 – Crea un file C per la tua chiamata di sistema.

Quindi crea il file identity.c all’interno della cartella identity con questo comando.

nano identity/identity.c

Adesso digita questo contenuto all’interno del file identity.c.

#include <linux/kernel.h>
#include <linux/syscalls.h>
SYSCALL_DEFINE0(identity)
{
   printk("Io sono una system call\n");
   return 0;
}

Ad ogni modo in questo file puoi programmare quello che vuoi, io per rendere la procedura molto più semplice ho preferito digitare il minimo delle istruzioni richieste.

Salva il file identity.c ed esci dall’editor di testo.

2.5 – Crea un Makefile per la tua chiamata di sistema.

Come ben sai, ogni file sorgente in linguaggio C per poter essere eseguito deve essere necessariamente compilato. Questa operazione viene svolta dal comando Make, al quale viene passato come argomento il Makefile. Questo file nella cartella del kernel, precedentemente scaricato, esiste già, noi adesso lo dobbiamo modificare inserendo una riga di compilazione aggiuntiva. Innanzitutto apriamo il file Makefile tramite nano con questo comando:

nano identity/Makefile

Aggiungi la seguente riga di codice alla fine del file.

obj-y := identity.o

Salva il Makefile ed esci dall’editor di testo.

2.6 – Aggiungi la home directory della tua chiamata di sistema al Makefile del kernel.

Pertanto riapriamo il Makefile con il seguente comando.

nano Makefile

Cerca core-y. Nei risultati elencati, vedrai una serie di directory come queste: kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/

Nel codice sorgente relativo al kernel Linux 5.8.1, dovresti trovare queste cartelle alla riga 1073. Aggiungi la directory home della tua chiamata di sistema, e cioè identity alla fine come segue:

kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ identity/

Salva nuovamente il Makefile ed esci dall’editor.

LEGGI ANCHE: Come creare una pendrive di emergenza

2.7 – Aggiungere un prototipo di funzione corrispondente per la chiamata di sistema al file di intestazione delle chiamate di sistema.

Come buona norma in C, ogni file sorgente ha un suo proprio file header. Parlo del file con estensione .h, che riporta il prototipo della nostra system call. Questo file header però, contiene i prototipi di tutte le system calls presenti nel kernel. Quindi apri il file header syscalls.h, con il seguente comando:

nano include/linux/syscalls.h

Vai alla fine del file e aggiungi la seguente riga di codice appena sopra la direttiva #endif:

asmlinkage long sys_identity(void);

Salva il file syscalls.h ed esci dall’editor.

2.8 – Aggiungi la tua chiamata di sistema alla tabella delle chiamate di sistema del kernel.

Adesso apri la tabella delle system call, cioè il file syscall_64.tbl, digitando il seguente comando:

nano arch/x86/entry/syscalls/syscall_64.tbl

Vai alla fine di questo file. Troverai una serie di chiamate di sistema x32. Scorri fino alla sezione sopra. Questa è la sezione che ci interessa. Aggiungi la seguente riga di codice alla fine di questa sezione rispettando il progressivo della riga e il formato della colonna. Usa Tab per lo spazio.

440     common  identity                sys_identity

Nel codice sorgente relativo al kernel Linux 5.8.1, il numero per la tua chiamata di sistema dovrebbe corrispondere alla chiamata nr. 440.

Salva il file syscall_64.tbl ed esci dall’editor.

3. Installazione e configurazione del kernel

In questa sezione installeremo il nuovo kernel e prepareremo il sistema operativo all’avvio.

3.1 – Configurare il kernel.

Apri il terminale e assicurati che la finestra sia ingrandita. A questo punto accedi alla configurazione per la compilazione con il seguente comando.

make menuconfig

Usa Tab per spostarti tra le opzioni. Non apportare modifiche per mantenerlo nelle impostazioni predefinite.

Salva ed esci.

3.2 – Scopri quanti core logici hai.

Adesso il processo di compilazione del kernel modificato, potrebbe richiedere del tempo. Per ottimizzare questa procedura è necessario sapere quanti core ha il nostro processore. Questo dato è reperibile digitando il seguente comando:

nproc

L’elaborazione parallela velocizzerà notevolmente il processo. Nel mio caso i core sono 12, pertanto, inserirò questo valore dopo il parametro  -j nei comandi che seguiranno.

3.3 – Compilare il codice sorgente del kernel.

Passiamo alla compilazione vera e propria del kernel. Quindi, digita il seguente comando: make -j12

3.4 – Preparare l’installer del kernel.

Successivamente digita questo comando: sudo make modules_install -j12

3.5 – Installa il kernel.

Con questo comando installeremo il kernel modificato: sudo make install -j12

3.6 – Aggiorna il bootloader del sistema operativo con il nuovo kernel.

Esegui un aggiornamento del sistema di gestione del boot digitando questo comando: sudo update-grub. Adesso riavvia il computer per vedere il risultato ottenuto.

4. Verifica stato esecuzione system call

In questa sezione, scriveremo un programma C per verificare se la nostra chiamata di sistema funziona o meno. Dopodiché, vedremo in azione la nostra prima system call.

4.1 – Ricontrolla la versione del kernel attuale.

digitando questo comando uname -r , dovresti visualizzare a video, una nuova versione del kernel, pertanto dovresti visualizzare quanto segue:

5.8.1

4.2 – Cambia la tua directory di lavoro nella tua directory home.

Dopo il riavvio del pc è necessario cambiare la directory di lavoro con il seguente comando: cd ~

4.3 – Crea un file C per generare un report con l’esito, positivo o meno, della system call.

Crea un file C, in questo caso ho deciso di chiamarlo report.c, con il seguente comando:

nano report.c

Digita le seguenti istruzioni all’interno del file report.c

#include <linux/kernel.h>
#include <sys/syscall.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define __NR_identity 440

long identity_syscall(void)
{
    return syscall(__NR_identity);
}

int main(int argc, char *argv[])
{
    long activity;
    activity = identity_syscall();

    if(activity < 0)
    {
        perror("Mi dispiace la tua system call non funziona.");
    }
    else
    {
        printf("Congratulazioni la tua system call funziona. Esegui il comando dmesg da terminale per visualizzare il log del kernel!\n");
    }
    return 0;
}

Puoi personalizzare i messaggi relativi al successo o meno della chiamata a tuo piacimento.

Salva il file report.c ed esci dall’editor.

4.4 – Compila il file report.c appena creato.

La compilazione di file in linguaggio C è estremamente semplice in linux, come abbiamo visto prima basta digitare il seguente comando da terminale: gcc -o report report.c

4.5 – Esegui il file C appena compilato.

Dopo la compilazione non ci resta che eseguire il file report.c, anche in questo caso basta digitare il seguente comando:

./report

Se viene visualizzato quanto segue, significa che tutto ha funziona come previsto.

Congratulazioni la tua system call funziona. Esegui il comando dmesg da terminale per visualizzare il log del kernel!

4.6 – Verificare il log del kernel con il comando dmesg.

Quindi digita il comando dmesg direttamente nel terminale di Linux, in basso, dovresti ora vedere quanto segue:

Io sono una system call

Qui c’è da spendere qualche parola a riguardo. sicuramente ti sarai chiesto “Perchè abbiamo utilizzato il comando printk nel file identity.c e non printf?” Innanzitutto la differenza sostanziale che c’è tra printf e printk e che, mentre printf stampa contenuti a video, printk stampa contenuti nel log del kernel.

Essendo una chiamata di sistema, eseguita nell’area riservata al kernel, non si ha mai la necessità di scrivere output a video, ma si ha la necessità di comunicare al programmatore e al kernel l’eventuale stato relativo al suo processo di esecuzione.

Logicamente ogni volta che viene processata la function printk, non vedremo mai nulla a video, ma possiamo vedere l’output elaborato da printk digitando il comand dmesg da terminale. Molte system call stampano nel log del kernel, il contenuto del log è visualizzato dal programmatore per tenere traccia di eventuali malfunzionamenti di sistema o di errori nelle system call.

In questo video è riassunto tutto quello che hai letto in questa guida. Quindi avrai modo di vedere come aggiungere una system call che stampa la famossissima frase “Hello World…!

Fonte: https://dev.to/

Seguimi su

Antonio Lamorgese

Amministratore di rete, programmatore ed esperto di pratiche SEO. Dopo anni di esperienza nel settore, ho ideato un generatore di codice PHP Scopri di più su https://www.phpcodewizard.it.

x