Syntax für Adress- und Adressbereich

Es gibt mehrere Möglichkeiten zum Angeben von Adressen im Debugger.

Adressen sind normalerweise virtuelle Adressen, es sei denn, die Dokumentation gibt eine andere Art von Adresse an. Im Benutzermodus interpretiert der Debugger virtuelle Adressen gemäß dem Seitenverzeichnis des aktuellen Prozesses. Im Kernelmodus interpretiert der Debugger virtuelle Adressen gemäß dem Seitenverzeichnis des Prozesses, den der Prozesskontext angibt. Sie können den Adresskontext des Benutzermodus auch direkt festlegen. Weitere Informationen zum Adresskontext des Benutzermodus finden Sie unter ".context" (Festlegen des Adresskontexts für den Benutzermodus).

In MASM-Ausdrücken können Sie den Poi-Operator verwenden, um jeden Zeiger abzuleiten. Wenn beispielsweise der Zeiger an der Adresse 0x0000008e'ed57b108 auf die Adressposition 0x805287637256 verweist, entsprechen die folgenden beiden Befehle.

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

Beispiel für die Anzeige der Speicheradresse

Um ein Beispiel für die Verwendung von Pois anzuzeigen, bestimmen Sie den Offset für den CurrentLocale des Threadumgebungsblocks (TEB). Verwenden Sie den Dx-Befehl, um @$teb anzuzeigen, bei dem es sich um ein Beispiel für pseudoregisterliche Adressen handelt, die allgemeine Adressen enthalten, z. B. den aktuellen Programmzählerspeicherort.

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

...

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

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

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

Verwenden Sie poi, um diese Adresse abzuleiten, 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 den Ausdrucksauswert auf C++ festzulegen.

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

Wenn der Ausdrucksauswert auf C++ festgelegt ist, können wir lange umwandeln.

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

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

Wenn der Ausdrucksauswert auf c++ festgelegt ist, können wir den Poipointer mit @@masm()umschließen, damit nur dieser Teil des Ausdrucks vom MASM-Ausdruck evaluator 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 Ausdrucksauswertern 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 durch ein Adresspaar oder durch eine Adresse und objektanzahl angeben.

Wenn Sie einen Bereich nach einem Adresspaar angeben möchten, geben Sie die Startadresse und die Endadresse an. Das folgende Beispiel ist beispielsweise ein Bereich von 8 Byte, beginnend mit der Adresse 0x00001000.

0x00001000  0x00001007

Wenn Sie einen Adressbereich durch eine Adresse und objektanzahl angeben möchten, 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 zu untersuchenden oder angezeigten Objekte an. Die Größe des Objekts hängt vom Befehl ab. Wenn die Objektgröße beispielsweise 1 Byte ist, ist das folgende Beispiel ein Bereich von 8 Byte, beginnend mit der Adresse 0x00001000.

0x00001000  L8

Wenn die Objektgröße jedoch ein Doppelwort (32 Bit oder 4 Byte) ist, weisen die folgenden beiden Bereiche jeweils einen 8-Byte-Bereich auf.

0x00001000  0x00001007
0x00001000  L2

L-Größenbereichsbezeichner

Es gibt zwei weitere Möglichkeiten zum Angeben des Werts (L Size Range Specifier):

  • L? Größe (mit Fragezeichen) bedeutet dasselbe wie "LSize", mit Ausnahme von "L"? Die Größe entfernt das automatische Bereichslimit des Debuggers. In der Regel gibt es einen Bereichsgrenzwert 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 den L verwenden? Größensyntax .

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

Einige Befehle, die nach Adressbereichen fragen, akzeptieren eine einzelne Adresse als Argument. In diesem Fall verwendet der Befehl einige Standardobjektanzahl, um die Größe des Bereichs zu berechnen. Normalerweise lassen Befehle, für die der Adressbereich der letzte 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 bestimmen wir die Adresse des Rip-Anweisungszeigerregisters mithilfe des MASM-Ausdrucksauswerters.

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

Dann suchen wir ab 00007ffc'0f180771 nach 1000000 mit dem Befehl s (Speicher durchsuchen). Wir geben den Bereich an, der mithilfe von L100000 durchsucht werden soll.

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 denselben Bereich wie diesen mit zwei Speicheradressen angeben.

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 nicht zusammengesetzten Arbeitsspeicher

In diesem Beispiel werden der Befehl "u" (unassemble) und der Parameter "L" 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 3-Byte-Speicherbereich 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
% flat 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 Realmodus. Nur x86-basiert.
# plain Adressen im Realmodus. 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 nachschlagen kann. Eine virtuelle 86-Adresse verwendet jedoch keine Selektoren und ordnet stattdessen direkt in die untere 1 MB zu.

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

Adressargumente

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

Syntax Bedeutung

offset

Die absolute Adresse im virtuellen Arbeitsspeicher 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 echte 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 Arbeitsspeicher. 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. Bei diesem Offset kann es sich um den Adressmodus handeln, der sein Präfix angibt. Kein Präfix gibt eine Standardmodusadresse an. Sie können den Offset als positive (+) oder negative (−) Wert angeben.

Verwenden Sie den Befehl "dg" (Anzeigeauswahl), um Segmentdeskriptorinformationen anzuzeigen.

Weitere Informationen

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

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

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

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