Udostępnij za pomocą


Numery i operatory MASM

W tym temacie opisano użycie składni wyrażeń programu Microsoft Macro Assembler (MASM) za pomocą narzędzi debugowania systemu Windows.

Debuger akceptuje dwa różne rodzaje wyrażeń liczbowych: wyrażenia języka C++ i wyrażenia 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 ? (Oceń wyrażenie).

W tym przykładzie the ? Polecenie wyświetla wartość rejestru wskaźnika instrukcji przy użyciu ewaluatora wyrażeń MASM.

0:000> ? @rip
Evaluate expression: 140709230544752 = 00007ff9`6bb40770

Ustaw ewaluator wyrażeń na MASM

Użyj wyrażenia .expr (Wybierz ewaluator wyrażeń), aby zobaczyć, czym jest domyślny ewaluator wyrażeń, i zmień go na MASM.

0:000> .expr /s masm
Current expression evaluator: MASM - Microsoft Assembler expressions

Teraz, gdy domyślny ewaluator wyrażeń został zmieniony, ? (Oceń wyrażenie) Polecenia można użyć do wyświetlania wyrażeń MASM. W tym przykładzie do rejestru rip jest dodana wartość szesnastkowa 8.

0:000> ? @rip + 8
Evaluate expression: 140709230544760 = 00007ff9`6bb40778

Szczegółowy opis odwołania do rejestru @rip znajduje się w sekcji Składnia Rejestru.

Liczby w wyrażeniach debuggera MASM

Liczby można umieszczać w wyrażeniach MASM w systemach liczbowych o podstawie 16, 10, 8 lub 2.

Użyj n (Ustaw bazę numerów), aby ustawić domyślny radix na 16, 10 lub 8. Wszystkie nieprefiksowane liczby są następnie interpretowane w tej bazie. Można zastąpić domyślny radix, określając prefiks 0x (szesnastkowy), prefiks 0n (dziesiętny), prefiks 0t (ósemkowy) lub 0y prefiks (binarny).

Można również określić liczby szesnastkowe, dodając h po liczbie. W cyfrach można użyć wielkich lub małych liter. Na przykład "0x4AB3", "0X4aB3", "4AB3h", "4ab3h" i "4aB3H" mają takie samo znaczenie.

Jeśli nie dodasz liczby po prefiksie w wyrażeniu, liczba jest odczytywana jako 0. W związku z tym można zapisać wartość 0 jako 0, prefiks, po którym następuje wartość 0, i tylko prefiks. Na przykład w systemie szesnastkowym, "0", "0x0" i "0x" mają to samo znaczenie.

Możesz wprowadzić wartości szesnastkowe 64-bitowe w formacie xxxxxxxx'xxxxxxxx . Można również pominąć znak akcentu (`). Jeśli dołączysz akcent grobowy, automatyczne rozszerzenie znaku zostanie wyłączone.

W tym przykładzie pokazano, jak dodać wartość dziesiętną, ósemkową i binarną, aby zarejestrować 10.

