Aan de slag met GNU Debugger onder Linux: Een snelcursus
Debuggen is een onmisbare vaardigheid voor programmeurs en beveiligingsonderzoekers. Een goede beheersing van debuggen stelt je in staat om een uitvoerbaar bestand op een lager niveau te begrijpen en om op de loer liggende fouten op te sporen.
GNU Debugger, beter bekend als GDB, is een duurzaam debug-hulpprogramma dat door de jaren heen vertrouwd en gebruikt is door ontwikkelaars. In deze sectie zullen we de toepassing ervan onderzoeken in de context van Linux systemen.
Voorbereiden van voorbeeldprogramma’s
Om in de mogelijkheden van GDB te duiken, is het nodig om het te testen met behulp van een executable waarvan de broncode en debugsymbolen toegankelijk zijn voor onderzoek. Als illustratief voorbeeld zullen we GDB draaien op twee verschillende programma’s - een programma dat toetsen controleert en een multi-threaded programma dat berichten op het scherm weergeeft - het ene ontwikkeld in C en gecompileerd met de GNU C Compiler, terwijl de broncode van het andere ontbreekt.
Je hebt de optie om een alternatieve C compiler te gebruiken, op voorwaarde dat je ervan afziet om de metadata van het uitvoerbare bestand te verwijderen, die informatie bevat over de herkomst van het programma en de wijzigingen die tijdens het compileren zijn gemaakt.
Om GDB effectief te gebruiken, is het zeer waarschijnlijk dat u het in uw eigen applicaties zult gebruiken. Daarom is het essentieel om ervoor te zorgen dat deze programma’s gecompileerd worden met de -g
vlag met gcc
, die het toevoegen van debugsymbolen mogelijk maakt.
Inderdaad, als de debug-symbolen ontbreken en de binary aanzienlijk geminimaliseerd is, moet men zich verdiepen in de gedemonteerde code van de applicatie. Zo’n poging vereist een vaardig begrip van assembleertaal en een ingewikkeld begrip van geheugentoewijzingsmechanismen binnen het Linux besturingssysteem, evenals de interpretatie van gegevens die zijn opgeslagen in de stack en registers.
Een programma uitvoeren in GDB
Als alternatief kun je er ook voor kiezen om de GNU Debugger te starten door “gdb” in te typen gevolgd door de gewenste commandoregelargumenten, die je vervolgens leiden naar de prompt-invoer voor de specifieke doelbinary die gedebugd moet worden. Eenmaal geladen kun je het programma uitvoeren door het commando “run” in te voeren of simpelweg sneltoetsen zoals “r” te gebruiken.
Om een programma correct te laten werken wanneer commandoregel argumenten nodig zijn, is het essentieel om deze toe te voegen na de programmatitel bij het aanroepen.De grammaticale structuur die door de GNU Debugger (GDB) wordt gebruikt voor het uploaden en uitvoeren van een applicatie samen met de argumenten kan als volgt worden geschetst:
gdb <program>
run <args>
Of:
gdb
file <program>
run <args>
Breakpoints instellen met GDB
Breakpoints, opzettelijke onderbrekingen in de uitvoeringsstroom tijdens het debuggen, dienen als vooraf bepaalde stoppunten binnen de code. Door deze markeringen strategisch te plaatsen, kunnen ontwikkelaars de voortgang van hun programma op specifieke momenten pauzeren en de effecten op zowel de gegevens als de variabelen nauwkeurig onderzoeken terwijl elke fase zich ontvouwt.
Wanneer de GNU Debugger (GDB) wordt gebruikt om een softwaretoepassing met debugsymbolen te debuggen, kan men kiezen voor twee methoden om breekpunten in te stellen. Deze omvatten het specificeren van een onderbrekingspunt met behulp van de titel van een bepaalde functie of het aanwijzen van een onderbrekingspunt gebaseerd op een specifiek regelnummer binnen de code. De bijbehorende syntaxis is als volgt:
break main
break 47
Om toegang te krijgen tot de volledige lijst van onderbrekingspunten die momenteel actief zijn tijdens deze debugsessie, voert u het volgende commando in:
info breakpoints
Om een specifiek of alle huidige onderbrekingspunten in uw code te verwijderen, kunt u het commando “delete " (als u er een verwijdert) of gewoon “delete” (om ze allemaal te verwijderen) gebruiken.
delete 2
delete 3-5
GDB biedt de mogelijkheid om conditionele breakpoints in te stellen, die vereisen dat aan een specifieke voorwaarde voldaan wordt om het programma te pauzeren tijdens de uitvoering. Deze voorwaarde kan betrekking hebben op veranderingen in de waarde van een variabele, een onproductieve functieaanroep of elk ander criterium dat je verkiest. De volgende syntaxis wordt gebruikt om conditionele onderbrekingspunten aan te geven:
break <location> if n == 2
Als je het programma wilt hervatten nadat je op een onderbrekingspunt bent gestuit, voer dan het commando “doorgaan” in.
continue
Door code stappen
Het regel voor regel bestuderen van de uitvoering van computerprogramma’s is essentieel voor het begrijpen van hun werking op informatie. Door zorgvuldig meerdere functies binnen de software te doorlopen en de toestand van gegevens te onderzoeken, kan men een dieper inzicht krijgen in de manier waarop het systeem de principes uitvoert die in de broncode zijn gecodeerd.
De software maakt een uitgebreide analyse van systeemstoringen mogelijk door de oorsprong ervan vast te stellen, en biedt tegelijkertijd een diepgaand onderzoek naar de werking van programma’s door middel van nauwkeurige controle over afzonderlijke coderegels. Het proces om elke regel opeenvolgend uit te voeren is mogelijk op drie verschillende manieren binnen het debug-gereedschap dat bekend staat als GDB.
De bovenstaande richtlijn instrueert de GNU Debugger (GDB) om verder te gaan met de volgende regels van het betreffende bronbestand, waardoor een uitgebreide traversal van de codebase op individuele basis mogelijk wordt.
Het “next” commando in Pycharm’s REPL maakt het mogelijk om de volgende regels code uit te voeren binnen de op dat moment actieve functie, waarna de uitvoering stopt. In tegenstelling tot het “step” commando, dat werkt op individuele instructies of statements, beschouwt “next” een hele functie als een doorlopend blok code, waardoor gebruikers meerdere regels kunnen uitvoeren zonder onderbreking.
De opdracht finish
voltooit effectief de uitvoering van alle resterende codeblokken binnen de huidige functie, gevolgd door een beëindiging van de procedure.
Variabelen onderzoeken
Om de wijziging van programmalogica te analyseren door variabele waarden te onderzoeken tijdens de uitvoering van een programma met behulp van GDB, kan men de volgende syntaxis gebruiken om de huidige waarde van specifieke variabelen weer te geven binnen de debugomgeving:
print <variable>
Om de wijzigingen in de waarde van een bepaalde variabele bij elke iteratie binnen een lus weer te geven, kan men het commando “weergeven” gebruiken. Een dergelijke aanpak kan bijzonder nuttig zijn voor het bewaken en documenteren van de voortgang van de waarde van die variabele tijdens het iteratieve proces.
display <variable>
Watchpoints instellen
Watchpoints en conditionele breakpoints zijn met elkaar verbonden omdat ze beide reageren op wijzigingen binnen een programma. Specifiek dienen watchpoints om wijzigingen in gegevens binnen de broncode te controleren. Ter illustratie, men kan wensen dat de software pauzeert wanneer de inhoud van een bepaalde variabele een verandering ondergaat. Het proces om deze functionaliteit met GDB te implementeren wordt hieronder beschreven:
watch <variable_name>
Draad-specifiek debuggen met GDB
Met behulp van de GDB-debugger is het mogelijk om foutopsporingsbewerkingen uit te voeren op individuele threads binnen multi-threaded applicaties. Ter illustratie een beknopt C-programma dat meerdere threads gebruikt voor het genereren van afgedrukte berichten.
Gebruik het commando “info” gevolgd door een lege regel om informatie te verkrijgen over de actieve threads binnen een applicatie.
info threads
Om een bepaalde thread in de lijst te benaderen en ermee te communiceren, kan de numerieke positie of index binnen de reeks beschikbare opties worden gebruikt.Hierdoor kan de gewenste thread worden geselecteerd en gemanipuleerd op basis van de toegewezen identifier.
thread 2
Nadat een specifieke thread is gekozen, kan de uitvoeringsvolgorde ervan progressief doorlopen worden door gebruik te maken van de stappende commando’s “step”, “next” en “finish”, zoals eerder geïllustreerd.
Debuggen op afstand met GDB
Om softwaretoepassingen die op een ander apparaat gehost worden op afstand te debuggen, moet de gdbserver op de doelcomputer geconfigureerd worden. Dit proces kan worden uitgevoerd door gebruik te maken van het voorgeïnstalleerde pakketbeheerprogramma in je besturingssysteem of door gebruik te maken van aanvullende pakketbeheerprogramma’s die op je systeem zijn geïnstalleerd.
Om gdbserver te installeren op een systeem dat Ubuntu of Debian als basis gebruikt, kan je de Advanced Package Tool (APT) hiervoor gebruiken.
sudo apt install gdbserver
Navigeer na het uitvoeren van het installatieproces naar de map die de binary bevat en voer op die locatie een specifiek commando uit om het programma gdbserver te starten.
gdbserver <ip>:<port> <binary>
Van de toepassing “gdbserver” wordt verwacht dat ze een bericht weergeeft dat aangeeft dat ze gestart is en actief luistert naar inkomende verbindingen op de gespecificeerde poort. Start vervolgens op een ander apparaat een instantie van de “GDB”-software en maak een verbinding met de verre server door gebruik te maken van het commando “target” gevolgd door de specifieke aanduiding voor het beoogde onderzoeksobject.
target remote <server_ip>:<port>
GDB-scripts schrijven om debuggen te automatiseren
Het gebruik van GDB-scripts stelt ontwikkelaars in staat om vooraf bepaalde GDB-opdrachten gemakkelijk herhaaldelijk uit te voeren. Bijgevolg stroomlijnt dit het debugging-proces door het elimineren van de noodzaak voor repetitieve taken zoals het instellen van breakpoints, het navigeren door de uitvoering van code en het afdrukken van variabele gegevens. Door gebruik te maken van deze scripts kunnen ontwikkelaars een anders vervelende reeks handelingen automatiseren en zo hun productiviteit verhogen tijdens het debuggen van specifieke delen van code.
Hier is een voorbeeld:
set logging enabled on
set logging file sample.out
break main
command 1
backtrace
print N
continue
end
quit
In het gegeven codefragment wordt geïnstrueerd dat het debug-gereedschap GDB zijn logfunctie moet activeren en het resulterende log moet opslaan in een bestand met de naam “sample.out”. Bovendien wordt er een onderbrekingspunt aangewezen binnen de primaire functie van het programma.
Om onderbrekingspunt één uit te voeren, dat zich aan het begin van de ‘main’-functie bevindt, volgt u deze stappen met behulp van de GNU Debugger (GDB):Start eerst een backtrace door het commando ‘backtrace’ in te voeren.Ten tweede, toon de waarde van de variabele ‘N’ met de instructie ‘print N’. Ten derde, ga verder met het uitvoeren van het programma door ‘continue’ te typen. De debugger zal in eerste instantie een backtrace uitvoeren om de huidige status van de programmastroom te bepalen. Vervolgens wordt de waarde van de variabele ‘N’ weergegeven. Daarna wordt het uitvoeringsproces hervat. Tenslotte zal GDB, zodra alle relevante informatie is weergegeven, zijn werk netjes beëindigen.
Gebruik om dit script uit te voeren:
gdb -x <script> <binary>
Nu weet je hoe je je programma’s moet debuggen met GDB!
Debuggen is een kritieke vaardigheid en het beheersen van het gebruik van GDB voor debugging-doeleinden verbetert iemands veelzijdigheid als ontwikkelaar aanzienlijk. De verschillende functionaliteiten die GDB biedt, zoals stap-voor-stap navigeren door code, het plaatsen van breakpoints, gericht debuggen op threads en andere, maken het een krachtig instrument bij het onderzoeken van binaire bestanden binnen de Linux besturingssysteemomgeving.
Voor diegenen die softwareprogramma’s binnen het Windows besturingssysteem willen analyseren en problemen willen oplossen, kan het de moeite waard zijn om meer kennis op te doen over Windbg, een ingebouwd debug-gereedschap voor Windows.