调试器能够识别两种表达式类型:MASM表达式和C++表达式。
和MASM的区别
默认用MASM表达式
最大的区别:
在MSAM表达式中,所有符号都被当作地址对待,C++表达式和真实的C++代码中一样,符号被当作适当的数据类型
有这样的规律:
MASM的符号值X,取它的指针大小的数据(poi(X)),刚好为C++的符号值Y,即poi(X) ==Y
比如char* g_char = "i am windbg":
0:000> ? g_char
Evaluate expression: 15560768 = 00ed7040
0:000> .expr /s c++ // 切换到c++
Current expression evaluator: C++ - C++ source expressions
0:000> ? g_char
Evaluate expression: 15554364 = 00ed573c
0:000> .expr /s masm
Current expression evaluator: MASM - Microsoft Assembler expressions
0:000> dd 00ed7040 L1
00ed7040 00ed573c
可以发现,MASM求得的g_char值,它的指针大小的数据,刚好就是C++求得的值
除了下面几种例外情况,所有命令和调试器信息窗口都通过默认表达式来解释他们的参数:
- ?? (Evaluate C++ Expression)命令总是使用C++ 表达式形式。
- Watch 窗口始终使用C++表达式。
- Locals 窗口始终使用C++ 表达式。
- 有些扩展命令始终使用MASM表达式(而有一些扩展命令只允许使用数字参数)。
- 如果将表达式用圆括号括起来,并在表达式前面插入两个at符号(@@),将会使用这种情况下通常不使用的表达式形式。
也就是有:
0:000> ? poi(g_char)
Evaluate expression: 15554364 = 00ed573
详细语法
C++ 表达式中的符号
在C++表达式中,符号按照它们的类型来解析。根据符号意义的不同,可能是整数、数据结构、函数指针,或其他数据类型。如果符号和C++数据类型不符合(例如未更改的模块名),则会产生一个语法错误。
C++ 表达式解析器支持所有 C++ 表达式语法形式。包括所有数据类型(包括指针,浮点数,数组)以及 C++ 所有一元和二元运算符。
?? 默认是对C++表达式求值
C++ 表达式中的数值
除非另有说明,C++ 表达式中的数值被当作十进制来解析。添加 0x 前缀指定十六进制整数。添加 0(零)指定八进制整这点比较坑,一定要特别注意,因为大家习惯是用16进制的
0:000> ? 1
Evaluate expression: 1 = 00000001
0:000> ?? 1
int 1
0:000> ?? 1f
float 1
0:000> ? 1f
Evaluate expression: 31 = 0000001f
0:000> ?? 0x1f
int 31
可以看到c++表达式输出的结果都是10进制
调试器使用的缺省基数不影响 C++ 表达式的输入,也就是n 16对C++的输出没有任何影响
C++ 表达式中的字符和字符串
输入字符时用单引号(')括住。可以使用标准的 C++ 转义字符。
输入字符串时用双引号(")括住。可以在字符串中使用转义字符序列 \" 。然而,字符串对于表达式求值器无意义
0:000> ?? 'c'
char 99 'c'
0:000> ?? "c:\"
EOF in literal '"c:\"'
C++ 表达式中的运算符
可以用圆括号来掩盖优先级规则数据类型按C++语言的常规方法来指定。可以识别表示数组([ ]),指针成员(->),UDT(译注:用户定义类型,User defined types)成员(.),以及所有的类成员(::)。支持所有算术运算符,包括赋值运算符和带副作用的运算符。但是,不允许使用new、delete 和throw 运算符,当然,也不能调用函数。
支持指针运算,也可以正确计算偏移。注意不能给函数指针加上某个偏移值 (如果需要这样做,首先把它强制转换为指向字符的指针)。
Class :: Member Class ::~Member :: Name | 类成员 类成员(析构函数) 全局的 |
0:002> ?? VerifySIgnDemo!CVerifySIgnDemoDlg::OnBtnTest
<function> 0x00402c00
void VerifySIgnDemo!CVerifySIgnDemoDlg::OnBtnTest+0( void )
0:002> ?? g_NewWinVerifyTrust
<function> * 0x00000000
我们可以对比下用MASM规则来:
0:002> ? VerifySIgnDemo!CVerifySIgnDemoDlg::OnBtnTest
Evaluate expression: 4205568 = 00402c00
0:002> ? g_NewWinVerifyTrust
Evaluate expression: 4290536 = 004177e8
可以看到,都变成求值了
Structure . Field Pointer -> Field Name [integer] LValue ++ LValue -- dynamic_cast <type>(Value) static_cast <type>(Value) reinterpret_cast <type>(Value) const_cast <type>(Value) | 结构成员 被引用结构的成员 数组下标 加一(计算后) 减一(计算后) 类型强制转换(总会执行) 类型强制转换(总会执行) 类型强制转换(总会执行) 类型强制转换(总会执行) |
0:000> ?? WinTrustData
struct _WINTRUST_DATA
+0x000 cbStruct : 0x2c
+0x004 pPolicyCallbackData : (null)
+0x008 pSIPClientData : (null)
+0x00c dwUIChoice : 2
+0x010 fdwRevocationChecks : 0
+0x014 dwUnionChoice : 1
+0x018 pFile : 0x0012f464 WINTRUST_FILE_INFO_
+0x018 pCatalog : 0x0012f464 WINTRUST_CATALOG_INFO_
+0x018 pBlob : 0x0012f464 WINTRUST_BLOB_INFO_
+0x018 pSgnr : 0x0012f464 WINTRUST_SGNR_INFO_
+0x018 pCert : 0x0012f464 WINTRUST_CERT_INFO_
+0x01c dwStateAction : 0
+0x020 hWVTStateData : (null)
+0x024 pwszURLReference : (null)
+0x028 dwProvFlags : 0
0:000> ?? WinTrustData.pFile
struct WINTRUST_FILE_INFO_ * 0x0012f464
+0x000 cbStruct : 0x10
+0x004 pcwszFilePath : 0x00278680 -> 0x65
+0x008 hFile : (null)
+0x00c pgKnownSubject : (null)
0:000> ?? static_cast<int>(&WinTrustData)
int 1242152
0:000> ? WinTrustData
Evaluate expression: 1242152 = 0012f428
Value * Value Value / Value Value % Value | 乘法 除法 求余 |
0:000> ?? 4*2
int 8
0:000> ?? 4 / 2
int 2
0:000> ?? 4/2
int 2
0:000> ?? 4%2
int 0
Value + Value Value - Value | 加法 减法 |
0:000> ?? 3+1
int 4
0:000> ?? 3-1
int 2
Value << Value Value >> Value | 左移位 右移位 |
0:000> ?? 2>>1
int 1
0:000> ?? 2<<1
int 4
(type) Value sizeof value sizeof( type ) ++ LValue -- LValue ~ Value ! Value - Value + Value & LValue * Value | 类型强制转换(总会执行) 表达式大小 数据类型大小 加一(计算前) 减一(计算前) 按位反 逻辑非 一元运算符,负数 一元运算符,正数 数据类型的地址 解引用,取值 |
0:000> ?? sizeof 1
unsigned int 4
0:000> ?? sizeof(WinTrustData)
unsigned int 0x2c
Value < Value Value <= Value Value > Value Value >= Value | 小于(比较) 小于等于(比较) 大于(比较) 大于等于(比较) |
0:000> ?? 1<2
bool true
0:000> ?? 1<=2
bool true
0:000> ?? 1>2
bool false
0:000> ?? 1>=2
bool false
Value == Value Value != Value | 等于(比较) 不等于(比较) |
0:000> ?? 1==2
bool false
0:000> ?? 1!=2
bool true
Value & Value | 按位与 |
Value ^ Value | 按位异或(不同于 OR) |
Value | Value | 按位或 |
Value && Value | 逻辑与 |
Value || Value | 逻辑或 |
0:000> ?? 1&2
int 0
0:000> ?? 1^2
int 3
0:000> ?? 1|2
int 3
0:000> ?? 1&&2
bool true
0:000> ?? 1||2
bool true
0:000> ?? 0&&2
bool false
LValue = Value LValue *= Value LValue /= Value LValue %= Value LValue += Value LValue -= Value LValue <<= Value LValue >>= Value LValue &= Value LValue |= Value LValue ^= Value | 赋值 乘之后赋值 除之后赋值 求余之后赋值 加之后赋值 减之后赋值 左移位之后赋值 右移位之后赋值 与之后赋值 或之后赋值 异或之后赋值 |
Value ? Value : Value | 三元条件运算符 |
Value , Value | 逗号运算符。计算所有值,保留最右边的值 |
使用@@c++(...)可以显式指定括号中表达式所使用的语法规则,
C++ 表达式中的寄存器和伪寄存器
C++ 表达式中可以使用寄存器和伪寄存器。它们必须带一个 at符号(@) 前缀。
表达式求解器会自动执行正确的强制类型转换。实际的寄存器和整型值伪寄存器被强制转换为 ULONG64。所有地址被强制转换为 PUCHAR,$thread 被强制转换为 ETHREAD*,$proc被强制转换为 EPROCESS*,$teb 被强制转换为 TEB*,$peb 被强制转换为 PEB*。
不能使用赋值运算符或者带副作用的运算符修改寄存器或者伪寄存器。必须使用 r (Registers)命令修改它们的值。
C++表达式中的宏
可以使用下表中的宏。这些宏的作用和Microsoft Windows中定义的同名宏有相同的定义。(Windows的宏在Winnt.h中定义。)
宏 | 返回值 |
---|---|
#CONTAINING_RECORD(Address, Type, Field) | 给定该结构的类型和它包含的一个字段的地址,返回该结构实例的基地址。 |
#FIELD_OFFSET(Type, Field) | 返回一个已知的结构类型中某个命名字段的字节偏移。 |
#RTL_CONTAINS_FIELD (Struct, Size, Field) | 指出是否给定的字节大小包含了需要的字段。 |
#RTL_FIELD_SIZE(Type, Field) | 返回已知类型的结构中某个字段的大小,不需要给出该字段的类型。 |
#RTL_NUMBER_OF(Array) | 返回一个静态分配的数组的元素个数。 |
#RTL_SIZEOF_THROUGH_FIELD(Type, Field) | 返回已知类型的结构中,从基地址到包含指定字段的位置的大小。 |