? @r10 + 0x10 + 0t10 + 0y10
Evaluate expression: 26 = 00000000`0000001a

Symbole w wyrażeniach MASM debugera

W wyrażeniach MASM wartość liczbowa dowolnego symbolu to jego adres pamięci. W zależności od tego, co odnosi się do symbolu, ten adres jest adresem zmiennej globalnej, zmiennej lokalnej, funkcji, segmentu, modułu lub innej rozpoznanej etykiety.

Aby określić, z którym modułem jest skojarzony adres, przed nazwą symbolu dołącz nazwę modułu i wykrzyknik (!). Jeśli symbol może być interpretowany jako liczba szesnastkowa, dołącz nazwę modułu i wykrzyknik lub tylko wykrzyknik przed nazwą symbolu. Aby uzyskać więcej informacji na temat rozpoznawania symboli, zobacz Składnia symboli i Dopasowywanie symboli.

Użyj dwóch dwukropków (::) lub dwóch podkreśleń (__), aby wskazać składowe klasy.

Użyj akcentu grobowego (') lub apostrofu (') w nazwie symbolu tylko w przypadku dodania nazwy modułu i wykrzyknika przed symbolem.

Operatory liczbowe w wyrażeniach MASM

Dowolny składnik wyrażenia można zmodyfikować przy użyciu operatora jednoargumentowego. Można połączyć dowolne dwa składniki przy użyciu operatora binarnego. Operatory jednoargumentowe mają pierwszeństwo przed operatorami binarnymi. W przypadku korzystania z wielu operatorów binarnych operatory stosują się do ustalonych reguł pierwszeństwa opisanych w poniższych tabelach.

Zawsze można użyć nawiasów, aby zastąpić reguły pierwszeństwa.

Jeśli część wyrażenia MASM jest ujęta w nawiasy i dwa znaki (@@) pojawiają się przed wyrażeniem, wyrażenie jest interpretowane zgodnie z regułami wyrażeń języka C++. Nie można dodać spacji między dwoma znakami at i nawiasem otwierającym. Można również określić ewaluatora wyrażeń przy użyciu @@c++( ... ) lub @@masm( ... ).

Podczas wykonywania obliczeń arytmetycznych ewaluator wyrażeń MASM traktuje wszystkie liczby i symbole jako typy ULONG64.

Operatory adresów jednoargumentowych zakładają, że DS jest domyślnym segmentem adresów. Wyrażenia są oceniane w kolejności priorytetu operatora. Jeśli sąsiadujące operatory mają równe pierwszeństwo, wyrażenie jest obliczane od lewej do prawej.

Możesz użyć następujących operatorów jednoargumentowych.

Obsługujący Znaczenie

+

Jednoargumentowy plus

-

Jednoargumentowy minus

nie

Zwraca wartość 1, jeśli argument ma wartość zero. Zwraca zero dla dowolnego argumentu niezerowego.

Cześć

Wyższe 16 bitów

niski

Małe 16 bitów

przez

Bajt o niskiej kolejności z określonego adresu.

$pby

Tak jak by, z tą różnicą, że pobiera adres fizyczny. Można odczytać tylko pamięć fizyczną, która używa domyślnego zachowania buforowania.

wo

Wyraz o niskiej kolejności z określonego adresu.

$pwo

Tak samo jak wo , z tą różnicą, że przyjmuje adres fizyczny. Można odczytać tylko pamięć fizyczną, która używa domyślnego zachowania buforowania.

dwo

Podwójne słowo z określonego adresu.

$pdwo

Tak samo jak dwo , z tą różnicą, że przyjmuje adres fizyczny. Można odczytać tylko pamięć fizyczną, która używa domyślnego zachowania buforowania.

qwo

Czterosłowo z określonego adresu.

$pqwo

Tak samo jak qwo , z tą różnicą, że przyjmuje adres fizyczny. Można odczytać tylko pamięć fizyczną, która używa domyślnego zachowania buforowania.

poi

Dane o rozmiarze wskaźnika z określonego adresu. Rozmiar wskaźnika to 32 bity lub 64 bity. W debugowaniu jądra ten rozmiar jest oparty na procesorze komputera docelowego . W związku z tym poi jest najlepszym operatorem do użycia, jeśli chcesz użyć danych o rozmiarze wskaźnika.

$ppoi

Tak samo jak poi , z tą różnicą, że przyjmuje adres fizyczny. Można odczytać tylko pamięć fizyczną, która używa domyślnego zachowania buforowania.

Przykłady

W poniższym przykładzie pokazano, jak użyć poi do dereferencji wskaźnika, aby zobaczyć wartość przechowywaną pod tym adresem w pamięci.

Najpierw określ interesujący cię adres pamięci. Na przykład możemy przyjrzeć się strukturze wątków i zdecydować, że chcemy zobaczyć wartość CurrentLocale.

0:000> dx @$teb
@$teb                 : 0x8eed57b000 [Type: _TEB *]
    [+0x000] NtTib            [Type: _NT_TIB]
    [+0x038] EnvironmentPointer : 0x0 [Type: void *]
    [+0x040] ClientId         [Type: _CLIENT_ID]
    [+0x050] ActiveRpcHandle  : 0x0 [Type: void *]
    [+0x058] ThreadLocalStoragePointer : 0x1f8f9d634a0 [Type: void *]
    [+0x060] ProcessEnvironmentBlock : 0x8eed57a000 [Type: _PEB *]
    [+0x068] LastErrorValue   : 0x0 [Type: unsigned long]
    [+0x06c] CountOfOwnedCriticalSections : 0x0 [Type: unsigned long]
    [+0x070] CsrClientThread  : 0x0 [Type: void *]
    [+0x078] Win32ThreadInfo  : 0x0 [Type: void *]
    [+0x080] User32Reserved   [Type: unsigned long [26]]
    [+0x0e8] UserReserved     [Type: unsigned long [5]]
    [+0x100] WOW32Reserved    : 0x0 [Type: void *]
    [+0x108] CurrentLocale    : 0x409 [Type: unsigned long]

CurrentLocale znajduje się 0x108 poza rozpoczęciem TEB.

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

Użyj poi, aby wyłuskać ten adres.

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

Zwrócona wartość 409 jest zgodna z wartością CurrentLocale w strukturze TEB.

Możesz też użyć poi i nawiasów, aby wyłusić obliczony adres.

0:000> ? poi(@$teb + 0x108)
Evaluate expression: 1033 = 00000000`00000409

Użyj operatorów jednoargumentowych by lub wo, aby zwrócić bajt lub słowo z podanego adresu docelowego.

