Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Istnieje kilka sposobów określania adresów w debugerze.
Adresy są zwykle adresami wirtualnymi, z wyjątkiem sytuacji, gdy dokumentacja wskazuje inny rodzaj adresu. W trybie użytkownika debuger interpretuje adresy wirtualne zgodnie z katalogiem strony bieżącego procesu. W trybie jądra debuger interpretuje adresy wirtualne zgodnie z katalogem strony procesu, który określa kontekst procesu . Można również bezpośrednio ustawić kontekst adresu trybu użytkownika. Aby uzyskać więcej informacji na temat kontekstu adresu trybu użytkownika, zobacz kontekst .context (Ustaw kontekst User-Mode adresu).
W wyrażeniach MASM można użyć operatora poi , aby wyłusić dowolny wskaźnik. Jeśli na przykład wskaźnik pod adresem 0x0000008e"ed57b108 wskazuje lokalizację adresu 0x805287637256, następujące dwa polecenia są równoważne.
0:000> dd 805287637256
0:000> dd poi(000000bb`7ee23108)
Przykład wyświetlania adresu pamięci
Aby zobaczyć przykład użycia poi, należy określić przesunięcie parametru CurrentLocale w bloku środowiska wątku (TEB). Użyj polecenia dx, aby wyświetlić @$teb, który jest przykładem pseudorejestrów, które przechowują wspólne adresy, takie jak bieżąca lokalizacja licznika programu.
0:000> dx @$teb
@$teb : 0x1483181000 [Type: _TEB *]
...
[+0x108] CurrentLocale : 0x409 [Type: unsigned long]
CurrentLocale znajduje się +0x108 od początku TEB. Następnie określ adres pamięci tej lokalizacji.
0:000> ? @$teb + 0x108
Evaluate expression: 613867303176 = 0000008e`ed57b108
Użyj polecenia poi, aby rozreferować ten adres i zobaczyć, że zawiera on wartość CurrentLocale 0x409.
0:000> ? poi(0000008e`ed57b108)
Evaluate expression: 1033 = 00000000`00000409
W wyrażeniach debugera języka C++ wskaźniki zachowują się jak wskaźniki w języku C++. Jednak liczby są interpretowane jako liczby całkowite. Jeśli musisz odreferować konkretną liczbę, może być konieczne najpierw rzutowanie jej, jak pokazano w poniższym przykładzie.
Aby to wypróbować, użyj .expr, aby ustawić interpretera wyrażeń na C++.
0:000> .expr /s C++
Current expression evaluator: C++ - C++ source expressions
Ustawiając ewaluator wyrażeń na C++, możemy rzutować za pomocą typu long.
0:000> d *((long*)0x00000014`83181108 )
00000000`00000409 ???????? ???????? ???????? ????????
Aby uzyskać więcej informacji na temat rzutowania wartości liczbowych, zobacz Liczby i operatory języka C++.
Jeśli ewaluator wyrażeń jest ustawiony na c++, możemy opakowować wskaźnik poi za pomocą @@masm(), aby tylko ta część wyrażenia obliczana przez ewaluatora wyrażeń MASM.
0:000> .expr /s c++
Current expression evaluator: C++ - C++ source expressions
0:000> ? @@masm(poi(00000078`267d7108))
Evaluate expression: 1033 = 00000000`00000409
Aby uzyskać więcej informacji na temat dwóch ewaluatorów wyrażeń, zobacz Ocena wyrażeń.
Możesz również wskazać adres w aplikacji, określając oryginalną nazwę pliku źródłowego i numer wiersza. Aby uzyskać więcej informacji na temat sposobu określania tych informacji, zobacz Składnia wiersza źródłowego.
Zakresy adresów
Zakres adresów można określić według pary adresów lub liczby adresów i obiektów.
Aby określić zakres według pary adresów, określ adres początkowy i końcowy. Na przykład poniższy przykład to zakres 8 bajtów, zaczynając od adresu 0x00001000.
0x00001000 0x00001007
Aby określić zakres adresów według adresu i liczby obiektów, określ argument adresu, literę L (wielkie lub małe litery) i argument wartości. Adres określa adres początkowy. Wartość określa liczbę obiektów do zbadania lub wyświetlenia. Rozmiar obiektu zależy od polecenia . Jeśli na przykład rozmiar obiektu wynosi 1 bajt, poniższy przykład to zakres 8 bajtów, zaczynając od adresu 0x00001000.
0x00001000 L8
Jeśli jednak rozmiar obiektu jest podwójnym słowem (32 bity lub 4 bajty), następujące dwa zakresy dają zakres 8 bajtów.
0x00001000 0x00001007
0x00001000 L2
Specyfikator zakresu rozmiaru L
Istnieją dwa inne sposoby określania wartości (specyfikator zakresu rozmiaruL):
L?Rozmiar (z znakiem zapytania) oznacza taki sam jak rozmiar L, z tą różnicą, że L?Rozmiar usuwa automatyczny limit zakresu debugera. Zazwyczaj istnieje limit zakresu wynoszący 256 MB, ponieważ większe zakresy to błędy typograficzne. Jeśli chcesz określić zakres większy niż 256 MB, musisz użyć L?Składnia rozmiaru .
Rozmiar L (z łącznikiem) określa zakres długości Rozmiar, który kończy się na danym adresie. Na przykład 8000000000 L20 określa zakres od 0x80000000 do 0x8000001F, a 800000000 L-20 określa zakres od 0x7FFFFFE0 do 0x7FFFFFFF.
Niektóre polecenia, które pytają o zakresy adresów, akceptują jeden adres jako argument. W takiej sytuacji polecenie używa pewnej domyślnej liczby obiektów do obliczenia rozmiaru zakresu. Zazwyczaj polecenia, dla których zakres adresów jest ostatnim parametrem, zezwalają na tę składnię. Aby uzyskać dokładną składnię i domyślny rozmiar zakresu dla każdego polecenia, zobacz tematy referencyjne dla każdego polecenia.
Przykład zakresu pamięci wyszukiwania
Najpierw określimy adres rejestru wskaźnika instrukcji rip przy użyciu ewaluatora wyrażeń MASM.
0:000> ? @rip
Evaluate expression: 140720561719153 = 00007ffc`0f180771
Następnie wyszukamy od adresu 00007ffc'0f180771, szukając 100000 przy użyciu polecenia s (szukaj w pamięci). Określamy zakres do wyszukiwania przy użyciu L100000.
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.
...
Możemy również określić taki sam zakres, jak ten, przy użyciu dwóch adresów pamięci.
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.
...
Na koniec możemy wyszukiwać wstecz w zakresie pamięci przy użyciu parametru L-length.
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
Przykład dezasemblacji pamięci
W tym przykładzie użyto polecenia u (unassemble) i parametru L, aby usunąć trzy bajty kodu.
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
Określ zakres pamięci obejmujący trzy bajty, aby go zdezasemblować.
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
Tryby adresów i obsługa segmentów
Na platformach opartych na architekturze x86 usługa CDB i KD obsługują następujące tryby adresowania. Te tryby są rozróżniane przez ich prefiksy.
| Prefiks | Nazwa | Typy adresów |
|---|---|---|
| % | płaski | 32-bitowe adresy (również selektory 16-bitowe wskazujące segmenty 32-bitowe) i 64-bitowe adresy w systemach 64-bitowych. |
| & | wirtualny 86 | Adresy trybu rzeczywistego. Tylko oparty na architekturze x86. |
| # | równina | Adresy trybu rzeczywistego. Tylko oparty na architekturze x86. |
Różnica między trybami zwykłego i wirtualnego 86 polega na tym, że w trybie zwykłym 16-bitowy adres używa wartości segmentu jako selektora i odwołuje się do deskryptora segmentu. Jednak wirtualny adres 86 nie używa selektorów i zamiast tego mapuje bezpośrednio do dolnej 1 MB.
Jeśli uzyskujesz dostęp do pamięci za pośrednictwem trybu adresowania, który nie jest bieżącym trybem domyślnym, możesz użyć prefiksów trybu adresu, aby zastąpić bieżący tryb adresu.
Rozwiązywanie argumentów
Argumenty adresowe określają lokalizację zmiennych i funkcji. W poniższej tabeli opisano składnię i znaczenie różnych adresów, których można użyć w usłudze CDB i KD.
| Składnia | Znaczenie |
|---|---|
offset |
Adres bezwzględny w przestrzeni pamięci wirtualnej z typem odpowiadającym bieżącemu trybowi wykonywania. Na przykład, gdy bieżący tryb wykonywania jest 16-bitowy, przesunięcie jest 16-bitowe. Jeśli tryb wykonywania jest 32-bitowy, przesunięcie jest podzielone na segmenty 32-bitowe. |
&[[ segment:]] przesunięcie |
Rzeczywisty adres. Oparte na architekturze x86 i x64. |
%segment:[[ offset]] |
Segmentowany adres 32-bitowy lub 64-bitowy. W oparciu o architekturę x86 i x64. |
%[[ przesunięcie]] |
Adres bezwzględny (32-bitowy lub 64-bitowy) w przestrzeni pamięci wirtualnej. Oparte na architekturze x86 i x64. |
name[[ +|− ]] przesunięcie |
Prosty adres 32-bitowy lub 64-bitowy. nazwa może być dowolnym symbolem. przesunięcie określa przesunięcie. To przesunięcie może być w dowolnym trybie adresowania wskazanym przez jego prefiks. Żaden prefiks nie określa adresu trybu domyślnego. Przesunięcie można określić jako wartość dodatnią (+) lub ujemną (−). |
Użyj polecenia dg (Display Selector), aby wyświetlić informacje deskryptora segmentu.
Zobacz też
Aby wyświetlić informacje o pamięci, użyj polecenia !address .
Aby wyszukać pamięć, użyj polecenia s (Wyszukaj pamięć).
Aby wyświetlić zawartość pamięci, użyj polecenia d, da, db, dc, dd, dD, df, dp, dq, du, dw (Display Memory).
Aby uzyskać informacje na temat sposobu wyświetlania i edytowania pamięci przy użyciu okna Pamięci, zobacz Korzystanie z okna pamięci.