it-swarm.com.de

Wie funktioniert ein Debugger?

Ich frage mich immer wieder, wie ein Debugger funktioniert. Insbesondere der, der an bereits ausgeführte ausführbare Dateien angehängt werden kann. Ich verstehe, dass der Compiler Code in Maschinensprache übersetzt, aber woher weiß der Debugger dann, womit er verbunden ist?

157
ya23

Die Details der Funktionsweise eines Debuggers hängen davon ab, was Sie debuggen und welches Betriebssystem Sie verwenden. Für das native Debuggen unter Windows finden Sie einige Details zu MSDN: Win32-Debugging-API .

Der Benutzer teilt dem Debugger mit, an welchen Prozess er Anhängen soll, entweder nach Name oder nach Prozess-ID. Wenn es sich um einen Namen handelt, schlägt der Debugger die Prozess-ID nach und leitet die Debugsitzung über einen Systemaufruf ein. unter Windows wäre dies DebugActiveProcess .

Nach dem Anhängen tritt der Debugger in eine Ereignisschleife ein, ähnlich wie bei jeder Benutzeroberfläche. Anstelle von Ereignissen, die vom Fenstersystem stammen, generiert das Betriebssystem Ereignisse basierend auf dem Vorgang, der während des Debuggens ausgeführt wird - zum Beispiel, wenn eine Ausnahme auftritt. Siehe WaitForDebugEvent .

Der Debugger kann den virtuellen Speicher des Zielprozesses lesen und schreiben sowie seine Registerwerte über vom Betriebssystem bereitgestellte APIs anpassen. Siehe die Liste von Debugging-Funktionen für Windows.

Der Debugger kann Informationen aus Symboldateien verwenden, um Adressen in Variablennamen und Speicherorte im Quellcode zu übersetzen. Die Informationen in der Symboldatei sind ein separater Satz von APIs und kein Kernbestandteil des Betriebssystems. Unter Windows erfolgt dies über das Debug Interface Access SDK .

Wenn Sie eine verwaltete Umgebung (.NET, Java usw.) debuggen, sieht der Prozess normalerweise ähnlich aus, die Details unterscheiden sich jedoch, da die Umgebung der virtuellen Maschine die Debug-API und nicht das zugrunde liegende Betriebssystem bereitstellt.

90
Rob Walker

So wie ich es verstehe:

Bei Software-Haltepunkten auf x86 ersetzt der Debugger das erste Byte der Anweisung durch CC ( int3 ). Dies geschieht mit WriteProcessMemory unter Windows. Wenn die CPU zu diesem Befehl gelangt und den Befehl int3 bewirkt, dass die CPU eine Debug-Ausnahme generiert. Das Betriebssystem empfängt diese Unterbrechung, stellt fest, dass der Prozess debuggt, und benachrichtigt den Debugger-Prozess, dass der Haltepunkt erreicht wurde.

Nachdem der Haltepunkt erreicht und der Prozess gestoppt wurde, sucht der Debugger in seiner Liste der Haltepunkte und ersetzt CC durch das ursprüngliche Byte. Der Debugger setzt TF, das Trap-Flag in EFLAGS (durch Ändern des CONTEXT =) und setzt den Vorgang fort. Das Trap-Flag bewirkt, dass die CPU automatisch eine Einzelschrittausnahme generiert ( INT 1 ) bei der nächsten Anweisung.

Wenn der zu debuggende Prozess das nächste Mal angehalten wird, ersetzt der Debugger das erste Byte der Haltepunktanweisung erneut durch CC, und der Prozess wird fortgesetzt.

Ich bin nicht sicher, ob dies genau so ist, wie es von allen Debuggern implementiert wird, aber ich habe ein Win32-Programm geschrieben, das es schafft, sich mithilfe dieses Mechanismus selbst zu debuggen. Völlig nutzlos, aber lehrreich.

54

Unter Linux beginnt das Debuggen eines Prozesses mit dem Systemaufruf ptrace (2) . Dieser Artikel enthält eine großartige Anleitung zur Verwendung von ptrace, um einige einfache Debugging-Konstrukte zu implementieren.

