共用方式為


MASM 數字和運算子

本主題說明如何搭配 Windows 偵錯工具使用 Microsoft 巨集組合器 (MASM) 運算式語法。

偵錯工具接受兩種不同類型的數值運算式:C++ 運算式和 MASM 運算式。 這些運算式中的每一個都遵循自己的輸入和輸出語法規則。

如需何時使用每個語法類型的詳細資訊,請參閱評估運算式?(評估運算式)。

在此範例中,? 命令會使用 MASM 運算式評估器來顯示指令指標暫存器的值。

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

將「運算表達式評估器」設定為 MASM

使用 .expr (選擇運算式評估器) 來檢視預設運算式評估器是什麼,並將其變更為 MASM。

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

現在預設運算式評估器已變更,? (評估運算式) 命令可用來顯示MASM運算式。 此示例將十六進位值8加到指令指標暫存器中。

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

暫存器語法中更詳細地描述了@rip的暫存器參考。

偵錯程式 MASM 運算式中的數字

您可以將數字放入以 16、10、8 或 2 為基數的 MASM 表達式中。

使用 n (設定數基數) 指令將預設基數設定為 16、10 或 8。 然後,所有無前綴的數字都會在此基數中解譯。 您可以透過指定 0x 前綴 (十六進位)、 0n 前綴 (十進位)、 0t 前綴 (八進位) 或 0y 前綴 (二進位) 來覆寫預設基數。

您也可以在數字後面加上 h 來指定十六進位數。 您可以在數字中使用大寫或小寫字母。 例如,“0x4AB3”、“0X4aB3”、“4AB3h”、“4ab3h”和“4aB3H”具有相同的含義。

如果您未在運算式的字首後面新增數字,則該數字會讀取為 0。 因此,您可以將 0 寫成 0,或前綴後面跟著 0,或只寫前綴。 例如,在十六進位中,「0」、「0x0」和「0x」具有相同的含義。

您可以以 xxxxxxxx'xxxxxxxx 格式輸入十六進位 64 位元值。 您也可以省略重音符號 (')。 如果您包含反引號重音符號,則會自動停用符號擴展

此範例示範如何將十進位、八進位和二進位值新增至暫存器 10。

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

偵錯工具 MASM 運算式中的符號

在 MASM 運算式中,任何符號的數值都是其記憶體位址。 根據符號所參考的內容,此位址是全域變數、局部變數、函式、區段、模組或任何其他已辨識卷標的位址。

若要指定位址與哪個模組相關聯,請在符號名稱之前包含模組名稱和驚嘆號 (!)。 如果符號可以解譯為十六進位數,請在符號名稱之前包含模組名稱和驚嘆號,或只是感嘆號。 如需符號辨識的詳細資訊,請參閱符號 語法和符號比對

使用兩個冒號 (::) 或兩個底線 (__) 來指出類別的成員。

只有在符號名稱之前新增模組名稱和驚嘆號時,才可在符號名稱中使用重音符號 (') 或撇號 (')。

MASM 運算式中的數值運算子

您可以使用一元運算子修改運算式的任何元件。 您可以使用二進位運算子來組合任意兩個元件。 一元運算子優先於二進位運算子。 當您使用多個二進位運算子時,運算子會遵循下表所述的固定優先順序規則。

您一律可以使用括弧來置換優先順序規則。

如果 MASM 運算式的一部分括在括弧中,且運算式前面出現兩個 at 符號 (@@) ,則會根據 C++ 運算式規則解譯運算式。 您無法在兩個 at 符號和左括號之間新增空格。 您也可以使用 @@c++( ... )@@masm ( ... ) 來指定運算式評估器

當您執行算術計算時,MASM 運算式評估器會將所有數字和符號視為ULONG64類型。

一元位址運算子會假設 DS 是位址的預設區段。 運算式會依運算子優先順序進行評估。 如果相鄰運算子具有相等的優先順序,則會從左到右評估運算式。

您可以使用下列一元運算子。

操作員 意義

+

一元加號

-

一元減號

不是

如果引數為零,則傳回 1。 針對任何非零引數傳回零。

高 16 位元

低 16 位元

來自指定位址的低階位元組。

$pby

by 相同,只是它需要一個物理地址。 只能讀取使用預設快取行為的實體記憶體。

wo

來自指定位址的低階字。

$pwo

wo 相同,只是它需要一個實際地址。 只能讀取使用預設快取行為的實體記憶體。

DWO

來自指定地址的雙字。

$pdwo

dwo 相同,只是它需要一個實體地址。 只能讀取使用預設快取行為的實體記憶體。

qwo

來自指定地址的四字。

$pqwo

qwo 相同,只是它需要一個物理地址。 只能讀取使用預設快取行為的實體記憶體。

POI

來自指定位址的指標大小資料。 指標大小為 32 位或 64 位。 在核心偵錯中,此大小是根據 目標 電腦的處理器而定。 因此,如果您想要指標大小的資料, poi 是最佳運算子。

$ppoi

poi 相同,只是它需要一個物理地址。 只能讀取使用預設快取行為的實體記憶體。

範例

下列範例示範如何使用 poi 來解引用指標,以查看儲存在該記憶體位置的值。

首先判斷感興趣的記憶體位址。 例如,我們可以查看線程結構,並決定我們想要查看 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 位於從 TEB 開頭計算後的 0x108 位址。

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

使用 poi 來取值該位址。

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

