Migliora la qualità del codice e previeni risultati imprevisti imparando a utilizzare GNU Debugger per rivelare bug indesiderati nel tuo codice.

Il debug è un'abilità indispensabile per programmatori e ricercatori di sicurezza. Avere una conoscenza approfondita del debug ti consente di comprendere un eseguibile a un livello inferiore e di individuare eventuali errori nascosti.

Il debugger GNU o GDB è uno strumento di debug senza tempo su cui i programmatori fanno affidamento ormai da anni. Ecco come utilizzare GDB su Linux.

Preparazione di programmi di esempio

Per esplorare le funzionalità di GDB avrai bisogno di un eseguibile con cui sperimentare. A scopo dimostrativo, eseguirai GDB su un programma di controllo delle chiavi una volta con codice sorgente e simboli di debug disponibili, una volta senza codice sorgente, e su un semplice programma multithread che stampa messaggi sullo schermo, sia scritti in C che compilati con GCC (GNU C Compilatore).

Puoi utilizzare qualsiasi altro compilatore C ma assicurati di non rimuovere il file binario.

instagram viewer

Molto probabilmente eseguirai GDB sui tuoi programmi. Quindi assicurati di compilarli con il file -G flag con gcc per abilitare i simboli di debug.

Senza i simboli di debug presenti e con un binario fortemente ridotto, dovrai eseguire il debug del disassemblaggio del programma. Ciò richiederà una conoscenza approfondita del linguaggio assembly e come funziona l'allocazione della memoria su Linux per comprendere i dati nello stack e nei registri.

Esecuzione di un programma in GDB

Puoi eseguire un programma in GDB in un paio di modi. Oppure digita gdb e una volta caricato, digita correre. Oppure avvia gdb e poi usa il file file comando, carica il binario in gdb, quindi eseguilo con il comando correre comando.

Se il tuo programma richiede argomenti della riga di comando per funzionare correttamente, assicurati di aggiungere gli argomenti dopo il nome del programma. Ecco la sintassi per caricare il programma su GDB ed eseguirlo con argomenti:

gdb 
run

O:

gdb
file
run

Impostazione dei punti di interruzione con GDB

I punti di interruzione nel debug sono arresti rigidi impostati manualmente nel codice che interrompono il flusso di esecuzione quando il programma raggiunge un punto di interruzione. L'impostazione dei punti di interruzione consente di scorrere il codice e verificare in che modo ciascuna fase di esecuzione influisce su dati e variabili.

In GDB, quando esegui il debug di un programma con simboli di debug, puoi impostare un punto di interruzione in base al nome della funzione o impostare un punto di interruzione in base al numero di riga. Ecco la sintassi:

break main
break 47

Per visualizzare tutti i punti di interruzione nella sessione di debug corrente, digitare:

info breakpoints

Per eliminare un particolare punto di interruzione o più punti di interruzione, digitare:

delete 2
delete 3-5

GDB consente anche di impostare punti di interruzione condizionali, il che significa che il programma si fermerà solo se una particolare condizione viene soddisfatta durante l'esecuzione. Potrebbe essere la modifica del valore di una variabile o una chiamata di funzione non riuscita o qualsiasi cosa tu voglia. Ecco la sintassi per impostare i breakpoint condizionali:

break  if n == 2

Se desideri continuare l'esecuzione del programma dopo aver raggiunto un punto di interruzione, digita il file Continua comando:

continue

Passare attraverso il codice

Scorrere il codice è fondamentale per comprendere come il programma gestisce i dati. Eseguendo le varie funzioni del programma ed esaminando lo stato dei dati, puoi comprendere meglio come il programma sta implementando la logica che hai scritto nel codice.

