214 Stimmen

Wie funktioniert ein Debugger?

Ich frage mich immer wieder, wie ein Debugger funktioniert? Vor allem der, der an eine bereits laufende ausführbare Datei "angehängt" werden kann. Ich verstehe, dass der Compiler den Code in Maschinensprache übersetzt, aber woher weiß der Debugger dann, woran er angehängt ist?

9 Stimmen

2 Stimmen

@Oktalist Dieser Artikel ist interessant, spricht aber nur über die Abstraktion auf API-Ebene für das Debugging unter Linux. Ich vermute, dass OP mehr über das Innenleben wissen möchte.

118voto

Rob Walker Punkte 45267

Wie ein Debugger im Detail funktioniert, hängt davon ab, was Sie debuggen wollen und welches Betriebssystem Sie verwenden. Für das native Debugging unter Windows finden Sie einige Details auf MSDN: Win32-Debugging-API .

Der Benutzer teilt dem Debugger mit, an welchen Prozess er sich anhängen soll, entweder über den Namen oder über die Prozess-ID. Wenn es sich um einen Namen handelt, wird der Debugger die Prozess-ID nachschlagen und die Debug-Sitzung über einen Systemaufruf initiieren; unter Windows wäre dies DebugActiveProcess .

Sobald der Debugger angehängt ist, tritt er in eine Ereignisschleife ein, ähnlich wie bei jeder Benutzeroberfläche, aber anstelle von Ereignissen, die vom Fenstersystem kommen, erzeugt das Betriebssystem Ereignisse auf der Grundlage dessen, was in dem zu debuggenden Prozess passiert - zum Beispiel das Auftreten einer Ausnahme. Siehe WaitForDebugEvent .

Der Debugger ist in der Lage, den virtuellen Speicher des Zielprozesses zu lesen und zu schreiben und sogar seine Registerwerte über vom Betriebssystem bereitgestellte APIs anzupassen. Siehe die Liste der Debugging-Funktionen für Windows.

Der Debugger ist in der Lage, Informationen aus Symboldateien zu verwenden, um Adressen in Variablennamen und Speicherstellen im Quellcode zu übersetzen. Die Informationen aus den Symboldateien sind ein separater Satz von APIs und kein Kernbestandteil des Betriebssystems als solches. Unter Windows erfolgt dies über die Debug Interface Access SDK .

Wenn Sie eine verwaltete Umgebung (.NET, Java usw.) debuggen, sieht der Prozess in der Regel ähnlich aus, aber die Details sind anders, da die Umgebung der virtuellen Maschine die Debug-API bereitstellt und nicht das zugrunde liegende Betriebssystem.

5 Stimmen

Diese Frage mag dumm klingen, aber wie verfolgt das Betriebssystem, ob eine bestimmte Adresse innerhalb des Programms erreicht wird. Z.B. wird ein Breackpoint auf Adresse 0x7710cafe gesetzt. Wenn sich der Befehlszeiger ändert, muss das Betriebssystem (oder vielleicht die CPU) den Befehlszeiger mit allen Haltepunktadressen vergleichen, oder irre ich mich da? Wie funktioniert das ?

3 Stimmen

@StefanFalk Ich schrieb eine Antwort das einige der Details auf niedrigerer Ebene (auf x86) behandelt.

0 Stimmen

Können Sie erklären, wie genau die Variablennamen den Adressen zugeordnet werden? Verwendet die Anwendung bei jeder Ausführung die gleichen Speicheradressen für die Variablen? Ich bin immer davon ausgegangen, dass die Variablen einfach aus dem verfügbaren Speicher zugeordnet werden, habe aber nie darüber nachgedacht, ob die Bytes direkt auf dieselbe Stelle im Speicher der Anwendung abgebildet werden. Das scheint ein großes Sicherheitsproblem zu sein.

94voto

Jonathon Reinhart Punkte 123962

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 dieser Anweisung gelangt und die int3 Dies führt dazu, dass die CPU eine Debug-Exception erzeugt. Das Betriebssystem empfängt diese Unterbrechung, erkennt, dass der Prozess debuggt wird, und teilt dem Debugger-Prozess mit, dass der Haltepunkt erreicht wurde.

