MASM 数字和运算符

本主题介绍 Microsoft 宏汇编程序 (MASM) 表达式语法与 Windows 调试工具的用法。

调试器接受两种不同类型的数值表达式: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 表达式中的数字

可以在 MASM 表达式中以 16、10、8 或 2 为底数的数字。

使用 n (Set Number Base) 命令将默认基数设置为 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 作为地址的默认段。 表达式按运算符优先顺序计算。 如果相邻运算符的优先级相等,则从左到右计算表达式。

可以使用以下一元运算符。

运算符 含义

+

一元加

-

一元负

not

如果参数为零,则返回 1。 对于任何非零参数,返回零。

你好

高 16 位

low

低 16 位

by

来自指定地址的低序字节。

$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 (或 %)

乘法

整数除法

余数 ()

+

-

加法

减法

<<

>>

>>>

左移

逻辑右移

算术右移

= (或 ==)

<

>

<=

>=

!=

等于

小于

大于

小于或等于

大于等于

不等于

(或 &)

位与

xor (或 ^)

按位异或 (异或)

(或 |)

按位“或”

如果表达式为 true,则 <、 >、=、==和 != 比较运算符的计算结果为 1;如果表达式为 false,则计算结果为零。 单等号 (=) 与 ==) (双等号相同。 不能在 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 (地址)

返回已加载模块列表中图像入口点的地址。 Address 指定可移植可执行 (PE) 映像基址。 通过在 Address 指定的映像的 PE 映像标头中查找图像入口点来找到该条目。

可以将此函数用于模块列表中已有的两个模块,以及通过使用 bu 命令设置未解析的断点

$scmp (“String1”、“String2”)

计算结果为 -1、0 或 1,类似于 使用 strcmp C 函数的 strcmp

$sicmp (“String1”、“String2”)

计算结果为 -1、0 或 1,类似于 stricmp Microsoft Win32 函数 。

$spat (“String”, “Pattern”)

计算结果为 TRUEFALSE ,具体取决于 字符串 是否与 模式匹配。 匹配不区分大小写。 模式 可以包含各种通配符和说明符。 有关语法的详细信息,请参阅 字符串通配符语法

$vvalid (地址长度)

确定从 Address 开始并扩展为 长度 字节的内存范围是否有效。 如果内存有效, $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 查看之前使用 lm 命令确定地址的COMCTL32的入口点。 它从 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 sign (@) 。 at 符号会使调试器更快地访问值。 对于最常见的基于 x86 的寄存器,不需要此 @ 符号。 对于其他寄存器和伪寄存器,建议添加 at 符号,但实际上并不是必需的。 如果省略较不常见寄存器的 at 符号,调试器会尝试将文本分析为十六进制数,然后将它解析为符号,最后分析为寄存器。

还可以使用句点 (.) 来指示当前指令指针。 不应在此句点之前添加 @ 符号,并且不能将句点用作 r 命令的第一个参数。 此句点与 伪寄存器$ip 的含义相同。

有关寄存器和伪寄存器的详细信息,请参阅 寄存器语法伪寄存器语法

使用 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++ 数字和运算符

符号扩展

? (计算表达式)