Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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.