Nachdem der Haltepunkt erreicht und der Prozess gestoppt wurde, sucht der Debugger in seiner Liste der Haltepunkte und ersetzt die CC mit dem Byte, das ursprünglich vorhanden war. Der Debugger setzt TF die Trap-Flagge sur EFLAGS (durch Änderung der CONTEXT ), und setzt den Prozess fort. Das Trap-Flag veranlasst die CPU, automatisch eine einstufige Ausnahme zu erzeugen ( INT 1 ) bei der nächsten Anweisung.

Wenn der zu debuggende Prozess das nächste Mal anhält, ersetzt der Debugger wieder das erste Byte der Haltepunktanweisung durch CC , und der Prozess geht weiter.

Ich bin mir nicht sicher, ob dies bei allen Debuggern genau so implementiert ist, aber ich habe ein Win32-Programm geschrieben, das sich mit diesem Mechanismus selbst debuggt. Völlig nutzlos, aber lehrreich.

34voto

Adam Rosenfield Punkte 373807

Unter Linux beginnt die Fehlersuche in einem Prozess mit dem Befehl ptrace(2) Systemaufruf. Dieser Artikel hat eine großartige Anleitung für die Verwendung von ptrace um einige einfache Debugging-Konstrukte zu implementieren.

1 Stimmen

Ist die (2) uns etwas mehr (oder weniger) sagen als "ptrace ist ein Systemaufruf"?

5 Stimmen

@eSKay, nein, nicht wirklich. Die (2) ist die Nummer des Handbuchabschnitts. Siehe de.wikipedia.org/wiki/Manpage#Handbuch_Abschnitte für eine Beschreibung der Handbuchabschnitte.

2 Stimmen

@AdamRosenfield Abgesehen von der Tatsache, dass Abschnitt 2 ausdrücklich "Systemaufrufe" heißt. Also indirekt, ja, es sagt uns, dass ptrace ist ein Systemaufruf.

10voto

Michael Burr Punkte 320591

Wenn Sie mit einem Windows-Betriebssystem arbeiten, ist das Buch "Debugging Applications for Microsoft .NET and Microsoft Windows" von John Robbins ein hervorragendes Hilfsmittel für diesen Zweck:

(oder sogar die ältere Ausgabe: "Anwendungen debuggen" )

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

Da ich mich mit den Details der Unix/Linux-Fehlersuche nicht auskenne, kann es sein, dass diese Dinge auf andere Betriebssysteme überhaupt nicht zutreffen. Aber ich würde vermuten, dass als Einführung in ein sehr komplexes Thema die Konzepte - wenn nicht sogar die Details und APIs - auf fast jedes Betriebssystem "portiert" werden können.

8voto

Server Overflow Punkte 19774

Meines Erachtens gibt es hier zwei wichtige Fragen zu beantworten:

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

Wenn eine Ausnahme in einem zu debuggenden Prozess auftritt, wird der Debugger vom Betriebssystem benachrichtigt, bevor alle im Zielprozess definierten Benutzer-Ausnahmebehandler die Chance erhalten, auf die Ausnahme zu reagieren. Wenn der Debugger sich dafür entscheidet, diese Ausnahmebenachrichtigung (auf Anhieb) nicht zu behandeln, wird der Ablauf der Ausnahmebehandlung fortgesetzt und der Ziel-Thread erhält dann die Möglichkeit, die Ausnahme zu behandeln, wenn er dies möchte. Wird die SEH-Ausnahme vom Zielprozess nicht behandelt, erhält der Debugger ein weiteres Debug-Ereignis, die so genannte Second-Chance-Meldung, die ihn darüber informiert, dass im Zielprozess eine unbehandelte Ausnahme aufgetreten ist. Quelle

enter image description here


2. Woher weiß der Debugger, wie er an einem Haltepunkt anhalten kann?

Die vereinfachte Antwort ist: Wenn Sie einen Haltepunkt in das Programm setzen, ersetzt der Debugger Ihren Code an dieser Stelle durch eine int3-Anweisung, die eine Software-Unterbrechung . Dies hat zur Folge, dass das Programm angehalten und der Debugger aufgerufen wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X