Ti aiuta anche a rilevare la radice dei crash e a studiare il comportamento del programma con precisione chirurgica poiché puoi scorrere ogni riga di codice come desideri. Puoi scorrere il codice in tre modi principali in GDB:

  1. fare un passo: Questo comando dice a GDB di passare alla riga successiva del file sorgente. Ciò consente essenzialmente di attraversare la lunghezza del codice sorgente riga per riga.
  2. Prossimo: Questo comando esegue la riga successiva del codice sorgente all'interno della funzione corrente e poi si interrompe. Prossimo tratta una funzione come una singola riga quindi se usi next prima di una chiamata di funzione, la tratterà come una singola riga e la scavalcherà, a differenza del fare un passo comando.
  3. fine: Il comando final esegue tutte le righe rimanenti all'interno della funzione corrente e poi si ferma.

Esaminare le variabili

Mentre procedi nel codice, ti consigliamo di esaminare il valore delle variabili per vedere come la logica del programma le modifica. Ecco la sintassi per visualizzare il valore delle variabili in GDB:

print 

Nel caso in cui desideriate stampare le variazioni del valore di una variabile ogni volta che questa viene aggiornata, dovreste utilizzare il comando display. Ciò è particolarmente utile quando vuoi tenere traccia e stampare il valore di una variabile in un ciclo:

display 

Impostazione dei punti di controllo

I watchpoint e i breakpoint condizionali sono strettamente correlati poiché entrambi rispondono ai cambiamenti in un programma. I watchpoint vengono utilizzati per tenere traccia delle modifiche ai dati nel codice. Ad esempio, potresti volere che il programma si interrompa ogni volta che cambia il valore di una variabile. Ecco come farlo con GDB:

watch 

Debug specifico del thread con GDB

GDB ti consente di eseguire il debugging specifico del thread quando lavori con programmi multithread. A titolo dimostrativo, lavoreremo con un semplice programma C che utilizza quattro thread per stampare messaggi con ciascun thread.

Per visualizzare i thread attualmente generati nel tuo programma, utilizza il file informazioni comando:

info threads

Per lavorare con un thread specifico, puoi selezionarlo dall'elenco utilizzando il suo numero di indice. Per esempio:

thread 2

Dopo aver selezionato il thread è possibile scorrere il flusso di esecuzione utilizzando il file fare un passo, Prossimo, E fine comandi come dimostrato sopra.

Debug remoto con GDB

Puoi anche eseguire il debug remoto di programmi situati su un sistema diverso. Per fare ciò, è necessario configurare gdbserver sul computer di destinazione. Puoi installarlo facilmente utilizzando il gestore pacchetti predefinito della tua distribuzione o altri gestori di pacchetti che hai installato sul tuo sistema.

Ad esempio, per installare gdbserver sui tuoi sistemi basati su Ubuntu o Debian, usa APT:

sudo apt install gdbserver

Una volta installato, spostatevi nella cartella del binario ed eseguite questo comando per avviare gdbserver:

gdbserver :

gdbserver dovrebbe restituire l'output che è attivo e in ascolto sulla porta che hai definito. Ora sul computer client, avvia GDB e quindi connettiti al server remoto utilizzando il file bersaglio comando:

target remote :

Scrittura di script GDB per automatizzare il debug

GDB consente ai programmatori di scrivere script GDB che eseguiranno automaticamente i comandi GDB. Ciò è di grande aiuto quando si tenta di eseguire il debug della stessa parte di un codice più volte. Invece di dover impostare il punto di interruzione, scorrere il codice e stampare i valori delle variabili ogni volta che carichi il file binario, puoi utilizzare uno script GDB per automatizzare l'intero processo.

Ecco un esempio:

set logging enabled on
set logging file sample.out
break main
command 1
backtrace
print N
continue
end
quit

Nello script sopra, stai dicendo a GDB di abilitare la registrazione e salvare il registro in un file chiamato campionamento.out, quindi impostare un punto di interruzione in principale funzione.

Per il punto di interruzione numero 1, in questo caso, il punto di interruzione nella funzione main, eseguire i seguenti comandi: retrocedere, stampa, Continua. Fondamentalmente, GDB eseguirà prima un backtrace, quindi stamperà il valore della variabile "N", continuerà l'esecuzione e infine uscirà.

Per eseguire questo script, utilizzare:

gdb -x