24
Adam Rosenfield

Wenn Sie ein Windows-Betriebssystem verwenden, ist "Debugging Applications for Microsoft .NET and Microsoft Windows" von John Robbins eine hervorragende Ressource:

(oder sogar die ältere Ausgabe: "Debugging Applications" )

Das Buch enthält ein Kapitel über die Funktionsweise eines Debuggers, das Code für einige einfache (aber funktionierende) Debugger enthält.

Da ich mit Einzelheiten zum Debuggen unter Unix/Linux nicht vertraut bin, kann dieses Zeug möglicherweise überhaupt nicht auf andere Betriebssysteme angewendet werden. Als Einführung in ein sehr komplexes Thema sollten die Konzepte - wenn nicht die Details und APIs - auf die meisten Betriebssysteme portiert werden.

10
Michael Burr

Eine weitere wertvolle Quelle, um das Debuggen zu verstehen, ist das Intel-CPU-Handbuch (Intel® 64- und IA-32-Architekturen, Software-Entwicklerhandbuch). In Band 3A, Kapitel 16, wurde die Hardwareunterstützung für das Debuggen eingeführt, z. B. spezielle Ausnahmen und Hardware-Debugging-Register. Folgendes stammt aus diesem Kapitel:

T (Trap) Flag, TSS - Erzeugt eine Debug-Ausnahme (#DB), wenn versucht wird, zu einer Task zu wechseln, in deren TSS das T-Flag gesetzt ist.

Ich bin mir nicht sicher, ob Windows oder Linux dieses Flag verwenden oder nicht, aber es ist sehr interessant, dieses Kapitel zu lesen.

Hoffe das hilft jemandem.

3
Jiang

Mein Verständnis ist, dass wenn Sie eine Anwendung oder DLL Datei kompilieren, was auch immer es kompiliert, Symbole enthält, die die Funktionen und die Variablen darstellen.

Bei einem Debugbuild sind diese Symbole wesentlich detaillierter als bei einem Releasebuild, sodass der Debugger Ihnen mehr Informationen geben kann. Wenn Sie den Debugger an einen Prozess anhängen, wird geprüft, auf welche Funktionen gerade zugegriffen wird, und alle verfügbaren Debugsymbole werden von hier aus aufgelöst , mit Inhalten von Ints, Floats, Strings usw.). Wie im ersten Poster bereits erwähnt, hängen diese Informationen und die Funktionsweise dieser Symbole stark von der Umgebung und der Sprache ab.

1
DavidG

Ich denke, hier sind zwei Hauptfragen zu beantworten:

1. Woher weiß der Debugger, dass eine Ausnahme aufgetreten ist?

Wenn in einem zu debuggenden Prozess eine Ausnahme auftritt, wird der Debugger vom Betriebssystem benachrichtigt, bevor im Zielprozess definierte Benutzerausnahmebehandlungsroutinen die Möglichkeit erhalten, auf die Ausnahme zu reagieren. Wenn der Debugger diese Ausnahmebedingungsbenachrichtigung (erste Chance) nicht behandelt, wird die Ausnahmeverteilungssequenz fortgesetzt, und der Zielthread erhält dann die Möglichkeit, die Ausnahme zu behandeln, wenn er dies möchte. Wenn die SEH-Ausnahme nicht vom Zielprozess behandelt wird, wird dem Debugger ein weiteres Debugereignis gesendet, das als Second-Chance-Benachrichtigung bezeichnet wird, um ihn darüber zu informieren, dass im Zielprozess eine nicht behandelte Ausnahme aufgetreten ist. Quelle

enter image description here


2. Woher weiß der Debugger, wie man an einem Haltepunkt stoppt?

Das vereinfachte Antwort lautet: Wenn Sie einen Haltepunkt in das Programm einfügen, ersetzt der Debugger Ihren Code an diesem Punkt durch eine int3-Anweisung, die ein Software-Interrupt ist. Infolgedessen wird das Programm angehalten und der Debugger aufgerufen.

0
WeGoToMars