Adress- und Adressbereichssyntax

Es gibt mehrere Möglichkeiten, Adressen im Debugger anzugeben.

Adressen sind normalerweise virtuelle Adressen, es sei denn, die Dokumentation weist ausdrücklich auf eine andere Art von Adresse hin. Im Benutzermodus interpretiert der Debugger virtuelle Adressen entsprechend dem Seitenverzeichnis des aktuellen Prozesses. Im Kernelmodus interpretiert der Debugger virtuelle Adressen entsprechend dem Seitenverzeichnis des Prozesses, den der Prozesskontext angibt. Sie können den Adresskontext im Benutzermodus auch direkt festlegen. Weitere Informationen zum Adresskontext im Benutzermodus finden Sie unter .context (Festlegen User-Mode Adresskontext).

In MASM-Ausdrücken können Sie den poi-Operator verwenden, um einen beliebigen Zeiger zu dereferenzieren. Wenn beispielsweise der Zeiger auf die Adresse 0x0000008e'ed57b108 auf den Adressspeicherort 0x805287637256 verweist, sind die folgenden beiden Befehle gleichwertig.

0:000> dd 805287637256
0:000> dd poi(000000bb`7ee23108)

Beispiel für die Anzeigespeicheradresse

Um ein Beispiel für die Verwendung von poi anzuzeigen, bestimmen Sie den Offset für currentLocale des Threadumgebungsblocks (TEB). Verwenden Sie den Dx-Befehl, um @$teb anzuzeigen. Dies ist ein Beispiel für Pseudoregister, die allgemeine Adressen enthalten, z. B. den aktuellen Speicherort des Programmzählers.

0:000> dx @$teb
@$teb                 : 0x1483181000 [Type: _TEB *]

...

    [+0x108] CurrentLocale    : 0x409 [Type: unsigned long]

CurrentLocale ist vom Anfang des TEB +0x108. Ermitteln Sie als Nächstes die Speicheradresse dieses Speicherorts.

0:000> ? @$teb + 0x108
Evaluate expression: 613867303176 = 0000008e`ed57b108

Verwenden Sie poi, um diese Adresse zu dereferenzieren, um festzustellen, dass sie den CurrentLocale-Wert von 0x409 enthält.

0:000> ? poi(0000008e`ed57b108)
Evaluate expression: 1033 = 00000000`00000409

In C++-Debuggerausdrücken verhalten sich Zeiger wie Zeiger in C++. Zahlen werden jedoch als ganze Zahlen interpretiert. Wenn Sie eine tatsächliche Zahl zurückstellen müssen, müssen Sie sie möglicherweise zuerst umwandeln, wie im folgenden Beispiel gezeigt.

Um dies zu versuchen, verwenden Sie .expr , um die Ausdrucksauswertung auf C++ festzulegen.

0:000> .expr /s C++
Current expression evaluator: C++ - C++ source expressions

Wenn der Ausdrucksauswerter auf C++ festgelegt ist, können wir die Umwandlung mit long durchführen.

0:000> d *((long*)0x00000014`83181108 ) 
00000000`00000409  ???????? ???????? ???????? ????????

Weitere Informationen zum Umwandeln numerischer Werte finden Sie unter C++-Zahlen und -Operatoren.

Wenn die Ausdrucksauswertung auf c++ festgelegt ist, können wir den Poi-Zeiger mit @@masm() umschließen, damit nur dieser Teil des Ausdrucks von der MASM-Ausdrucksauswertung ausgewertet wird.

0:000> .expr /s c++
Current expression evaluator: C++ - C++ source expressions

0:000> ? @@masm(poi(00000078`267d7108))
Evaluate expression: 1033 = 00000000`00000409

Weitere Informationen zu den beiden Ausdrucksauswertungen finden Sie unter Auswerten von Ausdrücken.

Sie können auch eine Adresse in einer Anwendung angeben, indem Sie den ursprünglichen Quelldateinamen und die Zeilennummer angeben. Weitere Informationen zum Angeben dieser Informationen finden Sie unter Quellzeilensyntax.

Adressbereiche

Sie können einen Adressbereich anhand eines Adresspaars oder einer Adress- und Objektanzahl angeben.

Um einen Bereich anhand eines Adresspaars anzugeben, geben Sie die Anfangsadresse und die Endadresse an. Das folgende Beispiel ist beispielsweise ein Bereich von 8 Bytes, beginnend bei der Adresse 0x00001000.

0x00001000  0x00001007

Um einen Adressbereich anhand einer Adress- und Objektanzahl anzugeben, geben Sie ein Adressargument, den Buchstaben L (Groß- oder Kleinbuchstaben) und ein Wertargument an. Die Adresse gibt die Startadresse an. Der -Wert gibt die Anzahl der Objekte an, die untersucht oder angezeigt werden sollen. Die Größe des Objekts hängt vom Befehl ab. Wenn die Objektgröße beispielsweise 1 Byte beträgt, ist das folgende Beispiel ein Bereich von 8 Byte, beginnend bei der Adresse 0x00001000.

0x00001000  L8

Wenn die Objektgröße jedoch ein doppeltes Wort (32 Bits oder 4 Bytes) ist, weisen die folgenden beiden Bereiche jeweils einen Bereich von 8 Byte auf.

0x00001000  0x00001007
0x00001000  L2

L-Größenbereichsspezifizierer

Es gibt zwei weitere Möglichkeiten, den Wert anzugeben (der L-Größenbereichsspezifizierer):

  • L?Größe (mit Fragezeichen) bedeutet dasselbe wie L-Größe, mit der Ausnahme, dass L?Die Größe entfernt den automatischen Bereichsgrenzwert des Debuggers. In der Regel gilt ein Bereichslimit von 256 MB, da größere Bereiche typografische Fehler sind. Wenn Sie einen Bereich angeben möchten, der größer als 256 MB ist, müssen Sie das L? Größensyntax.

  • L-Size (mit einem Bindestrich) gibt einen Längenbereich Größe an, der bei der angegebenen Adresse endet. Beispielsweise gibt 80000000 L20 den Bereich von 0x80000000 bis 0x8000001F an, und 800000000 L-20 gibt den Bereich von 0x7FFFFFE0 bis 0x7FFFFFFF an.

Einige Befehle, die nach Adressbereichen fragen, akzeptieren eine einzelne Adresse als Argument. In diesem Fall verwendet der Befehl eine Standardobjektanzahl, um die Größe des Bereichs zu berechnen. In der Regel lassen Befehle, für die der Adressbereich der endgültige Parameter ist, diese Syntax zu. Die genaue Syntax und die Standardbereichsgröße für jeden Befehl finden Sie in den Referenzthemen für jeden Befehl.

Beispiel für den Suchspeicherbereich

Zunächst ermitteln wir die Adresse des Zeigerregisters für die Rip-Anweisung mithilfe der MASM-Ausdrucksauswertung.

0:000> ? @rip 
Evaluate expression: 140720561719153 = 00007ffc`0f180771

Dann suchen wir ab 00007ffc'0f180771 nach 100000, indem wir den Befehl s (Arbeitsspeicher durchsuchen) verwenden . Wir geben den zu durchsuchenden Bereich mithilfe von L100000 an.

0:000> s -a 00007ffc`0f180771 L100000 "ntdll"  
00007ffc`0f1d48fa  6e 74 64 6c 6c 5c 6c 64-72 69 6e 69 74 2e 63 00  ntdll\ldrinit.c.
00007ffc`0f1d49c2  6e 74 64 6c 6c 5c 6c 64-72 6d 61 70 2e 63 00 00  ntdll\ldrmap.c..
00007ffc`0f1d4ab2  6e 74 64 6c 6c 5c 6c 64-72 72 65 64 69 72 65 63  ntdll\ldrredirec
00007ffc`0f1d4ad2  6e 74 64 6c 6c 5c 6c 64-72 73 6e 61 70 2e 63 00  ntdll\ldrsnap.c.
...

Wir können auch den gleichen Bereich wie diesen angeben, indem wir zwei Speicheradressen verwenden.

0:000> s -a 0x00007ffc`0f180771 0x00007ffc`0f280771 "ntdll"  
00007ffc`0f1d48fa  6e 74 64 6c 6c 5c 6c 64-72 69 6e 69 74 2e 63 00  ntdll\ldrinit.c.
00007ffc`0f1d49c2  6e 74 64 6c 6c 5c 6c 64-72 6d 61 70 2e 63 00 00  ntdll\ldrmap.c..
00007ffc`0f1d4ab2  6e 74 64 6c 6c 5c 6c 64-72 72 65 64 69 72 65 63  ntdll\ldrredirec
00007ffc`0f1d4ad2  6e 74 64 6c 6c 5c 6c 64-72 73 6e 61 70 2e 63 00  ntdll\ldrsnap.c.
...

Schließlich können wir mithilfe des L-length-Parameters rückwärts im Speicherbereich suchen.

0:000> s -a 00007ffc`0f1d4ad2 L-100000 "ntdll"  
00007ffc`0f1d48fa  6e 74 64 6c 6c 5c 6c 64-72 69 6e 69 74 2e 63 00  ntdll\ldrinit.c.
00007ffc`0f1d49c2  6e 74 64 6c 6c 5c 6c 64-72 6d 61 70 2e 63 00 00  ntdll\ldrmap.c..
00007ffc`0f1d4ab2  6e 74 64 6c 6c 5c 6c 64-72 72 65 64 69 72 65 63  ntdll\ldrredirec

Beispiel für nichtassemierbaren Arbeitsspeicher

In diesem Beispiel werden der Befehl u (unassemble) und der L-Parameter verwendet, um drei Byte Code aufzuheben.

0:000> u 00007ffc`0f1d48fa L3
ntdll!`string'+0xa:
00007ffc`0f1d48fa 6e              outs    dx,byte ptr [rsi]
00007ffc`0f1d48fb 7464            je      ntdll!`string'+0x21 (00007ffc`0f1d4961)
00007ffc`0f1d48fd 6c              ins     byte ptr [rdi],dx

Oder geben Sie einen Speicherbereich von drei Byte an, der wie folgt aufzuheben ist.

0:000> u 00007ffc`0f1d48fa 00007ffc`0f1d48fd
ntdll!`string'+0xa:
00007ffc`0f1d48fa 6e              outs    dx,byte ptr [rsi]
00007ffc`0f1d48fb 7464            je      ntdll!`string'+0x21 (00007ffc`0f1d4961)
00007ffc`0f1d48fd 6c              ins     byte ptr [rdi],dx

Adressmodi und Segmentunterstützung

Auf x86-basierten Plattformen unterstützen CDB und KD die folgenden Adressierungsmodi. Diese Modi unterscheiden sich durch ihre Präfixe.

Präfix Name Adresstypen
% Wohnung 32-Bit-Adressen (auch 16-Bit-Selektoren, die auf 32-Bit-Segmente verweisen) und 64-Bit-Adressen auf 64-Bit-Systemen.
& virtual 86 Adressen im realen Modus. Nur x86-basiert.
# plain Adressen im realen Modus. Nur x86-basiert.

Der Unterschied zwischen dem einfachen und dem virtuellen 86-Modus besteht darin, dass eine einfache 16-Bit-Adresse den Segmentwert als Selektor verwendet und den Segmentdeskriptor sucht. Eine virtuelle 86-Adresse verwendet jedoch keine Selektoren und wird stattdessen direkt den unteren 1 MB zugeordnet.

Wenn Sie über einen Adressierungsmodus auf arbeitsspeicher zugreifen, der nicht der aktuelle Standardmodus ist, können Sie die Adressmoduspräfixe verwenden, um den aktuellen Adressmodus außer Kraft zu setzen.

Adressargumente

Adressargumente geben den Speicherort von Variablen und Funktionen an. In der folgenden Tabelle werden die Syntax und die Bedeutung der verschiedenen Adressen erläutert, die Sie in CDB und KD verwenden können.

Syntax Bedeutung

offset

Die absolute Adresse im virtuellen Speicher mit einem Typ, der dem aktuellen Ausführungsmodus entspricht. Wenn der aktuelle Ausführungsmodus beispielsweise 16 Bit ist, beträgt der Offset 16 Bit. Wenn der Ausführungsmodus 32-Bit segmentiert ist, ist der Offset 32-Bit-segmentiert.

&[[ segment:]] offset

Die tatsächliche Adresse. x86-basiert und x64-basiert.

%segment:[[ offset]]

Eine segmentierte 32-Bit- oder 64-Bit-Adresse. x86-basiert und x64-basiert.

%[[ Offset]]

Eine absolute Adresse (32-Bit oder 64-Bit) im virtuellen Speicherbereich. x86-basiert und x64-basiert.

name[[ +| ]] offset

Eine flache 32-Bit- oder 64-Bit-Adresse. name kann ein beliebiges Symbol sein. offset gibt den Offset an. Dieser Offset kann unabhängig vom Adressmodus sein, der vom Präfix angegeben wird. Kein Präfix gibt eine Standardmodusadresse an. Sie können den Offset als positiver (+) oder negativer (−) Wert angeben.

Verwenden Sie den Befehl dg (Display Selector), um Segmentdeskriptorinformationen anzuzeigen.

Weitere Informationen

Verwenden Sie den Befehl !address , um Informationen zum Arbeitsspeicher anzuzeigen.

Verwenden Sie zum Durchsuchen des Arbeitsspeichers den Befehl s (Arbeitsspeicher durchsuchen).

Verwenden Sie zum Anzeigen des Speicherinhalts den Befehl d, da, db, dc, dd, dD, df, dp, dq, du, dw (Speicher anzeigen).

Informationen zum Anzeigen und Bearbeiten von Arbeitsspeicher mithilfe eines Speicherfensters finden Sie unter Verwenden eines Speicherfensters.