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
使用 by 或 wo 一元运算符从目标地址返回字节或单词。
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 (FnAddress、 RetVal、 Flag) |
将 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 映像标头中查找图像入口点来找到该条目。 |
$scmp (“String1”、“String2”) |
计算结果为 -1、0 或 1,类似于 使用 strcmp C 函数的 strcmp 。 |
$sicmp (“String1”、“String2”) |
计算结果为 -1、0 或 1,类似于 stricmp Microsoft Win32 函数 。 |
$spat (“String”, “Pattern”) |
计算结果为 TRUE 或 FALSE ,具体取决于 字符串 是否与 模式匹配。 匹配不区分大小写。 模式 可以包含各种通配符和说明符。 有关语法的详细信息,请参阅 字符串通配符语法。 |
$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 表达式中使用源文件和行号表达式。 必须使用严重重音符 (') 来包围这些表达式。 有关语法的详细信息,请参阅 源行语法。
另请参阅
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