地址和地址范围语法
可通过多种方式在调试器中指定地址。
地址通常是虚拟地址,除非文档明确指出另一种地址。 在用户模式下,调试器根据当前进程的页面目录解释虚拟地址。 在内核模式下,调试器根据进程上下文指定的进程的页目录解释虚拟地址。 还可以直接设置用户模式地址上下文。 有关用户模式地址上下文的详细信息,请参阅 .context(设置用户模式地址上下文)。
在 MASM 表达式中,可以使用 poi 运算符取消引用任何指针。 例如,如果地址 0x0000008e'ed57b108 处的指针指向地址位置 0x805287637256,则以下两个命令是等效的。
0:000> dd 805287637256
0:000> dd poi(000000bb`7ee23108)
显示内存地址示例
若要查看使用 poi 的示例,请确定线程环境块 (TEB) 的 CurrentLocale 偏移量。 使用 dx 命令显示 @$teb,这是一个 伪寄存器的示例,该寄存器包含常见地址,例如当前程序计数器位置。
0:000> dx @$teb
@$teb : 0x1483181000 [Type: _TEB *]
...
[+0x108] CurrentLocale : 0x409 [Type: unsigned long]
CurrentLocale 从 TEB 开始是 +0x108。 接下来确定该位置的内存地址。
0:000> ? @$teb + 0x108
Evaluate expression: 613867303176 = 0000008e`ed57b108
使用 poi 取消引用该地址,以查看它是否包含 0x409 的 CurrentLocale 值。
0:000> ? poi(0000008e`ed57b108)
Evaluate expression: 1033 = 00000000`00000409
在 C++ 调试器表达式中,指针的行为类似于 C++ 中的指针。 但是,数字被解释为整数。 如果必须服从实际数字,可能需要先进行强制转换,如以下示例所示。
若要尝试此操作,请使用 .expr 将表达式计算器设置为 C++ 。
0:000> .expr /s C++
Current expression evaluator: C++ - C++ source expressions
将表达式计算器设置为 C++ 后,可以使用 long 进行强制转换。
0:000> d *((long*)0x00000014`83181108 )
00000000`00000409 ???????? ???????? ???????? ????????
有关强制转换数值的详细信息,请参阅 C++ 数字和运算符。
如果表达式计算器设置为 c++,则可以用 @@masm()包装 poi 指针,使 MASM 表达式计算器只计算表达式的这一部分。
0:000> .expr /s c++
Current expression evaluator: C++ - C++ source expressions
0:000> ? @@masm(poi(00000078`267d7108))
Evaluate expression: 1033 = 00000000`00000409
有关两个表达式计算器的详细信息,请参阅计算表达式。
还可以通过指定原始源文件名称和行号来指明应用程序中的地址。 有关如何指定此信息的详细信息,请参阅源行语法。
地址范围
可以按一对地址或一个地址和对象计数指定地址范围。
若要按一对地址指定范围,请指定起始地址和结束地址。 例如,以下示例从地址 0x00001000 开始,范围为 8 个字节。
0x00001000 0x00001007
若要按地址和对象计数指定地址范围,请指定地址参数、字母 L(大写或小写)和值参数。 该地址指定起始地址。 该值指定要检查或显示的对象数。 对象的大小取决于命令。 例如,如果对象大小为 1 字节,则以下示例是从地址 0x00001000 开始的 8 个字节范围。
0x00001000 L8
但是,如果对象大小是双字(32 位或 4 个字节),则以下两个范围分别提供 8 字节范围。
0x00001000 0x00001007
0x00001000 L2
L 大小范围说明符
还有两种方法可以指定值(L大小范围说明符):
L?大小(带有问号)表示与 L大小相同,但 L 除外? 大小会删除调试器的自动范围限制。 通常,范围限制为 256 MB,因为更大的范围会出现排版错误。 如果要指定大于 256 MB 的范围,则必须使用 L? 大小 语法。
L- 大小(带连字符)指定长度大小范围,以给定地址结尾。 例如,80000000 L20 指定从 0x80000000 到 0x8000001F 的范围,800000000 L-20 指定从 0x7FFFFFE0 到 0x7FFFFFFF 的范围。
一些要求地址范围的命令接受单个地址作为参数。 在这种情况下,该命令使用一些默认对象计数来计算范围的大小。 通常,将地址范围作为最终参数的命令允许使用此语法。 有关每个命令的确切语法和默认范围大小,请参阅每个命令的参考主题。
搜索内存范围示例
首先,我们将使用 MASM 表达式计算器确定 rip 指令指针寄存器的地址。
0:000> ? @rip
Evaluate expression: 140720561719153 = 00007ffc`0f180771
然后,我们将使用 s (Search Memory) 命令从 00007ffc'0f180771 开始搜索 100000。 我们使用 L100000指定搜索范围。
0:000> s -a 00007ffc`0f180771 L100000 "ntdll"
00007ffc`0f1d48fa 6e 74 64 6c 6c 5c 6c 64-72 69 6e 69 74 2e 63 00 ntdll\ldrinit.c.
00007ffc`0f1d49c2 6e 74 64 6c 6c 5c 6c 64-72 6d 61 70 2e 63 00 00 ntdll\ldrmap.c..
00007ffc`0f1d4ab2 6e 74 64 6c 6c 5c 6c 64-72 72 65 64 69 72 65 63 ntdll\ldrredirec
00007ffc`0f1d4ad2 6e 74 64 6c 6c 5c 6c 64-72 73 6e 61 70 2e 63 00 ntdll\ldrsnap.c.
...
我们还可以使用两个内存地址来指定与此相同的范围。
0:000> s -a 0x00007ffc`0f180771 0x00007ffc`0f280771 "ntdll"
00007ffc`0f1d48fa 6e 74 64 6c 6c 5c 6c 64-72 69 6e 69 74 2e 63 00 ntdll\ldrinit.c.
00007ffc`0f1d49c2 6e 74 64 6c 6c 5c 6c 64-72 6d 61 70 2e 63 00 00 ntdll\ldrmap.c..
00007ffc`0f1d4ab2 6e 74 64 6c 6c 5c 6c 64-72 72 65 64 69 72 65 63 ntdll\ldrredirec
00007ffc`0f1d4ad2 6e 74 64 6c 6c 5c 6c 64-72 73 6e 61 70 2e 63 00 ntdll\ldrsnap.c.
...
最后,可以使用 L 长度参数在内存范围内向后搜索。
0:000> s -a 00007ffc`0f1d4ad2 L-100000 "ntdll"
00007ffc`0f1d48fa 6e 74 64 6c 6c 5c 6c 64-72 69 6e 69 74 2e 63 00 ntdll\ldrinit.c.
00007ffc`0f1d49c2 6e 74 64 6c 6c 5c 6c 64-72 6d 61 70 2e 63 00 00 ntdll\ldrmap.c..
00007ffc`0f1d4ab2 6e 74 64 6c 6c 5c 6c 64-72 72 65 64 69 72 65 63 ntdll\ldrredirec
取消汇编内存示例
此示例使用 u (unassemble) 命令和 L 参数取消汇编三个字节的代码。
0:000> u 00007ffc`0f1d48fa L3
ntdll!`string'+0xa:
00007ffc`0f1d48fa 6e outs dx,byte ptr [rsi]
00007ffc`0f1d48fb 7464 je ntdll!`string'+0x21 (00007ffc`0f1d4961)
00007ffc`0f1d48fd 6c ins byte ptr [rdi],dx
或者指定三个字节范围的内存以取消汇编,如下所示。
0:000> u 00007ffc`0f1d48fa 00007ffc`0f1d48fd
ntdll!`string'+0xa:
00007ffc`0f1d48fa 6e outs dx,byte ptr [rsi]
00007ffc`0f1d48fb 7464 je ntdll!`string'+0x21 (00007ffc`0f1d4961)
00007ffc`0f1d48fd 6c ins byte ptr [rdi],dx
地址模式和 Segment 支持
在基于 x86 的平台上,CDB 和 KD 支持以下寻址模式。 这些模式由前缀区分。
Prefix | 名称 | 地址类型 |
---|---|---|
% | flat | 32 位地址(也指向 32 位段的 16 位选择器)和 64 位系统上的 64 位地址。 |
& | 虚拟 86 | 实际模式地址。 仅限基于 x86。 |
# | plain | 实际模式地址。 仅限基于 x86。 |
普通模式和虚拟 86 模式之间的区别是,普通 16 位地址使用段值作为选择器并查找段描述符。 但虚拟 86 地址不使用选择器,而是直接映射到较低的 1 MB。
如果通过非当前默认模式的寻址模式访问内存,则可以使用地址模式前缀覆盖当前地址模式。
地址参数
地址参数指定变量和函数的位置。 下表介绍了可在 CDB 和 KD 中使用的各种地址的语法和含义。
语法 | 含义 |
---|---|
offset |
虚拟内存空间中的绝对地址,其类型与当前执行模式相对应。 例如,如果当前执行模式为 16 位,则偏移量为 16 位。 如果执行模式为 32 位分段,则偏移量为 32 位分段。 |
&[[ segment:]] 偏移量 |
真实地址。 基于 x86 和基于 x64。 |
%segment:[[ offset]] |
分段的 32 位或 64 位地址。 基于 x86 和基于 x64。 |
%[[ offset]] |
虚拟内存空间中的绝对地址(32 位或 64 位)。 基于 x86 和基于 x64。 |
name[[ +|− ]] offset |
平坦 32 位或 64 位地址。 name 可以是任何符号。 offset 指定偏移量。 此偏移量可以是其前缀指示的任何地址模式。 没有前缀指定默认模式地址。 可以将偏移量指定为正值 (+) 或负值 (−)。 |
使用 dg (显示选择器)命令查看段描述符信息。
另请参阅
若要显示有关内存的信息,请使用 !address 命令。
若要搜索内存,请使用 s(搜索内存)命令。
若要显示内存的内容,请使用 d、da、db、dc、dd、dD、df、dp、dq、du、dw (显示内存) 命令。
有关如何使用内存窗口查看和编辑内存的信息,请参阅使用内存窗口。