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.
W tym artykule opisano użycie składni wyrażeń języka C++ z narzędziami debugowania systemu Windows.
Debuger akceptuje dwa różne rodzaje wyrażeń liczbowych: wyrażenia języka C++ i wyrażenia microsoft Macro Assembler (MASM). Każde z tych wyrażeń jest zgodne z własnymi regułami składni dla danych wejściowych i wyjściowych.
Aby uzyskać więcej informacji na temat tego, kiedy jest używany każdy typ składni, zobacz Ocena wyrażeń i polecenie ? evaluate expression .
Analizator wyrażeń języka C++ obsługuje wszystkie formy składni wyrażeń języka C++. Składnia zawiera wszystkie typy danych, w tym wskaźniki, liczby zmiennoprzecinkowe i tablice oraz wszystkie operatory jednoargumentowe i binarne języka C++.
Okna Watch i Locals w debugerze zawsze używają ewaluatora wyrażeń języka C++.
W poniższym przykładzie polecenie ?? evaluate C++ expression wyświetla wartość rejestru wskaźnika instrukcji.
0:000> ?? @eip
unsigned int 0x771e1a02
Możemy użyć funkcji języka C++ sizeof , aby określić rozmiar struktur.
0:000> ?? (sizeof(_TEB))
unsigned int 0x1000
Ustawianie ewaluatora wyrażeń na C++
Użyj ewaluatora wyrażeń .expr choose expression, aby zobaczyć aktualnie używanego ewaluatora wyrażeń i zmienić go na C++.
0:000> .expr
Current expression evaluator: MASM - Microsoft Assembler expressions
0:000> .expr /s c++
Current expression evaluator: C++ - C++ source expressions
Po zmianie domyślnego ewaluatora wyrażeń można użyć polecenia ? evaluate expression do wyświetlania wyrażeń języka C++. W poniższym przykładzie wyświetlana jest wartość rejestru wskaźnika instrukcji.
0:000> ? @eip
Evaluate expression: 1998461442 = 771e1a02
Aby dowiedzieć się więcej na temat @eip odniesienia do rejestru, zobacz Składnia rejestru.
W tym przykładzie wartość szesnastkowa 0xD jest dodawana do rejestru eip.
0:000> ? @eip + 0xD
Evaluate expression: 1998461455 = 771e1a0f
Rejestry i pseudorejestracje w wyrażeniach języka C++
Rejestry i pseudorejestracje można używać w wyrażeniach języka C++. Znak @ należy dodać przed rejestracją lub pseudorejestrem.
Ewaluator wyrażeń automatycznie wykonuje odpowiednie rzutowanie. Rzeczywiste rejestry i pseudorejestry liczb całkowitych są konwertowane na ULONG64. Wszystkie adresy są rzutowane na PUCHAR, $thread jest rzutowane na ETHREAD*, $proc jest rzutowane na EPROCESS*, $teb jest rzutowane na TEB*, i $peb jest rzutowane na PEB*.
W tym przykładzie zostanie wyświetlona aplikacja TEB.
0:000> ?? @$teb
struct _TEB * 0x004ec000
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : (null)
+0x02c ThreadLocalStoragePointer : 0x004ec02c Void
+0x030 ProcessEnvironmentBlock : 0x004e9000 _PEB
+0x034 LastErrorValue : 0xbb
+0x038 CountOfOwnedCriticalSections : 0
Nie można zmienić rejestru ani pseudorejestrowania za pomocą operatora przypisania lub efektu ubocznego. Aby zmienić te wartości, należy użyć polecenia r registers.
Poniższy przykład ustawia pseudorejestrę na wartość 5, a następnie wyświetla ją.
0:000> r $t0 = 5
0:000> ?? @$t0
unsigned int64 5
Aby uzyskać więcej informacji na temat rejestrów i pseudorejestrów, zobacz Składnia rejestrów i Składnia pseudorejestrów.
Liczby w wyrażeniach języka C++
Liczby w wyrażeniach języka C++ są interpretowane jako liczby dziesiętne, chyba że zostaną określone w inny sposób. Aby określić liczbę szesnastkową, dodaj 0x przed liczbą. Aby określić liczbę całkowitą ósemkową, dodaj 0 (zero) przed liczbą.
Domyślny radix debugera nie ma wpływu na sposób wprowadzania wyrażeń języka C++. Nie można bezpośrednio wprowadzić liczby binarnej, z wyjątkiem zagnieżdżania wyrażenia MASM w wyrażeniu języka C++.
Możesz wprowadzić wartość szesnastkową 64-bitową w formacie xxxxxxxx`xxxxxxxx. Można również pominąć znak akcentu (`). Oba formaty generują tę samą wartość.
Można użyć sufiksów L, Ui I64 z wartościami całkowitymi. Rzeczywisty rozmiar utworzonej liczby zależy od sufiksu i wprowadzonej liczby. Aby uzyskać więcej informacji na temat tej interpretacji, zobacz dokumentację języka C++.
Dane wyjściowe ewaluatora wyrażeń języka C++ utrzymują typ danych określony przez reguły wyrażeń języka C++. Jeśli jednak używasz tego wyrażenia jako argumentu dla polecenia, zawsze jest wykonywane rzutowanie. Na przykład nie trzeba rzutować wartości całkowitych na wskaźniki, gdy są używane jako adresy w argumentach poleceń. Jeśli wartość wyrażenia nie może być prawidłowa rzutowana na liczbę całkowitą lub wskaźnik, wystąpi błąd składniowy.
Możesz użyć prefiksu 0n (dziesiętnego) dla niektórych danych wyjściowych, ale nie można go użyć dla danych wejściowych wyrażeń języka C++.
Znaki i ciągi w wyrażeniach języka C++
Znak można wprowadzić, otaczając go pojedynczym cudzysłowem ('). Dozwolone są standardowe znaki ucieczki języka C++.
Literały ciągu można wprowadzać, otaczając je podwójnymi cudzysłowami ("). Można użyć \" jako sekwencji ucieczki w ramach takiego ciągu. Jednak ciągi nie mają znaczenia dla ewaluatora wyrażeń.
Symbole w wyrażeniach języka C++
W wyrażeniu języka C++ każdy symbol jest interpretowany zgodnie z typem. W zależności od tego, co odnosi się do symbolu, może być interpretowany jako liczba całkowita, struktura danych, wskaźnik funkcji lub dowolny inny typ danych. Błąd składni występuje, jeśli używasz symbolu, który nie odpowiada typowi danych języka C++, takim jak niezmodyfikowana nazwa modułu, w wyrażeniu języka C++.
Możesz użyć akcentu grobowego (') lub apostrofu (') w nazwie symbolu tylko wtedy, gdy dodasz nazwę modułu i wykrzyknik przed nazwą symbolu. Po dodaniu < i > znaczników po nazwie szablonu, można dodać spacje między tymi znacznikami.
Jeśli symbol może być niejednoznaczny, możesz dodać nazwę modułu i wykrzyknik (!) lub tylko wykrzyknik przed symbolem. Aby określić, że symbol ma być lokalny, pomiń nazwę modułu i dołącz znak dolara i wykrzyknik ($!) przed nazwą symbolu. Aby uzyskać więcej informacji na temat rozpoznawania symboli, zobacz Składnia symboli i dopasowywanie symboli.
Struktury w wyrażeniach języka C++
Ewaluator wyrażeń w języku C++ rzutuje pseudorejestry na ich odpowiednie typy. Na przykład $teb jest rzutowany jako TEB*.
0:000> ?? @$teb
struct _TEB * 0x004ec000
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : (null)
+0x02c ThreadLocalStoragePointer : 0x004ec02c Void
+0x030 ProcessEnvironmentBlock : 0x004e9000 _PEB
+0x034 LastErrorValue : 0xbb
+0x038 CountOfOwnedCriticalSections : 0
W poniższym przykładzie przedstawiono identyfikator procesu w strukturze TEB, pokazując użycie wskaźnika do elementu tej struktury.
0:000> ?? @$teb->ClientId.UniqueProcess
void * 0x0000059c
Operatory w wyrażeniach języka C++
Za pomocą nawiasów można zastąpić reguły pierwszeństwa.
Jeśli ujęta jest część wyrażenia C++ w nawiasy i dodasz dwa znaki @@ przed wyrażeniem, to wyrażenie będzie interpretowane zgodnie z regułami wyrażeń MASM. Nie można dodać odstępu między dwoma znakami i nawiasem otwierającym. Końcowa wartość tego wyrażenia jest przekazywana do ewaluatora wyrażeń języka C++ jako wartości ULONG64. Możesz również określić ewaluatora wyrażeń przy użyciu polecenia @@c++( ... ) lub @@masm( ... ).
Typy danych są wskazywane jak zwykle w języku C++. Symbole wskazujące tablice ([ ]), składowe wskaźnika (->), składowe UDT (.) i elementy członkowskie klas (::) są rozpoznawane. Obsługiwane są wszystkie operatory arytmetyczne, w tym operatory przypisania i efektów ubocznych. Nie można jednak używać newoperatorów , deletei throw i nie można wywołać funkcji.
Arytmetyka wskaźników jest obsługiwana, a przesunięcia są prawidłowo skalowane. Pamiętaj, że nie można dodać przesunięcia do wskaźnika funkcji. Jeśli musisz dodać przesunięcie do wskaźnika funkcji, najpierw rzutuj przesunięcie na wskaźnik znaku.
Podobnie jak w języku C++, jeśli używasz operatorów z nieprawidłowymi typami danych, wystąpi błąd składniowy. Analizator wyrażeń języka C++ debugera używa nieco bardziej zrelaksowanych reguł niż większość kompilatorów języka C++, ale wymuszane są wszystkie główne reguły. Na przykład nie można przesunąć wartości niecałkowitej.
Możesz użyć następujących operatorów. Operatory w każdej komórce mają pierwszeństwo przed operatorami w niższych komórkach. Operatory w tej samej komórce mają taki sam priorytet i są analizowane od lewej do prawej.
Podobnie jak w przypadku języka C++, ocena wyrażeń kończy się, gdy jest znana jego wartość. To zakończenie umożliwia efektywne używanie wyrażeń, takich jak ?? myPtr && *myPtr.
Rzutowanie odwołań i typów
| Obsługujący | Znaczenie |
|---|---|
| Wyrażenie // Komentarz | Ignoruj cały kolejny tekst |
| Klasa :: składowa | Składowa klasy |
| Klasa ::~Członek | Składowa klasy (destruktor) |
| :: Nazwa | Światowy |
| Struktura . Pole | Pole w strukturze |
| Wskaźnik ->pole | Pole w strukturze odniesienia |
| Nazwa [liczba całkowita] | Indeks tablicy |
| Lvalue ++ | Przyrost (po ocenie) |
| Lvalue -- | Dekrementacja (po ocenie) |
| < dynamic_casttyp>(Wartość) | Rzutowanie typu (zawsze wykonywane) |
| static_cast<typ>(Wartość) | Rzutowanie typu (zawsze wykonywane) |
| reinterpret_cast<typ>(Wartość) | Rzutowanie typu (zawsze wykonywane) |
| < const_casttyp>(Wartość) | Rzutowanie typu (zawsze wykonywane) |
Operacje na wartościach
| Obsługujący | Znaczenie |
|---|---|
| (typ) Wartość | Rzutowanie (zawsze wykonywane) |
| sizeofwartość | Rozmiar wyrażenia |
| sizeof( typ ) | Rozmiar typu danych |
| ++ Lvalue | Przyrost (przed oceną) |
| -- Lvalue | Dekrementacja (przed oceną) |
| wartości ~ | Komplement bitowy |
| ! Wartość | Nie (wartość logiczna) |
| Wartość | Jednoargumentowy minus |
| wartości + | Jednoargumentowy plus |
| & LValue | Adres typu danych |
| Wartość | Dereferencja |
| Struktura . wskaźnika | Wskaźnik do elementu członkowskiego struktury |
| Wskaźnik —> * Wskaźnik | Wskaźnik do elementu członkowskiego, którego odwołuje się struktura |
Arytmetyka
| Obsługujący | Znaczenie |
|---|---|
| Wartość | Mnożenie |
| Wartość / Wartość | Dzielenie |
| Wartość % Wartość | Modulo |
| Wartość + Wartość | Dodatek |
| Wartość - Wartość | Odejmowanie |
| Wartość<<Wartość | Przesunięcie bitowe w lewo |
| Wartość>>Wartość | Przesunięcie bitowe w prawo |
| Wartość<Wartość | Mniejsze niż (porównanie) |
| Wartość<= Wartość | Mniejsze niż lub równe (porównanie) |
| Wartość>Wartość | Większe niż (porównanie) |
| Wartość>= Wartość | Większe niż lub równe (porównanie) |
| Wartość == Wartość | Równe (porównanie) |
| Wartość != wartość | Nie równa się (porównanie) |
| Bitowe I | |
| Wartość ^ Wartość | Bitowy XOR (wyłączny OR) |
| Wartość | Wartość | Bitowe OR |
| Wartość && Wartość | Logiczne AND |
| Wartość || Wartość | Alternatywa logiczna |
W poniższych przykładach przyjęto założenie, że pseudorejestracje są ustawione w sposób pokazany.
0:000> r $t0 = 0
0:000> r $t1 = 1
0:000> r $t2 = 2
0:000> ?? @$t1 + @$t2
unsigned int64 3
0:000> ?? @$t2/@$t1
unsigned int64 2
0:000> ?? @$t2|@$t1
unsigned int64 3
Zadanie
| Obsługujący | Znaczenie |
|---|---|
| Lvalue = Wartość | Przypisać |
| Lvalue *= Wartość | Mnożenie i przypisywanie |
| Lvalue /= Wartość | Dzielenie i przypisywanie |
| Lvalue %= Wartość | Modulo i przypisanie |
| Lvalue += Wartość | Dodawanie i przypisywanie |
| Lvalue -= Wartość | Odejmij i przypisz |
| Lvalue<<= Wartość | Przesuń w lewo i przypisz |
| Lvalue>>= Wartość | Przesunięcie w prawo i przypisanie |
| LValue &= Value | AND i przypisz |
| Lvalue |= Wartość | LUB i przypisz |
| Lvalue ^= Wartość | XOR i przypisz |
Ocena
| Obsługujący | Znaczenie |
|---|---|
| Wartość ? Wartość : Wartość | Ocena warunkowa |
| Wartość , Wartość | Oceń wszystkie wartości, a następnie odrzuć wszystkie z wyjątkiem najbardziej odpowiedniej wartości |
Makra w wyrażeniach języka C++
Makra można używać w wyrażeniach języka C++. Przed makrami należy dodać znak numeru (#).
Można użyć następujących makr. Te makra mają takie same definicje jak makra systemu Microsoft Windows o tej samej nazwie. Makra systemu Windows są zdefiniowane w pliku Winnt.h.
| Makro | Wartość zwracana |
|---|---|
| #CONTAINING_RECORD(Adres, Typ, Pole) | Zwraca adres podstawowy wystąpienia struktury, biorąc pod uwagę typ struktury i adres pola w strukturze. |
| #FIELD_OFFSET(Typ, Pole) | Zwraca przesunięcie bajtów nazwanego pola w znanym typie struktury. |
| #RTL_CONTAINS_FIELD(Struktura, Rozmiar, Pole) | Wskazuje, czy dany rozmiar bajtu zawiera żądane pole. |
| #RTL_FIELD_SIZE(Typ, Pole) | Zwraca rozmiar pola w strukturze znanego typu bez wymagania typu pola. |
| #RTL_NUMBER_OF(Tablica) | Zwraca liczbę elementów w tablicy o statycznym rozmiarze. |
| #RTL_SIZEOF_THROUGH_FIELD(Typ, Pole) | Zwraca rozmiar struktury znanego typu, w górę i włącznie z określonym polem. |
W tym przykładzie pokazano użycie makra #FIELD_OFFSET do obliczenia przesunięcia bajtu do pola w strukturze.
0:000> ?? #FIELD_OFFSET(_PEB, BeingDebugged)
long 0n2
Zobacz także
Wyrażenia MASM a wyrażenia języka C++
?? evaluate C++ expression (ocena wyrażenia języka C++)