傳回的 409 值符合 TEB 結構中 CurrentLocale 的值。

或使用 poi 和括弧來取值計算的位址。

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

使用 bywo 一元運算子,從目標地址傳回位元組或單字。

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

二元運算子

您可以使用下列二進位運算子。 每個儲存格中的運算元優先於較低儲存格中的運算元。 相同儲存格中的運算子的優先順序相同,且會從左至右剖析。

操作員 意義

*

/

mod (或 %)

乘法

整數除數

模數(餘數)

+

-

加法

減法

<<

>>

>>>

左移

邏輯右移

算術右移

= (或 ==)

<

>

<=

>=

!=

等於

小於

大於

小於或等於

大於或等於

不等於

(或&)

位 AND

xor(或 ^)

位元異或(XOR, exclusive OR)

(或 |)

位 OR

如果表達式為 true,則 <、>、=、== 和 != 比較運算符會求值為 1;如果表達式為 false,則為 0。 單一等號 (=) 與雙等號 (==) 相同。 您無法在 MASM 表達式中使用副作用或進行指派。

無效的作業(例如除以零)會導致「作數錯誤」傳回 調試程式命令視窗

我們可以使用 == 比較運算符來檢查傳回的值是否符合0x409。

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

MASM 運算式中的非數值運算元

您也可以在MASM運算式中使用下列其他運算符。

操作員 意義

$fnsucc(FnAddressRetValFlag

RetVal 值解譯為位於 FnAddress 位址之函式的傳回值。 如果此傳回值限定為成功碼, $fnsucc 會傳回 TRUE。 否則, $fnsucc 會傳回 FALSE

如果傳回類型為 BOOL、bool、HANDLE、HRESULT 或 NTSTATUS, $fnsucc 正確瞭解指定的傳回值是否限定為成功碼。 如果傳回類型是指針, NULL 以外的所有值都會限定為成功碼。 對於任何其他類型,成功是由 Flag 的值所定義。 如果 Flag 為 0,RetVal 的非零值表示成功。 如果 Flag 為 1,則 RetVal 的零值表示成功。

$iment (位址

傳回已載入模組清單中影像進入點的位址。 位址 指定可攜式可執行檔 (PE) 映像基位址。 在 Address 所指定的映像中,透過查閱該映像的 PE 映像標頭來找到映像入口點。

您可以將此函式用於模組清單中的這兩個模組,以及使用bu命令來設定未解析的斷點

$scmp(“String1”, “String2”)

使用 strcmp C 函式時,會像使用 strcmp 一樣,評估結果為 -1、0 或 1。

$sicmp(“String1”, “String2”)

評估為 -1、0 或 1,例如 stricmp Microsoft Win32 函式 。

$spat(“String”, “Pattern”)

根據字串是否符合模式,評估為 TRUEFALSE。 比對不區分大小寫。 模式 可以包含各種通配符和規範。 如需語法的詳細資訊,請參閱 字串通配符語法

$vvalid(位址長度

判斷從 Address 開始、延伸 Length 位元組的記憶體範圍是否有效。 如果記憶體有效, $vvalid 評估為1。 如果記憶體無效, $vvalid 評估為 0。

範例

下列顯示如何調查載入模組周圍的有效記憶體範圍

首先,使用 lm (List Loaded Modules 命令)來判斷感興趣的區域位址。


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

使用$vvalid檢查記憶體範圍。

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

使用$vvalid確認這個較大的範圍是無效的記憶體範圍。

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

這也是一個無效的範圍。

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

當記憶體範圍有效時,使用 來傳回零。

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

使用 $imnet 查看 COMCTL32 的進入點,這是我們之前使用 lm 命令確定的地址。 它從 00007ff9'591d0000 開始。

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

反組譯傳回的位址以檢查進入點代碼。

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,確認這是此模組的進入點。

MASM 運算式中的暫存器和 Pseudo-Registers

您可以在 MASM 運算式內使用暫存器和虛擬暫存器。 您可以在所有暫存器和虛擬暫存器之前新增 at 符號 (@)。 at 符號會導致偵錯工具更快速地存取值。 對於最常見的 x86 型暫存器來說,不需要此 @ 符號。 對於其他暫存器和虛擬暫存器,我們建議您新增 at 符號,但實際上不是必需的。 如果您省略在較不常見的暫存器中使用的 at 符號,偵錯工具會嘗試先將文字解析為十六進位數字,其次解析為符號,最後解析為暫存器。

您也可以使用句點 (.) 來指出現行指令指標。 您不應該在此句點之前新增 @ 符號,且您不能使用句點作為 r 指令的第一個參數。 這個區段與 $ip 偽寄存器具有相同的含義。

如需暫存器和虛擬暫存器的詳細資訊,請參閱 暫存器語法Pseudo-Register 語法

使用 r register 指令來查看暫存器的值 @rip 是否為 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

可以使用 來顯示相同的值。 句點快捷鍵。

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

我們可以確認這些值都是等價的,如果是,則傳回零,使用這個 MASM 運算式。

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

MASM 表達式中的源行號

您可以在 MASM 運算式中使用來源檔案和行號運算式。 您必須使用重音符號 (') 來括住這些運算式。 如需語法的詳細資訊,請參閱 原始碼行語法

另請參閱

MASM 運算式與C++表達式

混合表達式範例

C++數位和運算符

符號擴展

? (評估運算式)