0:000> ? by(0000008e`ed57b108)
Evaluate expression: 9 = 00000000`00000009
0:000> ? wo(0000008e`ed57b108)
Evaluate expression: 1033 = 00000000`00000409

Operatory binarne

Możesz użyć następujących operatorów binarnych. Operatory w każdej komórce mają pierwszeństwo przed tymi w niższych komórkach. Operatory w tej samej komórce mają taki sam priorytet i są analizowane od lewej do prawej.

Obsługujący Znaczenie

*

/

mod (lub %)

Mnożenie

Dzielenie liczb całkowitych

Modulo (pozostała część)

+

-

Dodatek

Odejmowanie

<<

>>

>>>

Przesunięcie w lewo

Przesunięcie w prawo logiczne

Arytmetyczne przesunięcie w prawo

= (lub ==)

<

>

<=

>=

!=

Równa się

Mniejsze niż

Większe niż

Mniej lub równe

Większe niż lub równe

Nie równa się

i (lub &)

Bitowe I

xor (lub ^)

Bitowy XOR (wyłączny OR)

lub (lub |)

Bitowe OR

<Operatory porównania , >, =, ==i != są obliczane na 1, jeśli wyrażenie ma wartość true lub zero, jeśli wyrażenie ma wartość false. Znak równości jednokrotnej (=) jest taki sam jak podwójny znak równości (==). Nie można używać efektów ubocznych ani przypisań w wyrażeniu MASM.

Nieprawidłowa operacja (na przykład dzielenie według zera) powoduje zwrócenie komunikatu "Błąd operandu" do okna polecenia debugera.

Możemy sprawdzić, czy zwrócona wartość jest zgodna 0x409 przy użyciu operatora porównania ==.

0:000> ? poi(@$teb + 0x108)==0x409
Evaluate expression: 1 = 00000000`00000001

Operatory nieliczbowe w wyrażeniach MASM

Możesz również użyć następujących dodatkowych operatorów w wyrażeniach MASM.

Obsługujący Znaczenie

$fnsucc(FnAddress, RetVal, Flag)

Interpretuje wartość RetVal jako wartość zwracaną dla funkcji, która znajduje się pod adresem FnAddress . Jeśli ta wartość zwracana kwalifikuje się jako kod powodzenia, $fnsucc zwraca wartość TRUE. W przeciwnym razie $fnsucc zwraca wartość FALSE.

Jeśli zwracany typ to BOOL, bool, HANDLE, HRESULT lub NTSTATUS, $fnsucc poprawnie rozumie, czy określona wartość zwracana kwalifikuje się jako kod sukcesu. Jeśli zwracany typ jest wskaźnikiem, wszystkie wartości inne niż NULL kwalifikują się jako kody sukcesu. W przypadku dowolnego innego typu powodzenie jest definiowane przez wartość Znacznik. Jeśli Flaga ma wartość 0, wówczas wartość niezerowa RetVal oznacza powodzenie. Jeśli Flag to 1, zerowa wartość RetVal wskazuje powodzenie.

$iment (adres)

Zwraca adres punktu wejścia obrazu na liście załadowanych modułów. Adres określa bazowy adres obrazu przenośnego pliku wykonywalnego (PE). Wpis można znaleźć poprzez wyszukanie punktu wejścia obrazu w nagłówku obrazu PE wskazywanym przez Address.

Tej funkcji można użyć dla obu modułów, które znajdują się już na liście modułów, i aby ustawić nierozwiązane punkty przerwania za pomocą polecenia bu .

$scmp("String1", "String2")

Daje wartość -1, 0 lub 1, podobnie jak strcmp za pomocą funkcji strcmp w języku C.

$sicmp("String1", "String2")

Daje wartość -1, 0 lub 1, na przykład funkcja stricmp Microsoft Win32.

$spat("String", "Pattern")

Ocenia jako TRUE lub FALSE w zależności od tego, czy ciąg pasuje do wzorca. Dopasowanie jest bez uwzględniania wielkości liter. Wzorzec może zawierać różne symbole wieloznaczne i specyfikatory. Aby uzyskać więcej informacji na temat składni, zobacz sekcję Składnia symboli wieloznacznych ciągów.

$vvalid(Adres,Długość)

Określa, czy zakres pamięci rozpoczynający się od Adresu i rozciągający się na Długości bajtów jest prawidłowy. Jeśli pamięć jest prawidłowa, $vvalid daje wartość 1. Jeśli pamięć jest nieprawidłowa, $vvalid daje wartość 0.

Przykłady

Poniżej pokazano, jak badać zakres prawidłowej pamięci wokół załadowanego modułu

Najpierw określ adres obszaru zainteresowania, na przykład za pomocą polecenia lm (Lista załadowanych modułów ).


0:000> lm
start             end                 module name
00007ff6`0f620000 00007ff6`0f658000   notepad    (deferred)
00007ff9`591d0000 00007ff9`5946a000   COMCTL32   (deferred)        
...

Użyj $vvalid, aby sprawdzić zakres pamięci.

0:000> ? $vvalid(0x00007ff60f620000, 0xFFFF)
Evaluate expression: 1 = 00000000`00000001

Użyj $vvalid, aby potwierdzić, że ten większy zakres jest nieprawidłowym zakresem pamięci.

0:000> ? $vvalid(0x00007ff60f620000, 0xFFFFF)
Evaluate expression: 0 = 00000000`00000000

Jest to również nieprawidłowy zakres.

0:000> ? $vvalid(0x0, 0xF)
Evaluate expression: 0 = 00000000`00000000

Nie należy zwracać zera, gdy zakres pamięci jest prawidłowy.

0:000> ? not($vvalid(0x00007ff60f620000, 0xFFFF))
Evaluate expression: 0 = 00000000`00000000

Użyj $imnet, aby przyjrzeć się punktowi wejścia COMCTL32, dla którego wcześniej użyliśmy polecenia lm do określenia adresu. Zaczyna się od 00007ff9'591d0000.

0:000> ? $iment(00007ff9`591d0000)
Evaluate expression: 140708919287424 = 00007ff9`59269e80

Zdezasembuj zwrócony adres, aby zbadać kod punktu wejścia.

0:000> u 00007ff9`59269e80
COMCTL32!DllMainCRTStartup:
00007ff9`59269e80 48895c2408      mov     qword ptr [rsp+8],rbx
00007ff9`59269e85 4889742410      mov     qword ptr [rsp+10h],rsi
00007ff9`59269e8a 57              push    rdi

COMCTL32 jest wyświetlany w danych wyjściowych, potwierdzając, że jest to punkt wejścia dla tego modułu.

Rejestry i Pseudo-Registers w wyrażeniach MASM

Rejestry i pseudorejestracje można używać w wyrażeniach MASM. Możesz dodać znak (@) przed wszystkimi rejestrami i pseudorejestrami. Znak at powoduje, że debuger może szybciej uzyskać dostęp do wartości. Ten znak @ jest niepotrzebny dla najczęściej używanych rejestrów opartych na architekturze x86. W przypadku innych rejestrów i pseudorejestrów zaleca się dodanie znaku @, ale nie jest to tak naprawdę wymagane. Jeśli pominięto znak at dla mniej typowych rejestrów, debuger próbuje przeanalizować tekst jako liczbę szesnastkową, a następnie jako symbol, a na koniec jako rejestr.

Możesz również użyć kropki (.), aby wskazać aktualną pozycję wskaźnika instrukcji. Nie należy dodawać znaku @ przed tą kropką i nie można użyć kropki jako pierwszego parametru polecenia r. Ten okres ma takie samo znaczenie jak pseudo-rejestr $ip.

Aby uzyskać więcej informacji na temat rejestrów i pseudorejestrów, zobacz Składnia rejestru i Pseudo-Register Składnia.

Użyj polecenia r register, aby zobaczyć, że wartość @rip rejestru to 00007ffb'7ed00770.

0:000> r
rax=0000000000000000 rbx=0000000000000010 rcx=00007ffb7eccd2c4
rdx=0000000000000000 rsi=00007ffb7ed61a80 rdi=00000027eb6a7000
rip=00007ffb7ed00770 rsp=00000027eb87f320 rbp=0000000000000000
 r8=00000027eb87f318  r9=0000000000000000 r10=0000000000000000
r11=0000000000000246 r12=0000000000000040 r13=0000000000000000
r14=00007ffb7ed548f0 r15=00000210ea090000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!LdrpDoDebuggerBreak+0x30:
00007ffb`7ed00770 cc              int     3

Tę samą wartość można wyświetlić przy użyciu elementu . skrót klawiszowy do kropki.

0:000> ? .
Evaluate expression: 140718141081456 = 00007ffb`7ed00770

Możemy potwierdzić, że te wartości są równoważne, a jeśli tak, zwrócić zero, używając tego wyrażenia w MASM.

0:000>  ? NOT(($ip = .) AND ($ip = @rip) AND (@rip =. ))
Evaluate expression: 0 = 00000000`00000000

Numery wierszy źródłowych w wyrażeniach MASM

W wyrażeniach MASM można używać wyrażeń pliku źródłowego i numeru wiersza. Te wyrażenia należy ująć przy użyciu akcentów grobowych ('). Aby uzyskać więcej informacji na temat składni, zobacz Składnia wiersza źródłowego.

Zobacz także

Wyrażenia MASM a wyrażenia języka C++

Przykłady wyrażeń mieszanych

Liczby i operatory języka C++

Rozszerzenie podpisywania

? (Oceń wyrażenie)